diff --git a/.circleci/config.yml b/.circleci/config.yml
index ca910568..c3674a9e 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -36,12 +36,15 @@ jobs:
- v1-SRB2-APT
- run:
name: Install SDK
- command: apt-get -qq -y install git build-essential nasm libpng12-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 gettext ccache wget gcc-multilib upx
+ command: apt-get -qq -y --no-install-recommends install git build-essential nasm libpng12-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 gettext ccache wget gcc-multilib upx openssh-client
- save_cache:
key: v1-SRB2-APT
paths:
- /var/cache/apt/archives
- checkout
+ - run:
+ name: Compile without network support
+ command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1
- run:
name: Clean build
command: make -C src LINUX=1 clean
diff --git a/.travis.yml b/.travis.yml
index e9c8d0d7..3081caa6 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -26,6 +26,7 @@ matrix:
- p7zip-full
- gcc-4.4
compiler: gcc-4.4
+ env: GCC44=1
if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/
#gcc-4.4 (Ubuntu/Linaro 4.4.7-8ubuntu1) 4.4.7
- os: linux
@@ -39,6 +40,7 @@ matrix:
- p7zip-full
- gcc-4.6
compiler: gcc-4.6
+ env: GCC46=1
if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/
#gcc-4.6 (Ubuntu/Linaro 4.6.4-6ubuntu2) 4.6.4
- os: linux
@@ -52,10 +54,12 @@ matrix:
- p7zip-full
- gcc-4.7
compiler: gcc-4.7
+ env: GCC47=1
if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/
#gcc-4.7
- os: linux
compiler: gcc
+ env: GCC48=1
if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/
#gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
- os: linux
@@ -71,6 +75,7 @@ matrix:
- p7zip-full
- gcc-4.8
compiler: gcc-4.8
+ env: GCC48=1
if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/
#gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
- os: linux
@@ -86,7 +91,7 @@ matrix:
- p7zip-full
- gcc-7
compiler: gcc-7
- env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wno-implicit-fallthrough"
+ env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wno-implicit-fallthrough" GCC72=1
if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/
#gcc-7 (Ubuntu 7.2.0-1ubuntu1~14.04) 7.2.0 20170802
- os: linux
@@ -102,7 +107,7 @@ matrix:
- p7zip-full
- gcc-8
compiler: gcc-8
- env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wno-implicit-fallthrough -Wno-error=format-overflow"
+ env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wno-implicit-fallthrough -Wno-error=format-overflow" GCC81=1
if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/
#gcc-8 (Ubuntu 7.2.0-1ubuntu1~14.04) 8.1.0
- os: linux
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 43142386..3ce321e2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
# DO NOT CHANGE THIS SRB2 STRING! Some variable names depend on this string.
# Version change is fine.
project(SRB2
- VERSION 1.0.2
+ VERSION 1.0.4
LANGUAGES C)
if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})
diff --git a/SRB2.cbp b/SRB2.cbp
index 2a1eb87b..acdc61c7 100644
--- a/SRB2.cbp
+++ b/SRB2.cbp
@@ -1545,6 +1545,10 @@ HW3SOUND for 3D hardware sound support
+
+
+
+
diff --git a/appveyor.yml b/appveyor.yml
index e7ce1b2f..10b65891 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 1.0.2.{branch}-{build}
+version: 1.0.4.{branch}-{build}
os: MinGW
environment:
@@ -29,7 +29,7 @@ environment:
##############################
DPL_ENABLED: 0
DPL_TAG_ENABLED: 0
- DPL_INSTALLER_NAME: srb2kart-v102
+ DPL_INSTALLER_NAME: srb2kart-v104
# Asset handling is barebones vs. Travis Deployer. We operate on 7z only.
# Include the README files and the OpenGL batch in the main and patch archives.
# The x86/x64 archives contain the DLL binaries.
diff --git a/assets/HISTORY.txt b/assets/HISTORY.txt
index dfa06f2c..5c7fe3b0 100644
--- a/assets/HISTORY.txt
+++ b/assets/HISTORY.txt
@@ -129,7 +129,7 @@ Oni: Sev, Sal, and toast were the most unexpected things to ever happen to this
[Oni wiping sweat off his brow] Things only got more drastically revamped… very very rapidly.
- The Mario aesthetic was entirely tossed out, as Sal was willing to work with me night and day on redoing most of everything about items… and then sounds. My power level for sprites massively jumped during TD development, so I decided to take it upon myself to do almost everything. They’re such friendly and cooperative coders that I can’t help but push a little harder than I used to (I was WAY lazier before they got here) to keep up.
+ The Mario aesthetic was entirely tossed out, as Sal was willing to work with me night and day on redoing most of everything about items… and then sounds. My power level for sprites massively jumped during TD development, so I decided to take it upon myself to do almost everything. They’re such friendly and cooperative coders that I can’t help but push a little harder than I used to (I was WAY lazier before they got here) to keep up.
diff --git a/libs/dll-binaries/i686/libgme.dll b/libs/dll-binaries/i686/libgme.dll
index ddf8b0d8..9a31bc4d 100644
Binary files a/libs/dll-binaries/i686/libgme.dll and b/libs/dll-binaries/i686/libgme.dll differ
diff --git a/libs/dll-binaries/x86_64/libgme.dll b/libs/dll-binaries/x86_64/libgme.dll
index 2ba99450..598c2d71 100644
Binary files a/libs/dll-binaries/x86_64/libgme.dll and b/libs/dll-binaries/x86_64/libgme.dll differ
diff --git a/libs/gme/CMakeLists.txt b/libs/gme/CMakeLists.txt
index 8beee872..392b0185 100644
--- a/libs/gme/CMakeLists.txt
+++ b/libs/gme/CMakeLists.txt
@@ -4,7 +4,7 @@ project(libgme)
include (CheckCXXCompilerFlag)
# When version is changed, also change the one in gme/gme.h to match
-set(GME_VERSION 0.6.0 CACHE INTERNAL "libgme Version")
+set(GME_VERSION 0.6.2 CACHE INTERNAL "libgme Version")
# 2.6+ always assumes FATAL_ERROR, but 2.4 and below don't.
# Of course, 2.4 might work, in which case you're welcome to drop
@@ -57,6 +57,8 @@ if (USE_GME_NSFE AND NOT USE_GME_NSF)
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE)
endif()
+option(BUILD_SHARED_LIBS "Build shared library (set to OFF for static library)" ON)
+
# Check for GCC "visibility" support.
if (CMAKE_COMPILER_IS_GNUCXX)
check_cxx_compiler_flag (-fvisibility=hidden __LIBGME_TEST_VISIBILITY)
@@ -79,10 +81,10 @@ if (CMAKE_COMPILER_IS_GNUCXX)
endif()
endif()
endif() # test visibility
-endif (CMAKE_COMPILER_IS_GNUCXX)
-# Cache this result
-set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility")
+ # Cache this result
+ set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility")
+endif (CMAKE_COMPILER_IS_GNUCXX)
# Shared library defined here
add_subdirectory(gme)
diff --git a/libs/gme/changes.txt b/libs/gme/changes.txt
index 62391ebb..034ba482 100644
--- a/libs/gme/changes.txt
+++ b/libs/gme/changes.txt
@@ -1,262 +1,5 @@
Game_Music_Emu Change Log
-------------------------
-Game_Music_Emu 0.6.0
---------------------
-
-- Note: A 0.5.6 release was referenced but never tagged or packaged.
-
-- SPC improvements:
- - Switched to newer snes_spc 0.9.0 for SPC emulation. Uses fast DSP.
- - Fixed Spc_Emu::gain().
- - Fixed support for files <0x10200 bytes.
-
-- Other bugfixes:
- - Fixed a couple of GBS bugs, one involving access of memory after
- realloc.
- - Blip_Buffer works on systems where 'double' is a single-precision
- floating-point type.
- - Fix uninitialized buffer size in dual_resampler.
- - Compilation warnings squashed out as of clang 3.3-pre and gcc 4.7.2.
-
-- API changes/additions:
- - Removed documentation of C++ interface, as the C interface in gme.h is
- the only supported one.
- - Added gme_enable_accuracy() for enabling more accurate sound emulation
- options (currently affects SPC only).
-
-- Build system improvements:
- - Add pkg_config support.
- - Fix build on case-insensitive systems.
- - Allow for install on Cygwin.
- - Fix install on multilib systems, such as many 64-bit distros (CMake must
- be able to figure out your system's libsuffix, if any).
- - C++ implementation symbols are not leaked into the resultant library
- file (requires symbol visibility support).
-
-- Sample player improvements:
- - Can toggle fast/accurate emulation (with the 'A' key).
-
-Game_Music_Emu 0.5.5
---------------------
-- CMake build support has been added. You can build Game_Music_Emu as
-a shared library and install it so that you do not have to include your
-own copy if you know libgme will be present on your target system.
-Requires CMake 2.6 or higher.
-
-
-Game_Music_Emu 0.5.2
---------------------
-- *TONS* of changes and improvements. You should re-read the new header
-files and documentation as the changes will allow you to simplify your
-code a lot (it might even be simpler to just rewrite it). Existing code
-should continue to work without changes in most cases (see Deprecated
-features in gme.txt).
-
-- New file formats: AY, HES, KSS, SAP, NSFE
-
-- All-new comprehensive C interface (also usable from C++). Simplifies
-many things, especially file loading, and brings everything together in
-one header file (gme.h).
-
-- Information tags and track names and times can be accessed for all
-game music formats
-
-- New features supported by all emulators: end of track fading,
-automatic silence detection, adjustable song tempo, seek to new time in
-track
-
-- Updated mini player example to support track names and times, echo,
-tempo, and channel muting, and added visual waveform display
-
-- Improved configuration to use blargg_config.h, which you can modify
-and keep when you update to a newer libary version. Includes flag for
-library to automatically handle gzipped files using zlib (so you don't
-need to use Gzip_File_Reader anymore).
-
-- GBS: Fixed wave channel to not reset waveform when APU is powered off
-(affected Garfield). Also improved invalid bank selection (affected Game
-& Watch and others).
-
-- VGM: Added support for alternate noise shifter register
-configurations, used by other systems like the BBC Micro.
-
-- SPC: Removed IPL ROM dump from emulator, as none of the SPC files I
-scanned needed it, and an SPC file can include a copy if necessary. Also
-re-enabled supposed clamping in gaussian interpolation between the third
-and fourth lookups, though I don't know whether it matters
-
-- Added Music_Emu::load_mem() to use music data already in memory
-(without copying it)
-
-- Added Music_Emu::warning(), which reports minor problems when loading
-and playing a music file
-
-- Added Music_Emu::set_gain() for uniform adjustment of gain. Can only
-be set during initialization, so not useful as a general volume control.
-
-- Added custom operator new to ensure that no exceptions are thrown in
-the library (I'd use std::nothrow if it were part of pre-ISO (ARM) C++)
-
-- Added BLIP_BUFFER_FAST flag to blargg_config.h to use a lower quality
-bandlimited synthesis in "classic" emulators, which might help
-performance on ancient processors (measure first!). Don't use this
-unless absolutely necessary, as quality suffers.
-
-- Improved performance a bit for x86 platforms
-
-- Text files now in DOS newline format so they will open in Notepad
-properly
-
-- Removed requirement that file header structures not have any padding
-added to the end
-
-- Fixed common bug in all CPU emulators where negative program counter
-could crash emulator (occurred during a negative branch from the
-beginning of memory). Also fixed related bug in Z80 emulator for
-IX/IY+displacement mode.
-
-- Eliminated all warnings when compiling on gcc 4.0. The following
-generates no diagnostics:
-
- gcc -S gme/*.cpp -o /dev/null -ansi -fno-gnu-keywords
- -fno-nonansi-builtins -pedantic -W -Wabi -Wall -Wcast-align
- -Wcast-qual -Wchar-subscripts -Wdisabled-optimization -Werror
- -Winline -Wlong-long -Wmultichar -Winvalid-offsetof
- -Wnon-virtual-dtor -Woverloaded-virtual -Wparentheses
- -Wpointer-arith -Wredundant-decls -Wreorder -Wsign-compare
- -Wsign-promo -Wunknown-pragmas -Wwrite-strings
-
-
-Game_Music_Emu 0.3.0
---------------------
-- Added more demos, including music player using the SDL multimedia
-library for sound, and improved documentation
-
-- All: Improved interface to emulators to allow simpler setup and
-loading. Instead of various init() functions, all now support
-set_sample_rate( long rate ) and load( const char* file_path ).
-
-- All: Removed error return from start_track() and play(), and added
-error_count() to get the total number of emulation errors since the
-track was last started. See demos for examples of new usage.
-
-- All: Fixed mute_voices() muting to be preserved after loading files
-and starting tracks, instead of being cleared as it was whenever a track
-was started
-
-- VGM: Rewrote Vgm_Emu to support Sega Genesis/Mega Drive FM sound at
-any sample rate with optional FM oversampling, support for alternate
-YM2612 sound cores, and support for optional YM2413
-
-- VGM: Added tempo control, useful for slowing 60Hz NTSC Sega Genesis
-music to 50Hz PAL
-
-- VGM: Removed Vgm_Emu::track_data(), since I realized that this
-information is already present in the VGM header (oops!)
-
-- GYM: Changed Gym_Emu::track_length() operation (see Gym_Emu.h)
-
-- NSF: Added support for Sunsoft FME-7 sound chip used by Gimmick
-soundtrack
-
-- NSF: Fixed Namco 106 problems with Final Lap and others
-
-- Moved library sources to gme/ directory to reduce clutter, and merged
-boost/ functionality into blargg_common.h
-
-- Added Gzip_File_Reader for transparently using gzipped files
-
-
-Game_Music_Emu 0.2.4
---------------------
-- Created a discussion forum for problems and feedback:
-http://groups-beta.google.com/group/blargg-sound-libs
-
-- Changed error return value of Blip_Buffer::sample_rate() (also for
-Stereo_Buffer, Effects_Buffer, etc.) to blargg_err_t (defined in
-blargg_common.h), to make error reporting consistent with other
-functions. This means the "no error" return value is the opposite of
-what it was before, which will break current code which checks the error
-return value:
-
- // current code (broken)
- if ( !buf.sample_rate( samples_per_sec ) )
- out_of_memory();
-
- // quick-and-dirty fix (just remove the ! operation)
- if ( buf.sample_rate( samples_per_sec ) )
- out_of_memory();
-
- // proper fix
- blargg_err_t error = buf.sample_rate( samples_per_sec );
- if ( error )
- report_error( error );
-
-- Implemented workaround for MSVC++ 6 compiler limitations, allowing it
-to work on that compiler again
-
-- Added sample clamping to avoid wrap-around at high volumes, allowing
-higher volume with little distortion
-
-- Added to-do list and design notes
-
-- Added Music_Emu::skip( long sample_count ) to skip ahead in current
-track
-
-- Added Gym_Emu::track_length() and Vgm_Emu::track_length() for
-determining the length of non-looped GYM and VGM files
-
-- Partially implemented DMC non-linearity when its value is directly set
-using $4011, which reduces previously over-emphasized "popping" of
-percussion on some games (TMNT II in particular)
-
-- Fixed Fir_Resampler, used for SPC and GYM playback (was incorrectly
-using abs() instead of fabs()...argh)
-
-- Fixed SPC emulation bugs: eliminated clicks in Plok! soundtrack and
-now stops sample slightly earlier than the end, as the SNES does. Fixed
-a totally broken CPU addressing mode.
-
-- Fixed Konami VRC6 saw wave (was very broken before). Now VRC6 music
-sounds decent
-
-- Fixed a minor GBS emulation bug
-
-- Fixed GYM loop point bug when track was restarted before loop point
-had been reached
-
-- Made default GBS frequency equalization less muffled
-
-- Added pseudo-surround effect removal for SPC files
-
-- Added Music_Emu::voice_names() which returns names for each voice.
-
-- Added BLARGG_SOURCE_BEGIN which allows custom compiler options to be
-easily set for library sources
-
-- Changed assignment of expansion sound chips in Nsf_Emu to be spread
-more evenly when using Effects_Buffer
-
-- Changed 'size_t' values in Blip_Buffer interface to 'long'
-
-- Changed demo to generate a WAVE sound file rather than an AIFF file
-
-
-Game_Music_Emu 0.2.0
---------------------
-- Redid framework and rewrote/cleaned up emulators
-
-- Changed licensing to GNU Lesser General Public License (LGPL)
-
-- Added Sega Genesis GYM and Super Nintendo SPC emulators
-
-- Added Namco-106 and Konami VRC6 sound chip support to NSF emulator
-
-- Eliminated use of static mutable data in emulators, allowing
-multi-instance safety
-
-
-Game_Music_Emu 0.1.0
---------------------
-- First release
+Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1)
+for the accurate change log.
diff --git a/libs/gme/demo/basics.c b/libs/gme/demo/basics.c
index 55178251..741574af 100644
--- a/libs/gme/demo/basics.c
+++ b/libs/gme/demo/basics.c
@@ -1,7 +1,5 @@
/* C example that opens a game music file and records 10 seconds to "out.wav" */
-static char filename [] = "test.nsf"; /* opens this file (can be any music type) */
-
#include "gme/gme.h"
#include "Wave_Writer.h" /* wave_ functions for writing sound file */
@@ -10,10 +8,15 @@ static char filename [] = "test.nsf"; /* opens this file (can be any music type)
void handle_error( const char* str );
-int main()
+int main(int argc, char *argv[])
{
+ const char *filename = "test.nsf"; /* Default file to open */
+ if ( argc >= 2 )
+ filename = argv[1];
+
long sample_rate = 44100; /* number of samples per second */
- int track = 0; /* index of track to play (0 = first) */
+ /* index of track to play (0 = first) */
+ int track = argc >= 3 ? atoi(argv[2]) : 0;
/* Open music file in new emulator */
Music_Emu* emu;
diff --git a/libs/gme/demo/cpp_basics.cpp b/libs/gme/demo/cpp_basics.cpp
index 53fab418..5222fe27 100644
--- a/libs/gme/demo/cpp_basics.cpp
+++ b/libs/gme/demo/cpp_basics.cpp
@@ -1,7 +1,5 @@
// C++ example that opens a game music file and records 10 seconds to "out.wav"
-static char filename [] = "test.nsf"; /* opens this file (can be any music type) */
-
#include "gme/Music_Emu.h"
#include "Wave_Writer.h"
@@ -10,10 +8,15 @@ static char filename [] = "test.nsf"; /* opens this file (can be any music type)
void handle_error( const char* str );
-int main()
+int main(int argc, char *argv[])
{
+ const char *filename = "test.nsf"; /* Default file to open */
+ if ( argc >= 2 )
+ filename = argv[1];
+
long sample_rate = 44100; // number of samples per second
- int track = 0; // index of track to play (0 = first)
+ // index of track to play (0 = first)
+ int track = argc >= 3 ? atoi(argv[2]) : 0;
// Determine file type
gme_type_t file_type;
diff --git a/libs/gme/gme.txt b/libs/gme/gme.txt
index d9a2452c..5a7d2f56 100644
--- a/libs/gme/gme.txt
+++ b/libs/gme/gme.txt
@@ -1,10 +1,10 @@
-Game_Music_Emu 0.6.0
+Game_Music_Emu 0.6.2
--------------------
-Author : Shay Green
-Website: http://www.slack.net/~ant/libs/
-Forum : http://groups.google.com/group/blargg-sound-libs
-Source : https://code.google.com/p/game-music-emu/
-License: GNU Lesser General Public License (LGPL)
+Author : Shay Green
+Maintainer : Michael Pyne
+Website : https://bitbucket.org/mpyne/game-music-emu/
+Source : https://bitbucket.org/mpyne/game-music-emu/
+License : GNU Lesser General Public License (LGPL), see LICENSE.txt
Contents
--------
diff --git a/libs/gme/gme/CMakeLists.txt b/libs/gme/gme/CMakeLists.txt
index 3c6464fc..534be8a8 100644
--- a/libs/gme/gme/CMakeLists.txt
+++ b/libs/gme/gme/CMakeLists.txt
@@ -143,7 +143,7 @@ add_definitions(-DBLARGG_BUILD_DLL)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# Add library to be compiled.
-add_library(gme SHARED ${libgme_SRCS})
+add_library(gme ${libgme_SRCS})
# The version is the release. The "soversion" is the API version. As long
# as only build fixes are performed (i.e. no backwards-incompatible changes
@@ -159,4 +159,4 @@ install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX}
ARCHIVE DESTINATION lib) # DLL platforms
install(FILES ${EXPORTED_HEADERS} DESTINATION include/gme)
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib/pkgconfig)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig)
diff --git a/libs/gme/gme/Data_Reader.cpp b/libs/gme/gme/Data_Reader.cpp
index 5bbfbf55..f18928f4 100644
--- a/libs/gme/gme/Data_Reader.cpp
+++ b/libs/gme/gme/Data_Reader.cpp
@@ -22,8 +22,13 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
const char Data_Reader::eof_error [] = "Unexpected end of file";
+#define RETURN_VALIDITY_CHECK( cond ) \
+ do { if ( unlikely( !(cond) ) ) return "Corrupt file"; } while(0)
+
blargg_err_t Data_Reader::read( void* p, long s )
{
+ RETURN_VALIDITY_CHECK( s > 0 );
+
long result = read_avail( p, s );
if ( result != s )
{
@@ -38,6 +43,8 @@ blargg_err_t Data_Reader::read( void* p, long s )
blargg_err_t Data_Reader::skip( long count )
{
+ RETURN_VALIDITY_CHECK( count >= 0 );
+
char buf [512];
while ( count )
{
@@ -54,7 +61,8 @@ long File_Reader::remain() const { return size() - tell(); }
blargg_err_t File_Reader::skip( long n )
{
- assert( n >= 0 );
+ RETURN_VALIDITY_CHECK( n >= 0 );
+
if ( !n )
return 0;
return seek( tell() + n );
@@ -67,13 +75,14 @@ Subset_Reader::Subset_Reader( Data_Reader* dr, long size )
in = dr;
remain_ = dr->remain();
if ( remain_ > size )
- remain_ = size;
+ remain_ = max( 0l, size );
}
long Subset_Reader::remain() const { return remain_; }
long Subset_Reader::read_avail( void* p, long s )
{
+ s = max( 0l, s );
if ( s > remain_ )
s = remain_;
remain_ -= s;
@@ -85,7 +94,7 @@ long Subset_Reader::read_avail( void* p, long s )
Remaining_Reader::Remaining_Reader( void const* h, long size, Data_Reader* r )
{
header = (char const*) h;
- header_end = header + size;
+ header_end = header + max( 0l, size );
in = r;
}
@@ -93,6 +102,7 @@ long Remaining_Reader::remain() const { return header_end - header + in->remain(
long Remaining_Reader::read_first( void* out, long count )
{
+ count = max( 0l, count );
long first = header_end - header;
if ( first )
{
@@ -107,8 +117,9 @@ long Remaining_Reader::read_first( void* out, long count )
long Remaining_Reader::read_avail( void* out, long count )
{
+ count = max( 0l, count );
long first = read_first( out, count );
- long second = count - first;
+ long second = max( 0l, count - first );
if ( second )
{
second = in->read_avail( (char*) out + first, second );
@@ -120,8 +131,9 @@ long Remaining_Reader::read_avail( void* out, long count )
blargg_err_t Remaining_Reader::read( void* out, long count )
{
+ count = max( 0l, count );
long first = read_first( out, count );
- long second = count - first;
+ long second = max( 0l, count - first );
if ( !second )
return 0;
return in->read( (char*) out + first, second );
@@ -131,7 +143,7 @@ blargg_err_t Remaining_Reader::read( void* out, long count )
Mem_File_Reader::Mem_File_Reader( const void* p, long s ) :
begin( (const char*) p ),
- size_( s )
+ size_( max( 0l, s ) )
{
pos = 0;
}
@@ -141,6 +153,7 @@ long Mem_File_Reader::size() const { return size_; }
long Mem_File_Reader::read_avail( void* p, long s )
{
long r = remain();
+ s = max( 0l, s );
if ( s > r )
s = r;
memcpy( p, begin + pos, s );
@@ -152,6 +165,7 @@ long Mem_File_Reader::tell() const { return pos; }
blargg_err_t Mem_File_Reader::seek( long n )
{
+ RETURN_VALIDITY_CHECK( n >= 0 );
if ( n > size_ )
return eof_error;
pos = n;
@@ -164,7 +178,7 @@ Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) :
callback( c ),
data( d )
{
- remain_ = size;
+ remain_ = max( 0l, size );
}
long Callback_Reader::remain() const { return remain_; }
@@ -173,13 +187,14 @@ long Callback_Reader::read_avail( void* out, long count )
{
if ( count > remain_ )
count = remain_;
- if ( Callback_Reader::read( out, count ) )
+ if ( count < 0 || Callback_Reader::read( out, count ) )
count = -1;
return count;
}
blargg_err_t Callback_Reader::read( void* out, long count )
{
+ RETURN_VALIDITY_CHECK( count >= 0 );
if ( count > remain_ )
return eof_error;
return callback( data, out, count );
@@ -210,11 +225,12 @@ long Std_File_Reader::size() const
long Std_File_Reader::read_avail( void* p, long s )
{
- return fread( p, 1, s, (FILE*) file_ );
+ return fread( p, 1, max( 0l, s ), (FILE*) file_ );
}
blargg_err_t Std_File_Reader::read( void* p, long s )
{
+ RETURN_VALIDITY_CHECK( s > 0 );
if ( s == (long) fread( p, 1, s, (FILE*) file_ ) )
return 0;
if ( feof( (FILE*) file_ ) )
diff --git a/libs/gme/gme/Data_Reader.h b/libs/gme/gme/Data_Reader.h
index acf571f6..6c22b678 100644
--- a/libs/gme/gme/Data_Reader.h
+++ b/libs/gme/gme/Data_Reader.h
@@ -129,6 +129,8 @@ private:
};
#ifdef HAVE_ZLIB_H
+#include
+
// Gzip compressed file reader
class Gzip_File_Reader : public File_Reader {
public:
@@ -143,7 +145,7 @@ public:
long tell() const;
blargg_err_t seek( long );
private:
- void* file_;
+ gzFile file_;
long size_;
};
#endif
diff --git a/libs/gme/gme/Music_Emu.cpp b/libs/gme/gme/Music_Emu.cpp
index 30b25dcf..942e86e2 100644
--- a/libs/gme/gme/Music_Emu.cpp
+++ b/libs/gme/gme/Music_Emu.cpp
@@ -178,6 +178,11 @@ blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const
return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo;
}
+long Music_Emu::tell_samples() const
+{
+ return out_time;
+}
+
long Music_Emu::tell() const
{
blargg_long rate = sample_rate() * stereo;
@@ -185,14 +190,18 @@ long Music_Emu::tell() const
return sec * 1000 + (out_time - sec * rate) * 1000 / rate;
}
-blargg_err_t Music_Emu::seek( long msec )
+blargg_err_t Music_Emu::seek_samples( long time )
{
- blargg_long time = msec_to_samples( msec );
if ( time < out_time )
RETURN_ERR( start_track( current_track_ ) );
return skip( time - out_time );
}
+blargg_err_t Music_Emu::seek( long msec )
+{
+ return seek_samples( msec_to_samples( msec ) );
+}
+
blargg_err_t Music_Emu::skip( long count )
{
require( current_track() >= 0 ); // start_track() must have been called already
diff --git a/libs/gme/gme/Music_Emu.h b/libs/gme/gme/Music_Emu.h
index b96f4b61..d98f7ce7 100644
--- a/libs/gme/gme/Music_Emu.h
+++ b/libs/gme/gme/Music_Emu.h
@@ -41,9 +41,15 @@ public:
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
long tell() const;
+ // Number of samples generated since beginning of track
+ long tell_samples() const;
+
// Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t seek( long msec );
+ // Equivalent to restarting track then skipping n samples
+ blargg_err_t seek_samples( long n );
+
// Skip n samples
blargg_err_t skip( long n );
diff --git a/libs/gme/gme/Nsfe_Emu.cpp b/libs/gme/gme/Nsfe_Emu.cpp
index 824a1a24..55ac4688 100644
--- a/libs/gme/gme/Nsfe_Emu.cpp
+++ b/libs/gme/gme/Nsfe_Emu.cpp
@@ -134,6 +134,9 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
RETURN_ERR( in.read( block_header, sizeof block_header ) );
blargg_long size = get_le32( block_header [0] );
blargg_long tag = get_le32( block_header [1] );
+
+ if ( size <= 0 )
+ return "Corrupt file";
//debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) );
diff --git a/libs/gme/gme/Spc_Cpu.cpp b/libs/gme/gme/Spc_Cpu.cpp
index 90f60ed2..19aae113 100644
--- a/libs/gme/gme/Spc_Cpu.cpp
+++ b/libs/gme/gme/Spc_Cpu.cpp
@@ -433,9 +433,7 @@ void Snes_Spc::cpu_write( int data, int addr, rel_time_t time )
#endif
// Registers other than $F2 and $F4-$F7
- //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
- // TODO: this is a bit on the fragile side
- if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
+ if ( reg != 2 && (reg < 4 || reg > 7) ) // 36%
cpu_write_smp_reg( data, time, reg );
}
// High mem/address wrap-around
diff --git a/libs/gme/gme/Spc_Cpu.h b/libs/gme/gme/Spc_Cpu.h
index 4742e099..10c24509 100644
--- a/libs/gme/gme/Spc_Cpu.h
+++ b/libs/gme/gme/Spc_Cpu.h
@@ -76,8 +76,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
// TODO: remove non-wrapping versions?
#define SPC_NO_SP_WRAPAROUND 0
-#define SET_SP( v ) (sp = ram + 0x101 + (v))
-#define GET_SP() (sp - 0x101 - ram)
+#define SET_SP( v ) (sp = ram + 0x101 + ((uint8_t) v))
+#define GET_SP() (uint8_t (sp - 0x101 - ram))
#if SPC_NO_SP_WRAPAROUND
#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
@@ -485,7 +485,7 @@ loop:
case 0xAF: // MOV (X)+,A
WRITE_DP( 0, x, a + no_read_before_write );
- x++;
+ x = (uint8_t) (x + 1);
goto loop;
// 5. 8-BIT LOGIC OPERATION COMMANDS
@@ -808,7 +808,7 @@ loop:
unsigned temp = y * a;
a = (uint8_t) temp;
nz = ((temp >> 1) | temp) & 0x7F;
- y = temp >> 8;
+ y = (uint8_t) (temp >> 8);
nz |= y;
goto loop;
}
@@ -838,6 +838,7 @@ loop:
nz = (uint8_t) a;
a = (uint8_t) a;
+ y = (uint8_t) y;
goto loop;
}
@@ -1004,7 +1005,7 @@ loop:
case 0x7F: // RET1
temp = *sp;
SET_PC( GET_LE16( sp + 1 ) );
- sp += 3;
+ SET_SP( GET_SP() + 3 );
goto set_psw;
case 0x8E: // POP PSW
POP( temp );
diff --git a/libs/gme/gme/blargg_source.h b/libs/gme/gme/blargg_source.h
index b011777a..b65afd30 100644
--- a/libs/gme/gme/blargg_source.h
+++ b/libs/gme/gme/blargg_source.h
@@ -18,6 +18,19 @@ all other #include lines. */
#undef require
#define require( expr ) assert( expr )
+// Use to provide hints to compiler for optimized code layout in situations where we
+// can almost always expect a conditional to go one way or the other. Should only be
+// used in situations where an unexpected branch is truly exceptional though!
+#undef likely
+#undef unlikely
+#ifdef __GNUC__
+ #define likely( x ) __builtin_expect(x, 1)
+ #define unlikely( x ) __builtin_expect(x, 0)
+#else
+ #define likely( x ) (x)
+ #define unlikely( x ) (x)
+#endif
+
// Like printf() except output goes to debug log file. Might be defined to do
// nothing (not even evaluate its arguments).
// void debug_printf( const char* format, ... );
diff --git a/libs/gme/gme/gme.cpp b/libs/gme/gme/gme.cpp
index c05f25eb..47709840 100644
--- a/libs/gme/gme/gme.cpp
+++ b/libs/gme/gme/gme.cpp
@@ -337,7 +337,9 @@ BLARGG_EXPORT gme_err_t gme_play ( Music_Emu* me, int n, short* p )
BLARGG_EXPORT void gme_set_fade ( Music_Emu* me, int start_msec ) { me->set_fade( start_msec ); }
BLARGG_EXPORT int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); }
BLARGG_EXPORT int gme_tell ( Music_Emu const* me ) { return me->tell(); }
+BLARGG_EXPORT int gme_tell_samples ( Music_Emu const* me ) { return me->tell_samples(); }
BLARGG_EXPORT gme_err_t gme_seek ( Music_Emu* me, int msec ) { return me->seek( msec ); }
+BLARGG_EXPORT gme_err_t gme_seek_samples ( Music_Emu* me, int n ) { return me->seek_samples( n ); }
BLARGG_EXPORT int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); }
BLARGG_EXPORT void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); }
BLARGG_EXPORT void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); }
diff --git a/libs/gme/gme/gme.h b/libs/gme/gme/gme.h
index 1f2a2d15..cb07061b 100644
--- a/libs/gme/gme/gme.h
+++ b/libs/gme/gme/gme.h
@@ -1,6 +1,6 @@
/* Game music emulator library C interface (also usable from C++) */
-/* Game_Music_Emu 0.6.0 */
+/* Game_Music_Emu 0.6.1 */
#ifndef GME_H
#define GME_H
@@ -8,7 +8,7 @@
extern "C" {
#endif
-#define GME_VERSION 0x000600 /* 1 byte major, 1 byte minor, 1 byte patch-level */
+#define GME_VERSION 0x000601 /* 1 byte major, 1 byte minor, 1 byte patch-level */
/* Error string returned by library functions, or NULL if no error (success) */
typedef const char* gme_err_t;
@@ -47,9 +47,15 @@ int gme_track_ended( Music_Emu const* );
/* Number of milliseconds (1000 = one second) played since beginning of track */
int gme_tell( Music_Emu const* );
+/* Number of samples generated since beginning of track */
+int gme_tell_samples( Music_Emu const* );
+
/* Seek to new time in track. Seeking backwards or far forward can take a while. */
gme_err_t gme_seek( Music_Emu*, int msec );
+/* Equivalent to restarting track then skipping n samples */
+gme_err_t gme_seek_samples( Music_Emu*, int n );
+
/******** Informational ********/
diff --git a/libs/gme/gme/libgme.pc.in b/libs/gme/gme/libgme.pc.in
index 4f420d9e..49fd5b1d 100644
--- a/libs/gme/gme/libgme.pc.in
+++ b/libs/gme/gme/libgme.pc.in
@@ -3,7 +3,7 @@
# later are used by pkg-config.
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
-lib_suffix=
+lib_suffix=@LIB_SUFFIX@
libdir=${exec_prefix}/lib${lib_suffix}
includedir=${prefix}/include
@@ -13,3 +13,4 @@ URL: http://code.google.com/p/game-music-emu/
Version: @GME_VERSION@
Cflags: -I${includedir}
Libs: -L${libdir} -lgme
+Libs.private: -lstdc++
diff --git a/libs/gme/include/gme/gme.h b/libs/gme/include/gme/gme.h
index 1f2a2d15..cb07061b 100644
--- a/libs/gme/include/gme/gme.h
+++ b/libs/gme/include/gme/gme.h
@@ -1,6 +1,6 @@
/* Game music emulator library C interface (also usable from C++) */
-/* Game_Music_Emu 0.6.0 */
+/* Game_Music_Emu 0.6.1 */
#ifndef GME_H
#define GME_H
@@ -8,7 +8,7 @@
extern "C" {
#endif
-#define GME_VERSION 0x000600 /* 1 byte major, 1 byte minor, 1 byte patch-level */
+#define GME_VERSION 0x000601 /* 1 byte major, 1 byte minor, 1 byte patch-level */
/* Error string returned by library functions, or NULL if no error (success) */
typedef const char* gme_err_t;
@@ -47,9 +47,15 @@ int gme_track_ended( Music_Emu const* );
/* Number of milliseconds (1000 = one second) played since beginning of track */
int gme_tell( Music_Emu const* );
+/* Number of samples generated since beginning of track */
+int gme_tell_samples( Music_Emu const* );
+
/* Seek to new time in track. Seeking backwards or far forward can take a while. */
gme_err_t gme_seek( Music_Emu*, int msec );
+/* Equivalent to restarting track then skipping n samples */
+gme_err_t gme_seek_samples( Music_Emu*, int n );
+
/******** Informational ********/
diff --git a/libs/gme/readme.txt b/libs/gme/readme.txt
index 82a501db..4cfe5e7a 100644
--- a/libs/gme/readme.txt
+++ b/libs/gme/readme.txt
@@ -1,4 +1,4 @@
-Game_Music_Emu 0.6.0: Game Music Emulators
+Game_Music_Emu 0.6.2: Game Music Emulators
------------------------------------------
Game_Music_Emu is a collection of video game music file emulators that
support the following formats and systems:
@@ -34,30 +34,45 @@ several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable,
GP2X, and Nintendo DS.
Author : Shay Green
-Website: http://www.slack.net/~ant/
-Forum : http://groups.google.com/group/blargg-sound-libs
+Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home
License: GNU Lesser General Public License (LGPL)
+Current Maintainer: Michael Pyne
Getting Started
---------------
Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and
-all source files in gme/. If you have CMake 2.6 or later, execute
+all source files in gme/.
- run cmake
- cd demo
- run make
+Or, if you have CMake 2.6 or later, execute at a command prompt (from the
+extracted source directory):
-Be sure "test.nsf" is in the same directory as the program. Running it
+ mkdir build
+ cd build
+ cmake ../ # <-- Pass any needed CMake flags here
+ make # To build the library
+ cd demo
+ make # To build the demo itself
+
+Be sure "test.nsf" is in the same directory as the demo program. Running it
should generate the recording "out.wav".
+You can use "make install" to install the library. To choose where to install
+the library to, use the CMake argument "-DCMAKE_INSTALL_PREFIX=/usr/local"
+(and replace /usr/local with the base path you wish to use). Alternately, you
+can specify the base path to install to when you run "make install" by passing
+'DESTDIR=/usr/local' on the make install command line (again, replace
+/usr/local as appropriate).
+
+To build a static library instead of shared (the default), pass
+-DBUILD_SHARED_LIBS=OFF to the cmake command when running cmake.
+
A slightly more extensive demo application is available in the player/
directory. It requires SDL to build.
Read gme.txt for more information. Post to the discussion forum for
assistance.
-
Files
-----
gme.txt General notes about the library
diff --git a/libs/gme/win32/libgme.dll.a b/libs/gme/win32/libgme.dll.a
index d56d8739..2c5e9585 100644
Binary files a/libs/gme/win32/libgme.dll.a and b/libs/gme/win32/libgme.dll.a differ
diff --git a/libs/gme/win64/libgme.dll.a b/libs/gme/win64/libgme.dll.a
index 38079dc2..8348f12d 100644
Binary files a/libs/gme/win64/libgme.dll.a and b/libs/gme/win64/libgme.dll.a differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 97360b4f..2f97c173 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -379,6 +379,12 @@ if(${SRB2_CONFIG_HAVE_PNG} AND ${SRB2_CONFIG_HAVE_ZLIB})
set(SRB2_HAVE_PNG ON)
add_definitions(-DHAVE_PNG)
add_definitions(-D_LARGEFILE64_SOURCE)
+ set(SRB2_PNG_SOURCES apng.c)
+ set(SRB2_PNG_HEADERS apng.h)
+ prepend_sources(SRB2_PNG_SOURCES)
+ prepend_sources(SRB2_PNG_HEADERS)
+ source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS}
+ ${SRB2_PNG_SOURCES} ${SRB2_PNG_HEADERS})
else()
message(WARNING "You have specified that PNG is available but it was not found. SRB2Kart may not compile correctly.")
endif()
diff --git a/src/Makefile b/src/Makefile
index 9d9e759c..214c2bf7 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -342,6 +342,8 @@ endif
LIBS+=$(PNG_LDFLAGS)
CFLAGS+=$(PNG_CFLAGS)
+
+OBJS+=$(OBJDIR)/apng.o
endif
ifdef HAVE_LIBGME
diff --git a/src/Makefile.cfg b/src/Makefile.cfg
index 1238050b..a0398154 100644
--- a/src/Makefile.cfg
+++ b/src/Makefile.cfg
@@ -7,6 +7,10 @@
# and other things
#
+ifdef GCC81
+GCC80=1
+endif
+
ifdef GCC80
GCC72=1
endif
@@ -116,6 +120,7 @@ WFLAGS+=-Wfloat-equal
#WFLAGS+=-Wtraditional
ifdef VCHELP
WFLAGS+=-Wdeclaration-after-statement
+ WFLAGS+=-Wno-error=declaration-after-statement
endif
WFLAGS+=-Wundef
ifndef GCC295
@@ -189,12 +194,6 @@ ifdef GCC46
WFLAGS+=-Wno-suggest-attribute=noreturn
endif
-ifndef MINGW
-ifdef GCC45
-WFLAGS+=-Wunsuffixed-float-constants
-endif
-endif
-
ifdef NOLDWARNING
LDFLAGS+=-Wl,--as-needed
endif
@@ -208,6 +207,9 @@ WFLAGS+=$(OLDWFLAGS)
ifdef GCC43
#WFLAGS+=-Wno-error=clobbered
endif
+ifdef GCC44
+ WFLAGS+=-Wno-error=array-bounds
+endif
ifdef GCC46
WFLAGS+=-Wno-error=suggest-attribute=noreturn
endif
@@ -228,6 +230,7 @@ ifdef GCC80
WFLAGS+=-Wno-format-overflow
WFLAGS+=-Wno-stringop-truncation
WFLAGS+=-Wno-stringop-overflow
+ WFLAGS+=-Wno-error=multistatement-macros
endif
diff --git a/src/apng.c b/src/apng.c
new file mode 100644
index 00000000..694b3d1e
--- /dev/null
+++ b/src/apng.c
@@ -0,0 +1,289 @@
+/*
+Copyright 2019, James R.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "apng.h"
+
+#define APNG_INFO_acTL 0x20000U
+
+#define APNG_WROTE_acTL 0x10000U
+
+struct apng_info_def
+{
+ png_uint_32 mode;
+ png_uint_32 valid;
+
+ png_uint_32 num_frames;
+ png_uint_32 num_plays;
+
+ long start_acTL;/* acTL is written here */
+
+ png_flush_ptr output_flush_fn;
+ apng_seek_ptr output_seek_fn;
+ apng_tell_ptr output_tell_fn;
+
+ apng_set_acTL_ptr set_acTL_fn;
+};
+
+/* PROTOS (FUCK COMPILER) */
+void apng_seek (png_structp, apng_const_infop, size_t);
+size_t apng_tell (png_structp, apng_const_infop);
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+void apng_flush (png_structp, apng_infop);
+#ifdef PNG_STDIO_SUPPORTED
+void apng_default_flush (png_structp);
+#endif/* PNG_STDIO_SUPPORTED */
+#endif/* PNG_WRITE_FLUSH_SUPPORTED */
+#ifdef PNG_STDIO_SUPPORTED
+void apng_default_seek (png_structp, size_t);
+size_t apng_default_tell (png_structp);
+#endif/* PNG_STDIO_SUPPORTED */
+void apng_write_IEND (png_structp);
+void apng_write_acTL (png_structp, png_uint_32, png_uint_32);
+#ifndef PNG_WRITE_APNG_SUPPORTED
+png_uint_32 apng_set_acTL_dummy (png_structp, png_infop,
+ png_uint_32, png_uint_32);
+#endif/* PNG_WRITE_APNG_SUPPORTED */
+
+apng_infop
+apng_create_info_struct (png_structp pngp)
+{
+ apng_infop ainfop;
+ (void)pngp;
+ if (( ainfop = calloc(sizeof (apng_info),1) ))
+ {
+ apng_set_write_fn(pngp, ainfop, 0, 0, 0, 0, 0);
+ apng_set_set_acTL_fn(pngp, ainfop, 0);
+ }
+ return ainfop;
+}
+
+void
+apng_destroy_info_struct (png_structp pngp, apng_infopp ainfopp)
+{
+ (void)pngp;
+ if (!( pngp && ainfopp ))
+ return;
+
+ free((*ainfopp));
+}
+
+void
+apng_seek (png_structp pngp, apng_const_infop ainfop, size_t l)
+{
+ (*(ainfop->output_seek_fn))(pngp, l);
+}
+
+size_t
+apng_tell (png_structp pngp, apng_const_infop ainfop)
+{
+ return (*(ainfop->output_tell_fn))(pngp);
+}
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+void
+apng_flush (png_structp pngp, apng_infop ainfop)
+{
+ if (ainfop->output_flush_fn)
+ (*(ainfop->output_flush_fn))(pngp);
+}
+
+#ifdef PNG_STDIO_SUPPORTED
+void
+apng_default_flush (png_structp pngp)
+{
+ if (!( pngp ))
+ return;
+
+ fflush((png_FILE_p)png_get_io_ptr);
+}
+#endif/* PNG_STDIO_SUPPORTED */
+#endif/* PNG_WRITE_FLUSH_SUPPORTED */
+
+#ifdef PNG_STDIO_SUPPORTED
+void
+apng_default_seek (png_structp pngp, size_t l)
+{
+ if (!( pngp ))
+ return;
+
+ if (fseek((png_FILE_p)png_get_io_ptr(pngp), (long)l, SEEK_SET) == -1)
+ png_error(pngp, "Seek Error");
+}
+
+size_t
+apng_default_tell (png_structp pngp)
+{
+ long l;
+
+ if (!( pngp ))
+ {
+ png_error(pngp, "Call to apng_default_tell with NULL pngp failed");
+ }
+
+ if (( l = ftell((png_FILE_p)png_get_io_ptr(pngp)) ) == -1)
+ png_error(pngp, "Tell Error");
+
+ return (size_t)l;
+}
+#endif/* PNG_STDIO_SUPPORTED */
+
+void
+apng_set_write_fn (png_structp pngp, apng_infop ainfop, png_voidp iop,
+ png_rw_ptr write_f, png_flush_ptr flush_f,
+ apng_seek_ptr seek_f, apng_tell_ptr tell_f)
+{
+ if (!( pngp && ainfop ))
+ return;
+
+ png_set_write_fn(pngp, iop, write_f, flush_f);
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+#ifdef PNG_STDIO_SUPPORTED
+ if (!flush_f)
+ ainfop->output_flush_fn = &apng_default_flush;
+ else
+#endif/* PNG_STDIO_SUPPORTED */
+ ainfop->output_flush_fn = flush_f;
+#endif/* PNG_WRITE_FLUSH_SUPPORTED */
+#ifdef PNG_STDIO_SUPPORTED
+ if (!seek_f)
+ ainfop->output_seek_fn = &apng_default_seek;
+ else
+#endif/* PNG_STDIO_SUPPORTED */
+ ainfop->output_seek_fn = seek_f;
+#ifdef PNG_STDIO_SUPPORTED
+ if (!seek_f)
+ ainfop->output_tell_fn = apng_default_tell;
+ else
+#endif/* PNG_STDIO_SUPPORTED */
+ ainfop->output_tell_fn = tell_f;
+}
+
+void
+apng_write_IEND (png_structp pngp)
+{
+ png_byte chunkc[] = "IEND";
+ png_write_chunk(pngp, chunkc, 0, 0);
+}
+
+void
+apng_write_acTL (png_structp pngp, png_uint_32 frames, png_uint_32 plays)
+{
+ png_byte chunkc[] = "acTL";
+ png_byte buf[8];
+ png_save_uint_32(buf, frames);
+ png_save_uint_32(buf + 4, plays);
+ png_write_chunk(pngp, chunkc, buf, 8);
+}
+
+png_uint_32
+apng_set_acTL (png_structp pngp, png_infop infop, apng_infop ainfop,
+ png_uint_32 frames, png_uint_32 plays)
+{
+ (void)pngp;
+ (void)infop;
+ if (!( pngp && infop && ainfop ))
+ return 0;
+
+ ainfop->num_frames = frames;
+ ainfop->num_plays = plays;
+
+ ainfop->valid |= APNG_INFO_acTL;
+
+ return 1;
+}
+
+void
+apng_write_info_before_PLTE (png_structp pngp, png_infop infop,
+ apng_infop ainfop)
+{
+ if (!( pngp && infop && ainfop ))
+ return;
+
+ png_write_info_before_PLTE(pngp, infop);
+
+ if (( ainfop->valid & APNG_INFO_acTL )&&!( ainfop->mode & APNG_WROTE_acTL ))
+ {
+ ainfop->start_acTL = apng_tell(pngp, ainfop);
+
+ apng_write_acTL(pngp, 0, 0);
+ /* modified for runtime dynamic linking */
+ (*(ainfop->set_acTL_fn))(pngp, infop, PNG_UINT_31_MAX, 0);
+
+ ainfop->mode |= APNG_WROTE_acTL;
+ }
+}
+
+void
+apng_write_info (png_structp pngp, png_infop infop,
+ apng_infop ainfop)
+{
+ apng_write_info_before_PLTE(pngp, infop, ainfop);
+ png_write_info(pngp, infop);
+}
+
+void
+apng_write_end (png_structp pngp, png_infop infop, apng_infop ainfop)
+{
+ (void)infop;
+ apng_write_IEND(pngp);
+ apng_seek(pngp, ainfop, ainfop->start_acTL);
+ apng_write_acTL(pngp, ainfop->num_frames, ainfop->num_plays);
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+#ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
+ apng_flush(pngp, infop);
+#endif/* PNG_WRITE_FLUSH_SUPPORTED */
+#endif/* PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED */
+}
+
+#ifndef PNG_WRITE_APNG_SUPPORTED
+png_uint_32
+apng_set_acTL_dummy (png_structp pngp, png_infop infop,
+ png_uint_32 frames, png_uint_32 plays)
+{
+ (void)pngp;
+ (void)infop;
+ (void)frames;
+ (void)plays;
+ return 0;
+}
+#endif/* PNG_WRITE_APNG_SUPPORTED */
+
+/* Dynamic runtime linking capable! (Hopefully.) */
+void
+apng_set_set_acTL_fn (png_structp pngp, apng_infop ainfop,
+ apng_set_acTL_ptr set_acTL_f)
+{
+ (void)pngp;
+ if (!ainfop->set_acTL_fn)
+#ifndef PNG_WRITE_APNG_SUPPORTED
+ ainfop->set_acTL_fn = &apng_set_acTL_dummy;
+#else
+ ainfop->set_acTL_fn = &png_set_acTL;
+#endif/* PNG_WRITE_APNG_SUPPORTED */
+ else
+ ainfop->set_acTL_fn = set_acTL_f;
+}
diff --git a/src/apng.h b/src/apng.h
new file mode 100644
index 00000000..aa7fac3d
--- /dev/null
+++ b/src/apng.h
@@ -0,0 +1,82 @@
+/*
+Copyright 2019, James R.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef APNG_H
+#define APNG_H
+
+#ifndef _MSC_VER
+#ifndef _WII
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+#endif
+#endif
+
+#ifndef _LFS64_LARGEFILE
+#define _LFS64_LARGEFILE
+#endif
+
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 0
+#endif
+
+#include
+
+typedef struct apng_info_def apng_info;
+typedef apng_info * apng_infop;
+typedef const apng_info * apng_const_infop;
+typedef apng_info * * apng_infopp;
+
+typedef void (*apng_seek_ptr)(png_structp, size_t);
+typedef size_t (*apng_tell_ptr)(png_structp);
+
+typedef png_uint_32 (*apng_set_acTL_ptr)(png_structp, png_infop,
+ png_uint_32, png_uint_32);
+
+apng_infop apng_create_info_struct (png_structp png_ptr);
+
+void apng_destroy_info_struct (png_structp png_ptr,
+ apng_infopp info_ptr_ptr);
+
+/* Call the following functions in place of the libpng counterparts. */
+
+png_uint_32 apng_set_acTL (png_structp png_ptr, png_infop info_ptr,
+ apng_infop ainfo_ptr,
+ png_uint_32 num_frames, png_uint_32 num_plays);
+
+void apng_write_info_before_PLTE (png_structp png_ptr, png_infop info_ptr,
+ apng_infop ainfo_ptr);
+void apng_write_info (png_structp png_ptr, png_infop info_ptr,
+ apng_infop ainfo_ptr);
+
+void apng_write_end (png_structp png_ptr, png_infop info_ptr,
+ apng_infop ainfo_ptr);
+
+void apng_set_write_fn (png_structp png_ptr, apng_infop ainfo_ptr,
+ png_voidp io_ptr,
+ png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn,
+ apng_seek_ptr output_seek_fn, apng_tell_ptr output_tell_fn);
+
+void apng_set_set_acTL_fn (png_structp png_ptr, apng_infop ainfo_ptr,
+ apng_set_acTL_ptr set_acTL_fn);
+
+#endif/* APNG_H */
diff --git a/src/command.c b/src/command.c
index bb2ea86e..a5d45bc1 100644
--- a/src/command.c
+++ b/src/command.c
@@ -1254,7 +1254,8 @@ found:
var->string = var->zstring = Z_StrDup(valstr);
- if (override)
+ if (var->flags & CV_PASSWORD); // Don't change value for password field
+ else if (override)
var->value = overrideval;
else if (var->flags & CV_FLOAT)
{
diff --git a/src/command.h b/src/command.h
index 82dfaf8a..f9d177e2 100644
--- a/src/command.h
+++ b/src/command.h
@@ -95,7 +95,8 @@ typedef enum
CV_HIDEN = 1024, // variable is not part of the cvar list so cannot be accessed by the console
// can only be set when we have the pointer to it
// used on menus
- CV_CHEAT = 2048 // Don't let this be used in multiplayer unless cheats are on.
+ CV_CHEAT = 2048, // Don't let this be used in multiplayer unless cheats are on.
+ CV_PASSWORD = 4096 // Password field
} cvflags_t;
typedef struct CV_PossibleValue_s
diff --git a/src/config.h.in b/src/config.h.in
index dd86966e..bd7e7861 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -36,8 +36,8 @@
/* Manually defined asset hashes for non-CMake builds
* Last updated 2015 / 05 / 03 - SRB2 v2.1.15 - srb2.srb
* Last updated 2018 / 12 / 23 - SRB2 v2.1.22 - patch.dta
- * Last updated 2018 / 11 / 16 - Kart v1.0.0 - Main assets
- * Last updated 2018 / 12 / 13 - Kart v1.0.2 - patch.kart
+ * Last updated 2019 / 01 / 18 - Kart v1.0.2 - Main assets
+ * Last updated 2019 / 03 / 11 - Kart v1.0.4 - patch.kart
*/
// Base SRB2 hashes
@@ -49,10 +49,10 @@
// SRB2Kart-specific hashes
#define ASSET_HASH_GFX_KART "99c39f223d84ebc78e67ab68f3bead95"
#define ASSET_HASH_TEXTURES_KART "ec8e9b7535cf585afe72ef277b08f490"
-#define ASSET_HASH_CHARS_KART "784ee9177b01c8cb26edff43eaf93d87"
-#define ASSET_HASH_MAPS_KART "84018d9b35d181ca9fcc2be5a9d43a45"
+#define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964"
+#define ASSET_HASH_MAPS_KART "1335cd064656aedca359cfbb5233ac4a"
#ifdef USE_PATCH_KART
-#define ASSET_HASH_PATCH_KART "843a13a73935dc4df721427a8948fb89"
+#define ASSET_HASH_PATCH_KART "b5f48e1abccfa47a5745199182e2fef4"
#endif
#endif
diff --git a/src/console.c b/src/console.c
index a10d73e7..44bb03a3 100644
--- a/src/console.c
+++ b/src/console.c
@@ -544,6 +544,22 @@ static void CON_MoveConsole(void)
}
}
+INT32 CON_ShiftChar(INT32 ch)
+{
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))
+ {
+ if (shiftdown ^ capslock)
+ ch = shiftxform[ch];
+ }
+ else // if we're holding shift we should still shift non letter symbols
+ {
+ if (shiftdown)
+ ch = shiftxform[ch];
+ }
+
+ return ch;
+}
+
// Clear time of console heads up messages
//
void CON_ClearHUD(void)
@@ -1084,16 +1100,6 @@ boolean CON_Responder(event_t *ev)
else if (key == KEY_KPADSLASH)
key = '/';
- // capslock
- if (key == KEY_CAPSLOCK) // it's a toggle.
- {
- if (capslock)
- capslock = false;
- else
- capslock = true;
- return true;
- }
-
// same capslock code as hu_stuff.c's HU_responder. Check there for details.
if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z'))
{
diff --git a/src/console.h b/src/console.h
index 98df6ee2..11621746 100644
--- a/src/console.h
+++ b/src/console.h
@@ -44,6 +44,8 @@ extern UINT8 *yellowmap, *purplemap, *greenmap, *bluemap, *graymap, *redmap, *or
// Console bg color (auto updated to match)
extern UINT8 *consolebgmap;
+INT32 CON_ShiftChar(INT32 ch);
+
void CON_SetupBackColormap(void);
void CON_ClearHUD(void); // clear heads up messages
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index be639034..e227ce2e 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -22,6 +22,7 @@
#include "i_video.h"
#include "d_net.h"
#include "d_main.h"
+#include "d_event.h"
#include "g_game.h"
#include "hu_stuff.h"
#include "keys.h"
@@ -72,6 +73,7 @@
#define PREDICTIONQUEUE BACKUPTICS
#define PREDICTIONMASK (PREDICTIONQUEUE-1)
#define MAX_REASONLENGTH 30
+#define FORCECLOSE 0x8000
boolean server = true; // true or false but !server == client
#define client (!server)
@@ -93,6 +95,7 @@ static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the
UINT16 pingmeasurecount = 1;
UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
+tic_t servermaxping = 800; // server's max ping. Defaults to 800
#endif
SINT8 nodetoplayer[MAXNETNODES];
SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
@@ -648,6 +651,8 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
rsp->jointime = (tic_t)LONG(players[i].jointime);
+ rsp->splitscreenindex = players[i].splitscreenindex;
+
rsp->hasmo = false;
//Transfer important mo information if the player has a body.
//This lets us resync players even if they are dead.
@@ -783,6 +788,8 @@ static void resynch_read_player(resynch_pak *rsp)
players[i].jointime = (tic_t)LONG(rsp->jointime);
+ players[i].splitscreenindex = rsp->splitscreenindex;
+
//We get a packet for each player in game.
if (!playeringame[i])
return;
@@ -1121,12 +1128,22 @@ typedef enum
CL_DOWNLOADSAVEGAME,
#endif
CL_CONNECTED,
- CL_ABORTED
+ CL_ABORTED,
+ CL_ASKDOWNLOADFILES,
+ CL_WAITDOWNLOADFILESRESPONSE,
+ CL_CHALLENGE
} cl_mode_t;
static void GetPackets(void);
static cl_mode_t cl_mode = CL_SEARCHING;
+static boolean cl_needsdownload = false;
+
+static UINT8 cl_challengenum = 0;
+static UINT8 cl_challengequestion[MD5_LEN+1];
+static char cl_challengepassword[65];
+static UINT8 cl_challengeanswer[MD5_LEN+1];
+static UINT8 cl_challengeattempted = 0;
// Player name send/load
@@ -1163,6 +1180,8 @@ static void CV_LoadPlayerNames(UINT8 **p)
}
#ifdef CLIENT_LOADINGSCREEN
+static UINT32 SL_SearchServer(INT32 node);
+
//
// CL_DrawConnectionStatus
//
@@ -1186,11 +1205,42 @@ static inline void CL_DrawConnectionStatus(void)
// 15 pal entries total.
const char *cltext;
- for (i = 0; i < 16; ++i)
- V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15));
+ if (cl_mode != CL_CHALLENGE)
+ for (i = 0; i < 16; ++i)
+ V_DrawFill((BASEVIDWIDTH/2-128) + (i * 16), BASEVIDHEIGHT-24, 16, 8, palstart + ((animtime - i) & 15));
switch (cl_mode)
{
+ case CL_CHALLENGE:
+ {
+ char asterisks[33];
+ size_t sl = min(32, strlen(cl_challengepassword));
+ UINT32 serverid;
+
+ memset(asterisks, '*', sl);
+ memset(asterisks+sl, 0, 33-sl);
+
+ V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_MONOSPACE|V_ALLOWLOWERCASE, asterisks);
+ V_DrawFixedPatch((BASEVIDWIDTH/2) << FRACBITS, (BASEVIDHEIGHT/2) << FRACBITS, FRACUNIT, 0, W_CachePatchName("BSRVLOCK", PU_CACHE), NULL);
+
+ serverid = SL_SearchServer(servernode);
+
+ if (serverid == UINT32_MAX)
+ {
+ M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT/2-8, 32, 1);
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, V_REDMAP, M_GetText("This server is password protected."));
+ }
+ else
+ {
+ M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT/2-8, 32, 3);
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, V_REDMAP, M_GetText("This server,"));
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2+8, V_ALLOWLOWERCASE, serverlist[serverid].info.servername);
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2+16, V_REDMAP, M_GetText("is password protected."));
+ }
+
+ cltext = M_GetText(cl_challengeattempted ? "Incorrect password. Please try again." : "Please enter the server password.");
+ }
+ break;
#ifdef JOININGAME
case CL_DOWNLOADSAVEGAME:
if (lastfilenum != -1)
@@ -1210,6 +1260,9 @@ static inline void CL_DrawConnectionStatus(void)
case CL_WAITJOINRESPONSE:
cltext = M_GetText("Requesting to join...");
break;
+ case CL_ASKDOWNLOADFILES:
+ case CL_WAITDOWNLOADFILESRESPONSE:
+ cltext = M_GetText("Waiting to download files...");
default:
cltext = M_GetText("Connecting to server...");
break;
@@ -1287,6 +1340,9 @@ static boolean CL_SendJoin(void)
netbuffer->u.clientcfg.localplayers = localplayers;
netbuffer->u.clientcfg.version = VERSION;
netbuffer->u.clientcfg.subversion = SUBVERSION;
+ netbuffer->u.clientcfg.needsdownload = cl_needsdownload;
+ netbuffer->u.clientcfg.challengenum = cl_challengenum;
+ memcpy(netbuffer->u.clientcfg.challengeanswer, cl_challengeanswer, MD5_LEN);
return HSendPacket(servernode, true, 0, sizeof (clientconfig_pak));
}
@@ -1307,7 +1363,13 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
netbuffer->u.serverinfo.gametype = (UINT8)(G_BattleGametype() ? VANILLA_GT_MATCH : VANILLA_GT_RACE); // SRB2Kart: Vanilla's gametype constants for MS support
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
- netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated;
+
+ netbuffer->u.serverinfo.kartvars = (UINT8) (
+ (cv_kartspeed.value & SV_SPEEDMASK) |
+ (dedicated ? SV_DEDICATED : 0) |
+ (D_IsJoinPasswordOn() ? SV_PASSWORD : 0)
+ );
+
strncpy(netbuffer->u.serverinfo.servername, cv_servername.string,
MAXSERVERNAME);
strncpy(netbuffer->u.serverinfo.mapname, G_BuildMapName(gamemap), 7);
@@ -1740,8 +1802,6 @@ static void SendAskInfo(INT32 node, boolean viams)
serverelem_t serverlist[MAXSERVERLIST];
UINT32 serverlistcount = 0;
-#define FORCECLOSE 0x8000
-
static void SL_ClearServerList(INT32 connectedserver)
{
UINT32 i;
@@ -1867,7 +1927,7 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
/** Called by CL_ServerConnectionTicker
*
* \param viams ???
- * \param asksent ???
+ * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit.
* \return False if the connection was aborted
* \sa CL_ServerConnectionTicker
* \sa CL_ConnectToServer
@@ -1960,9 +2020,12 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent)
), NULL, MM_NOTHING);
return false;
}
+
+ cl_mode = CL_ASKDOWNLOADFILES;
+
// no problem if can't send packet, we will retry later
- if (CL_SendRequestFile())
- cl_mode = CL_DOWNLOADFILES;
+ //if (CL_SendRequestFile())
+ // cl_mode = CL_DOWNLOADFILES;
}
}
else
@@ -1992,7 +2055,7 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent)
* \param viams ???
* \param tmpsave The name of the gamestate file???
* \param oldtic Used for knowing when to poll events and redraw
- * \param asksent ???
+ * \param asksent The last time we asked the server to join. We re-ask every second in case our request got lost in transmit.
* \return False if the connection was aborted
* \sa CL_ServerConnectionSearchTicker
* \sa CL_ConnectToServer
@@ -2030,6 +2093,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
/* FALLTHRU */
case CL_ASKJOIN:
+ cl_needsdownload = false;
CL_LoadServerFiles();
#ifdef JOININGAME
// prepare structures to save the file
@@ -2038,9 +2102,23 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
CL_PrepareDownloadSaveGame(tmpsave);
#endif
if (CL_SendJoin())
+ {
+ *asksent = I_GetTime();
cl_mode = CL_WAITJOINRESPONSE;
+ }
break;
+ case CL_ASKDOWNLOADFILES:
+ cl_needsdownload = true;
+
+ if (CL_SendJoin())
+ {
+ *asksent = I_GetTime();
+ cl_mode = CL_WAITDOWNLOADFILESRESPONSE;
+ }
+ break;
+
+
#ifdef JOININGAME
case CL_DOWNLOADSAVEGAME:
// At this state, the first (and only) needed file is the gamestate
@@ -2054,7 +2132,19 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
break;
#endif
+ case CL_CHALLENGE:
+ (*asksent) = I_GetTime() - NEWTICRATE; // Send password immediately upon entering
+ break;
+
case CL_WAITJOINRESPONSE:
+ case CL_WAITDOWNLOADFILESRESPONSE:
+ if (*asksent + NEWTICRATE < I_GetTime() && CL_SendJoin())
+ {
+ *asksent = I_GetTime();
+ }
+
+ break;
+
case CL_CONNECTED:
default:
break;
@@ -2072,19 +2162,10 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
// Call it only once by tic
if (*oldtic != I_GetTime())
{
- INT32 key;
-
I_OsPolling();
- key = I_GetKey();
- if (key == KEY_ESCAPE || key == KEY_JOY1+1)
- {
- CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
-// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
- D_QuitNetGame();
- CL_Reset();
- D_StartTitle();
+ D_ProcessEvents();
+ if (gamestate != GS_WAITINGPLAYERS)
return false;
- }
// why are these here? this is for servers, we're a client
//if (key == 's' && server)
@@ -2113,6 +2194,71 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
return true;
}
+boolean CL_Responder(event_t *ev)
+{
+ size_t len;
+ INT32 ch;
+
+ if (!(client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED))
+ return false; // Don't do anything outside of the connection screen
+
+ if (ev->type != ev_keydown)
+ return false;
+
+ ch = (INT32)ev->data1;
+
+ // Only ESC and non-keyboard keys abort connection
+ if (ch == KEY_ESCAPE || ch >= KEY_MOUSE1)
+ {
+ CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
+ //M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
+ D_QuitNetGame();
+ CL_Reset();
+ D_StartTitle();
+ return true;
+ }
+
+ if (cl_mode != CL_CHALLENGE)
+ return false;
+
+ if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART])
+ || ch == ' ') // Allow spaces, of course
+ {
+ len = strlen(cl_challengepassword);
+ if (len < 64)
+ {
+ cl_challengepassword[len+1] = 0;
+ cl_challengepassword[len] = CON_ShiftChar(ch);
+ }
+
+ cl_challengeattempted = 0;
+ }
+ else if (ch == KEY_BACKSPACE)
+ {
+ len = strlen(cl_challengepassword);
+
+ if (len > 0)
+ cl_challengepassword[len-1] = 0;
+
+ cl_challengeattempted = 0;
+ }
+ else if (ch == KEY_ENTER)
+ {
+ netgame = true;
+ multiplayer = true;
+
+#ifndef NONET
+ SL_ClearServerList(servernode);
+#endif
+ cl_mode = CL_SEARCHING;
+
+ D_ComputeChallengeAnswer(cl_challengequestion, cl_challengepassword, cl_challengeanswer);
+ cl_challengeattempted = 1;
+ }
+
+ return true;
+}
+
/** Use adaptive send using net_bandwidth and stat.sendbytes
*
* \param viams ???
@@ -2133,6 +2279,7 @@ static void CL_ConnectToServer(boolean viams)
#endif
cl_mode = CL_SEARCHING;
+ cl_challengenum = 0;
#ifdef CLIENT_LOADINGSCREEN
lastfilenum = -1;
@@ -2190,6 +2337,8 @@ static void CL_ConnectToServer(boolean viams)
SL_ClearServerList(servernode);
#endif
+ cl_challengeattempted = 0;
+
do
{
// If the connection was aborted for some reason, leave
@@ -2210,8 +2359,10 @@ static void CL_ConnectToServer(boolean viams)
}
while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes))));
+#ifndef NONET
if (netgame)
F_StartWaitingPlayers();
+#endif
DEBFILE(va("Synchronisation Finished\n"));
displayplayer = consoleplayer;
@@ -2539,6 +2690,8 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason)
#ifdef HAVE_BLUA
LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting
+#else
+ (void)reason;
#endif
// Reset player data
@@ -2722,7 +2875,10 @@ static void Command_Ban(void)
else
{
if (server) // only the server is allowed to do this right now
+ {
Ban_Add(COM_Argv(2));
+ D_SaveBan(); // save the ban list
+ }
if (COM_Argc() == 2)
{
@@ -2753,6 +2909,42 @@ static void Command_Ban(void)
}
+static void Command_BanIP(void)
+{
+ if (COM_Argc() < 2)
+ {
+ CONS_Printf(M_GetText("banip : ban an ip address\n"));
+ return;
+ }
+
+ if (server) // Only the server can use this, otherwise does nothing.
+ {
+ const char *address = (COM_Argv(1));
+ const char *reason;
+
+ if (COM_Argc() == 2)
+ reason = NULL;
+ else
+ reason = COM_Argv(2);
+
+
+ if (I_SetBanAddress && I_SetBanAddress(address, NULL))
+ {
+ if (reason)
+ CONS_Printf("Banned IP address %s for: %s\n", address, reason);
+ else
+ CONS_Printf("Banned IP address %s\n", address);
+
+ Ban_Add(reason);
+ D_SaveBan();
+ }
+ else
+ {
+ return;
+ }
+ }
+}
+
static void Command_Kick(void)
{
if (COM_Argc() < 2)
@@ -3024,6 +3216,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
}
}
+static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
+consvar_t cv_netticbuffer = {"netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
#ifdef VANILLAJOINNEXTROUND
consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
@@ -3056,6 +3251,7 @@ void D_ClientServerInit(void)
COM_AddCommand("getplayernum", Command_GetPlayerNum);
COM_AddCommand("kick", Command_Kick);
COM_AddCommand("ban", Command_Ban);
+ COM_AddCommand("banip", Command_BanIP);
COM_AddCommand("clearbans", Command_ClearBans);
COM_AddCommand("showbanlist", Command_ShowBan);
COM_AddCommand("reloadbans", Command_ReloadBan);
@@ -3079,7 +3275,6 @@ void D_ClientServerInit(void)
CV_RegisterVar(&cv_joinnextround);
#endif
CV_RegisterVar(&cv_showjoinaddress);
- CV_RegisterVar(&cv_resynchattempts);
CV_RegisterVar(&cv_blamecfail);
#ifdef DUMPCONSISTENCY
CV_RegisterVar(&cv_dumpconsistency);
@@ -3090,6 +3285,9 @@ void D_ClientServerInit(void)
gametic = 0;
localgametic = 0;
+ memset(cl_challengequestion, 0x00, MD5_LEN+1);
+ memset(cl_challengeanswer, 0x00, MD5_LEN+1);
+
// do not send anything before the real begin
SV_StopServer();
SV_ResetServer();
@@ -3311,6 +3509,8 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
addedtogame = true;
}
+ players[newplayernum].splitscreenindex = splitscreenplayer;
+
if (netgame)
{
if (server && cv_showjoinaddress.value)
@@ -3577,6 +3777,33 @@ static void HandleConnect(SINT8 node)
boolean newnode = false;
#endif
+ if (node != servernode && !nodeingame[node] && D_IsJoinPasswordOn())
+ {
+ // Ensure node sent the correct password challenge
+ boolean passed = false;
+
+ if (netbuffer->u.clientcfg.challengenum && D_VerifyJoinPasswordChallenge(netbuffer->u.clientcfg.challengenum, netbuffer->u.clientcfg.challengeanswer))
+ passed = true;
+
+ if (!passed)
+ {
+ D_MakeJoinPasswordChallenge(&netbuffer->u.joinchallenge.challengenum, netbuffer->u.joinchallenge.question);
+
+ netbuffer->packettype = PT_JOINCHALLENGE;
+ HSendPacket(node, true, 0, sizeof(joinchallenge_pak));
+ Net_CloseConnection(node);
+
+ return;
+ }
+ }
+
+ if (netbuffer->u.clientcfg.needsdownload)
+ {
+ netbuffer->packettype = PT_DOWNLOADFILESOKAY;
+ HSendPacket(node, true, 0, 0);
+ return;
+ }
+
// client authorised to join
nodewaiting[node] = (UINT8)(netbuffer->u.clientcfg.localplayers - playerpernode[node]);
if (!nodeingame[node])
@@ -3585,6 +3812,7 @@ static void HandleConnect(SINT8 node)
#ifndef NONET
newnode = true;
#endif
+
SV_AddNode(node);
/// \note Wait what???
@@ -3740,6 +3968,43 @@ static void HandlePacketFromAwayNode(SINT8 node)
Net_CloseConnection(node);
break;
+ case PT_JOINCHALLENGE:
+ if (server && serverrunning)
+ { // But wait I thought I'm the server?
+ Net_CloseConnection(node);
+ break;
+ }
+ SERVERONLY
+ if (cl_mode == CL_WAITJOINRESPONSE || cl_mode == CL_WAITDOWNLOADFILESRESPONSE)
+ {
+ cl_challengenum = netbuffer->u.joinchallenge.challengenum;
+ memcpy(cl_challengequestion, netbuffer->u.joinchallenge.question, 16);
+
+ Net_CloseConnection(node|FORCECLOSE); // Don't need to stay connected while challenging
+
+ cl_mode = CL_CHALLENGE;
+
+ switch (cl_challengeattempted)
+ {
+ case 2:
+ // We already sent a correct password, so throw it back up again.
+ D_ComputeChallengeAnswer(cl_challengequestion, cl_challengepassword, cl_challengeanswer);
+ cl_mode = CL_ASKJOIN;
+ break;
+
+ case 1:
+ // We entered the wrong password!
+ S_StartSound(NULL, sfx_s26d);
+ break;
+
+ default:
+ // First entry to the password screen.
+ S_StartSound(NULL, sfx_s224);
+ break;
+ }
+ }
+ break;
+
case PT_SERVERREFUSE: // Negative response of client join request
if (server && serverrunning)
{ // But wait I thought I'm the server?
@@ -3768,6 +4033,41 @@ static void HandlePacketFromAwayNode(SINT8 node)
}
break;
+ case PT_DOWNLOADFILESOKAY:
+ if (server && serverrunning)
+ { // But wait I thought I'm the server?
+ Net_CloseConnection(node);
+ break;
+ }
+
+ SERVERONLY
+
+ // This should've already been checked, but just to be safe...
+ if (!CL_CheckDownloadable())
+ {
+ D_QuitNetGame();
+ CL_Reset();
+ D_StartTitle();
+ M_StartMessage(M_GetText(
+ "You cannot connect to this server\n"
+ "because you cannot download the files\n"
+ "that you are missing from the server.\n\n"
+ "See the console or log file for\n"
+ "more details.\n\n"
+ "Press ESC\n"
+ ), NULL, MM_NOTHING);
+ break;
+ }
+
+ if (cl_challengeattempted == 1) // Successful password noise.
+ S_StartSound(NULL, sfx_s221);
+
+ cl_challengeattempted = 2;
+ CONS_Printf("trying to download\n");
+ if (CL_SendRequestFile())
+ cl_mode = CL_DOWNLOADFILES;
+ break;
+
case PT_SERVERCFG: // Positive response of client join request
{
INT32 j;
@@ -3783,6 +4083,9 @@ static void HandlePacketFromAwayNode(SINT8 node)
if (cl_mode != CL_WAITJOINRESPONSE)
break;
+ if (cl_challengeattempted == 1) // Successful password noise.
+ S_StartSound(NULL, sfx_s221);
+
if (client)
{
maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
@@ -3882,6 +4185,32 @@ static void HandlePacketFromAwayNode(SINT8 node)
#undef SERVERONLY
}
+/** Checks ticcmd for "speed hacks"
+ *
+ * \param p Which player
+ * \return True if player is hacking
+ * \sa HandlePacketFromPlayer
+ *
+ */
+static boolean CheckForSpeedHacks(UINT8 p)
+{
+ if (netcmds[maketic%BACKUPTICS][p].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][p].forwardmove < -MAXPLMOVE
+ || netcmds[maketic%BACKUPTICS][p].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][p].sidemove < -MAXPLMOVE
+ || netcmds[maketic%BACKUPTICS][p].driftturn > KART_FULLTURN || netcmds[maketic%BACKUPTICS][p].driftturn < -KART_FULLTURN)
+ {
+ XBOXSTATIC char buf[2];
+ CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), playernode[p]);
+ //D_Clearticcmd(k);
+
+ buf[0] = (char)p;
+ buf[1] = KICK_MSG_CON_FAIL;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ return true;
+ }
+
+ return false;
+}
+
/** Handles a packet received from a node that is in game
*
* \param node The packet sender
@@ -3976,18 +4305,8 @@ FILESTAMP
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
// Check ticcmd for "speed hacks"
- if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
- || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
- {
- XBOXSTATIC char buf[2];
- CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
- //D_Clearticcmd(k);
-
- buf[0] = (char)netconsole;
- buf[1] = KICK_MSG_CON_FAIL;
- SendNetXCmd(XD_KICK, &buf, 2);
+ if (CheckForSpeedHacks((UINT8)netconsole))
break;
- }
// Splitscreen cmd
if (((netbuffer->packettype == PT_CLIENT2CMD || netbuffer->packettype == PT_CLIENT2MIS)
@@ -3997,6 +4316,9 @@ FILESTAMP
{
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
&netbuffer->u.client2pak.cmd2, 1);
+
+ if (CheckForSpeedHacks((UINT8)nodetoplayer2[node]))
+ break;
}
if (((netbuffer->packettype == PT_CLIENT3CMD || netbuffer->packettype == PT_CLIENT3MIS)
@@ -4005,6 +4327,9 @@ FILESTAMP
{
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer3[node]],
&netbuffer->u.client3pak.cmd3, 1);
+
+ if (CheckForSpeedHacks((UINT8)nodetoplayer3[node]))
+ break;
}
if ((netbuffer->packettype == PT_CLIENT4CMD || netbuffer->packettype == PT_CLIENT4MIS)
@@ -4012,6 +4337,9 @@ FILESTAMP
{
G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer4[node]],
&netbuffer->u.client4pak.cmd4, 1);
+
+ if (CheckForSpeedHacks((UINT8)nodetoplayer4[node]))
+ break;
}
// A delay before we check resynching
@@ -4055,6 +4383,21 @@ FILESTAMP
else if (resynch_score[node])
--resynch_score[node];
break;
+ case PT_BASICKEEPALIVE:
+ if (client)
+ break;
+
+ // This should probably still timeout though, as the node should always have a player 1 number
+ if (netconsole == -1)
+ break;
+
+ // If a client sends this it should mean they are done receiving the savegame
+ sendingsavegame[node] = false;
+
+ // As long as clients send keep alives, the server can keep running, so reset the timeout
+ /// \todo Use a separate cvar for that kind of timeout?
+ freezetimeout[node] = I_GetTime() + connectiontimeout;
+ break;
case PT_TEXTCMD:
case PT_TEXTCMD2:
case PT_TEXTCMD3:
@@ -4308,10 +4651,12 @@ FILESTAMP
//Update client ping table from the server.
if (client)
{
- INT32 i;
+ UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
+
+ servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS];
}
break;
@@ -4558,6 +4903,15 @@ static INT16 Consistancy(void)
return (INT16)(ret & 0xFFFF);
}
+// confusing, but this DOESN'T send PT_NODEKEEPALIVE, it sends PT_BASICKEEPALIVE
+// used during wipes to tell the server that a node is still connected
+static void CL_SendClientKeepAlive(void)
+{
+ netbuffer->packettype = PT_BASICKEEPALIVE;
+
+ HSendPacket(servernode, false, 0, 0);
+}
+
// send the client packet to the server
static void CL_SendClientCmd(void)
{
@@ -4932,6 +5286,10 @@ void TryRunTics(tic_t realtics)
ExtraDataTicker();
gametic++;
consistancy[gametic%BACKUPTICS] = Consistancy();
+
+ // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
+ if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
+ break;
}
}
else
@@ -4941,6 +5299,18 @@ void TryRunTics(tic_t realtics)
}
#ifdef NEWPING
+
+/* Ping Update except better:
+ We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
+ If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
+
+ Why do we do that? Well, I'm a person with unfortunately sometimes unstable internet and happen to keep getting kicked very unconveniently for very short high spikes. (700+ ms)
+ Because my spikes are so high, the average ping is exponentially higher too (700s really add up...!) which leads me to getting kicked for a short burst of spiking.
+ With this change here, this doesn't happen anymore as it checks if my ping has been CONSISTENTLY bad for long enough before killing me.
+*/
+
+static INT32 pingtimeout[MAXPLAYERS];
+
static inline void PingUpdate(void)
{
INT32 i;
@@ -4961,6 +5331,9 @@ static inline void PingUpdate(void)
laggers[i] = true;
numlaggers++;
}
+ else
+ pingtimeout[i] = 0;
+
}
//kick lagging players... unless everyone but the server's ping sucks.
@@ -4971,12 +5344,20 @@ static inline void PingUpdate(void)
{
if (playeringame[i] && laggers[i])
{
- XBOXSTATIC char buf[2];
+ pingtimeout[i]++;
+ if (pingtimeout[i] > cv_pingtimeout.value) // ok your net has been bad for too long, you deserve to die.
+ {
+ XBOXSTATIC char buf[2];
- buf[0] = (char)i;
- buf[1] = KICK_MSG_PING_HIGH;
- SendNetXCmd(XD_KICK, &buf, 2);
+ pingtimeout[i] = 0;
+
+ buf[0] = (char)i;
+ buf[1] = KICK_MSG_PING_HIGH;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ }
}
+ else // you aren't lagging, but you aren't free yet. In case you'll keep spiking, we just make the timer go back down. (Very unstable net must still get kicked).
+ pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1);
}
}
}
@@ -4991,18 +5372,89 @@ static inline void PingUpdate(void)
realpingtable[i] = 0; //Reset each as we go.
}
+ // send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked.
+ netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value;
+
//send out our ping packets
for (i = 0; i < MAXNETNODES; i++)
if (nodeingame[i])
- HSendPacket(i, true, 0, sizeof(INT32) * MAXPLAYERS);
+ HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
pingmeasurecount = 1; //Reset count
}
#endif
+static tic_t gametime = 0;
+
+#ifdef NEWPING
+static void UpdatePingTable(void)
+{
+ INT32 i;
+ if (server)
+ {
+ if (netgame && !(gametime % 35)) // update once per second.
+ PingUpdate();
+ // update node latency values so we can take an average later.
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i])
+ realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
+ pingmeasurecount++;
+ }
+}
+#endif
+
+// Handle timeouts to prevent definitive freezes from happenning
+static void HandleNodeTimeouts(void)
+{
+ INT32 i;
+ if (server)
+ for (i = 1; i < MAXNETNODES; i++)
+ if (nodeingame[i] && freezetimeout[i] < I_GetTime())
+ Net_ConnectionTimeout(i);
+}
+
+// Keep the network alive while not advancing tics!
+void NetKeepAlive(void)
+{
+ tic_t nowtime;
+ INT32 realtics;
+
+ nowtime = I_GetTime();
+ realtics = nowtime - gametime;
+
+ // return if there's no time passed since the last call
+ if (realtics <= 0) // nothing new to update
+ return;
+
+#ifdef NEWPING
+ UpdatePingTable();
+#endif
+
+ if (server)
+ CL_SendClientKeepAlive();
+
+// Sryder: What is FILESTAMP???
+FILESTAMP
+ GetPackets();
+FILESTAMP
+
+ MasterClient_Ticker();
+
+ if (client)
+ {
+ // send keep alive
+ CL_SendClientKeepAlive();
+ // No need to check for resynch because we aren't running any tics
+ }
+ // No else because no tics are being run and we can't resynch during this
+
+ Net_AckTicker();
+ HandleNodeTimeouts();
+ SV_FileSendTicker();
+}
+
void NetUpdate(void)
{
- static tic_t gametime = 0;
static tic_t resptime = 0;
tic_t nowtime;
INT32 i;
@@ -5024,16 +5476,7 @@ void NetUpdate(void)
gametime = nowtime;
#ifdef NEWPING
- if (server)
- {
- if (netgame && !(gametime % 255))
- PingUpdate();
- // update node latency values so we can take an average later.
- for (i = 0; i < MAXPLAYERS; i++)
- if (playeringame[i])
- realpingtable[i] += G_TicsToMilliseconds(GetLag(playernode[i]));
- pingmeasurecount++;
- }
+ UpdatePingTable();
#endif
if (client)
@@ -5101,12 +5544,7 @@ FILESTAMP
}
}
Net_AckTicker();
- // Handle timeouts to prevent definitive freezes from happenning
- if (server)
- for (i = 1; i < MAXNETNODES; i++)
- if (nodeingame[i] && freezetimeout[i] < I_GetTime())
- Net_ConnectionTimeout(i);
- nowtime /= NEWTICRATERATIO;
+ HandleNodeTimeouts();
if (nowtime > resptime)
{
resptime = nowtime;
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index f1abffaf..f3a9011e 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -13,11 +13,14 @@
#ifndef __D_CLISRV__
#define __D_CLISRV__
+#include "d_event.h"
#include "d_ticcmd.h"
#include "d_netcmd.h"
#include "tables.h"
#include "d_player.h"
+#include "md5.h"
+
// Network play related stuff.
// There is a data struct that stores network
// communication related stuff, and another
@@ -71,6 +74,10 @@ typedef enum
PT_CLIENT3MIS,
PT_CLIENT4CMD, // 4P
PT_CLIENT4MIS,
+ PT_BASICKEEPALIVE,// Keep the network alive during wipes, as tics aren't advanced and NetUpdate isn't called
+
+ PT_JOINCHALLENGE, // You must give a password to joinnnnn
+ PT_DOWNLOADFILESOKAY, // You can download files from the server....
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
// allows HSendPacket(*, true, *, *) to return false.
@@ -282,6 +289,8 @@ typedef struct
tic_t jointime;
+ UINT8 splitscreenindex;
+
//player->mo stuff
UINT8 hasmo; // Boolean
@@ -350,9 +359,21 @@ typedef struct
UINT8 version; // Different versions don't work
UINT8 subversion; // Contains build version
UINT8 localplayers;
- UINT8 mode;
+ UINT8 needsdownload;
+ UINT8 challengenum; // Non-zero if trying to join with a password attempt
+ UINT8 challengeanswer[MD5_LEN]; // Join challenge
} ATTRPACK clientconfig_pak;
+typedef struct
+{
+ UINT8 challengenum; // Number to send back in join attempt
+ UINT8 question[MD5_LEN]; // Challenge data to be manipulated and answered with
+} ATTRPACK joinchallenge_pak;
+
+#define SV_SPEEDMASK 0x03
+#define SV_DEDICATED 0x40
+#define SV_PASSWORD 0x80
+
#define MAXSERVERNAME 32
#define MAXFILENEEDED 915
// This packet is too large
@@ -365,7 +386,7 @@ typedef struct
UINT8 gametype;
UINT8 modifiedgame;
UINT8 cheatsenabled;
- UINT8 isdedicated;
+ UINT8 kartvars; // Previously isdedicated, now appropriated for our own nefarious purposes
UINT8 fileneedednum;
SINT8 adminplayer;
tic_t time;
@@ -433,10 +454,10 @@ typedef struct
UINT8 reserved; // Padding
union
{
- clientcmd_pak clientpak; // 144 bytes
- client2cmd_pak client2pak; // 200 bytes
- client3cmd_pak client3pak; // 256 bytes(?)
- client4cmd_pak client4pak; // 312 bytes(?)
+ clientcmd_pak clientpak; // 145 bytes
+ client2cmd_pak client2pak; // 202 bytes
+ client3cmd_pak client3pak; // 258 bytes(?)
+ client4cmd_pak client4pak; // 316 bytes(?)
servertics_pak serverpak; // 132495 bytes (more around 360, no?)
serverconfig_pak servercfg; // 773 bytes
resynchend_pak resynchend; //
@@ -444,15 +465,16 @@ typedef struct
UINT8 resynchgot; //
UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...)
filetx_pak filetxpak; // 139 bytes
- clientconfig_pak clientcfg; // 136 bytes
+ clientconfig_pak clientcfg; // 153 bytes
+ joinchallenge_pak joinchallenge; // 17 bytes
serverinfo_pak serverinfo; // 1024 bytes
serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
askinfo_pak askinfo; // 61 bytes
msaskinfo_pak msaskinfo; // 22 bytes
- plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes (I'd say 36~38)
- plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE)
+ plrinfo playerinfo[MAXPLAYERS]; // 576 bytes(?)
+ plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?)
#ifdef NEWPING
- UINT32 pingtable[MAXPLAYERS]; // 128 bytes
+ UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes
#endif
} u; // This is needed to pack diff packet types data together
} ATTRPACK doomdata_t;
@@ -518,13 +540,14 @@ extern tic_t jointimeout;
extern UINT16 pingmeasurecount;
extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS];
+extern tic_t servermaxping;
#endif
extern consvar_t
#ifdef VANILLAJOINNEXTROUND
cv_joinnextround,
#endif
- cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed;
+ cv_netticbuffer, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed;
// Used in d_net, the only dependence
tic_t ExpandTics(INT32 low);
@@ -538,6 +561,7 @@ void SendNetXCmd3(netxcmd_t id, const void *param, size_t nparam); // splitsreen
void SendNetXCmd4(netxcmd_t id, const void *param, size_t nparam); // splitsreen4 player
// Create any new ticcmds and broadcast to other players.
+void NetKeepAlive(void);
void NetUpdate(void);
void SV_StartSinglePlayerServer(void);
@@ -550,6 +574,7 @@ void CL_RemoveSplitscreenPlayer(UINT8 p);
void CL_Reset(void);
void CL_ClearPlayer(INT32 playernum);
void CL_UpdateServerList(boolean internetsearch, INT32 room);
+boolean CL_Responder(event_t *ev);
// Is there a game running
boolean Playing(void);
diff --git a/src/d_main.c b/src/d_main.c
index 7cc4e18d..4ed701b1 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -111,6 +111,7 @@ UINT8 window_notinfocus = false;
//static INT32 demosequence;
static const char *pagename = "MAP1PIC";
static char *startupwadfiles[MAX_WADFILES];
+static char *startuppwads[MAX_WADFILES];
boolean devparm = false; // started game with -devparm
@@ -203,6 +204,8 @@ static inline void D_ModifierKeyResponder(event_t *ev)
case KEY_RCTRL: ctrldown |= 0x2; return;
case KEY_LALT: altdown |= 0x1; return;
case KEY_RALT: altdown |= 0x2; return;
+ case KEY_CAPSLOCK: capslock = !capslock; return;
+
default: return;
}
else if (ev->type == ev_keyup) switch (ev->data1)
@@ -236,6 +239,9 @@ void D_ProcessEvents(void)
if (M_ScreenshotResponder(ev))
continue; // ate the event
+ if (CL_Responder(ev))
+ continue;
+
if (gameaction == ga_nothing && gamestate == GS_TITLESCREEN)
{
if (cht_Responder(ev))
@@ -821,12 +827,12 @@ void D_StartTitle(void)
//
// D_AddFile
//
-static void D_AddFile(const char *file)
+static void D_AddFile(const char *file, char **filearray)
{
size_t pnumwadfiles;
char *newfile;
- for (pnumwadfiles = 0; startupwadfiles[pnumwadfiles]; pnumwadfiles++)
+ for (pnumwadfiles = 0; filearray[pnumwadfiles]; pnumwadfiles++)
;
newfile = malloc(strlen(file) + 1);
@@ -836,16 +842,16 @@ static void D_AddFile(const char *file)
}
strcpy(newfile, file);
- startupwadfiles[pnumwadfiles] = newfile;
+ filearray[pnumwadfiles] = newfile;
}
-static inline void D_CleanFile(void)
+static inline void D_CleanFile(char **filearray)
{
size_t pnumwadfiles;
- for (pnumwadfiles = 0; startupwadfiles[pnumwadfiles]; pnumwadfiles++)
+ for (pnumwadfiles = 0; filearray[pnumwadfiles]; pnumwadfiles++)
{
- free(startupwadfiles[pnumwadfiles]);
- startupwadfiles[pnumwadfiles] = NULL;
+ free(filearray[pnumwadfiles]);
+ filearray[pnumwadfiles] = NULL;
}
}
@@ -905,9 +911,9 @@ static void IdentifyVersion(void)
// Load the IWAD
if (srb2wad2 != NULL && FIL_ReadFileOK(srb2wad2))
- D_AddFile(srb2wad2);
+ D_AddFile(srb2wad2, startupwadfiles);
else if (srb2wad1 != NULL && FIL_ReadFileOK(srb2wad1))
- D_AddFile(srb2wad1);
+ D_AddFile(srb2wad1, startupwadfiles);
else
I_Error("SRB2.SRB/SRB2.WAD not found! Expected in %s, ss files: %s or %s\n", srb2waddir, srb2wad1, srb2wad2);
@@ -924,12 +930,12 @@ static void IdentifyVersion(void)
D_AddFile(va(pandf,srb2waddir,"patch.dta"));
#endif
- D_AddFile(va(pandf,srb2waddir,"gfx.kart"));
- D_AddFile(va(pandf,srb2waddir,"textures.kart"));
- D_AddFile(va(pandf,srb2waddir,"chars.kart"));
- D_AddFile(va(pandf,srb2waddir,"maps.kart"));
+ D_AddFile(va(pandf,srb2waddir,"gfx.kart"), startupwadfiles);
+ D_AddFile(va(pandf,srb2waddir,"textures.kart"), startupwadfiles);
+ D_AddFile(va(pandf,srb2waddir,"chars.kart"), startupwadfiles);
+ D_AddFile(va(pandf,srb2waddir,"maps.kart"), startupwadfiles);
#ifdef USE_PATCH_KART
- D_AddFile(va(pandf,srb2waddir,"patch.kart"));
+ D_AddFile(va(pandf,srb2waddir,"patch.kart"), startupwadfiles);
#endif
#if !defined (HAVE_SDL) || defined (HAVE_MIXER)
@@ -938,7 +944,7 @@ static void IdentifyVersion(void)
const char *musicpath = va(pandf,srb2waddir,str);\
int ms = W_VerifyNMUSlumps(musicpath); \
if (ms == 1) \
- D_AddFile(musicpath); \
+ D_AddFile(musicpath, startupwadfiles); \
else if (ms == 0) \
I_Error("File "str" has been modified with non-music/sound lumps"); \
}
@@ -1003,9 +1009,12 @@ static inline void D_MakeTitleString(char *s)
//
void D_SRB2Main(void)
{
- INT32 p;
+ INT32 p, i;
char srb2[82]; // srb2 title banner
char title[82];
+ lumpinfo_t *lumpinfo;
+ UINT16 wadnum;
+ char *name;
INT32 pstartmap = 1;
boolean autostart = false;
@@ -1157,11 +1166,7 @@ void D_SRB2Main(void)
const char *s = M_GetNextParm();
if (s) // Check for NULL?
- {
- if (!W_VerifyNMUSlumps(s))
- G_SetGameModified(true);
- D_AddFile(s);
- }
+ D_AddFile(s, startuppwads);
}
}
}
@@ -1186,7 +1191,7 @@ void D_SRB2Main(void)
else
{
if (!M_CheckParm("-server"))
- G_SetGameModified(true);
+ G_SetGameModified(true, true);
autostart = true;
}
}
@@ -1211,13 +1216,13 @@ void D_SRB2Main(void)
// load wad, including the main wad file
CONS_Printf("W_InitMultipleFiles(): Adding IWAD and main PWADs.\n");
- if (!W_InitMultipleFiles(startupwadfiles))
+ if (!W_InitMultipleFiles(startupwadfiles, false))
#ifdef _DEBUG
CONS_Error("A WAD file was not found or not valid.\nCheck the log to see which ones.\n");
#else
I_Error("A WAD file was not found or not valid.\nCheck the log to see which ones.\n");
#endif
- D_CleanFile();
+ D_CleanFile(startupwadfiles);
mainwads = 0;
@@ -1231,7 +1236,7 @@ void D_SRB2Main(void)
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_GFX_KART); // gfx.kart
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_TEXTURES_KART); // textures.kart
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_CHARS_KART); // chars.kart
- mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_MAPS_KART); // maps.kart
+ mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_MAPS_KART); // maps.kart -- 4 - If you touch this, make sure to touch up the majormods stuff below.
#ifdef USE_PATCH_KART
mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_PATCH_KART); // patch.kart
#endif
@@ -1251,6 +1256,66 @@ void D_SRB2Main(void)
mainwadstally = packetsizetally;
+ //
+ // search for maps
+ //
+ for (wadnum = 4; wadnum < 6; wadnum++) // fucking arbitrary numbers
+ {
+ lumpinfo = wadfiles[wadnum]->lumpinfo;
+ for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++)
+ {
+ name = lumpinfo->name;
+
+ if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers
+ {
+ INT16 num;
+ if (name[5] != '\0')
+ continue;
+ num = (INT16)M_MapNumber(name[3], name[4]);
+
+ // we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant
+ if (num <= NUMMAPS && mapheaderinfo[num - 1])
+ {
+ mapheaderinfo[num - 1]->menuflags |= LF2_EXISTSHACK;
+ }
+ }
+ }
+ }
+
+ if (!W_InitMultipleFiles(startuppwads, true))
+ CONS_Error("A PWAD file was not found or not valid.\nCheck the log to see which ones.\n");
+ D_CleanFile(startuppwads);
+
+ //
+ // search for maps... again.
+ //
+ for (wadnum = mainwads+1; wadnum < numwadfiles; wadnum++)
+ {
+ lumpinfo = wadfiles[wadnum]->lumpinfo;
+ for (i = 0; i < wadfiles[wadnum]->numlumps; i++, lumpinfo++)
+ {
+ name = lumpinfo->name;
+
+ if (name[0] == 'M' && name[1] == 'A' && name[2] == 'P') // Ignore the headers
+ {
+ INT16 num;
+ if (name[5] != '\0')
+ continue;
+ num = (INT16)M_MapNumber(name[3], name[4]);
+
+ // we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant
+ if (num <= NUMMAPS && mapheaderinfo[num - 1])
+ {
+ if (mapheaderinfo[num - 1]->menuflags & LF2_EXISTSHACK)
+ G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you
+ mapheaderinfo[num - 1]->menuflags |= LF2_EXISTSHACK;
+ }
+
+ CONS_Printf("%s\n", name);
+ }
+ }
+ }
+
cht_Init();
//---------------------------------------------------- READY SCREEN
@@ -1322,10 +1387,6 @@ void D_SRB2Main(void)
midi_disabled = true;
#endif
}
- else
- {
- CONS_Printf("S_InitSfxChannels(): Setting up sound channels.\n");
- }
if (M_CheckParm("-nosound"))
sound_disabled = true;
if (M_CheckParm("-nomusic")) // combines -nomidimusic and -nodigmusic
@@ -1344,10 +1405,18 @@ void D_SRB2Main(void)
if (M_CheckParm("-nodigmusic"))
digital_disabled = true; // WARNING: DOS version initmusic in I_StartupSound
}
- I_StartupSound();
- I_InitMusic();
- S_InitSfxChannels(cv_soundvolume.value);
- S_InitMusicDefs();
+ if (!( sound_disabled && digital_disabled
+#ifndef NO_MIDI
+ && midi_disabled
+#endif
+ ))
+ {
+ CONS_Printf("S_InitSfxChannels(): Setting up sound channels.\n");
+ I_StartupSound();
+ I_InitMusic();
+ S_InitSfxChannels(cv_soundvolume.value);
+ S_InitMusicDefs();
+ }
CONS_Printf("ST_Init(): Init status bar.\n");
ST_Init();
diff --git a/src/d_net.c b/src/d_net.c
index 62301dc1..9f719967 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -821,10 +821,6 @@ static const char *packettypename[NUMPACKETTYPE] =
"CLIENTMIS",
"CLIENT2CMD",
"CLIENT2MIS",
- "CLIENT3CMD",
- "CLIENT3MIS",
- "CLIENT4CMD",
- "CLIENT4MIS",
"NODEKEEPALIVE",
"NODEKEEPALIVEMIS",
"SERVERTICS",
@@ -841,6 +837,15 @@ static const char *packettypename[NUMPACKETTYPE] =
"RESYNCHEND",
"RESYNCHGET",
+ "CLIENT3CMD",
+ "CLIENT3MIS",
+ "CLIENT4CMD",
+ "CLIENT4MIS",
+ "BASICKEEPALIVE",
+
+ "JOINCHALLENGE",
+ "DOWNLOADFILESOKAY",
+
"FILEFRAGMENT",
"TEXTCMD",
"TEXTCMD2",
@@ -868,7 +873,7 @@ static void DebugPrintpacket(const char *header)
break;
case PT_CLIENTJOIN:
fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers,
- netbuffer->u.clientcfg.mode);
+ netbuffer->u.clientcfg.needsdownload);
break;
case PT_SERVERTICS:
{
@@ -903,6 +908,9 @@ static void DebugPrintpacket(const char *header)
(UINT32)ExpandTics(netbuffer->u.clientpak.client_tic),
(UINT32)ExpandTics (netbuffer->u.clientpak.resendfrom));
break;
+ case PT_BASICKEEPALIVE:
+ fprintf(debugfile, " keep alive\n");
+ break;
case PT_TEXTCMD:
case PT_TEXTCMD2:
case PT_TEXTCMD3:
diff --git a/src/d_net.h b/src/d_net.h
index 9cc1bbd2..8e518e40 100644
--- a/src/d_net.h
+++ b/src/d_net.h
@@ -19,7 +19,7 @@
#define __D_NET__
// Max computers in a game
-#define MAXNETNODES 16
+#define MAXNETNODES (MAXPLAYERS+4)
#define BROADCASTADDR MAXNETNODES
#define MAXSPLITSCREENPLAYERS 4 // Max number of players on a single computer
#define NETSPLITSCREEN // Kart's splitscreen netgame feature
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index e15ed9aa..08bf3318 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -169,6 +169,7 @@ static void Got_Verification(UINT8 **cp, INT32 playernum);
static void Got_Removal(UINT8 **cp, INT32 playernum);
static void Command_Verify_f(void);
static void Command_RemoveAdmin_f(void);
+static void Command_ChangeJoinPassword_f(void);
static void Command_MotD_f(void);
static void Got_MotD_f(UINT8 **cp, INT32 playernum);
@@ -214,7 +215,7 @@ static CV_PossibleValue_t autobalance_cons_t[] = {{0, "MIN"}, {4, "MAX"}, {0, NU
static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}};
static CV_PossibleValue_t startingliveslimit_cons_t[] = {{1, "MIN"}, {99, "MAX"}, {0, NULL}};
-static CV_PossibleValue_t sleeping_cons_t[] = {{-1, "MIN"}, {1000/TICRATE, "MAX"}, {0, NULL}};
+static CV_PossibleValue_t sleeping_cons_t[] = {{0, "MIN"}, {1000/TICRATE, "MAX"}, {0, NULL}};
static CV_PossibleValue_t competitionboxes_cons_t[] = {{0, "Normal"}, {1, "Random"}, {2, "Teleports"},
{3, "None"}, {0, NULL}};
@@ -236,6 +237,9 @@ static consvar_t cv_dummyconsvar = {"dummyconsvar", "Off", CV_CALL|CV_NOSHOWHELP
consvar_t cv_restrictskinchange = {"restrictskinchange", "No", CV_NETVAR|CV_CHEAT, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_allowteamchange = {"allowteamchange", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
+static CV_PossibleValue_t ingamecap_cons_t[] = {{0, "MIN"}, {MAXPLAYERS-1, "MAX"}, {0, NULL}};
+consvar_t cv_ingamecap = {"ingamecap", "0", CV_NETVAR, ingamecap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
consvar_t cv_startinglives = {"startinglives", "3", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, startingliveslimit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t respawntime_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}};
@@ -357,7 +361,7 @@ consvar_t cv_kartfrantic = {"kartfrantic", "Off", CV_NETVAR|CV_CHEAT|CV_CALL|CV_
consvar_t cv_kartcomeback = {"kartcomeback", "On", CV_NETVAR|CV_CHEAT|CV_CALL|CV_NOINIT, CV_OnOff, KartComeback_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartencore = {"kartencore", "Off", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartEncore_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartvoterulechanges_cons_t[] = {{0, "Never"}, {1, "Sometimes"}, {2, "Frequent"}, {3, "Always"}, {0, NULL}};
-consvar_t cv_kartvoterulechanges = {"kartvoterulechanges", "Sometimes", CV_NETVAR, kartvoterulechanges_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_kartvoterulechanges = {"kartvoterulechanges", "Frequent", CV_NETVAR, kartvoterulechanges_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t kartspeedometer_cons_t[] = {{0, "Off"}, {1, "Kilometers"}, {2, "Miles"}, {3, "Fracunits"}, {0, NULL}};
consvar_t cv_kartspeedometer = {"kartdisplayspeed", "Off", CV_SAVE, kartspeedometer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; // use tics in display
static CV_PossibleValue_t kartvoices_cons_t[] = {{0, "Never"}, {1, "Tasteful"}, {2, "Meme"}, {0, NULL}};
@@ -375,6 +379,7 @@ consvar_t cv_kartdebughuddrop = {"kartdebughuddrop", "Off", CV_NETVAR|CV_CHEAT|C
consvar_t cv_kartdebugcheckpoint = {"kartdebugcheckpoint", "Off", CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_kartdebugnodes = {"kartdebugnodes", "Off", CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_kartdebugcolorize = {"kartdebugcolorize", "Off", CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t votetime_cons_t[] = {{10, "MIN"}, {3600, "MAX"}, {0, NULL}};
consvar_t cv_votetime = {"votetime", "20", CV_NETVAR, votetime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -432,6 +437,14 @@ consvar_t cv_jointimeout = {"jointimeout", "105", CV_CALL|CV_SAVE, nettimeout_co
#ifdef NEWPING
static CV_PossibleValue_t maxping_cons_t[] = {{0, "MIN"}, {1000, "MAX"}, {0, NULL}};
consvar_t cv_maxping = {"maxping", "800", CV_SAVE, maxping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}};
+consvar_t cv_pingtimeout = {"pingtimeout", "10", CV_SAVE, pingtimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// show your ping on the HUD next to framerate. Defaults to warning only (shows up if your ping is > maxping)
+static CV_PossibleValue_t showping_cons_t[] = {{0, "Off"}, {1, "Always"}, {2, "Warning"}, {0, NULL}};
+consvar_t cv_showping = {"showping", "Always", CV_SAVE, showping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
#endif
// Intermission time Tails 04-19-2002
static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}};
@@ -447,7 +460,7 @@ consvar_t cv_runscripts = {"runscripts", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL
consvar_t cv_pause = {"pausepermission", "Server", CV_NETVAR, pause_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mute = {"mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_OnChange, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_sleep = {"cpusleep", "-1", CV_SAVE, sleeping_cons_t, NULL, -1, NULL, NULL, 0, 0, NULL};
+consvar_t cv_sleep = {"cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL, -1, NULL, NULL, 0, 0, NULL};
INT16 gametype = GT_RACE; // SRB2kart
boolean forceresetplayers = false;
@@ -483,6 +496,7 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
"SETUPVOTE",
"MODIFYVOTE",
"PICKVOTE",
+ "REMOVEPLAYER",
#ifdef HAVE_BLUA
"LUACMD",
"LUAVAR"
@@ -521,6 +535,8 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_PICKVOTE, Got_PickVotecmd);
// Remote Administration
+ CV_RegisterVar(&cv_dummyjoinpassword);
+ COM_AddCommand("joinpassword", Command_ChangeJoinPassword_f);
COM_AddCommand("password", Command_Changepassword_f);
RegisterNetXCmd(XD_LOGIN, Got_Login);
COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
@@ -641,11 +657,13 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_allowexitlevel);
CV_RegisterVar(&cv_restrictskinchange);
CV_RegisterVar(&cv_allowteamchange);
+ CV_RegisterVar(&cv_ingamecap);
CV_RegisterVar(&cv_respawntime);
CV_RegisterVar(&cv_killingdead);
// d_clisrv
CV_RegisterVar(&cv_maxplayers);
+ CV_RegisterVar(&cv_resynchattempts);
CV_RegisterVar(&cv_maxsend);
CV_RegisterVar(&cv_noticedownload);
CV_RegisterVar(&cv_downloadspeed);
@@ -658,6 +676,8 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_sleep);
#ifdef NEWPING
CV_RegisterVar(&cv_maxping);
+ CV_RegisterVar(&cv_pingtimeout);
+ CV_RegisterVar(&cv_showping);
#endif
#ifdef SEENAMES
@@ -761,6 +781,7 @@ void D_RegisterClientCommands(void)
#endif
CV_RegisterVar(&cv_rollingdemos);
CV_RegisterVar(&cv_netstat);
+ CV_RegisterVar(&cv_netticbuffer);
#ifdef NETGAME_DEVMODE
CV_RegisterVar(&cv_fishcake);
@@ -800,6 +821,8 @@ void D_RegisterClientCommands(void)
//CV_RegisterVar(&cv_alwaysfreelook2);
//CV_RegisterVar(&cv_chasefreelook);
//CV_RegisterVar(&cv_chasefreelook2);
+ CV_RegisterVar(&cv_showfocuslost);
+ CV_RegisterVar(&cv_pauseifunfocused);
// g_input.c
CV_RegisterVar(&cv_turnaxis);
@@ -839,14 +862,6 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_addons_search_type);
CV_RegisterVar(&cv_addons_search_case);
- // filesrch.c
- CV_RegisterVar(&cv_addons_option);
- CV_RegisterVar(&cv_addons_folder);
- CV_RegisterVar(&cv_addons_md5);
- CV_RegisterVar(&cv_addons_showall);
- CV_RegisterVar(&cv_addons_search_type);
- CV_RegisterVar(&cv_addons_search_case);
-
// WARNING: the order is important when initialising mouse2
// we need the mouse2port
CV_RegisterVar(&cv_mouse2port);
@@ -2214,10 +2229,12 @@ static void Command_Map_f(void)
return;
}
- if (!(netgame || multiplayer) && (!modifiedgame || savemoddata))
+ if (!(netgame || multiplayer) && !majormods)
{
if (COM_CheckParm("-force"))
- G_SetGameModified(false);
+ {
+ G_SetGameModified(false, true);
+ }
else
{
CONS_Printf(M_GetText("Sorry, level change disabled in single player.\n"));
@@ -2501,6 +2518,12 @@ static void Command_Respawn(void)
return;
}
+ if (players[consoleplayer].mo && !P_IsObjectOnGround(players[consoleplayer].mo)) // KART: Nice try, but no, you won't be cheesing spb anymore.
+ {
+ CONS_Printf(M_GetText("You must be on the floor to use this.\n"));
+ return;
+ }
+
/*if (!G_RaceGametype()) // srb2kart: not necessary, respawning makes you lose a bumper in battle, so it's not desirable to use as a way to escape a hit
{
CONS_Printf(M_GetText("You may only use this in co-op, race, and competition!\n"));
@@ -2522,7 +2545,7 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum)
{
INT32 respawnplayer = READINT32(*cp);
- // You can't respawn someone else. Nice try, there.
+ // You can't respawn someone else. Nice try, there.
if (respawnplayer != playernum) // srb2kart: "|| (!G_RaceGametype())"
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal respawn command received from %s\n"), player_names[playernum]);
@@ -2537,6 +2560,10 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum)
return;
}
+ // incase the above checks were modified to allow sending a respawn on these occasions:
+ if (players[respawnplayer].mo && !P_IsObjectOnGround(players[respawnplayer].mo))
+ return;
+
if (players[respawnplayer].mo)
P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 10000);
}
@@ -3405,6 +3432,7 @@ static void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt,
if (len > 256-sl)
len = 256-sl;
+
memcpy(tmpbuf, buffer, len);
memmove(&tmpbuf[len], salt, sl);
//strcpy(&tmpbuf[len], salt);
@@ -3418,7 +3446,7 @@ static void D_MD5PasswordPass(const UINT8 *buffer, size_t len, const char *salt,
}
#define BASESALT "basepasswordstorage"
-static UINT8 adminpassmd5[16];
+static UINT8 adminpassmd5[MD5_LEN];
static boolean adminpasswordset = false;
void D_SetPassword(const char *pw)
@@ -3457,7 +3485,7 @@ static void Command_Login_f(void)
// If we have no MD5 support then completely disable XD_LOGIN responses for security.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else
- XBOXSTATIC UINT8 finalmd5[16];
+ XBOXSTATIC UINT8 finalmd5[MD5_LEN];
const char *pw;
if (!netgame)
@@ -3480,11 +3508,11 @@ static void Command_Login_f(void)
D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &finalmd5);
// Do the final pass to get the comparison the server will come up with
- D_MD5PasswordPass(finalmd5, 16, va("PNUM%02d", consoleplayer), &finalmd5);
+ D_MD5PasswordPass(finalmd5, MD5_LEN, va("PNUM%02d", consoleplayer), &finalmd5);
CONS_Printf(M_GetText("Sending login... (Notice only given if password is correct.)\n"));
- SendNetXCmd(XD_LOGIN, finalmd5, 16);
+ SendNetXCmd(XD_LOGIN, finalmd5, MD5_LEN);
#endif
}
@@ -3495,9 +3523,9 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
(void)cp;
(void)playernum;
#else
- UINT8 sentmd5[16], finalmd5[16];
+ UINT8 sentmd5[MD5_LEN], finalmd5[MD5_LEN];
- READMEM(*cp, sentmd5, 16);
+ READMEM(*cp, sentmd5, MD5_LEN);
if (client)
return;
@@ -3509,9 +3537,9 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
}
// Do the final pass to compare with the sent md5
- D_MD5PasswordPass(adminpassmd5, 16, va("PNUM%02d", playernum), &finalmd5);
+ D_MD5PasswordPass(adminpassmd5, MD5_LEN, va("PNUM%02d", playernum), &finalmd5);
- if (!memcmp(sentmd5, finalmd5, 16))
+ if (!memcmp(sentmd5, finalmd5, MD5_LEN))
{
CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[playernum]);
COM_BufInsertText(va("promote %d\n", playernum)); // do this immediately
@@ -3680,6 +3708,131 @@ static void Got_Removal(UINT8 **cp, INT32 playernum)
CONS_Printf(M_GetText("You are no longer a server administrator.\n"));
}
+// Join password stuff
+consvar_t cv_dummyjoinpassword = {"dummyjoinpassword", "", CV_HIDEN|CV_NOSHOWHELP|CV_PASSWORD, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+#define NUMJOINCHALLENGES 32
+static UINT8 joinpassmd5[MD5_LEN+1];
+boolean joinpasswordset = false;
+static UINT8 joinpasschallenges[NUMJOINCHALLENGES][MD5_LEN];
+static tic_t joinpasschallengeson[NUMJOINCHALLENGES];
+
+boolean D_IsJoinPasswordOn(void)
+{
+ return joinpasswordset;
+}
+
+static inline void GetChallengeAnswer(UINT8 *question, UINT8 *passwordmd5, UINT8 *answer)
+{
+ D_MD5PasswordPass(question, MD5_LEN, (char *) passwordmd5, answer);
+}
+
+void D_ComputeChallengeAnswer(UINT8 *question, const char *pw, UINT8 *answer)
+{
+ static UINT8 passwordmd5[MD5_LEN+1];
+
+ memset(passwordmd5, 0x00, MD5_LEN+1);
+ D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &passwordmd5);
+ GetChallengeAnswer(question, passwordmd5, answer);
+}
+
+void D_SetJoinPassword(const char *pw)
+{
+ memset(joinpassmd5, 0x00, MD5_LEN+1);
+ D_MD5PasswordPass((const UINT8 *)pw, strlen(pw), BASESALT, &joinpassmd5);
+ joinpasswordset = true;
+}
+
+boolean D_VerifyJoinPasswordChallenge(UINT8 num, UINT8 *answer)
+{
+ boolean passed = false;
+
+ num %= NUMJOINCHALLENGES;
+
+ //@TODO use a constant-time memcmp....
+ if (joinpasschallengeson[num] > 0 && memcmp(answer, joinpasschallenges[num], MD5_LEN) == 0)
+ passed = true;
+
+ // Wipe and reset the challenge so that it can't be tried against again, as a small measure against brute-force attacks.
+ memset(joinpasschallenges[num], 0x00, MD5_LEN);
+ joinpasschallengeson[num] = 0;
+
+ return passed;
+}
+
+void D_MakeJoinPasswordChallenge(UINT8 *num, UINT8 *question)
+{
+ size_t i;
+
+ for (i = 0; i < NUMJOINCHALLENGES; i++)
+ {
+ (*num) = M_RandomKey(NUMJOINCHALLENGES);
+
+ if (joinpasschallengeson[(*num)] == 0)
+ break;
+ }
+
+ if (joinpasschallengeson[(*num)] > 0)
+ {
+ // Ugh, all challenges are (probably) taken. Let's find the oldest one and overwrite it.
+ tic_t oldesttic = INT32_MAX;
+
+ for (i = 0; i < NUMJOINCHALLENGES; i++)
+ {
+ if (joinpasschallengeson[i] < oldesttic)
+ {
+ (*num) = i;
+ oldesttic = joinpasschallengeson[i];
+ }
+ }
+ }
+
+ joinpasschallengeson[(*num)] = I_GetTime();
+
+ memset(question, 0x00, MD5_LEN);
+ for (i = 0; i < MD5_LEN; i++)
+ question[i] = M_RandomByte();
+
+ // Store the answer in memory. What was the question again?
+ GetChallengeAnswer(question, joinpassmd5, joinpasschallenges[(*num)]);
+
+ // This ensures that num is always non-zero and will be valid when used for the answer
+ if ((*num) == 0)
+ (*num) = NUMJOINCHALLENGES;
+}
+
+// Remote Administration
+static void Command_ChangeJoinPassword_f(void)
+{
+#ifdef NOMD5
+ // If we have no MD5 support then completely disable XD_LOGIN responses for security.
+ CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
+#else
+ if (client) // cannot change remotely
+ {
+ CONS_Printf(M_GetText("Only the server can use this.\n"));
+ return;
+ }
+
+ if (COM_Argc() != 2)
+ {
+ CONS_Printf(M_GetText("joinpassword : set a password to join the server\nUse -remove to disable the password.\n"));
+ return;
+ }
+
+ if (strcmp(COM_Argv(1), "-remove") == 0)
+ {
+ joinpasswordset = false;
+ CONS_Printf(M_GetText("Join password removed.\n"));
+ }
+ else
+ {
+ D_SetJoinPassword(COM_Argv(1));
+ CONS_Printf(M_GetText("Join password set.\n"));
+ }
+#endif
+}
+
static void Command_MotD_f(void)
{
size_t i, j;
@@ -3786,7 +3939,7 @@ static void Command_RunSOC(void)
if (!P_RunSOC(fn))
CONS_Printf(M_GetText("Could not find SOC.\n"));
else
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, false);
return;
}
@@ -3840,7 +3993,7 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum)
}
P_RunSOC(filename);
- G_SetGameModified(true);
+ G_SetGameModified(true, false);
}
/** Adds a pwad at runtime.
@@ -3877,7 +4030,7 @@ static void Command_Addfile(void)
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
}
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, false);
}
// Add file on your client directly if it is trivial, or you aren't in a netgame.
@@ -4123,7 +4276,7 @@ static void Got_Addfilecmd(UINT8 **cp, INT32 playernum)
return;
}
- G_SetGameModified(true);
+ G_SetGameModified(true, false);
}
static void Command_ListWADS_f(void)
@@ -4480,7 +4633,7 @@ static void Ringslinger_OnChange(void)
}
if (cv_ringslinger.value) // Only if it's been turned on
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
}
static void Gravity_OnChange(void)
@@ -4501,7 +4654,7 @@ static void Gravity_OnChange(void)
#endif
if (!CV_IsSetToDefault(&cv_gravity))
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
gravity = cv_gravity.value;
}
@@ -4897,7 +5050,7 @@ static void Fishcake_OnChange(void)
// so don't make modifiedgame always on!
if (cv_debug)
{
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
}
else if (cv_debug != cv_fishcake.value)
@@ -4913,12 +5066,14 @@ static void Fishcake_OnChange(void)
*/
static void Command_Isgamemodified_f(void)
{
- if (savemoddata)
- CONS_Printf(M_GetText("modifiedgame is true, but you can save medal and record data in this mod.\n"));
+ if (majormods)
+ CONS_Printf("The game has been modified with major add-ons, so you cannot play Record Attack.\n");
+ else if (savemoddata)
+ CONS_Printf("The game has been modified with an add-on with its own save data, so you can play Record Attack and earn medals.\n");
else if (modifiedgame)
- CONS_Printf(M_GetText("modifiedgame is true, extras will not be unlocked\n"));
+ CONS_Printf("The game has been modified with only minor add-ons. You can play Record Attack, earn medals and unlock extras.\n");
else
- CONS_Printf(M_GetText("modifiedgame is false, you can unlock extras\n"));
+ CONS_Printf("The game has not been modified. You can play Record Attack, earn medals and unlock extras.\n");
}
static void Command_Cheats_f(void)
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 7927aea0..166c5e00 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -33,6 +33,8 @@ extern consvar_t cv_skin3;
extern consvar_t cv_playername4;
extern consvar_t cv_playercolor4;
extern consvar_t cv_skin4;
+// preferred number of players
+extern consvar_t cv_splitplayers;
#ifdef SEENAMES
extern consvar_t cv_seenames, cv_allowseenames;
@@ -91,7 +93,7 @@ extern consvar_t cv_mute;
extern consvar_t cv_killingdead;
extern consvar_t cv_pause;
-extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_respawntime;
+extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_ingamecap, cv_respawntime;
/*extern consvar_t cv_teleporters, cv_superring, cv_supersneakers, cv_invincibility;
extern consvar_t cv_jumpshield, cv_watershield, cv_ringshield, cv_forceshield, cv_bombshield;
@@ -124,7 +126,7 @@ extern consvar_t cv_karteliminatelast;
extern consvar_t cv_votetime;
extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugshrink, cv_kartdebugdistribution, cv_kartdebughuddrop;
-extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes;
+extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize;
extern consvar_t cv_itemfinder;
@@ -143,6 +145,8 @@ extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionb
#ifdef NEWPING
extern consvar_t cv_maxping;
+extern consvar_t cv_pingtimeout;
+extern consvar_t cv_showping;
#endif
extern consvar_t cv_skipmapcheck;
@@ -244,6 +248,14 @@ void RemoveAdminPlayer(INT32 playernum);
void ItemFinder_OnChange(void);
void D_SetPassword(const char *pw);
+extern consvar_t cv_dummyjoinpassword;
+extern boolean joinpasswordset;
+boolean D_IsJoinPasswordOn(void);
+void D_ComputeChallengeAnswer(UINT8 *question, const char *pw, UINT8 *answer);
+void D_SetJoinPassword(const char *pw);
+boolean D_VerifyJoinPasswordChallenge(UINT8 num, UINT8 *answer);
+void D_MakeJoinPasswordChallenge(UINT8 *num, UINT8 *question);
+
// used for the player setup menu
UINT8 CanChangeSkin(INT32 playernum);
diff --git a/src/d_netfil.c b/src/d_netfil.c
index c7cfdbc1..99a05840 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -426,7 +426,7 @@ void CL_LoadServerFiles(void)
else if (fileneeded[i].status == FS_FOUND)
{
P_AddWadFile(fileneeded[i].filename);
- G_SetGameModified(true);
+ G_SetGameModified(true, false);
fileneeded[i].status = FS_OPEN;
}
else if (fileneeded[i].status == FS_MD5SUMBAD)
diff --git a/src/d_player.h b/src/d_player.h
index 2d9ed5c8..decd9655 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -32,13 +32,7 @@
// Extra abilities/settings for skins (combinable stuff)
typedef enum
{
- SF_SUPER = 1, // Can turn super in singleplayer/co-op mode.
- SF_SUPERANIMS = 1<<1, // If super, use the super sonic animations
- SF_SUPERSPIN = 1<<2, // Should spin frames be played while super?
- SF_HIRES = 1<<3, // Draw the sprite 2x as small?
- SF_NOSKID = 1<<4, // No skid particles etc
- SF_NOSPEEDADJUST = 1<<5, // Skin-specific version of disablespeedadjust
- SF_RUNONWATER = 1<<6, // Run on top of water FOFs?
+ SF_HIRES = 1, // Draw the sprite 2x as small?
} skinflags_t;
//Primary and secondary skin abilities
@@ -275,6 +269,7 @@ typedef enum
k_nextcheck, // Next checkpoint distance; for p_user.c (was "pw_ncd")
k_waypoint, // Waypoints.
k_starpostwp, // Temporarily stores player waypoint for... some reason. Used when respawning and finishing.
+ k_starpostflip, // the last starpost we hit requires flipping?
k_respawn, // Timer for the DEZ laser respawn effect
k_dropdash, // Charge up for respawn Drop Dash
@@ -348,10 +343,13 @@ typedef enum
k_wanted, // Timer for determining WANTED status, lowers when hitting people, prevents the game turning into Camp Lazlo
k_yougotem, // "You Got Em" gfx when hitting someone as a karma player via a method that gets you back in the game instantly
- // v1.0.2 vars
+ // v1.0.2+ vars
k_itemblink, // Item flashing after roulette, prevents Hyudoro stealing AND serves as a mashing indicator
k_itemblinkmode, // Type of flashing: 0 = white (normal), 1 = red (mashing), 2 = rainbow (enhanced items)
k_getsparks, // Disable drift sparks at low speed, JUST enough to give acceleration the actual headstart above speed
+ k_jawztargetdelay, // Delay for Jawz target switching, to make it less twitchy
+ k_spectatewait, // How long have you been waiting as a spectator
+ k_growcancel, // Hold the item button down to cancel Grow
NUMKARTSTUFF
} kartstufftype_t;
@@ -416,6 +414,8 @@ typedef struct player_s
// SRB2kart stuff
INT32 kartstuff[NUMKARTSTUFF];
angle_t frameangle; // for the player add the ability to have the sprite only face other angles
+ INT16 lturn_max[MAXPREDICTTICS]; // What's the expected turn value for full-left for a number of frames back (to account for netgame latency)?
+ INT16 rturn_max[MAXPREDICTTICS]; // Ditto but for full-right
// Bit flags.
// See pflags_t, above.
@@ -567,6 +567,8 @@ typedef struct player_s
UINT8 bot;
tic_t jointime; // Timer when player joins game to change skin/color
+
+ UINT8 splitscreenindex;
#ifdef HWRENDER
fixed_t fovadd; // adjust FOV for hw rendering
#endif
diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h
index 7df2d412..dab758f8 100644
--- a/src/d_ticcmd.h
+++ b/src/d_ticcmd.h
@@ -21,6 +21,8 @@
#pragma interface
#endif
+#define MAXPREDICTTICS 12
+
// Button/action code definitions.
typedef enum
{
@@ -60,6 +62,7 @@ typedef struct
INT16 aiming; // vertical aiming, see G_BuildTicCmd
UINT16 buttons;
INT16 driftturn; // SRB2Kart: Used for getting drift turn speed
+ UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end?
} ATTRPACK ticcmd_t;
#if defined(_MSC_VER)
diff --git a/src/dehacked.c b/src/dehacked.c
index 7f91b37f..1c88fe83 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -21,6 +21,7 @@
#include "w_wad.h"
#include "m_menu.h"
#include "m_misc.h"
+#include "filesrch.h" // for refreshdirmenu
#include "f_finale.h"
#include "dehacked.h"
#include "st_stuff.h"
@@ -79,8 +80,6 @@ static powertype_t get_power(const char *word);
boolean deh_loaded = false;
static int dbg_line;
-static boolean gamedataadded = false;
-
#ifdef DELFILE
typedef struct undehacked_s
{
@@ -436,11 +435,11 @@ static void readAnimTex(MYFILE *f, INT32 num)
static boolean findFreeSlot(INT32 *num)
{
// Send the character select entry to a free slot.
- while (*num < 32 && PlayerMenu[*num].status != IT_DISABLED)
+ while (*num < MAXSKINS && PlayerMenu[*num].status != IT_DISABLED)
*num = *num+1;
// No more free slots. :(
- if (*num >= 32)
+ if (*num >= MAXSKINS)
return false;
// Found one! ^_^
@@ -602,6 +601,14 @@ done:
Z_Free(s);
}
+static int freeslotusage[2][2] = {{0, 0}, {0, 0}}; // [S_, MT_][max, previous .wad's max]
+
+void DEH_UpdateMaxFreeslots(void)
+{
+ freeslotusage[0][1] = freeslotusage[0][0];
+ freeslotusage[1][1] = freeslotusage[1][0];
+}
+
// TODO: Figure out how to do undolines for this....
// TODO: Warnings for running out of freeslots
static void readfreeslots(MYFILE *f)
@@ -664,6 +671,7 @@ static void readfreeslots(MYFILE *f)
if (!FREE_STATES[i]) {
FREE_STATES[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
strcpy(FREE_STATES[i],word);
+ freeslotusage[0][0]++;
break;
}
}
@@ -673,6 +681,7 @@ static void readfreeslots(MYFILE *f)
if (!FREE_MOBJS[i]) {
FREE_MOBJS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
strcpy(FREE_MOBJS[i],word);
+ freeslotusage[1][0]++;
break;
}
}
@@ -3094,11 +3103,6 @@ static void readmaincfg(MYFILE *f)
if (creditscutscene > 128)
creditscutscene = 128;
}
- else if (fastcmp(word, "DISABLESPEEDADJUST"))
- {
- DEH_WriteUndoline(word, va("%d", disableSpeedAdjust), UNDO_NONE);
- disableSpeedAdjust = (value || word2[0] == 'T' || word2[0] == 'Y');
- }
else if (fastcmp(word, "NUMDEMOS"))
{
DEH_WriteUndoline(word, va("%d", numDemos), UNDO_NONE);
@@ -3139,6 +3143,7 @@ static void readmaincfg(MYFILE *f)
strlcpy(gamedatafilename, word2, sizeof (gamedatafilename));
strlwr(gamedatafilename);
savemoddata = true;
+ majormods = false;
// Also save a time attack folder
filenamelen = strlen(gamedatafilename)-4; // Strip off the extension
@@ -3151,7 +3156,7 @@ static void readmaincfg(MYFILE *f)
// can't use sprintf since there is %u in savegamename
strcatbf(savegamename, srb2home, PATHSEP);
- gamedataadded = true;
+ refreshdirmenu |= REFRESHDIR_GAMEDATA;
}
else if (fastcmp(word, "RESETDATA"))
{
@@ -3382,8 +3387,6 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
for (i = 0; i < NUMSFX; i++)
savesfxnames[i] = S_sfx[i].name;
- gamedataadded = false;
-
// it doesn't test the version of SRB2 and version of dehacked file
dbg_line = -1; // start at -1 so the first line is 0.
while (!myfeof(f))
@@ -3417,10 +3420,12 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
if (fastcmp(word, "FREESLOT"))
{
readfreeslots(f);
+ // This is not a major mod.
continue;
}
else if (fastcmp(word, "MAINCFG"))
{
+ G_SetGameModified(multiplayer, true);
readmaincfg(f);
DEH_WriteUndoline(word, "", UNDO_HEADER);
continue;
@@ -3429,6 +3434,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
{
readwipes(f);
DEH_WriteUndoline(word, "", UNDO_HEADER);
+ // This is not a major mod.
continue;
}
word2 = strtok(NULL, " ");
@@ -3449,6 +3455,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
ignorelines(f);
}
DEH_WriteUndoline(word, word2, UNDO_HEADER);
+ // This is not a major mod.
continue;
}
if (word2)
@@ -3462,19 +3469,25 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
// Read texture from spec file.
readtexture(f, word2);
DEH_WriteUndoline(word, word2, UNDO_HEADER);
+ // This is not a major mod.
}
else if (fastcmp(word, "PATCH"))
{
// Read patch from spec file.
readpatch(f, word2, wad);
DEH_WriteUndoline(word, word2, UNDO_HEADER);
+ // This is not a major mod.
}
else if (fastcmp(word, "THING") || fastcmp(word, "MOBJ") || fastcmp(word, "OBJECT"))
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_mobjtype(word2); // find a thing by name
if (i < NUMMOBJTYPES && i >= 0)
+ {
+ if (i < (MT_FIRSTFREESLOT+freeslotusage[1][1]))
+ G_SetGameModified(multiplayer, true); // affecting something earlier than the first freeslot allocated in this .wad? DENIED
readthing(f, i);
+ }
else
{
deh_warning("Thing %d out of range (0 - %d)", i, NUMMOBJTYPES-1);
@@ -3485,6 +3498,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
/* else if (fastcmp(word, "ANIMTEX"))
{
readAnimTex(f, i);
+ // This is not a major mod.
}*/
else if (fastcmp(word, "LIGHT"))
{
@@ -3498,6 +3512,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
ignorelines(f);
}
DEH_WriteUndoline(word, word2, UNDO_HEADER);
+ // This is not a major mod.
#endif
}
else if (fastcmp(word, "SPRITE"))
@@ -3513,6 +3528,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
ignorelines(f);
}
DEH_WriteUndoline(word, word2, UNDO_HEADER);
+ // This is not a major mod.
#endif
}
else if (fastcmp(word, "LEVEL"))
@@ -3525,7 +3541,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
i = M_MapNumber(word2[0], word2[1]);
if (i > 0 && i <= NUMMAPS)
+ {
+ if (mapheaderinfo[i])
+ G_SetGameModified(multiplayer, true); // only mark as a major mod if it replaces an already-existing mapheaderinfo
readlevelheader(f, i);
+ }
else
{
deh_warning("Level number %d out of range (1 - %d)", i, NUMMAPS);
@@ -3543,13 +3563,18 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
ignorelines(f);
}
DEH_WriteUndoline(word, word2, UNDO_HEADER);
+ //G_SetGameModified(multiplayer, true); -- might have to reconsider in a future update
}
else if (fastcmp(word, "FRAME") || fastcmp(word, "STATE"))
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_state(word2); // find a state by name
if (i < NUMSTATES && i >= 0)
+ {
+ if (i < (S_FIRSTFREESLOT+freeslotusage[0][1]))
+ G_SetGameModified(multiplayer, true); // affecting something earlier than the first freeslot allocated in this .wad? DENIED
readframe(f, i);
+ }
else
{
deh_warning("Frame %d out of range (0 - %d)", i, NUMSTATES-1);
@@ -3578,6 +3603,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
}
else
deh_warning("pointer (Frame %d) : missing ')'", i);
+ G_SetGameModified(multiplayer, true);
}*/
else if (fastcmp(word, "SOUND"))
{
@@ -3591,6 +3617,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
ignorelines(f);
}
DEH_WriteUndoline(word, word2, UNDO_HEADER);
+ // This is not a major mod.
}
/* else if (fastcmp(word, "SPRITE"))
{
@@ -3611,6 +3638,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
}
else
deh_warning("Sprite %d doesn't exist",i);
+ // This is not a major mod.
}*/
else if (fastcmp(word, "HUDITEM"))
{
@@ -3624,10 +3652,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
ignorelines(f);
}
DEH_WriteUndoline(word, word2, UNDO_HEADER);
+ // This is not a major mod.
}
else if (fastcmp(word, "EMBLEM"))
{
- if (!gamedataadded)
+ if (!(refreshdirmenu & REFRESHDIR_GAMEDATA))
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
ignorelines(f);
@@ -3647,7 +3676,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
}
else if (fastcmp(word, "EXTRAEMBLEM"))
{
- if (!gamedataadded)
+ if (!(refreshdirmenu & REFRESHDIR_GAMEDATA))
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
ignorelines(f);
@@ -3667,7 +3696,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
}
else if (fastcmp(word, "UNLOCKABLE"))
{
- if (!gamedataadded)
+ if (!(refreshdirmenu & REFRESHDIR_GAMEDATA))
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
ignorelines(f);
@@ -3683,7 +3712,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
}
else if (fastcmp(word, "CONDITIONSET"))
{
- if (!gamedataadded)
+ if (!(refreshdirmenu & REFRESHDIR_GAMEDATA))
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
ignorelines(f);
@@ -3698,13 +3727,18 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
// no undo support for this insanity yet
//DEH_WriteUndoline(word, word2, UNDO_HEADER);
}
- else if (fastcmp(word, "SRB2"))
+ else if (fastcmp(word, "SRB2KART"))
{
INT32 ver = searchvalue(strtok(NULL, "\n"));
if (ver != PATCHVERSION)
deh_warning("Patch is for SRB2Kart version %d,\nonly version %d is supported", ver, PATCHVERSION);
//DEH_WriteUndoline(word, va("%d", ver), UNDO_NONE);
}
+ else if (fastcmp(word, "SRB2"))
+ {
+ if (mainwads) // srb2.srb triggers this warning otherwise
+ deh_warning("Patch is only compatible with base SRB2.");
+ }
// Clear all data in certain locations (mostly for unlocks)
// Unless you REALLY want to piss people off,
// define a custom gamedata /before/ doing this!!
@@ -3713,7 +3747,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
{
boolean clearall = (fastcmp(word2, "ALL"));
- if (!gamedataadded)
+ if (!(refreshdirmenu & REFRESHDIR_GAMEDATA))
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
continue;
@@ -3750,8 +3784,8 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
deh_warning("No word in this line: %s", s);
} // end while
- if (gamedataadded)
- G_LoadGameData();
+ /*if (gamedataadded) -- REFRESHDIR_GAMEDATA murdered this
+ G_LoadGameData();*/
dbg_line = -1;
if (deh_num_warning)
@@ -7111,6 +7145,13 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_KARMAFIREWORK4",
"S_KARMAFIREWORKTRAIL",
+ // Opaque smoke version, to prevent lag
+ "S_OPAQUESMOKE1",
+ "S_OPAQUESMOKE2",
+ "S_OPAQUESMOKE3",
+ "S_OPAQUESMOKE4",
+ "S_OPAQUESMOKE5",
+
#ifdef SEENAMES
"S_NAMECHECK",
#endif
@@ -8095,89 +8136,164 @@ static const char *const ML_LIST[16] = {
// This DOES differ from r_draw's Color_Names, unfortunately.
// Also includes Super colors
static const char *COLOR_ENUMS[] = { // Rejigged for Kart.
- "NONE", // 00 // SKINCOLOR_NONE
- "WHITE", // 01 // SKINCOLOR_WHITE
- "SILVER", // 02 // SKINCOLOR_SILVER
- "GREY", // 03 // SKINCOLOR_GREY
- "NICKEL", // 04 // SKINCOLOR_NICKEL
- "BLACK", // 05 // SKINCOLOR_BLACK
- "SEPIA", // 06 // SKINCOLOR_SEPIA
- "BEIGE", // 07 // SKINCOLOR_BEIGE
- "BROWN", // 08 // SKINCOLOR_BROWN
- "LEATHER", // 09 // SKINCOLOR_LEATHER
- "SALMON", // 10 // SKINCOLOR_SALMON
- "PINK", // 11 // SKINCOLOR_PINK
- "ROSE", // 12 // SKINCOLOR_ROSE
- "RUBY", // 13 // SKINCOLOR_RUBY
- "RASPBERRY", // 14 // SKINCOLOR_RASPBERRY
- "RED", // 15 // SKINCOLOR_RED
- "CRIMSON", // 16 // SKINCOLOR_CRIMSON
- "KETCHUP", // 17 // SKINCOLOR_KETCHUP
- "DAWN", // 18 // SKINCOLOR_DAWN
- "CREAMSICLE", // 19 // SKINCOLOR_CREAMSICLE
- "ORANGE", // 20 // SKINCOLOR_ORANGE
- "PUMPKIN", // 21 // SKINCOLOR_PUMPKIN
- "ROSEWOOD", // 22 // SKINCOLOR_ROSEWOOD
- "BURGUNDY", // 23 // SKINCOLOR_BURGUNDY
- "TANGERINE", // 24 // SKINCOLOR_TANGERINE
- "PEACH", // 25 // SKINCOLOR_PEACH
- "CARAMEL", // 26 // SKINCOLOR_CARAMEL
- "GOLD", // 27 // SKINCOLOR_GOLD
- "BRONZE", // 28 // SKINCOLOR_BRONZE
- "YELLOW", // 29 // SKINCOLOR_YELLOW
- "MUSTARD", // 30 // SKINCOLOR_MUSTARD
- "OLIVE", // 31 // SKINCOLOR_OLIVE
- "VOMIT", // 32 // SKINCOLOR_VOMIT
- "GARDEN", // 33 // SKINCOLOR_GARDEN
- "LIME", // 34 // SKINCOLOR_LIME
- "TEA", // 35 // SKINCOLOR_TEA
- "PISTACHIO", // 36 // SKINCOLOR_PISTACHIO
- "ROBOHOOD", // 37 // SKINCOLOR_ROBOHOOD
- "MOSS", // 38 // SKINCOLOR_MOSS
- "MINT", // 39 // SKINCOLOR_MINT
- "GREEN", // 40 // SKINCOLOR_GREEN
- "PINETREE", // 41 // SKINCOLOR_PINETREE
- "EMERALD", // 42 // SKINCOLOR_EMERALD
- "SWAMP", // 43 // SKINCOLOR_SWAMP
- "DREAM", // 44 // SKINCOLOR_DREAM
- "AQUA", // 45 // SKINCOLOR_AQUA
- "TEAL", // 46 // SKINCOLOR_TEAL
- "CYAN", // 47 // SKINCOLOR_CYAN
- "JAWZ", // 48 // SKINCOLOR_JAWZ
- "CERULEAN", // 49 // SKINCOLOR_CERULEAN
- "NAVY", // 50 // SKINCOLOR_NAVY
- "SLATE", // 51 // SKINCOLOR_SLATE
- "STEEL", // 52 // SKINCOLOR_STEEL
- "JET", // 53 // SKINCOLOR_JET
- "SAPPHIRE", // 54 // SKINCOLOR_SAPPHIRE
- "PERIWINKLE", // 55 // SKINCOLOR_PERIWINKLE
- "BLUE", // 56 // SKINCOLOR_BLUE
- "BLUEBERRY", // 57 // SKINCOLOR_BLUEBERRY
- "DUSK", // 58 // SKINCOLOR_DUSK
- "PURPLE", // 59 // SKINCOLOR_PURPLE
- "LAVENDER", // 60 // SKINCOLOR_LAVENDER
- "BYZANTIUM", // 61 // SKINCOLOR_BYZANTIUM
- "POMEGRANATE", // 62 // SKINCOLOR_POMEGRANATE
- "LILAC", // 63 // SKINCOLOR_LILAC
+ "NONE", // SKINCOLOR_NONE
+ "WHITE", // SKINCOLOR_WHITE
+ "SILVER", // SKINCOLOR_SILVER
+ "GREY", // SKINCOLOR_GREY
+ "NICKEL", // SKINCOLOR_NICKEL
+ "BLACK", // SKINCOLOR_BLACK
+ "FAIRY", // SKINCOLOR_FAIRY
+ "POPCORN", // SKINCOLOR_POPCORN
+ "SEPIA", // SKINCOLOR_SEPIA
+ "BEIGE", // SKINCOLOR_BEIGE
+ "BROWN", // SKINCOLOR_BROWN
+ "LEATHER", // SKINCOLOR_LEATHER
+ "SALMON", // SKINCOLOR_SALMON
+ "PINK", // SKINCOLOR_PINK
+ "ROSE", // SKINCOLOR_ROSE
+ "BRICK", // SKINCOLOR_BRICK
+ "RUBY", // SKINCOLOR_RUBY
+ "RASPBERRY", // SKINCOLOR_RASPBERRY
+ "CHERRY", // SKINCOLOR_CHERRY
+ "RED", // SKINCOLOR_RED
+ "CRIMSON", // SKINCOLOR_CRIMSON
+ "MAROON", // SKINCOLOR_MAROON
+ "FLAME", // SKINCOLOR_FLAME
+ "SCARLET", // SKINCOLOR_SCARLET
+ "KETCHUP", // SKINCOLOR_KETCHUP
+ "DAWN", // SKINCOLOR_DAWN
+ "SUNSET", // SKINCOLOR_SUNSET
+ "CREAMSICLE", // SKINCOLOR_CREAMSICLE
+ "ORANGE", // SKINCOLOR_ORANGE
+ "PUMPKIN", // SKINCOLOR_PUMPKIN
+ "ROSEWOOD", // SKINCOLOR_ROSEWOOD
+ "BURGUNDY", // SKINCOLOR_BURGUNDY
+ "TANGERINE", // SKINCOLOR_TANGERINE
+ "PEACH", // SKINCOLOR_PEACH
+ "CARAMEL", // SKINCOLOR_CARAMEL
+ "CREAM", // SKINCOLOR_CREAM
+ "GOLD", // SKINCOLOR_GOLD
+ "ROYAL", // SKINCOLOR_ROYAL
+ "BRONZE", // SKINCOLOR_BRONZE
+ "COPPER", // SKINCOLOR_COPPER
+ "YELLOW", // SKINCOLOR_YELLOW
+ "MUSTARD", // SKINCOLOR_MUSTARD
+ "OLIVE", // SKINCOLOR_OLIVE
+ "VOMIT", // SKINCOLOR_VOMIT
+ "GARDEN", // SKINCOLOR_GARDEN
+ "LIME", // SKINCOLOR_LIME
+ "HANDHELD", // SKINCOLOR_HANDHELD
+ "TEA", // SKINCOLOR_TEA
+ "PISTACHIO", // SKINCOLOR_PISTACHIO
+ "MOSS", // SKINCOLOR_MOSS
+ "CAMOUFLAGE", // SKINCOLOR_CAMOUFLAGE
+ "ROBOHOOD", // SKINCOLOR_ROBOHOOD
+ "MINT", // SKINCOLOR_MINT
+ "GREEN", // SKINCOLOR_GREEN
+ "PINETREE", // SKINCOLOR_PINETREE
+ "EMERALD", // SKINCOLOR_EMERALD
+ "SWAMP", // SKINCOLOR_SWAMP
+ "DREAM", // SKINCOLOR_DREAM
+ "PLAGUE", // SKINCOLOR_PLAGUE
+ "ALGAE", // SKINCOLOR_ALGAE
+ "CARIBBEAN", // SKINCOLOR_CARIBBEAN
+ "AQUA", // SKINCOLOR_AQUA
+ "TEAL", // SKINCOLOR_TEAL
+ "CYAN", // SKINCOLOR_CYAN
+ "JAWZ", // SKINCOLOR_JAWZ
+ "CERULEAN", // SKINCOLOR_CERULEAN
+ "NAVY", // SKINCOLOR_NAVY
+ "PLATINUM", // SKINCOLOR_PLATINUM
+ "SLATE", // SKINCOLOR_SLATE
+ "STEEL", // SKINCOLOR_STEEL
+ "RUST", // SKINCOLOR_RUST
+ "JET", // SKINCOLOR_JET
+ "SAPPHIRE", // SKINCOLOR_SAPPHIRE
+ "PERIWINKLE", // SKINCOLOR_PERIWINKLE
+ "BLUE", // SKINCOLOR_BLUE
+ "BLUEBERRY", // SKINCOLOR_BLUEBERRY
+ "NOVA", // SKINCOLOR_NOVA
+ "PASTEL", // SKINCOLOR_PASTEL
+ "MOONSLAM", // SKINCOLOR_MOONSLAM
+ "ULTRAVIOLET", // SKINCOLOR_ULTRAVIOLET
+ "DUSK", // SKINCOLOR_DUSK
+ "BUBBLEGUM", // SKINCOLOR_BUBBLEGUM
+ "PURPLE", // SKINCOLOR_PURPLE
+ "FUCHSIA", // SKINCOLOR_FUCHSIA
+ "TOXIC", // SKINCOLOR_TOXIC
+ "MAUVE", // SKINCOLOR_MAUVE
+ "LAVENDER", // SKINCOLOR_LAVENDER
+ "BYZANTIUM", // SKINCOLOR_BYZANTIUM
+ "POMEGRANATE", // SKINCOLOR_POMEGRANATE
+ "LILAC", // SKINCOLOR_LILAC
- // Super special awesome Super flashing colors!
- "SUPER1", // SKINCOLOR_SUPER1
- "SUPER2", // SKINCOLOR_SUPER2,
- "SUPER3", // SKINCOLOR_SUPER3,
- "SUPER4", // SKINCOLOR_SUPER4,
- "SUPER5", // SKINCOLOR_SUPER5,
- // Super Tails
- "TSUPER1", // SKINCOLOR_TSUPER1,
- "TSUPER2", // SKINCOLOR_TSUPER2,
- "TSUPER3", // SKINCOLOR_TSUPER3,
- "TSUPER4", // SKINCOLOR_TSUPER4,
- "TSUPER5", // SKINCOLOR_TSUPER5,
- // Super Knuckles
- "KSUPER1", // SKINCOLOR_KSUPER1,
- "KSUPER2", // SKINCOLOR_KSUPER2,
- "KSUPER3", // SKINCOLOR_KSUPER3,
- "KSUPER4", // SKINCOLOR_KSUPER4,
- "KSUPER5" // SKINCOLOR_KSUPER5,
+
+
+
+
+ // Special super colors
+ // Super Sonic Yellow
+ "SUPER1", // SKINCOLOR_SUPER1
+ "SUPER2", // SKINCOLOR_SUPER2,
+ "SUPER3", // SKINCOLOR_SUPER3,
+ "SUPER4", // SKINCOLOR_SUPER4,
+ "SUPER5", // SKINCOLOR_SUPER5,
+
+ // Super Tails Orange
+ "TSUPER1", // SKINCOLOR_TSUPER1,
+ "TSUPER2", // SKINCOLOR_TSUPER2,
+ "TSUPER3", // SKINCOLOR_TSUPER3,
+ "TSUPER4", // SKINCOLOR_TSUPER4,
+ "TSUPER5", // SKINCOLOR_TSUPER5,
+
+ // Super Knuckles Red
+ "KSUPER1", // SKINCOLOR_KSUPER1,
+ "KSUPER2", // SKINCOLOR_KSUPER2,
+ "KSUPER3", // SKINCOLOR_KSUPER3,
+ "KSUPER4", // SKINCOLOR_KSUPER4,
+ "KSUPER5", // SKINCOLOR_KSUPER5,
+
+ // Hyper Sonic Pink
+ "PSUPER1", // SKINCOLOR_PSUPER1,
+ "PSUPER2", // SKINCOLOR_PSUPER2,
+ "PSUPER3", // SKINCOLOR_PSUPER3,
+ "PSUPER4", // SKINCOLOR_PSUPER4,
+ "PSUPER5", // SKINCOLOR_PSUPER5,
+
+ // Hyper Sonic Blue
+ "BSUPER1", // SKINCOLOR_BSUPER1,
+ "BSUPER2", // SKINCOLOR_BSUPER2,
+ "BSUPER3", // SKINCOLOR_BSUPER3,
+ "BSUPER4", // SKINCOLOR_BSUPER4,
+ "BSUPER5", // SKINCOLOR_BSUPER5,
+
+ // Aqua Super
+ "ASUPER1", // SKINCOLOR_ASUPER1,
+ "ASUPER2", // SKINCOLOR_ASUPER2,
+ "ASUPER3", // SKINCOLOR_ASUPER3,
+ "ASUPER4", // SKINCOLOR_ASUPER4,
+ "ASUPER5", // SKINCOLOR_ASUPER5,
+
+ // Hyper Sonic Green
+ "GSUPER1", // SKINCOLOR_GSUPER1,
+ "GSUPER2", // SKINCOLOR_GSUPER2,
+ "GSUPER3", // SKINCOLOR_GSUPER3,
+ "GSUPER4", // SKINCOLOR_GSUPER4,
+ "GSUPER5", // SKINCOLOR_GSUPER5,
+
+ // Hyper Sonic White
+ "WSUPER1", // SKINCOLOR_WSUPER1,
+ "WSUPER2", // SKINCOLOR_WSUPER2,
+ "WSUPER3", // SKINCOLOR_WSUPER3,
+ "WSUPER4", // SKINCOLOR_WSUPER4,
+ "WSUPER5", // SKINCOLOR_WSUPER5,
+
+ // Creamy Super (Shadow?)
+ "CSUPER1", // SKINCOLOR_CSUPER1,
+ "CSUPER2", // SKINCOLOR_CSUPER2,
+ "CSUPER3", // SKINCOLOR_CSUPER3,
+ "CSUPER4", // SKINCOLOR_CSUPER4,
+ "CSUPER5" // SKINCOLOR_CSUPER5,
};
static const char *const POWERS_LIST[] = {
@@ -8215,6 +8331,7 @@ static const char *const POWERS_LIST[] = {
"INGOOP" // In goop
};
+#ifdef HAVE_BLUA
static const char *const KARTSTUFF_LIST[] = {
"POSITION",
"OLDPOSITION",
@@ -8223,6 +8340,7 @@ static const char *const KARTSTUFF_LIST[] = {
"NEXTCHECK",
"WAYPOINT",
"STARPOSTWP",
+ "STARPOSTFLIP",
"RESPAWN",
"DROPDASH",
@@ -8294,8 +8412,12 @@ static const char *const KARTSTUFF_LIST[] = {
"ITEMBLINK",
"ITEMBLINKMODE",
- "GETSPARKS"
+ "GETSPARKS",
+ "JAWZTARGETDELAY",
+ "SPECTATEWAIT",
+ "GROWCANCEL"
};
+#endif
static const char *const HUDITEMS_LIST[] = {
"LIVESNAME",
@@ -8518,13 +8640,7 @@ struct {
{"RW_RAIL",RW_RAIL},
// Character flags (skinflags_t)
- {"SF_SUPER",SF_SUPER},
- {"SF_SUPERANIMS",SF_SUPERANIMS},
- {"SF_SUPERSPIN",SF_SUPERSPIN},
{"SF_HIRES",SF_HIRES},
- {"SF_NOSKID",SF_NOSKID},
- {"SF_NOSPEEDADJUST",SF_NOSPEEDADJUST},
- {"SF_RUNONWATER",SF_RUNONWATER},
// Character abilities!
// Primary
@@ -9001,20 +9117,6 @@ static powertype_t get_power(const char *word)
return pw_invulnerability;
}
-static kartstufftype_t get_kartstuff(const char *word)
-{ // Returns the vlaue of k_ enumerations
- kartstufftype_t i;
- if (*word >= '0' && *word <= '9')
- return atoi(word);
- if (fastncmp("K_",word,2))
- word += 2; // take off the k_
- for (i = 0; i < NUMKARTSTUFF; i++)
- if (fastcmp(word, KARTSTUFF_LIST[i]))
- return i;
- deh_warning("Couldn't find power named 'k_%s'",word);
- return k_position;
-}
-
/// \todo Make ANY of this completely over-the-top math craziness obey the order of operations.
static fixed_t op_mul(fixed_t a, fixed_t b) { return a*b; }
static fixed_t op_div(fixed_t a, fixed_t b) { return a/b; }
@@ -9351,6 +9453,7 @@ static inline int lib_freeslot(lua_State *L)
CONS_Printf("State S_%s allocated.\n",word);
FREE_STATES[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
strcpy(FREE_STATES[i],word);
+ freeslotusage[0][0]++;
lua_pushinteger(L, i);
r++;
break;
@@ -9366,6 +9469,7 @@ static inline int lib_freeslot(lua_State *L)
CONS_Printf("MobjType MT_%s allocated.\n",word);
FREE_MOBJS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL);
strcpy(FREE_MOBJS[i],word);
+ freeslotusage[1][0]++;
lua_pushinteger(L, i);
r++;
break;
@@ -9735,6 +9839,9 @@ static inline int lib_getenum(lua_State *L)
} else if (fastcmp(word,"modifiedgame")) {
lua_pushboolean(L, modifiedgame && !savemoddata);
return 1;
+ } else if (fastcmp(word,"majormods")) {
+ lua_pushboolean(L, majormods);
+ return 1;
} else if (fastcmp(word,"menuactive")) {
lua_pushboolean(L, menuactive);
return 1;
@@ -9806,6 +9913,9 @@ static inline int lib_getenum(lua_State *L)
} else if (fastcmp(word,"indirectitemcooldown")) {
lua_pushinteger(L, indirectitemcooldown);
return 1;
+ } else if (fastcmp(word,"hyubgone")) {
+ lua_pushinteger(L, hyubgone);
+ return 1;
} else if (fastcmp(word,"thwompsactive")) {
lua_pushboolean(L, thwompsactive);
return 1;
diff --git a/src/dehacked.h b/src/dehacked.h
index 411cb6c1..0d6cc902 100644
--- a/src/dehacked.h
+++ b/src/dehacked.h
@@ -37,6 +37,8 @@ void DEH_UnloadDehackedWad(UINT16 wad);
void DEH_LoadDehackedLump(lumpnum_t lumpnum);
void DEH_LoadDehackedLumpPwad(UINT16 wad, UINT16 lump);
+void DEH_UpdateMaxFreeslots(void);
+
void DEH_Check(void);
fixed_t get_number(const char *word);
@@ -54,7 +56,7 @@ extern const char *superactions[MAXRECURSION];
extern UINT8 superstack;
// If the dehacked patch does not match this version, we throw a warning
-#define PATCHVERSION 210
+#define PATCHVERSION 1
#define MAXLINELEN 1024
diff --git a/src/djgppdos/i_video.c b/src/djgppdos/i_video.c
index 612c7221..7829acbb 100644
--- a/src/djgppdos/i_video.c
+++ b/src/djgppdos/i_video.c
@@ -94,6 +94,9 @@ void I_FinishUpdate (void)
if (cv_ticrate.value)
SCR_DisplayTicRate();
+ if (cv_showping.value && netgame && consoleplayer != serverplayer)
+ SCR_DisplayLocalPing();
+
//blast it to the screen
// this code sucks
//memcpy(dascreen,screens[0],screenwidth*screenheight);
diff --git a/src/doomdef.h b/src/doomdef.h
index c44e70c0..6664ff51 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -150,9 +150,9 @@ extern FILE *logstream;
// we use comprevision and compbranch instead.
#else
#define VERSION 100 // Game version
-#define SUBVERSION 2 // more precise version number
-#define VERSIONSTRING "v1.0.2"
-#define VERSIONSTRINGW L"v1.0.2"
+#define SUBVERSION 4 // more precise version number
+#define VERSIONSTRING "v1.0.4"
+#define VERSIONSTRINGW L"v1.0.4"
// Hey! If you change this, add 1 to the MODVERSION below!
// Otherwise we can't force updates!
#endif
@@ -221,7 +221,7 @@ extern FILE *logstream;
// it's only for detection of the version the player is using so the MS can alert them of an update.
// Only set it higher, not lower, obviously.
// Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
-#define MODVERSION 2
+#define MODVERSION 4
// Filter consvars by version
// To version config.cfg, MAJOREXECVERSION is set equal to MODVERSION automatically.
@@ -244,7 +244,7 @@ extern FILE *logstream;
// NOTE: it needs more than this to increase the number of players...
#define MAXPLAYERS 16
-#define MAXSKINS 32
+#define MAXSKINS 128
#define PLAYERSMASK (MAXPLAYERS-1)
#define MAXPLAYERNAME 21
@@ -256,6 +256,8 @@ typedef enum
SKINCOLOR_GREY,
SKINCOLOR_NICKEL,
SKINCOLOR_BLACK,
+ SKINCOLOR_FAIRY,
+ SKINCOLOR_POPCORN,
SKINCOLOR_SEPIA,
SKINCOLOR_BEIGE,
SKINCOLOR_BROWN,
@@ -263,12 +265,18 @@ typedef enum
SKINCOLOR_SALMON,
SKINCOLOR_PINK,
SKINCOLOR_ROSE,
+ SKINCOLOR_BRICK,
SKINCOLOR_RUBY,
SKINCOLOR_RASPBERRY,
+ SKINCOLOR_CHERRY,
SKINCOLOR_RED,
SKINCOLOR_CRIMSON,
+ SKINCOLOR_MAROON,
+ SKINCOLOR_FLAME,
+ SKINCOLOR_SCARLET,
SKINCOLOR_KETCHUP,
SKINCOLOR_DAWN,
+ SKINCOLOR_SUNSET,
SKINCOLOR_CREAMSICLE,
SKINCOLOR_ORANGE,
SKINCOLOR_PUMPKIN,
@@ -277,68 +285,130 @@ typedef enum
SKINCOLOR_TANGERINE,
SKINCOLOR_PEACH,
SKINCOLOR_CARAMEL,
+ SKINCOLOR_CREAM,
SKINCOLOR_GOLD,
+ SKINCOLOR_ROYAL,
SKINCOLOR_BRONZE,
+ SKINCOLOR_COPPER,
SKINCOLOR_YELLOW,
SKINCOLOR_MUSTARD,
SKINCOLOR_OLIVE,
SKINCOLOR_VOMIT,
SKINCOLOR_GARDEN,
SKINCOLOR_LIME,
+ SKINCOLOR_HANDHELD,
SKINCOLOR_TEA,
SKINCOLOR_PISTACHIO,
- SKINCOLOR_ROBOHOOD,
SKINCOLOR_MOSS,
+ SKINCOLOR_CAMOUFLAGE,
+ SKINCOLOR_ROBOHOOD,
SKINCOLOR_MINT,
SKINCOLOR_GREEN,
SKINCOLOR_PINETREE,
SKINCOLOR_EMERALD,
SKINCOLOR_SWAMP,
SKINCOLOR_DREAM,
+ SKINCOLOR_PLAGUE,
+ SKINCOLOR_ALGAE,
+ SKINCOLOR_CARIBBEAN,
SKINCOLOR_AQUA,
SKINCOLOR_TEAL,
SKINCOLOR_CYAN,
SKINCOLOR_JAWZ, // Oni's torment
SKINCOLOR_CERULEAN,
SKINCOLOR_NAVY,
+ SKINCOLOR_PLATINUM,
SKINCOLOR_SLATE,
SKINCOLOR_STEEL,
+ SKINCOLOR_RUST,
SKINCOLOR_JET,
SKINCOLOR_SAPPHIRE, // sweet mother, i cannot weave - slender aphrodite has overcome me with longing for a girl
SKINCOLOR_PERIWINKLE,
SKINCOLOR_BLUE,
SKINCOLOR_BLUEBERRY,
+ SKINCOLOR_NOVA,
+ SKINCOLOR_PASTEL,
+ SKINCOLOR_MOONSLAM,
+ SKINCOLOR_ULTRAVIOLET,
SKINCOLOR_DUSK,
+ SKINCOLOR_BUBBLEGUM,
SKINCOLOR_PURPLE,
+ SKINCOLOR_FUCHSIA,
+ SKINCOLOR_TOXIC,
+ SKINCOLOR_MAUVE,
SKINCOLOR_LAVENDER,
SKINCOLOR_BYZANTIUM,
SKINCOLOR_POMEGRANATE,
SKINCOLOR_LILAC,
- // Careful! MAXSKINCOLORS cannot be greater than 0x40 -- Which it is now.
+ // "Careful! MAXSKINCOLORS cannot be greater than 0x40 -- Which it is now."
+ // (This comment is a dirty liar! This is only limited by the integer type, so 255 for UINT8.)
MAXSKINCOLORS,
// Super special awesome Super flashing colors!
+ // Super Sonic Yellow
SKINCOLOR_SUPER1 = MAXSKINCOLORS,
SKINCOLOR_SUPER2,
SKINCOLOR_SUPER3,
SKINCOLOR_SUPER4,
SKINCOLOR_SUPER5,
- // Super Tails
+ // Super Tails Orange
SKINCOLOR_TSUPER1,
SKINCOLOR_TSUPER2,
SKINCOLOR_TSUPER3,
SKINCOLOR_TSUPER4,
SKINCOLOR_TSUPER5,
- // Super Knuckles
+ // Super Knuckles Red
SKINCOLOR_KSUPER1,
SKINCOLOR_KSUPER2,
SKINCOLOR_KSUPER3,
SKINCOLOR_KSUPER4,
SKINCOLOR_KSUPER5,
+ // Hyper Sonic Pink
+ SKINCOLOR_PSUPER1,
+ SKINCOLOR_PSUPER2,
+ SKINCOLOR_PSUPER3,
+ SKINCOLOR_PSUPER4,
+ SKINCOLOR_PSUPER5,
+
+ // Hyper Sonic Blue
+ SKINCOLOR_BSUPER1,
+ SKINCOLOR_BSUPER2,
+ SKINCOLOR_BSUPER3,
+ SKINCOLOR_BSUPER4,
+ SKINCOLOR_BSUPER5,
+
+ // Aqua Super
+ SKINCOLOR_ASUPER1,
+ SKINCOLOR_ASUPER2,
+ SKINCOLOR_ASUPER3,
+ SKINCOLOR_ASUPER4,
+ SKINCOLOR_ASUPER5,
+
+ // Hyper Sonic Green
+ SKINCOLOR_GSUPER1,
+ SKINCOLOR_GSUPER2,
+ SKINCOLOR_GSUPER3,
+ SKINCOLOR_GSUPER4,
+ SKINCOLOR_GSUPER5,
+
+ // Hyper Sonic White
+ SKINCOLOR_WSUPER1,
+ SKINCOLOR_WSUPER2,
+ SKINCOLOR_WSUPER3,
+ SKINCOLOR_WSUPER4,
+ SKINCOLOR_WSUPER5,
+
+ // Creamy Super (Shadow?)
+ SKINCOLOR_CSUPER1,
+ SKINCOLOR_CSUPER2,
+ SKINCOLOR_CSUPER3,
+ SKINCOLOR_CSUPER4,
+ SKINCOLOR_CSUPER5,
+
MAXTRANSLATIONS
} skincolors_t;
@@ -471,13 +541,17 @@ INT32 I_GetKey(void);
#define max(x, y) (((x) > (y)) ? (x) : (y))
#endif
+#ifndef M_PIl
+#define M_PIl 3.1415926535897932384626433832795029L
+#endif
+
// Floating point comparison epsilons from float.h
#ifndef FLT_EPSILON
#define FLT_EPSILON 1.1920928955078125e-7f
#endif
#ifndef DBL_EPSILON
-#define DBL_EPSILON 2.2204460492503131e-16
+#define DBL_EPSILON 2.2204460492503131e-16l
#endif
// An assert-type mechanism.
diff --git a/src/doomstat.h b/src/doomstat.h
index 6d710e28..834b3a7c 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -54,9 +54,9 @@ extern boolean gamecomplete;
// Set if homebrew PWAD stuff has been added.
extern boolean modifiedgame;
+extern boolean majormods;
extern UINT16 mainwads;
extern boolean savemoddata; // This mod saves time/emblem data.
-extern boolean disableSpeedAdjust; // Don't alter the duration of player states if true
extern boolean imcontinuing; // Temporary flag while continuing
extern boolean metalrecording;
@@ -280,6 +280,8 @@ typedef struct
#define LF2_NIGHTSATTACK 8 ///< Show this map in NiGHTS mode menu
#define LF2_NOVISITNEEDED 16 ///< Available in time attack/nights mode without visiting the level
+#define LF2_EXISTSHACK 128 ///< Map lump exists; as noted, a single-bit hack that can be freely movable to other variables without concern.
+
// Save override
#define SAVE_NEVER -1
#define SAVE_DEFAULT 0
@@ -465,6 +467,7 @@ extern boolean comeback;
extern SINT8 battlewanted[4];
extern tic_t wantedcalcdelay;
extern tic_t indirectitemcooldown;
+extern tic_t hyubgone;
extern tic_t mapreset;
extern UINT8 nospectategrief;
extern boolean thwompsactive;
diff --git a/src/f_finale.c b/src/f_finale.c
index d210b9c8..fb67e927 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -181,7 +181,7 @@ static void F_SkyScroll(INT32 scrollspeed)
{
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, V_SNAPTOTOP|V_SNAPTOLEFT, pat, NULL);
x += SHORT(pat->width);
- }
+ }
x = -anim2;
y = BASEVIDHEIGHT - SHORT(pat2->height);
@@ -438,7 +438,7 @@ static const char *credits[] = {
"\"ZarroTsu\"",
"",
"\1Support Programming",
- "\"fickle\"",
+ "Colette \"fickleheart\" Bordelon",
"\"Lat\'\"",
"\"Monster Iestyn\"",
"\"Shuffle\"",
@@ -479,40 +479,10 @@ static const char *credits[] = {
"\"VAdaPEGA\"",
"\"VelocitOni\"",
"",
- "\1Music", // Can't list song names here, so we're listing artists
- "\"Arrow\"",
- "Jonny Atma",
- "Moot Booxle", // Booxlé, add the accent char later?
- "Malcolm Brown",
- "Karl Brueggemann",
+ "\1Music",
"\"DrTapeworm\"",
- "\"Elwood\"",
"Wesley \"Charyb\" Gillebaard",
- "\"gxf4c3\"",
"James \"SeventhSentinel\" Hall",
- "Chris Holland",
- "Johnny \"J\"",
- "Masato Kouda",
- "Fumie Kumatani",
- "Luke Kwing",
- "James Landino",
- "\"Lange\"",
- "Takenobu Mitsuyoshi",
- "\"Nib Roc\"",
- "Tomoya Ohtani",
- "Vincent Rubinetti",
- "Jun Senoue",
- "\"SSNTails\"",
- "Michael \"MaxieDaMan\" Staple",
- "Simon Stalenhag", // Stålenhag, add the accent char later?
- "\"Synthescissor\"",
- "Yuko Takehara",
- "Tony Thai",
- "\"The8BitDrummer\"",
- "Kenichi Tokoi",
- "\"Tokyo Active NEETs\"",
- "\"xaki\"",
- "Michiru Yamane",
"",
"\1Lead Level Design",
"\"Blitz-T\"",
@@ -586,7 +556,7 @@ static struct {
// This Tyler52 gag is troublesome
// Alignment should be ((spaces+1 * 100) + (headers+1 * 38) + (lines * 15))
// Current max image spacing: (200*17)
- {112, (15*100)+(17*38)+(102*15), "TYLER52", SKINCOLOR_NONE},
+ {112, (15*100)+(17*38)+(72*15), "TYLER52", SKINCOLOR_NONE},
{0, 0, NULL, SKINCOLOR_NONE}
};
@@ -641,7 +611,7 @@ void F_CreditDrawer(void)
if (credits_pics[i].colorize != SKINCOLOR_NONE)
{
- colormap = R_GetTranslationColormap(TC_RAINBOW, credits_pics[i].colorize, 0);
+ colormap = R_GetTranslationColormap(TC_RAINBOW, credits_pics[i].colorize, GTC_MENUCACHE);
sc = FRACUNIT; // quick hack so I don't have to add another field to credits_pics
}
@@ -1133,6 +1103,10 @@ void F_StartWaitingPlayers(void)
finalecount = 0;
randskin = M_RandomKey(numskins);
+
+ if (waitcolormap)
+ Z_Free(waitcolormap);
+
waitcolormap = R_GetTranslationColormap(randskin, skins[randskin].prefcolor, 0);
for (i = 0; i < 2; i++)
diff --git a/src/f_wipe.c b/src/f_wipe.c
index f7a5992a..3c8713d1 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -26,6 +26,7 @@
#include "console.h"
#include "d_main.h"
#include "m_misc.h" // movie mode
+#include "d_clisrv.h" // So the network state can be updated during the wipe
#ifdef HWRENDER
#include "hardware/hw_main.h"
@@ -96,7 +97,7 @@ static fixed_t paldiv;
* \return fademask_t for lump
*/
static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
- static char lumpname[10] = "FADEmmss";
+ static char lumpname[9] = "FADEmmss";
static fademask_t fm = {NULL,0,0,0,0,0};
lumpnum_t lumpnum;
UINT8 *lump, *mask;
@@ -106,7 +107,14 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
if (masknum > 99 || scrnnum > 99)
goto freemask;
- sprintf(&lumpname[4], "%.2hu%.2hu", (UINT16)masknum, (UINT16)scrnnum);
+ // SRB2Kart: This suddenly triggers ERRORMODE now
+ //sprintf(&lumpname[4], "%.2hu%.2hu", (UINT16)masknum, (UINT16)scrnnum);
+
+ lumpname[4] = '0'+(masknum/10);
+ lumpname[5] = '0'+(masknum%10);
+
+ lumpname[6] = '0'+(scrnnum/10);
+ lumpname[7] = '0'+(scrnnum%10);
lumpnum = W_CheckNumForName(lumpname);
if (lumpnum == LUMPERROR)
@@ -375,6 +383,8 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
if (moviemode)
M_SaveFrame();
+
+ NetKeepAlive(); // Update the network so we don't cause timeouts
}
WipeInAction = false;
#endif
diff --git a/src/filesrch.h b/src/filesrch.h
index 4186271b..01a52848 100644
--- a/src/filesrch.h
+++ b/src/filesrch.h
@@ -88,7 +88,8 @@ typedef enum
REFRESHDIR_WARNING = 4,
REFRESHDIR_ERROR = 8,
REFRESHDIR_NOTLOADED = 16,
- REFRESHDIR_MAX = 32
+ REFRESHDIR_MAX = 32,
+ REFRESHDIR_GAMEDATA = 64
} refreshdir_enum;
void closefilemenu(boolean validsize);
diff --git a/src/g_game.c b/src/g_game.c
index 1e0c7e46..ad25c8ce 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -16,6 +16,7 @@
#include "d_main.h"
#include "d_player.h"
#include "f_finale.h"
+#include "filesrch.h" // for refreshdirmenu
#include "p_setup.h"
#include "p_saveg.h"
#include "i_system.h"
@@ -86,11 +87,11 @@ INT16 lastmapsaved = 0; // Last map we auto-saved at
boolean gamecomplete = false;
UINT16 mainwads = 0;
-boolean modifiedgame; // Set if homebrew PWAD stuff has been added.
+boolean modifiedgame = false; // Set if homebrew PWAD stuff has been added.
+boolean majormods = false; // Set if Lua/Gameplay SOC/replacement map has been added.
boolean savemoddata = false;
UINT8 paused;
UINT8 modeattacking = ATTACKING_NONE;
-boolean disableSpeedAdjust = true;
boolean imcontinuing = false;
boolean runemeraldmanager = false;
@@ -265,6 +266,7 @@ SINT8 pickedvote; // What vote the host rolls
SINT8 battlewanted[4]; // WANTED players in battle, worth x2 points
tic_t wantedcalcdelay; // Time before it recalculates WANTED
tic_t indirectitemcooldown; // Cooldown before any more Shrink, SPB, or any other item that works indirectly is awarded
+tic_t hyubgone; // Cooldown before hyudoro is allowed to be rerolled
tic_t mapreset; // Map reset delay when enough players have joined an empty game
UINT8 nospectategrief; // How many players need to be in-game to eliminate last; for preventing spectate griefing
boolean thwompsactive; // Thwomps activate on lap 2
@@ -435,6 +437,9 @@ consvar_t cv_chatbacktint = {"chatbacktint", "On", CV_SAVE, CV_OnOff, NULL, 0, N
static CV_PossibleValue_t consolechat_cons_t[] = {{0, "Window"}, {1, "Console"}, {2, "Window (Hidden)"}, {0, NULL}};
consvar_t cv_consolechat = {"chatmode", "Window", CV_SAVE, consolechat_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+// Pause game upon window losing focus
+consvar_t cv_pauseifunfocused = {"pauseifunfocused", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
+
// Display song credits
consvar_t cv_songcredits = {"songcredits", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -752,16 +757,21 @@ void G_SetNightsRecords(void)
}*/
// for consistency among messages: this modifies the game and removes savemoddata.
-void G_SetGameModified(boolean silent)
+void G_SetGameModified(boolean silent, boolean major)
{
- if (modifiedgame && !savemoddata)
+ if ((majormods && modifiedgame) || !mainwads || (refreshdirmenu & REFRESHDIR_GAMEDATA)) // new gamedata amnesty?
return;
modifiedgame = true;
- savemoddata = false;
+
+ if (!major)
+ return;
+
+ //savemoddata = false; -- there is literally no reason to do this anymore.
+ majormods = true;
if (!silent)
- CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to record statistics.\n"));
+ CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to play record attack.\n"));
// If in record attack recording, cancel it.
if (modeattacking)
@@ -1209,7 +1219,7 @@ boolean camspin, camspin2, camspin3, camspin4;
static fixed_t forwardmove[2] = {25<>16, 50<>16};
static fixed_t sidemove[2] = {2<>16, 4<>16};
-static fixed_t angleturn[3] = {400, 800, 200}; // + slow turn
+static fixed_t angleturn[3] = {KART_FULLTURN/2, KART_FULLTURN, KART_FULLTURN/4}; // + slow turn
void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
{
@@ -1352,41 +1362,30 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
// let movement keys cancel each other out
if (turnright && !(turnleft))
{
- cmd->angleturn = (INT16)(cmd->angleturn - (angleturn[tspeed] * realtics));
- cmd->driftturn = (INT16)(cmd->driftturn - (angleturn[tspeed] * realtics));
+ cmd->angleturn = (INT16)(cmd->angleturn - (angleturn[tspeed]));
+ cmd->driftturn = (INT16)(cmd->driftturn - (angleturn[tspeed]));
+ side += sidemove[1];
}
else if (turnleft && !(turnright))
{
- cmd->angleturn = (INT16)(cmd->angleturn + (angleturn[tspeed] * realtics));
- cmd->driftturn = (INT16)(cmd->driftturn + (angleturn[tspeed] * realtics));
+ cmd->angleturn = (INT16)(cmd->angleturn + (angleturn[tspeed]));
+ cmd->driftturn = (INT16)(cmd->driftturn + (angleturn[tspeed]));
+ side -= sidemove[1];
}
if (analogjoystickmove && axis != 0)
{
// JOYAXISRANGE should be 1023 (divide by 1024)
- cmd->angleturn = (INT16)(cmd->angleturn - (((axis * angleturn[1]) >> 10) * realtics)); // ANALOG!
- cmd->driftturn = (INT16)(cmd->driftturn - (((axis * angleturn[1]) >> 10) * realtics));
+ cmd->angleturn = (INT16)(cmd->angleturn - (((axis * angleturn[1]) >> 10))); // ANALOG!
+ cmd->driftturn = (INT16)(cmd->driftturn - (((axis * angleturn[1]) >> 10)));
+ side += ((axis * sidemove[0]) >> 10);
}
// Specator mouse turning
if (player->spectator)
{
- cmd->angleturn = (INT16)(cmd->angleturn - ((mousex*(encoremode ? -1 : 1)*8) * realtics));
- cmd->driftturn = (INT16)(cmd->driftturn - ((mousex*(encoremode ? -1 : 1)*8) * realtics));
- }
-
- // Speed bump strafing
- if (!demoplayback && ((player->pflags & PF_FORCESTRAFE) || (player->kartstuff[k_pogospring])))
- {
- if (turnright)
- side += sidemove[1];
- if (turnleft)
- side -= sidemove[1];
- if (analogjoystickmove && axis != 0)
- {
- // JOYAXISRANGE is supposed to be 1023 (divide by 1024)
- side += ((axis * sidemove[0]) >> 10);
- }
+ cmd->angleturn = (INT16)(cmd->angleturn - ((mousex*(encoremode ? -1 : 1)*8)));
+ cmd->driftturn = (INT16)(cmd->driftturn - ((mousex*(encoremode ? -1 : 1)*8)));
}
if (player->spectator || objectplacing) // SRB2Kart: spectators need special controls
@@ -1531,15 +1530,6 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
else if (side < -MAXPLMOVE)
side = -MAXPLMOVE;
- // No additional acceleration when moving forward/backward and strafing simultaneously.
- // do this AFTER we cap to MAXPLMOVE so people can't find ways to cheese around this.
- // SRB2Kart: We don't need this; we WANT bounce strafing to plain stack on top of normal movement.
- /*if (!bouncestrafe && forward && side)
- {
- forward = FixedMul(forward, 3*FRACUNIT/4);
- side = FixedMul(side, 3*FRACUNIT/4);
- }*/
-
if (forward || side)
{
cmd->forwardmove = (SINT8)(cmd->forwardmove + forward);
@@ -1549,19 +1539,21 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
//{ SRB2kart - Drift support
// Not grouped with the rest of turn stuff because it needs to know what buttons you're pressing for rubber-burn turn
// limit turning to angleturn[1] to stop mouselook letting you look too fast
- if (cmd->angleturn > (angleturn[1] * realtics))
- cmd->angleturn = (angleturn[1] * realtics);
- else if (cmd->angleturn < (-angleturn[1] * realtics))
- cmd->angleturn = (-angleturn[1] * realtics);
+ if (cmd->angleturn > (angleturn[1]))
+ cmd->angleturn = (angleturn[1]);
+ else if (cmd->angleturn < (-angleturn[1]))
+ cmd->angleturn = (-angleturn[1]);
- if (cmd->driftturn > (angleturn[1] * realtics))
- cmd->driftturn = (angleturn[1] * realtics);
- else if (cmd->driftturn < (-angleturn[1] * realtics))
- cmd->driftturn = (-angleturn[1] * realtics);
+ if (cmd->driftturn > (angleturn[1]))
+ cmd->driftturn = (angleturn[1]);
+ else if (cmd->driftturn < (-angleturn[1]))
+ cmd->driftturn = (-angleturn[1]);
if (player->mo)
cmd->angleturn = K_GetKartTurnValue(player, cmd->angleturn);
+ cmd->angleturn *= realtics;
+
// SRB2kart - no additional angle if not moving
if (((player->mo && player->speed > 0) // Moving
|| (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn
@@ -1571,6 +1563,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
lang += (cmd->angleturn<<16);
cmd->angleturn = (INT16)(lang >> 16);
+ cmd->latency = modeattacking ? 0 : (leveltime & 0xFF); // Send leveltime when this tic was generated to the server for control lag calculations
if (!hu_stopped)
{
@@ -1612,10 +1605,26 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
}
}
+ /* Lua: Allow this hook to overwrite ticcmd.
+ We check if we're actually in a level because for some reason this Hook would run in menus and on the titlescreen otherwise.
+ Be aware that within this hook, nothing but this player's cmd can be edited (otherwise we'd run in some pretty bad synching problems since this is clientsided, or something)
+
+ Possible usages for this are:
+ -Forcing the player to perform an action, which could otherwise require terrible, terrible hacking to replicate.
+ -Preventing the player to perform an action, which would ALSO require some weirdo hacks.
+ -Making some galaxy brain autopilot Lua if you're a masochist
+ -Making a Mario Kart 8 Deluxe tier baby mode that steers you away from walls and whatnot. You know what, do what you want!
+ */
+#ifdef HAVE_BLUA
+ if (gamestate == GS_LEVEL)
+ LUAh_PlayerCmd(player, cmd);
+#endif
+
//Reset away view if a command is given.
if ((cmd->forwardmove || cmd->sidemove || cmd->buttons)
&& displayplayer != consoleplayer && ssplayer == 1)
displayplayer = consoleplayer;
+
}
// User has designated that they want
@@ -1814,80 +1823,6 @@ static INT32 spectatedelay, spectatedelay2, spectatedelay3, spectatedelay4 = 0;
//
boolean G_Responder(event_t *ev)
{
- // allow spy mode changes even during the demo
- if (gamestate == GS_LEVEL && ev->type == ev_keydown
- && (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1]))
- {
- if (splitscreen || !netgame)
- displayplayer = consoleplayer;
- else
- {
- UINT8 i = 0; // spy mode
- for (i = 0; i < MAXPLAYERS; i++)
- {
- displayplayer++;
- if (displayplayer == MAXPLAYERS)
- displayplayer = 0;
-
- if (displayplayer == consoleplayer)
- break; // End loop
-
- if (!playeringame[displayplayer])
- continue;
-
- if (players[displayplayer].spectator)
- continue;
-
- // SRB2Kart: Only go through players who are actually playing
- if (players[displayplayer].exiting)
- continue;
-
- if (players[displayplayer].pflags & PF_TIMEOVER)
- continue;
-
- // I don't know if we want this actually, but I'll humor the suggestion anyway
- if (G_BattleGametype())
- {
- if (players[displayplayer].kartstuff[k_bumper] <= 0)
- continue;
- }
-
- // SRB2Kart: we have no team-based modes, YET...
- /*if (G_GametypeHasTeams())
- {
- if (players[consoleplayer].ctfteam
- && players[displayplayer].ctfteam != players[consoleplayer].ctfteam)
- continue;
- }
- else if (gametype == GT_HIDEANDSEEK)
- {
- if (players[consoleplayer].pflags & PF_TAGIT)
- continue;
- }
- // Other Tag-based gametypes?
- else if (G_TagGametype())
- {
- if (!players[consoleplayer].spectator
- && (players[consoleplayer].pflags & PF_TAGIT) != (players[displayplayer].pflags & PF_TAGIT))
- continue;
- }
- else if (G_GametypeHasSpectators() && G_BattleGametype())
- {
- if (!players[consoleplayer].spectator)
- continue;
- }*/
-
- break;
- }
-
- // change statusbar also if playing back demo
- if (singledemo)
- ST_changeDemoView();
-
- return true;
- }
- }
-
// any other key pops up menu if in demos
if (gameaction == ga_nothing && !singledemo &&
((demoplayback && !modeattacking && !titledemo) || gamestate == GS_TITLESCREEN))
@@ -1965,6 +1900,80 @@ boolean G_Responder(event_t *ev)
if (HU_Responder(ev))
return true; // chat ate the event
+ // allow spy mode changes even during the demo
+ if (gamestate == GS_LEVEL && ev->type == ev_keydown
+ && (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1]))
+ {
+ if (splitscreen || !netgame)
+ displayplayer = consoleplayer;
+ else
+ {
+ UINT8 i = 0; // spy mode
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ displayplayer++;
+ if (displayplayer == MAXPLAYERS)
+ displayplayer = 0;
+
+ if (displayplayer == consoleplayer)
+ break; // End loop
+
+ if (!playeringame[displayplayer])
+ continue;
+
+ if (players[displayplayer].spectator)
+ continue;
+
+ // SRB2Kart: Only go through players who are actually playing
+ if (players[displayplayer].exiting)
+ continue;
+
+ if (players[displayplayer].pflags & PF_TIMEOVER)
+ continue;
+
+ // I don't know if we want this actually, but I'll humor the suggestion anyway
+ if (G_BattleGametype())
+ {
+ if (players[displayplayer].kartstuff[k_bumper] <= 0)
+ continue;
+ }
+
+ // SRB2Kart: we have no team-based modes, YET...
+ /*if (G_GametypeHasTeams())
+ {
+ if (players[consoleplayer].ctfteam
+ && players[displayplayer].ctfteam != players[consoleplayer].ctfteam)
+ continue;
+ }
+ else if (gametype == GT_HIDEANDSEEK)
+ {
+ if (players[consoleplayer].pflags & PF_TAGIT)
+ continue;
+ }
+ // Other Tag-based gametypes?
+ else if (G_TagGametype())
+ {
+ if (!players[consoleplayer].spectator
+ && (players[consoleplayer].pflags & PF_TAGIT) != (players[displayplayer].pflags & PF_TAGIT))
+ continue;
+ }
+ else if (G_GametypeHasSpectators() && G_BattleGametype())
+ {
+ if (!players[consoleplayer].spectator)
+ continue;
+ }*/
+
+ break;
+ }
+
+ // change statusbar also if playing back demo
+ if (singledemo)
+ ST_changeDemoView();
+
+ return true;
+ }
+ }
+
// update keys current state
G_MapEventsToControls(ev);
@@ -2154,6 +2163,9 @@ void G_Ticker(boolean run)
players[i].kartstuff[k_throwdir] = 0;
G_CopyTiccmd(cmd, &netcmds[buf][i], 1);
+
+ // Use the leveltime sent in the player's ticcmd to determine control lag
+ cmd->latency = modeattacking ? 0 : min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max
}
}
@@ -2348,6 +2360,7 @@ void G_PlayerReborn(INT32 player)
UINT8 skincolor;
INT32 skin;
tic_t jointime;
+ UINT8 splitscreenindex;
boolean spectator;
INT16 bot;
SINT8 pity;
@@ -2362,6 +2375,7 @@ void G_PlayerReborn(INT32 player)
INT32 bumper;
INT32 comebackpoints;
INT32 wanted;
+ INT32 respawnflip;
boolean songcredit = false;
score = players[player].score;
@@ -2371,6 +2385,7 @@ void G_PlayerReborn(INT32 player)
ctfteam = players[player].ctfteam;
exiting = players[player].exiting;
jointime = players[player].jointime;
+ splitscreenindex = players[player].splitscreenindex;
spectator = players[player].spectator;
pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED|PF_ANALOGMODE|PF_WANTSTOJOIN));
@@ -2402,6 +2417,7 @@ void G_PlayerReborn(INT32 player)
starposty = players[player].starposty;
starpostz = players[player].starpostz;
starpostnum = players[player].starpostnum;
+ respawnflip = players[player].kartstuff[k_starpostflip]; //SRB2KART
starpostangle = players[player].starpostangle;
jumpfactor = players[player].jumpfactor;
thokitem = players[player].thokitem;
@@ -2467,6 +2483,7 @@ void G_PlayerReborn(INT32 player)
p->pflags = pflags;
p->ctfteam = ctfteam;
p->jointime = jointime;
+ p->splitscreenindex = splitscreenindex;
p->spectator = spectator;
// save player config truth reborn
@@ -2521,6 +2538,7 @@ void G_PlayerReborn(INT32 player)
p->kartstuff[k_comebacktimer] = comebacktime;
p->kartstuff[k_wanted] = wanted;
p->kartstuff[k_eggmanblame] = -1;
+ p->kartstuff[k_starpostflip] = respawnflip;
// Don't do anything immediately
p->pflags |= PF_USEDOWN;
@@ -3173,7 +3191,7 @@ INT16 G_SometimesGetDifferentGametype(void)
break;
case 1: // sometimes
default:
- encorepossible = M_RandomChance(FRACUNIT>>3);
+ encorepossible = M_RandomChance(FRACUNIT>>2);
break;
}
if (encorepossible != (boolean)cv_kartencore.value)
@@ -3188,12 +3206,12 @@ INT16 G_SometimesGetDifferentGametype(void)
randmapbuffer[NUMMAPS] = 1; // every other vote (or always if !encorepossible)
break;
case 1: // sometimes
- randmapbuffer[NUMMAPS] = 10; // ...every two cups?
+ randmapbuffer[NUMMAPS] = 5; // per "cup"
break;
default:
// fallthrough - happens when clearing buffer, but needs a reasonable countdown if cvar is modified
case 2: // frequent
- randmapbuffer[NUMMAPS] = 5; // per "cup"
+ randmapbuffer[NUMMAPS] = 2; // ...every 1/2th-ish cup?
break;
}
@@ -3940,7 +3958,6 @@ void G_LoadGameData(void)
// Saves the main data file, which stores information such as emblems found, etc.
void G_SaveGameData(boolean force)
{
- const boolean wasmodified = modifiedgame;
size_t length;
INT32 i, j;
UINT8 btemp;
@@ -3957,9 +3974,7 @@ void G_SaveGameData(boolean force)
return;
}
- if (force) // SRB2Kart: for enabling unlocks online, even if the game is modified
- modifiedgame = savemoddata; // L-let's just sort of... hack around the cheat protection, because I'm too worried about just removing it @@;
- else if (modifiedgame && !savemoddata)
+ if (majormods && !force)
{
free(savebuffer);
save_p = savebuffer = NULL;
@@ -3972,7 +3987,7 @@ void G_SaveGameData(boolean force)
WRITEUINT32(save_p, totalplaytime);
WRITEUINT32(save_p, matchesplayed);
- btemp = (UINT8)(savemoddata || modifiedgame);
+ btemp = (UINT8)(savemoddata); // what used to be here was profoundly dunderheaded
WRITEUINT8(save_p, btemp);
// TODO put another cipher on these things? meh, I don't care...
@@ -4058,9 +4073,6 @@ void G_SaveGameData(boolean force)
FIL_WriteFile(va(pandf, srb2home, gamedatafilename), savebuffer, length);
free(savebuffer);
save_p = savebuffer = NULL;
-
- if (force) // Eeeek, I'm sorry for my sins!
- modifiedgame = wasmodified;
}
#define VERSIONSIZE 16
@@ -4561,6 +4573,7 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
dest[i].aiming = (INT16)SHORT(src[i].aiming);
dest[i].buttons = (UINT16)SHORT(src[i].buttons);
dest[i].driftturn = (INT16)SHORT(src[i].driftturn);
+ dest[i].latency = (INT16)SHORT(src[i].latency);
}
return dest;
}
@@ -4759,7 +4772,8 @@ void G_WriteGhostTic(mobj_t *ghost)
// GZT_XYZ is only useful if you've moved 256 FRACUNITS or more in a single tic.
if (abs(ghost->x-oldghost.x) > MAXMOM
|| abs(ghost->y-oldghost.y) > MAXMOM
- || abs(ghost->z-oldghost.z) > MAXMOM)
+ || abs(ghost->z-oldghost.z) > MAXMOM
+ || (leveltime & 255) == 1) // Hack to enable slightly nicer resyncing
{
oldghost.x = ghost->x;
oldghost.y = ghost->y;
@@ -4773,8 +4787,8 @@ void G_WriteGhostTic(mobj_t *ghost)
{
// For moving normally:
// Store one full byte of movement, plus one byte of fractional movement.
- INT16 momx = (INT16)((ghost->x-oldghost.x)>>8);
- INT16 momy = (INT16)((ghost->y-oldghost.y)>>8);
+ INT16 momx = (INT16)((ghost->x-oldghost.x + (1<<4))>>8);
+ INT16 momy = (INT16)((ghost->y-oldghost.y + (1<<4))>>8);
if (momx != oldghost.momx
|| momy != oldghost.momy)
{
@@ -4784,7 +4798,7 @@ void G_WriteGhostTic(mobj_t *ghost)
WRITEINT16(demo_p,momx);
WRITEINT16(demo_p,momy);
}
- momx = (INT16)((ghost->z-oldghost.z)>>8);
+ momx = (INT16)((ghost->z-oldghost.z + (1<<4))>>8);
if (momx != oldghost.momz)
{
oldghost.momz = momx;
@@ -4888,8 +4902,9 @@ void G_WriteGhostTic(mobj_t *ghost)
void G_ConsGhostTic(void)
{
UINT8 ziptic;
- UINT16 px,py,pz,gx,gy,gz;
+ fixed_t px,py,pz,gx,gy,gz;
mobj_t *testmo;
+ fixed_t syncleeway;
boolean nightsfail = false;
if (!demo_p || !demo_start)
@@ -4906,6 +4921,7 @@ void G_ConsGhostTic(void)
oldghost.x = READFIXED(demo_p);
oldghost.y = READFIXED(demo_p);
oldghost.z = READFIXED(demo_p);
+ syncleeway = 0;
}
else
{
@@ -4919,6 +4935,7 @@ void G_ConsGhostTic(void)
oldghost.x += oldghost.momx;
oldghost.y += oldghost.momy;
oldghost.z += oldghost.momz;
+ syncleeway = FRACUNIT;
}
if (ziptic & GZT_ANGLE)
demo_p++;
@@ -4984,14 +5001,14 @@ void G_ConsGhostTic(void)
}
// Re-synchronise
- px = testmo->x>>FRACBITS;
- py = testmo->y>>FRACBITS;
- pz = testmo->z>>FRACBITS;
- gx = oldghost.x>>FRACBITS;
- gy = oldghost.y>>FRACBITS;
- gz = oldghost.z>>FRACBITS;
+ px = testmo->x;
+ py = testmo->y;
+ pz = testmo->z;
+ gx = oldghost.x;
+ gy = oldghost.y;
+ gz = oldghost.z;
- if (nightsfail || px != gx || py != gy || pz != gz)
+ if (nightsfail || abs(px-gx) > syncleeway || abs(py-gy) > syncleeway || abs(pz-gz) > syncleeway)
{
if (demosynced)
CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n"));
@@ -5102,17 +5119,16 @@ void G_GhostTicker(void)
INT32 type = -1;
if (g->mo->skin)
{
- skin_t *skin = (skin_t *)g->mo->skin;
switch (ziptic & EZT_THOKMASK)
{
case EZT_THOK:
- type = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem;
+ type = (UINT32)mobjinfo[MT_PLAYER].painchance;
break;
case EZT_SPIN:
- type = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem;
+ type = (UINT32)mobjinfo[MT_PLAYER].damage;
break;
case EZT_REV:
- type = skin->revitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem;
+ type = (UINT32)mobjinfo[MT_PLAYER].raisestate;
break;
}
}
@@ -5147,22 +5163,20 @@ void G_GhostTicker(void)
if (ziptic & EZT_HIT)
{ // Spawn hit poofs for killing things!
UINT16 i, count = READUINT16(g->p), health;
- UINT32 type;
+ //UINT32 type;
fixed_t x,y,z;
angle_t angle;
mobj_t *poof;
for (i = 0; i < count; i++)
{
g->p += 4; // reserved
- type = READUINT32(g->p);
+ g->p += 4; // backwards compat., type used to be here
health = READUINT16(g->p);
x = READFIXED(g->p);
y = READFIXED(g->p);
z = READFIXED(g->p);
angle = READANGLE(g->p);
- if (!(mobjinfo[type].flags & MF_SHOOTABLE)
- || !(mobjinfo[type].flags & (MF_ENEMY|MF_MONITOR))
- || health != 0 || i >= 4) // only spawn for the first 4 hits per frame, to prevent ghosts from splode-spamming too bad.
+ if (health != 0 || i >= 4) // only spawn for the first 4 hits per frame, to prevent ghosts from splode-spamming too bad.
continue;
poof = P_SpawnMobj(x, y, z, MT_GHOST);
poof->angle = angle;
@@ -5918,6 +5932,32 @@ void G_DoPlayDemo(char *defdemoname)
return;
}
+ // Skin not loaded?
+ if (!SetPlayerSkin(0, skin))
+ {
+ snprintf(msg, 1024, M_GetText("%s features a character that is not currently loaded.\n"), pdemoname);
+ CONS_Alert(CONS_ERROR, "%s", msg);
+ M_StartMessage(msg, NULL, MM_NOTHING);
+ Z_Free(pdemoname);
+ Z_Free(demobuffer);
+ demoplayback = false;
+ titledemo = false;
+ return;
+ }
+
+ // ...*map* not loaded?
+ if (!gamemap || (gamemap > NUMMAPS) || !mapheaderinfo[gamemap-1] || !(mapheaderinfo[gamemap-1]->menuflags & LF2_EXISTSHACK))
+ {
+ snprintf(msg, 1024, M_GetText("%s features a course that is not currently loaded.\n"), pdemoname);
+ CONS_Alert(CONS_ERROR, "%s", msg);
+ M_StartMessage(msg, NULL, MM_NOTHING);
+ Z_Free(pdemoname);
+ Z_Free(demobuffer);
+ demoplayback = false;
+ titledemo = false;
+ return;
+ }
+
Z_Free(pdemoname);
memset(&oldcmd,0,sizeof(oldcmd));
@@ -5949,9 +5989,6 @@ void G_DoPlayDemo(char *defdemoname)
P_SetRandSeed(randseed);
G_InitNew(false, G_BuildMapName(gamemap), true, true); // Doesn't matter whether you reset or not here, given changes to resetplayer.
- // Set skin
- SetPlayerSkin(0, skin);
-
// Set color
for (i = 0; i < MAXSKINCOLORS; i++)
if (!stricmp(KartColor_Names[i],color)) // SRB2kart
@@ -6001,6 +6038,7 @@ void G_AddGhost(char *defdemoname)
UINT8 *buffer,*p;
mapthing_t *mthing;
UINT16 count, ghostversion;
+ skin_t *ghskin = &skins[0];
name[16] = '\0';
skin[16] = '\0';
@@ -6146,6 +6184,21 @@ void G_AddGhost(char *defdemoname)
return;
}
+ for (i = 0; i < numskins; i++)
+ if (!stricmp(skins[i].name,skin))
+ {
+ ghskin = &skins[i];
+ break;
+ }
+
+ if (i == numskins)
+ {
+ CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid character.\n"), pdemoname);
+ Z_Free(pdemoname);
+ Z_Free(buffer);
+ return;
+ }
+
gh = Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL);
gh->next = ghosts;
gh->buffer = buffer;
@@ -6191,14 +6244,7 @@ void G_AddGhost(char *defdemoname)
gh->oldmo.z = gh->mo->z;
// Set skin
- gh->mo->skin = &skins[0];
- for (i = 0; i < numskins; i++)
- if (!stricmp(skins[i].name,skin))
- {
- gh->mo->skin = &skins[i];
- break;
- }
- gh->oldmo.skin = gh->mo->skin;
+ gh->mo->skin = gh->oldmo.skin = ghskin;
// Set color
gh->mo->color = ((skin_t*)gh->mo->skin)->prefcolor;
diff --git a/src/g_game.h b/src/g_game.h
index 14dc12d0..eea149c9 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -56,6 +56,7 @@ extern INT16 rw_maximums[NUM_WEAPONS];
// used in game menu
extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection/*, cv_compactscoreboard*/;
extern consvar_t cv_songcredits;
+extern consvar_t cv_pauseifunfocused;
//extern consvar_t cv_crosshair, cv_crosshair2, cv_crosshair3, cv_crosshair4;
extern consvar_t cv_invertmouse/*, cv_alwaysfreelook, cv_chasefreelook, cv_mousemove*/;
extern consvar_t cv_invertmouse2/*, cv_alwaysfreelook2, cv_chasefreelook2, cv_mousemove2*/;
@@ -66,7 +67,6 @@ extern consvar_t cv_turnaxis2,cv_moveaxis2,cv_brakeaxis2,cv_aimaxis2,cv_lookaxis
extern consvar_t cv_turnaxis3,cv_moveaxis3,cv_brakeaxis3,cv_aimaxis3,cv_lookaxis3,cv_fireaxis3,cv_driftaxis3;
extern consvar_t cv_turnaxis4,cv_moveaxis4,cv_brakeaxis4,cv_aimaxis4,cv_lookaxis4,cv_fireaxis4,cv_driftaxis4;
extern consvar_t cv_ghost_besttime, cv_ghost_bestlap, cv_ghost_last, cv_ghost_guest, cv_ghost_staff;
-extern consvar_t cv_splitplayers;
typedef enum
{
@@ -227,7 +227,7 @@ boolean G_GetRetryFlag(void);
void G_LoadGameData(void);
void G_LoadGameSettings(void);
-void G_SetGameModified(boolean silent);
+void G_SetGameModified(boolean silent, boolean major);
void G_SetGamestate(gamestate_t newstate);
diff --git a/src/g_input.c b/src/g_input.c
index d7b7be91..cab35830 100644
--- a/src/g_input.c
+++ b/src/g_input.c
@@ -37,7 +37,7 @@ INT32 mlooky; // like mousey but with a custom sensitivity for mlook
INT32 mouse2x, mouse2y, mlook2y;
// joystick values are repeated
-INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], joy2ymove[JOYAXISSET],
+INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], joy2ymove[JOYAXISSET],
joy3xmove[JOYAXISSET], joy3ymove[JOYAXISSET], joy4xmove[JOYAXISSET], joy4ymove[JOYAXISSET];
// current state of the keys: true if pushed
@@ -120,7 +120,7 @@ void G_MapEventsToControls(event_t *ev)
case ev_joystick2: // buttons are virtual keys
i = ev->data1;
- if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on)
+ if (i >= JOYAXISSET || menuactive)
break;
if (ev->data2 != INT32_MAX) joy2xmove[i] = ev->data2;
if (ev->data3 != INT32_MAX) joy2ymove[i] = ev->data3;
@@ -1308,7 +1308,7 @@ void G_Controldefault(UINT8 player)
gamecontrol[gc_brake ][1] = KEY_JOY1+1; // B
gamecontrol[gc_fire ][1] = KEY_JOY1+4; // LB
gamecontrol[gc_drift ][1] = KEY_JOY1+5; // RB
-
+
// Extra controls
gamecontrol[gc_pause ][0] = KEY_PAUSE;
gamecontrol[gc_console ][0] = KEY_CONSOLE;
diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c
index 48393249..9e454bcd 100644
--- a/src/hardware/hw_bsp.c
+++ b/src/hardware/hw_bsp.c
@@ -201,7 +201,7 @@ static polyvertex_t *fracdivline(fdivline_t *bsp, polyvertex_t *v1,
// (do not accept hit with the extensions)
num = (v2x - v1x)*v2dy + (v1y - v2y)*v2dx;
frac = num / den;
- if (frac < 0.0 || frac > 1.0)
+ if (frac < 0.0l || frac > 1.0l)
return NULL;
// now get the frac along the BSP line
diff --git a/src/hardware/hw_clip.c b/src/hardware/hw_clip.c
index 4bdc753e..a6352708 100644
--- a/src/hardware/hw_clip.c
+++ b/src/hardware/hw_clip.c
@@ -72,6 +72,7 @@
#include "../v_video.h"
#include "hw_clip.h"
#include "hw_glob.h"
+#include "../r_main.h"
#include "../r_state.h"
#include "../tables.h"
#include "r_opengl/r_opengl.h"
@@ -328,7 +329,7 @@ angle_t gld_FrustumAngle(void)
// NEWCLIP TODO: SRB2CBTODO: make a global render_fov for this function
- float render_fov = FIXED_TO_FLOAT(cv_grfov.value);
+ float render_fov = FIXED_TO_FLOAT(cv_fov.value);
float render_fovratio = (float)BASEVIDWIDTH / (float)BASEVIDHEIGHT; // SRB2CBTODO: NEWCLIPTODO: Is this right?
float render_multiplier = 64.0f / render_fovratio / RMUL;
diff --git a/src/hardware/hw_dll.h b/src/hardware/hw_dll.h
index 6b9f4d53..46645278 100644
--- a/src/hardware/hw_dll.h
+++ b/src/hardware/hw_dll.h
@@ -61,9 +61,6 @@ typedef void (*I_Error_t) (const char *error, ...) FUNCIERROR;
// ==========================================================================
// Constants
-#ifndef M_PIl
-#define M_PIl 3.1415926535897932384626433832795029L
-#endif
#define DEGREE (0.017453292519943295769236907684883l) // 2*PI/360
void DBG_Printf(const char *lpFmt, ...) /*FUNCPRINTF*/;
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index b5c6cbfb..5c09a0bc 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -86,12 +86,10 @@ static UINT32 atohex(const char *s);
static void CV_filtermode_ONChange(void);
static void CV_anisotropic_ONChange(void);
static void CV_FogDensity_ONChange(void);
-static void CV_grFov_OnChange(void);
// ==========================================================================
// 3D ENGINE COMMANDS & CONSOLE VARS
// ==========================================================================
-static CV_PossibleValue_t grfov_cons_t[] = {{0, "MIN"}, {179*FRACUNIT, "MAX"}, {0, NULL}};
static CV_PossibleValue_t grfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSAMPLED, "Nearest"},
{HWD_SET_TEXTUREFILTER_BILINEAR, "Bilinear"}, {HWD_SET_TEXTUREFILTER_TRILINEAR, "Trilinear"},
{HWD_SET_TEXTUREFILTER_MIXED1, "Linear_Nearest"},
@@ -112,7 +110,6 @@ static consvar_t cv_grbeta = {"gr_beta", "0", 0, CV_Unsigned, NULL, 0, NULL, NUL
static float HWRWipeCounter = 1.0f;
consvar_t cv_grrounddown = {"gr_rounddown", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_grfov = {"gr_fov", "90", CV_FLOAT|CV_CALL, grfov_cons_t, CV_grFov_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grfogdensity = {"gr_fogdensity", "150", CV_CALL|CV_NOINIT, CV_Unsigned,
CV_FogDensity_ONChange, 0, NULL, NULL, 0, 0, NULL};
@@ -5263,7 +5260,8 @@ static void HWR_DrawSprites(void)
#endif
if (spr->mobj && spr->mobj->skin && spr->mobj->sprite == SPR_PLAY)
{
- if (!cv_grmd2.value || md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound || md2_playermodels[(skin_t*)spr->mobj->skin-skins].scale < 0.0f)
+ // 8/1/19: Only don't display player models if no default SPR_PLAY is found.
+ if (!cv_grmd2.value || ((md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound || md2_playermodels[(skin_t*)spr->mobj->skin-skins].scale < 0.0f) && (md2_models[SPR_PLAY].notfound || md2_models[SPR_PLAY].scale < 0.0f)))
HWR_DrawSprite(spr);
else
HWR_DrawMD2(spr);
@@ -5906,7 +5904,7 @@ void HWR_SetViewSize(void)
// ==========================================================================
void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player)
{
- const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd);
+ const float fpov = FIXED_TO_FLOAT(cv_fov.value+player->fovadd);
postimg_t *type;
UINT8 ssplayer = 0;
@@ -6072,7 +6070,7 @@ if (0)
viewangle = localaiming4;
// Handle stuff when you are looking farther up or down.
- if ((aimingangle || cv_grfov.value+player->fovadd > 90*FRACUNIT))
+ if ((aimingangle || cv_fov.value+player->fovadd > 90*FRACUNIT))
{
dup_viewangle += ANGLE_90;
HWR_ClearClipSegs();
@@ -6150,7 +6148,7 @@ if (0)
// ==========================================================================
void HWR_RenderPlayerView(INT32 viewnumber, player_t *player)
{
- const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd);
+ const float fpov = FIXED_TO_FLOAT(cv_fov.value+player->fovadd);
postimg_t *type;
UINT8 ssplayer = 0;
@@ -6331,7 +6329,7 @@ if (0)
viewangle = localaiming4;
// Handle stuff when you are looking farther up or down.
- if ((aimingangle || cv_grfov.value+player->fovadd > 90*FRACUNIT))
+ if ((aimingangle || cv_fov.value+player->fovadd > 90*FRACUNIT))
{
dup_viewangle += ANGLE_90;
HWR_ClearClipSegs();
@@ -6454,11 +6452,6 @@ static void HWR_FoggingOn(void)
// ==========================================================================
-static void CV_grFov_OnChange(void)
-{
- if ((netgame || multiplayer) && !cv_debug && cv_grfov.value != 90*FRACUNIT)
- CV_Set(&cv_grfov, cv_grfov.defaultvalue);
-}
static void Command_GrStats_f(void)
{
@@ -6481,7 +6474,6 @@ static void Command_GrStats_f(void)
void HWR_AddCommands(void)
{
CV_RegisterVar(&cv_grrounddown);
- CV_RegisterVar(&cv_grfov);
CV_RegisterVar(&cv_grfogdensity);
CV_RegisterVar(&cv_grfiltermode);
CV_RegisterVar(&cv_granisotropicmode);
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index 86ee5ea7..8c9590e9 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -79,7 +79,6 @@ extern consvar_t cv_grstaticlighting;
extern consvar_t cv_grcoronas;
extern consvar_t cv_grcoronasize;
#endif
-extern consvar_t cv_grfov;
extern consvar_t cv_grmd2;
extern consvar_t cv_grfog;
extern consvar_t cv_grfogcolor;
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 94739106..07ccd3d8 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -162,7 +162,7 @@ static GrTextureFormat_t PNG_Load(const char *filename, int *w, int *h, GLPatch_
jmp_buf jmpbuf;
#endif
#endif
- png_FILE_p png_FILE;
+ volatile png_FILE_p png_FILE;
//Filename checking fixed ~Monster Iestyn and Golden
char *pngfilename = va("%s"PATHSEP"md2"PATHSEP"%s", srb2home, filename);
@@ -508,11 +508,12 @@ void HWR_InitMD2(void)
}
while (fscanf(f, "%19s %31s %f %f", name, filename, &scale, &offset) == 4)
{
- if (stricmp(name, "PLAY") == 0)
+ /*if (stricmp(name, "PLAY") == 0)
{
CONS_Printf("MD2 for sprite PLAY detected in kmd2.dat, use a player skin instead!\n");
continue;
- }
+ }*/
+ // 8/1/19: Allow PLAY to load for default MD2.
for (i = 0; i < NUMSPRITES; i++)
{
@@ -874,6 +875,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
// MD2 colormap fix
// colormap test
+ if (spr->mobj->subsector)
{
sector_t *sector = spr->mobj->subsector->sector;
UINT8 lightlevel = 255;
@@ -905,6 +907,10 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
else
Surf.FlatColor.rgba = HWR_Lighting(lightlevel, NORMALFOG, FADEFOG, false, false);
}
+ else
+ {
+ Surf.FlatColor.rgba = 0xFFFFFFFF;
+ }
// Look at HWR_ProjectSprite for more
{
@@ -933,19 +939,19 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
// 1. load model+texture if not already loaded
// 2. draw model with correct position, rotation,...
- if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) // Use the player MD2 list if the mobj has a skin and is using the player sprites
+ if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY && !md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound) // Use the player MD2 list if the mobj has a skin and is using the player sprites
{
md2 = &md2_playermodels[(skin_t*)spr->mobj->skin-skins];
md2->skin = (skin_t*)spr->mobj->skin-skins;
}
- else
+ else // if we can't find the player md2, use SPR_PLAY's MD2.
md2 = &md2_models[spr->mobj->sprite];
if (md2->error)
return; // we already failed loading this before :(
if (!md2->model)
{
- //CONS_Debug(DBG_RENDER, "Loading MD2... (%s)", sprnames[spr->mobj->sprite]);
+ CONS_Debug(DBG_RENDER, "Loading MD2... (%s, %s)", sprnames[spr->mobj->sprite], md2->filename);
sprintf(filename, "md2/%s", md2->filename);
md2->model = md2_readModel(filename);
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index e01e7764..cb2b6ae4 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -1951,10 +1951,11 @@ EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration,
EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
{
static boolean special_splitscreen;
+ GLdouble used_fov;
pglLoadIdentity();
if (stransform)
{
- boolean fovx90;
+ used_fov = stransform->fovxangle;
// keep a trace of the transformation for md2
memcpy(&md2_transform, stransform, sizeof (md2_transform));
@@ -1973,32 +1974,27 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
pglRotatef(stransform->angley+270.0f, 0.0f, 1.0f, 0.0f);
pglTranslatef(-stransform->x, -stransform->z, -stransform->y);
- pglMatrixMode(GL_PROJECTION);
- pglLoadIdentity();
- fovx90 = stransform->fovxangle > 0.0f && fabsf(stransform->fovxangle - 90.0f) < 0.5f;
- special_splitscreen = (stransform->splitscreen == 1 && fovx90);
- if (special_splitscreen)
- GLPerspective(53.13f, 2*ASPECT_RATIO); // 53.13 = 2*atan(0.5)
- else
- GLPerspective(stransform->fovxangle, ASPECT_RATIO);
- pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer)
- pglMatrixMode(GL_MODELVIEW);
+ special_splitscreen = (stransform->splitscreen == 1);
}
else
{
+ //Hurdler: is "fov" correct?
+ used_fov = fov;
pglScalef(1.0f, 1.0f, -1.0f);
-
- pglMatrixMode(GL_PROJECTION);
- pglLoadIdentity();
- if (special_splitscreen)
- GLPerspective(53.13f, 2*ASPECT_RATIO); // 53.13 = 2*atan(0.5)
- else
- //Hurdler: is "fov" correct?
- GLPerspective(fov, ASPECT_RATIO);
- pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer)
- pglMatrixMode(GL_MODELVIEW);
}
+ pglMatrixMode(GL_PROJECTION);
+ pglLoadIdentity();
+ if (special_splitscreen)
+ {
+ used_fov = atan(tan(used_fov*M_PIl/360.0l)*0.8l)*360/M_PIl;
+ GLPerspective(used_fov, 2*ASPECT_RATIO);
+ }
+ else
+ GLPerspective(used_fov, ASPECT_RATIO);
+ pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer)
+ pglMatrixMode(GL_MODELVIEW);
+
pglGetFloatv(GL_MODELVIEW_MATRIX, modelMatrix); // added for new coronas' code (without depth buffer)
}
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index a38e91a7..b4357073 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -74,6 +74,14 @@ patch_t *nightsnum[10]; // 0-9
patch_t *lt_font[LT_FONTSIZE];
patch_t *cred_font[CRED_FONTSIZE];
+// ping font
+// Note: I'd like to adress that at this point we might *REALLY* want to work towards a common drawString function that can take any font we want because this is really turning into a MESS. :V -Lat'
+patch_t *pingnum[10];
+patch_t *pinggfx[5]; // small ping graphic
+
+patch_t *framecounter;
+patch_t *frameslash; // framerate stuff. Used in screen.c
+
static player_t *plr;
boolean chat_on; // entering a chat message?
static char w_chat[HU_MAXMSGLEN];
@@ -263,6 +271,8 @@ void HU_LoadGraphics(void)
tallnum[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
sprintf(buffer, "NGTNUM%d", i);
nightsnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ sprintf(buffer, "PINGN%d", i);
+ pingnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
}
// minus for negative tallnums
@@ -295,6 +305,17 @@ void HU_LoadGraphics(void)
tinyemeraldpics[6] = W_CachePatchName("TEMER7", PU_HUDGFX);
songcreditbg = W_CachePatchName("K_SONGCR", PU_HUDGFX);
+
+ // cache ping gfx:
+ for (i = 0; i < 5; i++)
+ {
+ sprintf(buffer, "PINGGFX%d", i+1);
+ pinggfx[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ // fps stuff
+ framecounter = W_CachePatchName("FRAMER", PU_HUDGFX);
+ frameslash = W_CachePatchName("FRAMESL", PU_HUDGFX);;
}
// Initialise Heads up
@@ -757,44 +778,139 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
}
}
else
- {
+ {
const UINT8 color = players[playernum].skincolor;
cstart = "\x83";
- if (color <= SKINCOLOR_SILVER || color == SKINCOLOR_SLATE)
- cstart = "\x80"; // white
- else if (color <= SKINCOLOR_BLACK || color == SKINCOLOR_JET)
- cstart = "\x86"; // V_GRAYMAP
- else if (color <= SKINCOLOR_LEATHER)
- cstart = "\x8e"; // V_BROWNMAP
- else if (color <= SKINCOLOR_ROSE || color == SKINCOLOR_LILAC)
- cstart = "\x8d"; // V_PINKMAP
- else if (color <= SKINCOLOR_KETCHUP)
- cstart = "\x85"; // V_REDMAP
- else if (color <= SKINCOLOR_TANGERINE)
- cstart = "\x87"; // V_ORANGEMAP
- else if (color <= SKINCOLOR_CARAMEL)
- cstart = "\x8f"; // V_PEACHMAP
- else if (color <= SKINCOLOR_BRONZE)
- cstart = "\x8A"; // V_GOLDMAP
- else if (color <= SKINCOLOR_OLIVE)
- cstart = "\x82"; // V_YELLOWMAP
- else if (color <= SKINCOLOR_PISTACHIO)
- cstart = "\x8b"; // V_TEAMAP
- else if (color <= SKINCOLOR_DREAM || color == SKINCOLOR_LIME)
- cstart = "\x83"; // V_GREENMAP
- else if (color <= SKINCOLOR_NAVY || color == SKINCOLOR_SAPPHIRE)
- cstart = "\x88"; // V_SKYMAP
- else if (color <= SKINCOLOR_STEEL)
- cstart = "\x8c"; // V_STEELMAP
- else if (color <= SKINCOLOR_BLUEBERRY)
- cstart = "\x84"; // V_BLUEMAP
- else if (color == SKINCOLOR_PURPLE)
- cstart = "\x81"; // V_PURPLEMAP
- else //if (color <= SKINCOLOR_POMEGRANATE)
- cstart = "\x89"; // V_LAVENDERMAP
- }
+ switch (color)
+ {
+ case SKINCOLOR_WHITE:
+ case SKINCOLOR_SILVER:
+ case SKINCOLOR_SLATE:
+ cstart = "\x80"; // White
+ break;
+ case SKINCOLOR_GREY:
+ case SKINCOLOR_NICKEL:
+ case SKINCOLOR_BLACK:
+ case SKINCOLOR_JET:
+ cstart = "\x86"; // V_GRAYMAP
+ break;
+ case SKINCOLOR_SEPIA:
+ case SKINCOLOR_BEIGE:
+ case SKINCOLOR_BROWN:
+ case SKINCOLOR_LEATHER:
+ case SKINCOLOR_RUST:
+ cstart = "\x8e"; // V_BROWNMAP
+ break;
+ case SKINCOLOR_FAIRY:
+ case SKINCOLOR_SALMON:
+ case SKINCOLOR_PINK:
+ case SKINCOLOR_ROSE:
+ case SKINCOLOR_BRICK:
+ case SKINCOLOR_BUBBLEGUM:
+ case SKINCOLOR_LILAC:
+ cstart = "\x8d"; // V_PINKMAP
+ break;
+ case SKINCOLOR_RUBY:
+ case SKINCOLOR_RASPBERRY:
+ case SKINCOLOR_CHERRY:
+ case SKINCOLOR_RED:
+ case SKINCOLOR_CRIMSON:
+ case SKINCOLOR_MAROON:
+ case SKINCOLOR_FLAME:
+ case SKINCOLOR_SCARLET:
+ case SKINCOLOR_KETCHUP:
+ cstart = "\x85"; // V_REDMAP
+ break;
+ case SKINCOLOR_DAWN:
+ case SKINCOLOR_SUNSET:
+ case SKINCOLOR_CREAMSICLE:
+ case SKINCOLOR_ORANGE:
+ case SKINCOLOR_PUMPKIN:
+ case SKINCOLOR_ROSEWOOD:
+ case SKINCOLOR_BURGUNDY:
+ case SKINCOLOR_TANGERINE:
+ cstart = "\x87"; // V_ORANGEMAP
+ break;
+ case SKINCOLOR_PEACH:
+ case SKINCOLOR_CARAMEL:
+ case SKINCOLOR_CREAM:
+ cstart = "\x8f"; // V_PEACHMAP
+ break;
+ case SKINCOLOR_GOLD:
+ case SKINCOLOR_ROYAL:
+ case SKINCOLOR_BRONZE:
+ case SKINCOLOR_COPPER:
+ cstart = "\x8A"; // V_GOLDMAP
+ break;
+ case SKINCOLOR_POPCORN:
+ case SKINCOLOR_YELLOW:
+ case SKINCOLOR_MUSTARD:
+ case SKINCOLOR_OLIVE:
+ cstart = "\x82"; // V_YELLOWMAP
+ break;
+ case SKINCOLOR_VOMIT:
+ case SKINCOLOR_GARDEN:
+ case SKINCOLOR_TEA:
+ case SKINCOLOR_PISTACHIO:
+ cstart = "\x8b"; // V_TEAMAP
+ break;
+ case SKINCOLOR_LIME:
+ case SKINCOLOR_HANDHELD:
+ case SKINCOLOR_MOSS:
+ case SKINCOLOR_CAMOUFLAGE:
+ case SKINCOLOR_ROBOHOOD:
+ case SKINCOLOR_MINT:
+ case SKINCOLOR_GREEN:
+ case SKINCOLOR_PINETREE:
+ case SKINCOLOR_EMERALD:
+ case SKINCOLOR_SWAMP:
+ case SKINCOLOR_DREAM:
+ case SKINCOLOR_PLAGUE:
+ case SKINCOLOR_ALGAE:
+ cstart = "\x83"; // V_GREENMAP
+ break;
+ case SKINCOLOR_CARIBBEAN:
+ case SKINCOLOR_AQUA:
+ case SKINCOLOR_TEAL:
+ case SKINCOLOR_CYAN:
+ case SKINCOLOR_JAWZ:
+ case SKINCOLOR_CERULEAN:
+ case SKINCOLOR_NAVY:
+ case SKINCOLOR_SAPPHIRE:
+ cstart = "\x88"; // V_SKYMAP
+ break;
+ case SKINCOLOR_PLATINUM:
+ case SKINCOLOR_STEEL:
+ cstart = "\x8c"; // V_STEELMAP
+ break;
+ case SKINCOLOR_PERIWINKLE:
+ case SKINCOLOR_BLUE:
+ case SKINCOLOR_BLUEBERRY:
+ case SKINCOLOR_NOVA:
+ cstart = "\x84"; // V_BLUEMAP
+ break;
+ case SKINCOLOR_ULTRAVIOLET:
+ case SKINCOLOR_PURPLE:
+ case SKINCOLOR_FUCHSIA:
+ cstart = "\x81"; // V_PURPLEMAP
+ break;
+ case SKINCOLOR_PASTEL:
+ case SKINCOLOR_MOONSLAM:
+ case SKINCOLOR_DUSK:
+ case SKINCOLOR_TOXIC:
+ case SKINCOLOR_MAUVE:
+ case SKINCOLOR_LAVENDER:
+ case SKINCOLOR_BYZANTIUM:
+ case SKINCOLOR_POMEGRANATE:
+ cstart = "\x89"; // V_LAVENDERMAP
+ break;
+ default:
+ break;
+ }
+ }
+
prefix = cstart;
// Give admins and remote admins their symbols.
@@ -1109,8 +1225,6 @@ static INT16 typelines = 1; // number of drawfill lines we need when drawing the
//
boolean HU_Responder(event_t *ev)
{
- INT32 c=0;
-
if (ev->type != ev_keydown)
return false;
@@ -1136,18 +1250,6 @@ boolean HU_Responder(event_t *ev)
return false;
}
- c = (INT32)ev->data1;
-
- // capslock (now handled outside of chat on so that it works everytime......)
- if (c && c == KEY_CAPSLOCK) // it's a toggle.
- {
- if (capslock)
- capslock = false;
- else
- capslock = true;
- return true;
- }
-
#ifndef NONET
if (!chat_on)
{
@@ -1175,6 +1277,7 @@ boolean HU_Responder(event_t *ev)
}
else // if chat_on
{
+ INT32 c = (INT32)ev->data1;
// Ignore modifier keys
// Note that we do this here so users can still set
@@ -1184,20 +1287,13 @@ boolean HU_Responder(event_t *ev)
|| ev->data1 == KEY_LALT || ev->data1 == KEY_RALT)
return true;
- c = (INT32)ev->data1;
+ // Ignore non-keyboard keys, except when the talk key is bound
+ if (ev->data1 >= KEY_MOUSE1
+ && (ev->data1 != gamecontrol[gc_talkkey][0]
+ && ev->data1 != gamecontrol[gc_talkkey][1]))
+ return false;
- // I know this looks very messy but this works. If it ain't broke, don't fix it!
- // shift LETTERS to uppercase if we have capslock or are holding shift
- if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
- {
- if (shiftdown ^ capslock)
- c = shiftxform[c];
- }
- else // if we're holding shift we should still shift non letter symbols
- {
- if (shiftdown)
- c = shiftxform[c];
- }
+ c = CON_ShiftChar(c);
// pasting. pasting is cool. chat is a bit limited, though :(
if (((c == 'v' || c == 'V') && ctrldown) && !CHAT_MUTE)
@@ -1421,13 +1517,16 @@ static void HU_drawMiniChat(void)
y = chaty - charheight*(msglines+1);
+#ifdef NETSPLITSCREEN
if (splitscreen)
{
y -= BASEVIDHEIGHT/2;
if (splitscreen > 1)
y += 16;
}
- y -= (cv_kartspeedometer.value ? 16 : 0);
+ else
+#endif
+ y -= (cv_kartspeedometer.value ? 16 : 0);
dx = 0;
dy = 0;
@@ -1603,9 +1702,9 @@ static void HU_drawChatLog(INT32 offset)
// draw arrows to indicate that we can (or not) scroll.
if (chat_scroll > 0)
- V_DrawThinString(chatx-9, ((justscrolledup) ? (chat_topy-1) : (chat_topy)), V_SNAPTOBOTTOM | V_SNAPTOLEFT | highlight, "\x1A"); // up arrow
+ V_DrawCharacter(chatx-9, ((justscrolledup) ? (chat_topy-1) : (chat_topy)), V_SNAPTOBOTTOM | V_SNAPTOLEFT | highlight | '\x1A', false); // up arrow
if (chat_scroll < chat_maxscroll)
- V_DrawThinString(chatx-9, chat_bottomy-((justscrolleddown) ? 5 : 6), V_SNAPTOBOTTOM | V_SNAPTOLEFT | highlight, "\x1B"); // down arrow
+ V_DrawCharacter(chatx-9, chat_bottomy-((justscrolleddown) ? 5 : 6), V_SNAPTOBOTTOM | V_SNAPTOLEFT | highlight | '\x1B', false); // down arrow
justscrolleddown = false;
justscrolledup = false;
@@ -2169,6 +2268,7 @@ static void HU_DrawSongCredits(void)
V_DrawRightAlignedThinString(cursongcredit.x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE|V_SNAPTOLEFT|(cursongcredit.trans<= 640) // how sad, we're using a shit resolution.
- V_DrawSmallString(dx, y+4, V_ALLOWLOWERCASE, va("%dms", ping));
-
- for (i=0; (i<3); i++) // Draw the ping bar
- {
- V_DrawFill(x+2 *(i-1), y+yoffset-4, 2, 8-yoffset, 31);
- if (i < numbars)
- V_DrawFill(x+2 *(i-1), y+yoffset-3, 1, 8-yoffset-1, barcolor);
-
- yoffset -= 2;
- }
+ V_DrawScaledPatch(x, y, flags, pinggfx[gfxnum]);
+ if (servermaxping && ping > servermaxping && hu_tick < 4) // flash ping red if too high
+ V_DrawPingNum(x, y+9, flags, ping, colormap);
+ else
+ V_DrawPingNum(x, y+9, flags, ping, NULL);
}
//
diff --git a/src/hu_stuff.h b/src/hu_stuff.h
index 8173c065..0f316bc7 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -63,6 +63,7 @@ typedef struct
//------------------------------------
#define HU_MAXMSGLEN 224
#define CHAT_BUFSIZE 64 // that's enough messages, right? We'll delete the older ones when that gets out of hand.
+#define NETSPLITSCREEN // why the hell WOULDN'T we want this?
#ifdef NETSPLITSCREEN
#define OLDCHAT (cv_consolechat.value == 1 || dedicated || vid.width < 640)
#else
@@ -79,7 +80,11 @@ extern boolean chat_on;
extern patch_t *hu_font[HU_FONTSIZE], *kart_font[KART_FONTSIZE], *tny_font[HU_FONTSIZE]; // SRB2kart
extern patch_t *tallnum[10];
+extern patch_t *pingnum[10];
+extern patch_t *pinggfx[5];
extern patch_t *nightsnum[10];
+extern patch_t *framecounter;
+extern patch_t *frameslash;
extern patch_t *lt_font[LT_FONTSIZE];
extern patch_t *cred_font[CRED_FONTSIZE];
extern patch_t *emeraldpics[7];
@@ -108,7 +113,7 @@ void HU_Drawer(void);
char HU_dequeueChatChar(void);
void HU_Erase(void);
void HU_clearChatChars(void);
-void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext); // Lat': Ping drawer for scoreboard.
+void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags); // Lat': Ping drawer for scoreboard.
//void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer);
//void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer);
void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol);
diff --git a/src/info.c b/src/info.c
index 93804b0c..d609bf55 100644
--- a/src/info.c
+++ b/src/info.c
@@ -3140,11 +3140,11 @@ state_t states[NUMSTATES] =
{SPR_NULL, 0, 1, {A_FZBoomSmoke}, 1, 0, S_FZEROBOOM12}, // S_FZEROBOOM11
{SPR_NULL, 0, 1, {A_FZBoomSmoke}, 0, 0, S_NULL}, // S_FZEROBOOM12
- {SPR_SMOK, FF_TRANS30, 30, {NULL}, 0, 0, S_FZSLOWSMOKE2}, // S_FZSLOWSMOKE1
- {SPR_SMOK, FF_TRANS30|1, 30, {NULL}, 0, 0, S_FZSLOWSMOKE3}, // S_FZSLOWSMOKE2
- {SPR_SMOK, FF_TRANS30|2, 30, {NULL}, 0, 0, S_FZSLOWSMOKE4}, // S_FZSLOWSMOKE3
- {SPR_SMOK, FF_TRANS30|3, 30, {NULL}, 0, 0, S_FZSLOWSMOKE5}, // S_FZSLOWSMOKE4
- {SPR_SMOK, FF_TRANS30|4, 30, {NULL}, 0, 0, S_NULL}, // S_FZSLOWSMOKE5
+ {SPR_SMOK, 0, 30, {NULL}, 0, 0, S_FZSLOWSMOKE2}, // S_FZSLOWSMOKE1
+ {SPR_SMOK, 1, 30, {NULL}, 0, 0, S_FZSLOWSMOKE3}, // S_FZSLOWSMOKE2
+ {SPR_SMOK, 2, 30, {NULL}, 0, 0, S_FZSLOWSMOKE4}, // S_FZSLOWSMOKE3
+ {SPR_SMOK, 3, 30, {NULL}, 0, 0, S_FZSLOWSMOKE5}, // S_FZSLOWSMOKE4
+ {SPR_SMOK, 4, 30, {NULL}, 0, 0, S_NULL}, // S_FZSLOWSMOKE5
// Various plants
{SPR_SBUS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SONICBUSH
@@ -3297,39 +3297,39 @@ state_t states[NUMSTATES] =
{SPR_CNDL, 1|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_BLUEFIRE3}, // S_BLUEFIRE2
{SPR_CNDL, 2|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_BLUEFIRE4}, // S_BLUEFIRE3
{SPR_CNDL, 3|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_BLUEFIRE1}, // S_BLUEFIRE4
-
+
{SPR_CNDL, 4|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_GREENFIRE2}, // S_GREENFIRE1
{SPR_CNDL, 5|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_GREENFIRE3}, // S_GREENFIRE2
{SPR_CNDL, 6|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_GREENFIRE4}, // S_GREENFIRE3
{SPR_CNDL, 7|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_GREENFIRE1}, // S_GREENFIRE4
-
+
{SPR_CHES, 0, -1, {NULL}, 0, 0, S_NULL}, // S_REGALCHEST
{SPR_CHIM, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_CHIMERASTATUE
{SPR_DRGN, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_DRAGONSTATUE
{SPR_LZMN, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_LIZARDMANSTATUE
{SPR_PGSS, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_PEGASUSSTATUE
-
+
{SPR_ZTCH, FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_ZELDAFIRE2}, // S_ZELDAFIRE1
{SPR_ZTCH, 1|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_ZELDAFIRE3}, // S_ZELDAFIRE2
{SPR_ZTCH, 2|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_ZELDAFIRE4}, // S_ZELDAFIRE3
{SPR_ZTCH, 3|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_ZELDAFIRE1}, // S_ZELDAFIRE4
-
+
{SPR_DOCH, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GANBARETHING
{SPR_DUCK, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GANBAREDUCK
{SPR_GTRE, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GANBARETREE
-
+
{SPR_MKMA, 1, 2, {A_Look}, (256<<16)|1, 0, S_MONOIDLE}, // S_MONOIDLE
{SPR_MKMA, 0, 3, {A_Chase}, 3, 0, S_MONOCHASE2}, // S_MONOCHASE1
{SPR_MKMA, 1, 3, {A_Chase}, 3, 0, S_MONOCHASE3}, // S_MONOCHASE2
{SPR_MKMA, 2, 3, {A_Chase}, 3, 0, S_MONOCHASE4}, // S_MONOCHASE3
{SPR_MKMA, 3, 3, {A_Chase}, 3, 0, S_MONOCHASE1}, // S_MONOCHASE4
{SPR_MKMP, 0, 24, {A_Pain}, 3, 0, S_MONOIDLE}, // S_MONOPAIN
-
+
{SPR_RTCH, FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_REDZELDAFIRE2}, // S_REDZELDAFIRE1
{SPR_RTCH, 1|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_REDZELDAFIRE3}, // S_REDZELDAFIRE2
{SPR_RTCH, 2|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_REDZELDAFIRE4}, // S_REDZELDAFIRE3
{SPR_RTCH, 3|FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_REDZELDAFIRE1}, // S_REDZELDAFIRE4
-
+
{SPR_BOWL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BOWLINGPIN
{SPR_BOWH, 0, 4, {A_BunnyHop}, 5, 20, S_BOWLINGHIT2}, // S_BOWLINGHIT1
{SPR_BOWH, 1, 2, {NULL}, 0, 0, S_BOWLINGHIT3}, // S_BOWLINGHIT2
@@ -3341,7 +3341,7 @@ state_t states[NUMSTATES] =
{SPR_TOAH, 1, 3, {NULL}, 0, 0, S_TOADHIT3}, // S_TOADHIT2
{SPR_TOAH, 2, 3, {NULL}, 0, 0, S_TOADHIT4}, // S_TOADHIT3
{SPR_TOAH, 3, 3, {NULL}, 0, 0, S_EBARREL18}, // S_TOADHIT4
-
+
{SPR_BRRL, 0, 1, {A_Look}, (96<<16)|1, 0, S_EBARRELIDLE}, // S_EBARRELIDLE
{SPR_BRRR, 0, 4, {NULL}, 0, 0, S_EBARREL2}, // S_EBARREL1
{SPR_BRRR, 1, 4, {NULL}, 0, 0, S_EBARREL3}, // S_EBARREL2
@@ -3361,30 +3361,30 @@ state_t states[NUMSTATES] =
{SPR_BRRR, 15, 4, {NULL}, 0, 0, S_EBARREL17}, // S_EBARREL16
{SPR_BRRR, 16, 4, {NULL}, 0, 0, S_EBARREL18}, // S_EBARREL17
{SPR_BRRR, 16, 0, {A_MineExplode}, MT_MINEEXPLOSION, 0, S_NULL}, // S_EBARREL18
-
+
{SPR_HRSE, 0, 230, {A_PlaySeeSound}, 0, 0, S_MERRYHORSE}, // S_MERRYHORSE
-
+
{SPR_BFRT, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLUEFRUIT
{SPR_OFRT, 0, -1, {NULL}, 0, 0, S_NULL}, // S_ORANGEFRUIT
{SPR_RFRT, 0, -1, {NULL}, 0, 0, S_NULL}, // S_REDFRUIT
{SPR_PFRT, 0, -1, {NULL}, 0, 0, S_NULL}, // S_PINKFRUIT
-
+
{SPR_ASPK, 0, 50, {A_PlayAttackSound}, 0, 0, S_ADVENTURESPIKEA2}, // S_ADVENTURESPIKEA1
{SPR_ASPK, 0, 50, {A_BunnyHop}, 20, 0, S_ADVENTURESPIKEA1}, // S_ADVENTURESPIKEA2
{SPR_ASPK, 0, 50, {A_PlayAttackSound}, 0, 0, S_ADVENTURESPIKEB2}, // S_ADVENTURESPIKEB1
{SPR_ASPK, 0, 35, {A_BunnyHop}, 15, 0, S_ADVENTURESPIKEB1}, // S_ADVENTURESPIKEB2
{SPR_ASPK, 0, 50, {A_PlayAttackSound}, 0, 0, S_ADVENTURESPIKEC2}, // S_ADVENTURESPIKEC1
{SPR_ASPK, 0, 65, {A_BunnyHop}, 25, 0, S_ADVENTURESPIKEC1}, // S_ADVENTURESPIKEC1
-
+
{SPR_HBST, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_BOOSTPROMPT2}, // S_BOOSTPROMPT1
{SPR_HBST, 1|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_BOOSTPROMPT1}, // S_BOOSTPROMPT2
-
+
{SPR_HBSF, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_BOOSTOFF2}, // S_BOOSTOFF1
{SPR_HBSF, 1|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_BOOSTOFF1}, // S_BOOSTOFF2
-
+
{SPR_HBSO, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_BOOSTON2}, // S_BOOSTON1
{SPR_HBSO, 1|FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_BOOSTON1}, // S_BOOSTON2
-
+
{SPR_WBLZ, 0, -1, {NULL}, 0, 0, S_NULL}, // S_LIZARDMAN
{SPR_WBLN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_LIONMAN
@@ -3394,6 +3394,13 @@ state_t states[NUMSTATES] =
{SPR_FWRK, 3|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK1}, // S_KARMAFIREWORK4
{SPR_FWRK, 4|FF_FULLBRIGHT, TICRATE, {NULL}, 0, 0, S_NULL}, // S_KARMAFIREWORKTRAIL
+ // Opaque smoke
+ {SPR_SMOK, 0, 4, {NULL}, 0, 0, S_OPAQUESMOKE2}, // S_OPAQUESMOKE1
+ {SPR_SMOK, 1, 5, {NULL}, 0, 0, S_OPAQUESMOKE3}, // S_OPAQUESMOKE2
+ {SPR_SMOK, 2, 6, {NULL}, 0, 0, S_OPAQUESMOKE4}, // S_OPAQUESMOKE3
+ {SPR_SMOK, 3, 7, {NULL}, 0, 0, S_OPAQUESMOKE5}, // S_OPAQUESMOKE4
+ {SPR_SMOK, 4, 8, {NULL}, 0, 0, S_NULL}, // S_OPAQUESMOKE5
+
#ifdef SEENAMES
{SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK
#endif
@@ -15018,8 +15025,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // xdeathstate
sfx_None, // deathsound
8, // speed
- 8*FRACUNIT, // radius
- 8*FRACUNIT, // height
+ 32*FRACUNIT, // radius
+ 64*FRACUNIT, // height
1, // display offset
100, // mass
0, // damage
@@ -15449,7 +15456,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_ORBINAUT_SHIELDDEAD, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
- 10*FRACUNIT, // speed
+ 4*FRACUNIT, // speed
16*FRACUNIT, // radius
32*FRACUNIT, // height
0, // display offset
@@ -15530,7 +15537,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_JAWZ_DEAD1, // deathstate
S_JAWZ_DEAD2, // xdeathstate
sfx_None, // deathsound
- 10*FRACUNIT, // speed
+ 4*FRACUNIT, // speed
16*FRACUNIT, // radius
32*FRACUNIT, // height
0, // display offset
@@ -15574,7 +15581,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
1, // spawnhealth
S_NULL, // seestate
sfx_tossed, // seesound
- 6*TICRATE, // reactiontime
+ 0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
192*FRACUNIT, // painchance
@@ -15861,7 +15868,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
1, // damage
sfx_None, // activesound
- MF_BOUNCE|MF_FLOAT|MF_NOCLIPTHING|MF_MISSILE|MF_SHOOTABLE|MF_DONTENCOREMAP, // flags
+ MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
diff --git a/src/info.h b/src/info.h
index 2ebd11fe..0e23a07c 100644
--- a/src/info.h
+++ b/src/info.h
@@ -608,7 +608,7 @@ typedef enum sprite
// Kart Items
SPR_RSHE, // Rocket sneaker
SPR_FITM, // Eggman Monitor
- SPR_BANA, // Banana Peel
+ SPR_BANA, // Banana Peel
SPR_ORBN, // Orbinaut
SPR_JAWZ, // Jawz
SPR_SSMN, // SS Mine
@@ -3871,7 +3871,7 @@ typedef enum state
S_GARU1,
S_GARU2,
S_GARU3,
- S_TGARU0,
+ S_TGARU0,
S_TGARU1,
S_TGARU2,
S_TGARU3, // Wind attack used by Roaming Shadows on Players.
@@ -4052,6 +4052,12 @@ typedef enum state
S_KARMAFIREWORK4,
S_KARMAFIREWORKTRAIL,
+ S_OPAQUESMOKE1,
+ S_OPAQUESMOKE2,
+ S_OPAQUESMOKE3,
+ S_OPAQUESMOKE4,
+ S_OPAQUESMOKE5,
+
#ifdef SEENAMES
S_NAMECHECK,
#endif
@@ -4612,14 +4618,14 @@ typedef enum mobj_type
MT_EGGMANITEM_SHIELD,
MT_BANANA, // Banana Stuff
- MT_BANANA_SHIELD,
+ MT_BANANA_SHIELD,
MT_ORBINAUT, // Orbinaut stuff
MT_ORBINAUT_SHIELD,
MT_JAWZ, // Jawz stuff
MT_JAWZ_DUD,
- MT_JAWZ_SHIELD,
+ MT_JAWZ_SHIELD,
MT_PLAYERRETICULE, // Jawz reticule
diff --git a/src/k_kart.c b/src/k_kart.c
index 320105ef..70075858 100644
--- a/src/k_kart.c
+++ b/src/k_kart.c
@@ -1,8282 +1,8922 @@
-// SONIC ROBO BLAST 2 KART ~ ZarroTsu
-//-----------------------------------------------------------------------------
-/// \file k_kart.c
-/// \brief SRB2kart general.
-/// All of the SRB2kart-unique stuff.
-
-#include "doomdef.h"
-#include "hu_stuff.h"
-#include "g_game.h"
-#include "m_random.h"
-#include "p_local.h"
-#include "p_slopes.h"
-#include "r_draw.h"
-#include "r_local.h"
-#include "s_sound.h"
-#include "st_stuff.h"
-#include "v_video.h"
-#include "z_zone.h"
-#include "m_misc.h"
-#include "m_cond.h"
-#include "k_kart.h"
-#include "f_finale.h"
-#include "lua_hud.h" // For Lua hud checks
-#include "lua_hook.h" // For MobjDamage and ShouldDamage
-
-// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H:
-// gamespeed is cc (0 for easy, 1 for normal, 2 for hard)
-// franticitems is Frantic Mode items, bool
-// encoremode is Encore Mode (duh), bool
-// comeback is Battle Mode's karma comeback, also bool
-// battlewanted is an array of the WANTED player nums, -1 for no player in that slot
-// indirectitemcooldown is timer before anyone's allowed another Shrink/SPB
-// mapreset is set when enough players fill an empty server
-// nospectategrief is the players in-game needed to eliminate the person in last
-
-
-//{ SRB2kart Color Code
-
-#define SKIN_RAMP_LENGTH 16
-#define DEFAULT_STARTTRANSCOLOR 160
-#define NUM_PALETTE_ENTRIES 256
-
-// These should be within 14 characters to fit on the character select screen
-const char *KartColor_Names[MAXSKINCOLORS] =
-{
- "None", // 00 // SKINCOLOR_NONE
- "White", // 01 // SKINCOLOR_WHITE
- "Silver", // 02 // SKINCOLOR_SILVER
- "Grey", // 03 // SKINCOLOR_GREY
- "Nickel", // 04 // SKINCOLOR_NICKEL
- "Black", // 05 // SKINCOLOR_BLACK
- "Sepia", // 06 // SKINCOLOR_SEPIA
- "Beige", // 07 // SKINCOLOR_BEIGE
- "Brown", // 08 // SKINCOLOR_BROWN
- "Leather", // 09 // SKINCOLOR_LEATHER
- "Salmon", // 10 // SKINCOLOR_SALMON
- "Pink", // 11 // SKINCOLOR_PINK
- "Rose", // 12 // SKINCOLOR_ROSE
- "Ruby", // 13 // SKINCOLOR_RUBY
- "Raspberry", // 14 // SKINCOLOR_RASPBERRY
- "Red", // 15 // SKINCOLOR_RED
- "Crimson", // 16 // SKINCOLOR_CRIMSON
- "Ketchup", // 17 // SKINCOLOR_KETCHUP
- "Dawn", // 18 // SKINCOLOR_DAWN
- "Creamsicle", // 19 // SKINCOLOR_CREAMSICLE
- "Orange", // 20 // SKINCOLOR_ORANGE
- "Pumpkin", // 21 // SKINCOLOR_PUMPKIN
- "Rosewood", // 22 // SKINCOLOR_ROSEWOOD
- "Burgundy", // 23 // SKINCOLOR_BURGUNDY
- "Tangerine", // 24 // SKINCOLOR_TANGERINE
- "Peach", // 25 // SKINCOLOR_PEACH
- "Caramel", // 26 // SKINCOLOR_CARAMEL
- "Gold", // 27 // SKINCOLOR_GOLD
- "Bronze", // 28 // SKINCOLOR_BRONZE
- "Yellow", // 29 // SKINCOLOR_YELLOW
- "Mustard", // 30 // SKINCOLOR_MUSTARD
- "Olive", // 31 // SKINCOLOR_OLIVE
- "Vomit", // 32 // SKINCOLOR_VOMIT
- "Garden", // 33 // SKINCOLOR_GARDEN
- "Lime", // 34 // SKINCOLOR_LIME
- "Tea", // 35 // SKINCOLOR_TEA
- "Pistachio", // 36 // SKINCOLOR_PISTACHIO
- "Robo-Hood", // 37 // SKINCOLOR_ROBOHOOD
- "Moss", // 38 // SKINCOLOR_MOSS
- "Mint", // 39 // SKINCOLOR_MINT
- "Green", // 40 // SKINCOLOR_GREEN
- "Pinetree", // 41 // SKINCOLOR_PINETREE
- "Emerald", // 42 // SKINCOLOR_EMERALD
- "Swamp", // 43 // SKINCOLOR_SWAMP
- "Dream", // 44 // SKINCOLOR_DREAM
- "Aqua", // 45 // SKINCOLOR_AQUA
- "Teal", // 46 // SKINCOLOR_TEAL
- "Cyan", // 47 // SKINCOLOR_CYAN
- "Jawz", // 48 // SKINCOLOR_JAWZ
- "Cerulean", // 49 // SKINCOLOR_CERULEAN
- "Navy", // 50 // SKINCOLOR_NAVY
- "Slate", // 51 // SKINCOLOR_SLATE
- "Steel", // 52 // SKINCOLOR_STEEL
- "Jet", // 53 // SKINCOLOR_JET
- "Sapphire", // 54 // SKINCOLOR_SAPPHIRE
- "Periwinkle", // 55 // SKINCOLOR_PERIWINKLE
- "Blue", // 56 // SKINCOLOR_BLUE
- "Blueberry", // 57 // SKINCOLOR_BLUEBERRY
- "Dusk", // 58 // SKINCOLOR_DUSK
- "Purple", // 59 // SKINCOLOR_PURPLE
- "Lavender", // 60 // SKINCOLOR_LAVENDER
- "Byzantium", // 61 // SKINCOLOR_BYZANTIUM
- "Pomegranate", // 62 // SKINCOLOR_POMEGRANATE
- "Lilac" // 63 // SKINCOLOR_LILAC
-};
-
-// Color_Opposite replacement; frame setting has not been changed from 8 for most, should be done later
-const UINT8 KartColor_Opposite[MAXSKINCOLORS*2] =
-{
- SKINCOLOR_NONE,8, // 00 // SKINCOLOR_NONE
- SKINCOLOR_BLACK,8, // 01 // SKINCOLOR_WHITE
- SKINCOLOR_NICKEL,8, // 02 // SKINCOLOR_SILVER
- SKINCOLOR_GREY,8, // 03 // SKINCOLOR_GREY
- SKINCOLOR_SILVER,8, // 04 // SKINCOLOR_NICKEL
- SKINCOLOR_WHITE,8, // 05 // SKINCOLOR_BLACK
- SKINCOLOR_LEATHER,6, // 06 // SKINCOLOR_SEPIA
- SKINCOLOR_BROWN,2, // 07 // SKINCOLOR_BEIGE
- SKINCOLOR_BEIGE,8, // 08 // SKINCOLOR_BROWN
- SKINCOLOR_SEPIA,8, // 09 // SKINCOLOR_LEATHER
- SKINCOLOR_TEA,8, // 10 // SKINCOLOR_SALMON
- SKINCOLOR_PISTACHIO,8, // 11 // SKINCOLOR_PINK
- SKINCOLOR_MOSS,8, // 12 // SKINCOLOR_ROSE
- SKINCOLOR_SAPPHIRE,8, // 13 // SKINCOLOR_RUBY
- SKINCOLOR_MINT,8, // 14 // SKINCOLOR_RASPBERRY
- SKINCOLOR_GREEN,6, // 15 // SKINCOLOR_RED
- SKINCOLOR_PINETREE,6, // 16 // SKINCOLOR_CRIMSON
- SKINCOLOR_MUSTARD,10, // 17 // SKINCOLOR_KETCHUP
- SKINCOLOR_DUSK,8, // 18 // SKINCOLOR_DAWN
- SKINCOLOR_PERIWINKLE,8, // 19 // SKINCOLOR_CREAMSICLE
- SKINCOLOR_BLUE,8, // 20 // SKINCOLOR_ORANGE
- SKINCOLOR_BLUEBERRY,8, // 21 // SKINCOLOR_PUMPKIN
- SKINCOLOR_NAVY,6, // 22 // SKINCOLOR_ROSEWOOD
- SKINCOLOR_JET,8, // 23 // SKINCOLOR_BURGUNDY
- SKINCOLOR_LIME,8, // 24 // SKINCOLOR_TANGERINE
- SKINCOLOR_CYAN,8, // 25 // SKINCOLOR_PEACH
- SKINCOLOR_CERULEAN,8, // 26 // SKINCOLOR_CARAMEL
- SKINCOLOR_SLATE,8, // 27 // SKINCOLOR_GOLD
- SKINCOLOR_STEEL,8, // 28 // SKINCOLOR_BRONZE
- SKINCOLOR_AQUA,8, // 29 // SKINCOLOR_YELLOW
- SKINCOLOR_KETCHUP,8, // 30 // SKINCOLOR_MUSTARD
- SKINCOLOR_TEAL,8, // 31 // SKINCOLOR_OLIVE
- SKINCOLOR_ROBOHOOD,8, // 32 // SKINCOLOR_VOMIT
- SKINCOLOR_LAVENDER,6, // 33 // SKINCOLOR_GARDEN
- SKINCOLOR_TANGERINE,8, // 34 // SKINCOLOR_LIME
- SKINCOLOR_SALMON,8, // 35 // SKINCOLOR_TEA
- SKINCOLOR_PINK,6, // 36 // SKINCOLOR_PISTACHIO
- SKINCOLOR_VOMIT,8, // 37 // SKINCOLOR_ROBOHOOD
- SKINCOLOR_ROSE,8, // 38 // SKINCOLOR_MOSS
- SKINCOLOR_RASPBERRY,8, // 39 // SKINCOLOR_MINT
- SKINCOLOR_RED,8, // 40 // SKINCOLOR_GREEN
- SKINCOLOR_CRIMSON,8, // 41 // SKINCOLOR_PINETREE
- SKINCOLOR_PURPLE,8, // 42 // SKINCOLOR_EMERALD
- SKINCOLOR_BYZANTIUM,8, // 43 // SKINCOLOR_SWAMP
- SKINCOLOR_POMEGRANATE,8, // 44 // SKINCOLOR_DREAM
- SKINCOLOR_YELLOW,8, // 45 // SKINCOLOR_AQUA
- SKINCOLOR_OLIVE,8, // 46 // SKINCOLOR_TEAL
- SKINCOLOR_PEACH,8, // 47 // SKINCOLOR_CYAN
- SKINCOLOR_LILAC,10, // 48 // SKINCOLOR_JAWZ
- SKINCOLOR_CARAMEL,8, // 49 // SKINCOLOR_CERULEAN
- SKINCOLOR_ROSEWOOD,8, // 50 // SKINCOLOR_NAVY
- SKINCOLOR_GOLD,10, // 51 // SKINCOLOR_SLATE
- SKINCOLOR_BRONZE,10, // 52 // SKINCOLOR_STEEL
- SKINCOLOR_BURGUNDY,8, // 53 // SKINCOLOR_JET
- SKINCOLOR_RUBY,6, // 54 // SKINCOLOR_SAPPHIRE
- SKINCOLOR_CREAMSICLE,8, // 55 // SKINCOLOR_PERIWINKLE
- SKINCOLOR_ORANGE,8, // 56 // SKINCOLOR_BLUE
- SKINCOLOR_PUMPKIN,8, // 57 // SKINCOLOR_BLUEBERRY
- SKINCOLOR_DAWN,6, // 58 // SKINCOLOR_DUSK
- SKINCOLOR_EMERALD,8, // 59 // SKINCOLOR_PURPLE
- SKINCOLOR_GARDEN,6, // 60 // SKINCOLOR_LAVENDER
- SKINCOLOR_SWAMP,8, // 61 // SKINCOLOR_BYZANTIUM
- SKINCOLOR_DREAM,8, // 62 // SKINCOLOR_POMEGRANATE
- SKINCOLOR_JAWZ,6 // 63 // SKINCOLOR_LILAC
-};
-
-UINT8 colortranslations[MAXSKINCOLORS][16] = {
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // SKINCOLOR_NONE
- {120, 120, 120, 120, 0, 1, 3, 4, 6, 7, 10, 14, 18, 22, 25, 28}, // SKINCOLOR_WHITE
- { 0, 1, 2, 4, 5, 7, 8, 10, 13, 15, 18, 20, 23, 25, 28, 30}, // SKINCOLOR_SILVER
- { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, // SKINCOLOR_GREY
- { 12, 14, 16, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, // SKINCOLOR_NICKEL
- { 16, 17, 19, 21, 22, 24, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_BLACK
- { 0, 1, 3, 5, 7, 9, 34, 36, 38, 40, 42, 44, 60, 61, 62, 63}, // SKINCOLOR_SEPIA
- { 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}, // SKINCOLOR_BEIGE
- { 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63}, // SKINCOLOR_BROWN
- { 51, 52, 53, 55, 56, 57, 58, 60, 61, 63, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_LEATHER
- {120, 120, 120, 121, 121, 122, 122, 123, 124, 125, 126, 128, 129, 131, 133, 135}, // SKINCOLOR_SALMON
- {120, 121, 121, 122, 144, 145, 146, 147, 148, 149, 150, 151, 134, 136, 138, 140}, // SKINCOLOR_PINK
- {144, 145, 146, 147, 148, 149, 150, 151, 134, 135, 136, 137, 138, 139, 140, 141}, // SKINCOLOR_ROSE
- {121, 122, 145, 146, 147, 149, 131, 132, 133, 134, 135, 197, 197, 198, 199, 255}, // SKINCOLOR_RUBY
- {120, 121, 122, 123, 124, 125, 126, 127, 128, 130, 131, 133, 134, 136, 137, 139}, // SKINCOLOR_RASPBERRY
- {125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140}, // SKINCOLOR_RED
- {130, 131, 132, 133, 134, 136, 137, 138, 139, 139, 140, 140, 141, 141, 142, 143}, // SKINCOLOR_CRIMSON
- {104, 113, 113, 85, 86, 88, 128, 129, 131, 133, 134, 136, 138, 139, 141, 143}, // SKINCOLOR_KETCHUP
- {120, 121, 122, 123, 124, 147, 147, 148, 90, 91, 92, 93, 94, 95, 152, 154}, // SKINCOLOR_DAWN
- {120, 120, 80, 80, 81, 82, 83, 83, 84, 85, 86, 88, 89, 91, 93, 95}, // SKINCOLOR_CREAMSICLE
- { 80, 81, 82, 83, 84, 85, 86, 88, 89, 91, 94, 95, 154, 156, 158, 159}, // SKINCOLOR_ORANGE
- { 84, 85, 86, 87, 88, 90, 92, 93, 94, 95, 152, 153, 154, 156, 157, 159}, // SKINCOLOR_PUMPKIN
- { 90, 91, 92, 93, 94, 152, 153, 154, 155, 156, 157, 158, 159, 139, 141, 143}, // SKINCOLOR_ROSEWOOD
- { 94, 95, 152, 153, 154, 156, 157, 159, 141, 141, 141, 142, 142, 143, 143, 31}, // SKINCOLOR_BURGUNDY
- { 98, 98, 112, 112, 113, 113, 84, 85, 87, 89, 91, 93, 95, 153, 156, 159}, // SKINCOLOR_TANGERINE
- { 64, 65, 67, 68, 70, 71, 73, 74, 76, 77, 79, 48, 50, 53, 56, 59}, // SKINCOLOR_PEACH
- { 64, 66, 68, 70, 72, 74, 76, 78, 48, 50, 52, 54, 56, 58, 60, 62}, // SKINCOLOR_CARAMEL
- {112, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119}, // SKINCOLOR_GOLD
- {112, 113, 114, 115, 116, 117, 118, 119, 156, 157, 158, 159, 141, 141, 142, 143}, // SKINCOLOR_BRONZE
- { 96, 97, 98, 100, 101, 102, 104, 113, 114, 115, 116, 117, 118, 119, 156, 159}, // SKINCOLOR_YELLOW
- { 96, 98, 99, 112, 113, 114, 114, 106, 106, 107, 107, 108, 108, 109, 110, 111}, // SKINCOLOR_MUSTARD
- {105, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 31}, // SKINCOLOR_OLIVE
- {121, 144, 145, 72, 73, 84, 114, 115, 107, 108, 109, 183, 223, 207, 30, 246}, // SKINCOLOR_VOMIT
- { 98, 99, 112, 101, 113, 114, 106, 179, 180, 180, 181, 182, 183, 173, 174, 175}, // SKINCOLOR_GARDEN
- { 96, 97, 99, 100, 102, 104, 160, 162, 164, 166, 168, 171, 223, 223, 207, 31}, // SKINCOLOR_LIME
- {120, 120, 176, 176, 176, 177, 177, 178, 178, 179, 179, 180, 180, 181, 182, 183}, // SKINCOLOR_TEA
- {120, 120, 176, 176, 177, 177, 178, 179, 165, 166, 167, 168, 169, 170, 171, 172}, // SKINCOLOR_PISTACHIO
- {176, 176, 177, 178, 165, 166, 167, 167, 168, 169, 182, 182, 182, 183, 183, 183}, // SKINCOLOR_ROBOHOOD
- {178, 178, 178, 179, 179, 180, 181, 182, 183, 172, 172, 173, 173, 174, 174, 175}, // SKINCOLOR_MOSS
- {120, 176, 176, 176, 177, 163, 164, 165, 167, 221, 221, 222, 223, 207, 207, 31}, // SKINCOLOR_MINT
- {160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175}, // SKINCOLOR_GREEN
- {160, 161, 162, 164, 165, 167, 169, 170, 171, 171, 172, 173, 174, 175, 30, 31}, // SKINCOLOR_PINETREE
- {160, 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, 189, 189, 190, 191, 175}, // SKINCOLOR_EMERALD
- {186, 187, 188, 188, 188, 189, 189, 190, 190, 191, 175, 175, 30, 30, 31, 31}, // SKINCOLOR_SWAMP
- {120, 120, 80, 80, 81, 177, 162, 164, 228, 228, 204, 204, 205, 205, 206, 207}, // SKINCOLOR_DREAM
- {120, 208, 208, 210, 212, 214, 220, 220, 220, 221, 221, 222, 222, 223, 223, 191}, // SKINCOLOR_AQUA
- {210, 213, 220, 220, 220, 216, 216, 221, 221, 221, 222, 222, 223, 223, 191, 31}, // SKINCOLOR_TEAL
- {120, 120, 208, 208, 209, 210, 211, 212, 213, 215, 216, 217, 218, 219, 222, 223}, // SKINCOLOR_CYAN
- {120, 120, 208, 209, 210, 226, 215, 216, 217, 229, 229, 205, 205, 206, 207, 31}, // SKINCOLOR_JAWZ
- {208, 209, 211, 213, 215, 216, 216, 217, 217, 218, 218, 219, 205, 206, 207, 207}, // SKINCOLOR_CERULEAN
- {211, 212, 213, 215, 216, 218, 219, 205, 206, 206, 207, 207, 28, 29, 30, 31}, // SKINCOLOR_NAVY
- {120, 120, 200, 200, 200, 201, 201, 201, 202, 202, 202, 203, 204, 205, 206, 207}, // SKINCOLOR_SLATE
- {120, 200, 200, 201, 201, 202, 202, 203, 203, 204, 204, 205, 205, 206, 207, 31}, // SKINCOLOR_STEEL
- {225, 226, 227, 228, 229, 205, 205, 206, 207, 207, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_JET
- {208, 209, 211, 213, 215, 217, 229, 230, 232, 234, 236, 238, 240, 242, 244, 246}, // SKINCOLOR_SAPPHIRE
- {120, 224, 225, 226, 226, 227, 228, 228, 229, 230, 231, 234, 235, 237, 239, 241}, // SKINCOLOR_PERIWINKLE
- {224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239}, // SKINCOLOR_BLUE
- {228, 229, 230, 231, 232, 233, 234, 235, 237, 238, 239, 240, 242, 243, 244, 245}, // SKINCOLOR_BLUEBERRY
- {192, 192, 248, 249, 250, 251, 204, 204, 205, 205, 206, 206, 207, 29, 30, 31}, // SKINCOLOR_DUSK
- {192, 192, 192, 193, 193, 194, 194, 195, 195, 196, 196, 197, 197, 198, 198, 199}, // SKINCOLOR_PURPLE
- {248, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 253, 254, 254, 255}, // SKINCOLOR_LAVENDER
- {192, 248, 249, 250, 251, 252, 253, 254, 255, 255, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_BYZANTIUM
- {144, 145, 146, 147, 148, 149, 150, 251, 251, 252, 252, 253, 254, 255, 29, 30}, // SKINCOLOR_POMEGRANATE
- {120, 120, 120, 121, 121, 122, 122, 123, 192, 248, 249, 250, 251, 252, 253, 254}, // SKINCOLOR_LILAC
- /* Removed Colours
- {120, 121, 123, 124, 126, 127, 129, 130, 132, 133, 135, 136, 138, 139, 141, 143}, // old SKINCOLOR_RUBY, removed for other colors
- {224, 225, 226, 228, 229, 231, 232, 234, 235, 237, 238, 240, 241, 243, 244, 246}, // old SKINCOLOR_SAPPHIRE, removed for other colors
- { 72, 73, 74, 75, 76, 77, 78, 79, 48, 49, 50, 51, 52, 53, 54, 55}, // old SKINCOLOR_CARAMEL, new Caramel was previously Shiny Caramel
- {215, 216, 217, 218, 204, 205, 206, 237, 238, 239, 240, 241, 242, 243, 244, 245}, // old SKINCOLOR_NAVY, too similar to Jet
- { 80, 81, 83, 85, 86, 88, 90, 91, 93, 95, 152, 153, 154, 156, 157, 159}, // SKINCOLOR_AMBER, removed for other colors
- {160, 160, 160, 184, 184, 184, 185, 185, 185, 186, 187, 187, 188, 188, 189, 190}, // SKINCOLOR_JADE, removed for other colors
- {224, 225, 226, 212, 213, 213, 214, 215, 220, 221, 172, 222, 173, 223, 174, 175}, // SKINCOLOR_FROST, merged into Aqua
- { 96, 97, 99, 100, 102, 104, 105, 105, 106, 107, 107, 108, 109, 109, 110, 111}, // SKINCOLOR_CANARY, replaced with Mustard
- {192, 193, 194, 195, 196, 197, 198, 199, 255, 255, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_INDIGO, too similar to Byzantium
- { 1, 145, 125, 73, 83, 114, 106, 180, 187, 168, 219, 205, 236, 206, 199, 255}, // SKINCOLOR_RAINBOW, is Vomit 2.0
- */
-};
-
-// Define for getting accurate color brightness readings according to how the human eye sees them.
-// https://en.wikipedia.org/wiki/Relative_luminance
-// 0.2126 to red
-// 0.7152 to green
-// 0.0722 to blue
-// (See this same define in hw_md2.c!)
-#define SETBRIGHTNESS(brightness,r,g,b) \
- brightness = (UINT8)(((1063*((UINT16)r)/5000) + (3576*((UINT16)g)/5000) + (361*((UINT16)b)/5000)) / 3)
-
-/** \brief Generates the rainbow colourmaps that are used when a player has the invincibility power
-
- \param dest_colormap colormap to populate
- \param skincolor translation color
-*/
-void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor)
-{
- INT32 i;
- RGBA_t color;
- UINT8 brightness;
- INT32 j;
- UINT8 colorbrightnesses[16];
- UINT16 brightdif;
- INT32 temp;
-
- // first generate the brightness of all the colours of that skincolour
- for (i = 0; i < 16; i++)
- {
- color = V_GetColor(colortranslations[skincolor][i]);
- SETBRIGHTNESS(colorbrightnesses[i], color.s.red, color.s.green, color.s.blue);
- }
-
- // next, for every colour in the palette, choose the transcolor that has the closest brightness
- for (i = 0; i < NUM_PALETTE_ENTRIES; i++)
- {
- if (i == 0 || i == 31 || i == 120) // pure black and pure white don't change
- {
- dest_colormap[i] = (UINT8)i;
- continue;
- }
- color = V_GetColor(i);
- SETBRIGHTNESS(brightness, color.s.red, color.s.green, color.s.blue);
- brightdif = 256;
- for (j = 0; j < 16; j++)
- {
- temp = abs((INT16)brightness - (INT16)colorbrightnesses[j]);
- if (temp < brightdif)
- {
- brightdif = (UINT16)temp;
- dest_colormap[i] = colortranslations[skincolor][j];
- }
- }
- }
-}
-
-#undef SETBRIGHTNESS
-
-/** \brief Generates a translation colormap for Kart, to replace R_GenerateTranslationColormap in r_draw.c
-
- \param dest_colormap colormap to populate
- \param skinnum number of skin, TC_DEFAULT or TC_BOSS
- \param color translation color
-
- \return void
-*/
-void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color)
-{
- INT32 i;
- INT32 starttranscolor;
-
- // Handle a couple of simple special cases
- if (skinnum == TC_BOSS
- || skinnum == TC_ALLWHITE
- || skinnum == TC_METALSONIC
- || skinnum == TC_BLINK
- || color == SKINCOLOR_NONE)
- {
- for (i = 0; i < NUM_PALETTE_ENTRIES; i++)
- {
- if (skinnum == TC_ALLWHITE)
- dest_colormap[i] = 0;
- else if (skinnum == TC_BLINK)
- dest_colormap[i] = colortranslations[color][3];
- else
- dest_colormap[i] = (UINT8)i;
- }
-
- // White!
- if (skinnum == TC_BOSS)
- dest_colormap[31] = 0;
- else if (skinnum == TC_METALSONIC)
- dest_colormap[239] = 0;
-
- return;
- }
- else if (skinnum == TC_RAINBOW)
- {
- K_RainbowColormap(dest_colormap, color);
- return;
- }
-
- starttranscolor = (skinnum != TC_DEFAULT) ? skins[skinnum].starttranscolor : DEFAULT_STARTTRANSCOLOR;
-
- // Fill in the entries of the palette that are fixed
- for (i = 0; i < starttranscolor; i++)
- dest_colormap[i] = (UINT8)i;
-
- for (i = (UINT8)(starttranscolor + 16); i < NUM_PALETTE_ENTRIES; i++)
- dest_colormap[i] = (UINT8)i;
-
- // Build the translated ramp
- for (i = 0; i < SKIN_RAMP_LENGTH; i++)
- {
- // Sryder 2017-10-26: What was here before was most definitely not particularly readable, check above for new color translation table
- dest_colormap[starttranscolor + i] = colortranslations[color][i];
- }
-}
-
-/** \brief Pulls kart color by name, to replace R_GetColorByName in r_draw.c
-
- \param name color name
-
- \return 0
-*/
-UINT8 K_GetKartColorByName(const char *name)
-{
- UINT8 color = (UINT8)atoi(name);
- if (color > 0 && color < MAXSKINCOLORS)
- return color;
- for (color = 1; color < MAXSKINCOLORS; color++)
- if (!stricmp(KartColor_Names[color], name))
- return color;
- return 0;
-}
-
-//}
-
-//{ SRB2kart Net Variables
-
-void K_RegisterKartStuff(void)
-{
- CV_RegisterVar(&cv_sneaker);
- CV_RegisterVar(&cv_rocketsneaker);
- CV_RegisterVar(&cv_invincibility);
- CV_RegisterVar(&cv_banana);
- CV_RegisterVar(&cv_eggmanmonitor);
- CV_RegisterVar(&cv_orbinaut);
- CV_RegisterVar(&cv_jawz);
- CV_RegisterVar(&cv_mine);
- CV_RegisterVar(&cv_ballhog);
- CV_RegisterVar(&cv_selfpropelledbomb);
- CV_RegisterVar(&cv_grow);
- CV_RegisterVar(&cv_shrink);
- CV_RegisterVar(&cv_thundershield);
- CV_RegisterVar(&cv_hyudoro);
- CV_RegisterVar(&cv_pogospring);
- CV_RegisterVar(&cv_kitchensink);
-
- CV_RegisterVar(&cv_triplesneaker);
- CV_RegisterVar(&cv_triplebanana);
- CV_RegisterVar(&cv_decabanana);
- CV_RegisterVar(&cv_tripleorbinaut);
- CV_RegisterVar(&cv_quadorbinaut);
- CV_RegisterVar(&cv_dualjawz);
-
- CV_RegisterVar(&cv_kartminimap);
- CV_RegisterVar(&cv_kartcheck);
- CV_RegisterVar(&cv_kartinvinsfx);
- CV_RegisterVar(&cv_kartspeed);
- CV_RegisterVar(&cv_kartbumpers);
- CV_RegisterVar(&cv_kartfrantic);
- CV_RegisterVar(&cv_kartcomeback);
- CV_RegisterVar(&cv_kartencore);
- CV_RegisterVar(&cv_kartvoterulechanges);
- CV_RegisterVar(&cv_kartspeedometer);
- CV_RegisterVar(&cv_kartvoices);
- CV_RegisterVar(&cv_karteliminatelast);
- CV_RegisterVar(&cv_votetime);
-
- CV_RegisterVar(&cv_kartdebugitem);
- CV_RegisterVar(&cv_kartdebugamount);
- CV_RegisterVar(&cv_kartdebugshrink);
- CV_RegisterVar(&cv_kartdebugdistribution);
- CV_RegisterVar(&cv_kartdebughuddrop);
-
- CV_RegisterVar(&cv_kartdebugcheckpoint);
- CV_RegisterVar(&cv_kartdebugnodes);
-}
-
-//}
-
-boolean K_IsPlayerLosing(player_t *player)
-{
- INT32 winningpos = 1;
- UINT8 i, pcount = 0;
-
- if (player->kartstuff[k_position] == 1)
- return false;
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator)
- continue;
- if (players[i].kartstuff[k_position] > pcount)
- pcount = players[i].kartstuff[k_position];
- }
-
- if (pcount <= 1)
- return false;
-
- winningpos = pcount/2;
- if (pcount % 2) // any remainder?
- winningpos++;
-
- return (player->kartstuff[k_position] > winningpos);
-}
-
-boolean K_IsPlayerWanted(player_t *player)
-{
- UINT8 i;
- if (!(G_BattleGametype()))
- return false;
- for (i = 0; i < 4; i++)
- {
- if (battlewanted[i] == -1)
- break;
- if (player == &players[battlewanted[i]])
- return true;
- }
- return false;
-}
-
-//{ SRB2kart Roulette Code - Position Based
-
-#define NUMKARTODDS 80
-
-// Less ugly 2D arrays
-static INT32 K_KartItemOddsRace[NUMKARTRESULTS][10] =
-{
- //P-Odds 0 1 2 3 4 5 6 7 8 9
- /*Sneaker*/ {20, 0, 0, 4, 6, 6, 0, 0, 0, 0 }, // Sneaker
- /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 1, 3, 5, 3, 0 }, // Rocket Sneaker
- /*Invincibility*/ { 0, 0, 0, 0, 0, 1, 4, 6,14, 0 }, // Invincibility
- /*Banana*/ { 0, 9, 4, 2, 1, 0, 0, 0, 0, 0 }, // Banana
- /*Eggman Monitor*/ { 0, 4, 3, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor
- /*Orbinaut*/ { 0, 6, 5, 3, 2, 0, 0, 0, 0, 0 }, // Orbinaut
- /*Jawz*/ { 0, 0, 3, 2, 1, 1, 0, 0, 0, 0 }, // Jawz
- /*Mine*/ { 0, 0, 2, 2, 1, 0, 0, 0, 0, 0 }, // Mine
- /*Ballhog*/ { 0, 0, 0, 2, 1, 0, 0, 0, 0, 0 }, // Ballhog
- /*Self-Propelled Bomb*/ { 0, 0, 1, 2, 3, 4, 2, 2, 0,20 }, // Self-Propelled Bomb
- /*Grow*/ { 0, 0, 0, 0, 0, 1, 3, 5, 3, 0 }, // Grow
- /*Shrink*/ { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 }, // Shrink
- /*Thunder Shield*/ { 0, 1, 2, 0, 0, 0, 0, 0, 0, 0 }, // Thunder Shield
- /*Hyudoro*/ { 0, 0, 0, 0, 1, 2, 1, 0, 0, 0 }, // Hyudoro
- /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring
- /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
- /*Sneaker x3*/ { 0, 0, 0, 0, 3, 7, 9, 2, 0, 0 }, // Sneaker x3
- /*Banana x3*/ { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // Banana x3
- /*Banana x10*/ { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, // Banana x10
- /*Orbinaut x3*/ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // Orbinaut x3
- /*Orbinaut x4*/ { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // Orbinaut x4
- /*Jawz x2*/ { 0, 0, 0, 1, 2, 0, 0, 0, 0, 0 } // Jawz x2
-};
-
-static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][6] =
-{
- //P-Odds 0 1 2 3 4 5
- /*Sneaker*/ { 3, 2, 2, 2, 0, 2 }, // Sneaker
- /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 0 }, // Rocket Sneaker
- /*Invincibility*/ { 0, 1, 2, 3, 4, 2 }, // Invincibility
- /*Banana*/ { 2, 1, 0, 0, 0, 0 }, // Banana
- /*Eggman Monitor*/ { 1, 1, 0, 0, 0, 0 }, // Eggman Monitor
- /*Orbinaut*/ { 6, 2, 1, 0, 0, 0 }, // Orbinaut
- /*Jawz*/ { 3, 3, 3, 2, 0, 2 }, // Jawz
- /*Mine*/ { 2, 3, 3, 1, 0, 2 }, // Mine
- /*Ballhog*/ { 0, 1, 2, 1, 0, 2 }, // Ballhog
- /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb
- /*Grow*/ { 0, 0, 1, 2, 4, 2 }, // Grow
- /*Shrink*/ { 0, 0, 0, 0, 0, 0 }, // Shrink
- /*Thunder Shield*/ { 0, 0, 0, 0, 0, 0 }, // Thunder Shield
- /*Hyudoro*/ { 1, 1, 0, 0, 0, 0 }, // Hyudoro
- /*Pogo Spring*/ { 1, 1, 0, 0, 0, 0 }, // Pogo Spring
- /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
- /*Sneaker x3*/ { 0, 0, 0, 2, 4, 2 }, // Sneaker x3
- /*Banana x3*/ { 1, 2, 1, 0, 0, 0 }, // Banana x3
- /*Banana x10*/ { 0, 0, 1, 1, 0, 2 }, // Banana x10
- /*Orbinaut x3*/ { 0, 1, 2, 1, 0, 0 }, // Orbinaut x3
- /*Orbinaut x4*/ { 0, 0, 1, 3, 4, 2 }, // Orbinaut x4
- /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2
-};
-
-/** \brief Item Roulette for Kart
-
- \param player player
- \param getitem what item we're looking for
-
- \return void
-*/
-static void K_KartGetItemResult(player_t *player, SINT8 getitem)
-{
- switch (getitem)
- {
- // Special roulettes first, then the generic ones are handled by default
- case KRITEM_TRIPLESNEAKER: // Sneaker x3
- player->kartstuff[k_itemtype] = KITEM_SNEAKER;
- player->kartstuff[k_itemamount] = 3;
- break;
- case KRITEM_TRIPLEBANANA: // Banana x3
- player->kartstuff[k_itemtype] = KITEM_BANANA;
- player->kartstuff[k_itemamount] = 3;
- break;
- case KRITEM_TENFOLDBANANA: // Banana x10
- player->kartstuff[k_itemtype] = KITEM_BANANA;
- player->kartstuff[k_itemamount] = 10;
- break;
- case KRITEM_TRIPLEORBINAUT: // Orbinaut x3
- player->kartstuff[k_itemtype] = KITEM_ORBINAUT;
- player->kartstuff[k_itemamount] = 3;
- break;
- case KRITEM_QUADORBINAUT: // Orbinaut x4
- player->kartstuff[k_itemtype] = KITEM_ORBINAUT;
- player->kartstuff[k_itemamount] = 4;
- break;
- case KRITEM_DUALJAWZ: // Jawz x2
- player->kartstuff[k_itemtype] = KITEM_JAWZ;
- player->kartstuff[k_itemamount] = 2;
- break;
- case KITEM_SPB:
- case KITEM_SHRINK: // Indirect items
- indirectitemcooldown = 20*TICRATE;
- /* FALLTHRU */
- default:
- if (getitem <= 0 || getitem >= NUMKARTRESULTS) // Sad (Fallback)
- {
- if (getitem != 0)
- CONS_Printf("ERROR: P_KartGetItemResult - Item roulette gave bad item (%d) :(\n", getitem);
- player->kartstuff[k_itemtype] = KITEM_SAD;
- }
- else
- player->kartstuff[k_itemtype] = getitem;
- player->kartstuff[k_itemamount] = 1;
- break;
- }
-}
-
-/** \brief Item Roulette for Kart
-
- \param player player object passed from P_KartPlayerThink
-
- \return void
-*/
-
-static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed)
-{
- const INT32 distvar = (64*14);
- INT32 newodds;
- INT32 i;
- UINT8 pingame = 0, pexiting = 0, pinvin = 0;
- SINT8 first = -1, second = -1;
- INT32 secondist = 0;
-
- if (G_BattleGametype())
- newodds = K_KartItemOddsBattle[item-1][pos];
- else
- newodds = K_KartItemOddsRace[item-1][pos];
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator)
- continue;
-
- pingame++;
- if (players[i].exiting)
- pexiting++;
- if (players[i].mo)
- {
- if (players[i].kartstuff[k_position] == 1 && first == -1)
- first = i;
- if (players[i].kartstuff[k_position] == 2 && second == -1)
- second = i;
- if (players[i].kartstuff[k_itemtype] == KITEM_INVINCIBILITY
- || players[i].kartstuff[k_itemtype] == KITEM_GROW
- || players[i].kartstuff[k_invincibilitytimer]
- || players[i].kartstuff[k_growshrinktimer] > 0)
- pinvin++;
- }
- }
-
- if (first != -1 && second != -1) // calculate 2nd's distance from 1st, for SPB
- {
- secondist = P_AproxDistance(P_AproxDistance(players[first].mo->x - players[second].mo->x,
- players[first].mo->y - players[second].mo->y),
- players[first].mo->z - players[second].mo->z) / mapobjectscale;
- if (franticitems)
- secondist = (15*secondist/14);
- if (pingame < 8 && !G_BattleGametype())
- secondist = ((28+(8-pingame))*secondist/28);
- }
-
- // POWERITEMODDS handles all of the "frantic item" related functionality, for all of our powerful items.
- // First, it multiplies it by 2 if franticitems is true; easy-peasy.
- // Next, it multiplies it again if it's in SPB mode and 2nd needs to apply pressure to 1st.
- // Then, it multiplies it further if there's less than 8 players in game.
- // This is done to make low player count races more fair & interesting. (1v1s are basically the same as franticitems false in a normal race)
- // Lastly, it *divides* it by your mashed value, which was determined in K_KartItemRoulette, to punish those who are impatient.
- // The last two are very fractional and complicated, very sorry!
-#define POWERITEMODDS(odds) \
- if (franticitems) \
- odds *= 2; \
- if (pingame < 8 && !G_BattleGametype()) \
- odds = FixedMul(odds*FRACUNIT, FRACUNIT+min((8-pingame)*(FRACUNIT/25), FRACUNIT))/FRACUNIT; \
- if (mashed > 0) \
- odds = FixedDiv(odds*FRACUNIT, mashed+FRACUNIT)/FRACUNIT \
-
- switch (item)
- {
- case KITEM_SNEAKER:
- if ((!cv_sneaker.value) && (!modeattacking)) newodds = 0;
- break;
- case KITEM_ROCKETSNEAKER:
- POWERITEMODDS(newodds);
- if (!cv_rocketsneaker.value) newodds = 0;
- break;
- case KITEM_INVINCIBILITY:
- POWERITEMODDS(newodds);
- if ((!cv_invincibility.value) || (pinvin >= 2)) newodds = 0;
- break;
- case KITEM_BANANA:
- if (!cv_banana.value) newodds = 0;
- break;
- case KITEM_EGGMAN:
- if (!cv_eggmanmonitor.value) newodds = 0;
- break;
- case KITEM_ORBINAUT:
- if (!cv_orbinaut.value) newodds = 0;
- break;
- case KITEM_JAWZ:
- POWERITEMODDS(newodds);
- if (!cv_jawz.value) newodds = 0;
- break;
- case KITEM_MINE:
- POWERITEMODDS(newodds);
- if (!cv_mine.value) newodds = 0;
- break;
- case KITEM_BALLHOG:
- POWERITEMODDS(newodds);
- if (!cv_ballhog.value) newodds = 0;
- break;
- case KITEM_SPB:
- //POWERITEMODDS(newodds);
- if (((!cv_selfpropelledbomb.value)
- || (indirectitemcooldown > 0)
- || (pexiting > 0)
- || (secondist/distvar < 3))
- && (pos != 9)) // Force SPB
- newodds = 0;
- newodds *= min((secondist/distvar)-4, 3);
- break;
- case KITEM_GROW:
- POWERITEMODDS(newodds);
- if ((!cv_grow.value) || (pinvin >= 2)) newodds = 0;
- break;
- case KITEM_SHRINK:
- POWERITEMODDS(newodds);
- if ((!cv_shrink.value)
- || (indirectitemcooldown > 0)
- || (pingame-1 <= pexiting)) newodds = 0;
- break;
- case KITEM_THUNDERSHIELD:
- POWERITEMODDS(newodds);
- if (!cv_thundershield.value) newodds = 0;
- break;
- case KITEM_HYUDORO:
- if (!cv_hyudoro.value) newodds = 0;
- break;
- case KITEM_POGOSPRING:
- if (!cv_pogospring.value) newodds = 0;
- break;
- case KITEM_KITCHENSINK:
- newodds = 0; // Not obtained via normal means.
- break;
- case KRITEM_TRIPLESNEAKER:
- POWERITEMODDS(newodds);
- if (!cv_triplesneaker.value) newodds = 0;
- break;
- case KRITEM_TRIPLEBANANA:
- POWERITEMODDS(newodds);
- if (!cv_triplebanana.value) newodds = 0;
- break;
- case KRITEM_TENFOLDBANANA:
- POWERITEMODDS(newodds);
- if (!cv_decabanana.value) newodds = 0;
- break;
- case KRITEM_TRIPLEORBINAUT:
- POWERITEMODDS(newodds);
- if (!cv_tripleorbinaut.value) newodds = 0;
- break;
- case KRITEM_QUADORBINAUT:
- POWERITEMODDS(newodds);
- if (!cv_quadorbinaut.value) newodds = 0;
- break;
- case KRITEM_DUALJAWZ:
- POWERITEMODDS(newodds);
- if (!cv_dualjawz.value) newodds = 0;
- break;
- default:
- break;
- }
-#undef POWERITEMODDS
-
- return newodds;
-}
-
-//{ SRB2kart Roulette Code - Distance Based, no waypoints
-
-static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT32 bestbumper, boolean spbrush, boolean dontforcespb)
-{
- const INT32 distvar = (64*14);
- INT32 i;
- INT32 pdis = 0, useodds = 0;
- UINT8 disttable[14];
- UINT8 distlen = 0;
- boolean oddsvalid[10];
-
- for (i = 0; i < 10; i++)
- {
- INT32 j;
- boolean available = false;
-
- if (G_BattleGametype() && i > 5)
- {
- oddsvalid[i] = false;
- break;
- }
-
- for (j = 0; j < NUMKARTRESULTS; j++)
- {
- if (K_KartGetItemOdds(i, j, mashed) > 0)
- {
- available = true;
- break;
- }
- }
-
- oddsvalid[i] = available;
- }
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (playeringame[i] && !players[i].spectator && players[i].mo
- && players[i].kartstuff[k_position] < player->kartstuff[k_position])
- pdis += P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x,
- players[i].mo->y - player->mo->y),
- players[i].mo->z - player->mo->z) / mapobjectscale
- * (pingame - players[i].kartstuff[k_position])
- / max(1, ((pingame - 1) * (pingame + 1) / 3));
- }
-
-#define SETUPDISTTABLE(odds, num) \
- for (i = num; i; --i) disttable[distlen++] = odds
-
- if (G_BattleGametype()) // Battle Mode
- {
- if (oddsvalid[0]) SETUPDISTTABLE(0,1);
- if (oddsvalid[1]) SETUPDISTTABLE(1,1);
- if (oddsvalid[2]) SETUPDISTTABLE(2,1);
- if (oddsvalid[3]) SETUPDISTTABLE(3,1);
- if (oddsvalid[4]) SETUPDISTTABLE(4,1);
-
- if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[5]) // 5 is the extreme odds of player-controlled "Karma" items
- useodds = 5;
- else
- {
- SINT8 wantedpos = (bestbumper-player->kartstuff[k_bumper]); // 0 is the best player's bumper count, 1 is a bumper below best, 2 is two bumpers below, etc
- if (K_IsPlayerWanted(player))
- wantedpos++;
- if (wantedpos > 4) // Don't run off into karma items
- wantedpos = 4;
- if (wantedpos < 0) // Don't go below somehow
- wantedpos = 0;
- useodds = disttable[(wantedpos * distlen) / 5];
- }
- }
- else
- {
- if (oddsvalid[1]) SETUPDISTTABLE(1,1);
- if (oddsvalid[2]) SETUPDISTTABLE(2,1);
- if (oddsvalid[3]) SETUPDISTTABLE(3,1);
- if (oddsvalid[4]) SETUPDISTTABLE(4,2);
- if (oddsvalid[5]) SETUPDISTTABLE(5,2);
- if (oddsvalid[6]) SETUPDISTTABLE(6,3);
- if (oddsvalid[7]) SETUPDISTTABLE(7,3);
- if (oddsvalid[8]) SETUPDISTTABLE(8,1);
-
- if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items
- pdis = (15*pdis)/14;
- if (spbrush) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell
- pdis = (3*pdis)/2;
- if (pingame < 8)
- pdis = ((28+(8-pingame))*pdis)/28;
-
- if (pingame == 1 && oddsvalid[0]) // Record Attack, or just alone
- useodds = 0;
- else if (pdis <= 0) // (64*14) * 0 = 0
- useodds = disttable[0];
- else if (player->kartstuff[k_position] == 2 && pdis > (distvar*6)
- && spbplace == -1 && !indirectitemcooldown && !dontforcespb
- && oddsvalid[9]) // Force SPB in 2nd
- useodds = 9;
- else if (pdis > distvar * ((12 * distlen) / 14)) // (64*14) * 12 = 10752
- useodds = disttable[distlen-1];
- else
- {
- for (i = 1; i < 13; i++)
- {
- if (pdis <= distvar * ((i * distlen) / 14))
- {
- useodds = disttable[((i * distlen) / 14)];
- break;
- }
- }
- }
- }
-
-#undef SETUPDISTTABLE
-
- //CONS_Printf("Got useodds %d. (position: %d, distance: %d)\n", useodds, player->kartstuff[k_position], pdis);
-
- return useodds;
-}
-
-static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
-{
- INT32 i;
- UINT8 pingame = 0;
- UINT8 roulettestop;
- INT32 useodds = 0;
- INT32 spawnchance[NUMKARTRESULTS * NUMKARTODDS];
- INT32 chance = 0, numchoices = 0;
- INT32 bestbumper = 0;
- fixed_t mashed = 0;
- boolean dontforcespb = false;
-
- // This makes the roulette cycle through items - if this is 0, you shouldn't be here.
- if (player->kartstuff[k_itemroulette])
- player->kartstuff[k_itemroulette]++;
- else
- return;
-
- // Gotta check how many players are active at this moment.
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator)
- continue;
- pingame++;
- if (players[i].exiting)
- dontforcespb = true;
- if (players[i].kartstuff[k_bumper] > bestbumper)
- bestbumper = players[i].kartstuff[k_bumper];
- }
-
- // This makes the roulette produce the random noises.
- if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsLocalPlayer(player))
- {
-#define PLAYROULETTESND S_StartSound(NULL, sfx_itrol1 + ((player->kartstuff[k_itemroulette] / 3) % 8));
- if (splitscreen)
- {
- if (players[displayplayer].kartstuff[k_itemroulette])
- {
- if (player == &players[displayplayer])
- PLAYROULETTESND;
- }
- else if (players[secondarydisplayplayer].kartstuff[k_itemroulette])
- {
- if (player == &players[secondarydisplayplayer])
- PLAYROULETTESND;
- }
- else if (players[thirddisplayplayer].kartstuff[k_itemroulette] && splitscreen > 1)
- {
- if (player == &players[thirddisplayplayer])
- PLAYROULETTESND;
- }
- else if (players[fourthdisplayplayer].kartstuff[k_itemroulette] && splitscreen > 2)
- {
- if (player == &players[fourthdisplayplayer])
- PLAYROULETTESND;
- }
- }
- else
- PLAYROULETTESND;
-#undef PLAYROULETTESND
- }
-
- roulettestop = TICRATE + (3*(pingame - player->kartstuff[k_position]));
-
- // If the roulette finishes or the player presses BT_ATTACK, stop the roulette and calculate the item.
- // I'm returning via the exact opposite, however, to forgo having another bracket embed. Same result either way, I think.
- // Finally, if you get past this check, now you can actually start calculating what item you get.
- if ((cmd->buttons & BT_ATTACK) && !(player->kartstuff[k_eggmanheld] || player->kartstuff[k_itemheld]) && player->kartstuff[k_itemroulette] >= roulettestop && !modeattacking)
- {
- // Mashing reduces your chances for the good items
- mashed = FixedDiv((player->kartstuff[k_itemroulette])*FRACUNIT, ((TICRATE*3)+roulettestop)*FRACUNIT) - FRACUNIT;
- }
- else if (!(player->kartstuff[k_itemroulette] >= (TICRATE*3)))
- return;
-
- if (cmd->buttons & BT_ATTACK)
- player->pflags |= PF_ATTACKDOWN;
-
- if (player->kartstuff[k_roulettetype] == 2) // Fake items
- {
- player->kartstuff[k_eggmanexplode] = 4*TICRATE;
- //player->kartstuff[k_itemblink] = TICRATE;
- //player->kartstuff[k_itemblinkmode] = 1;
- player->kartstuff[k_itemroulette] = 0;
- player->kartstuff[k_roulettetype] = 0;
- if (P_IsLocalPlayer(player))
- S_StartSound(NULL, sfx_itrole);
- return;
- }
-
- if (cv_kartdebugitem.value != 0 && !modeattacking)
- {
- K_KartGetItemResult(player, cv_kartdebugitem.value);
- player->kartstuff[k_itemamount] = cv_kartdebugamount.value;
- player->kartstuff[k_itemblink] = TICRATE;
- player->kartstuff[k_itemblinkmode] = 2;
- player->kartstuff[k_itemroulette] = 0;
- player->kartstuff[k_roulettetype] = 0;
- if (P_IsLocalPlayer(player))
- S_StartSound(NULL, sfx_dbgsal);
- return;
- }
-
- // Initializes existing spawnchance values
- for (i = 0; i < (NUMKARTRESULTS * NUMKARTODDS); i++)
- spawnchance[i] = 0;
-
- // Split into another function for a debug function below
- useodds = K_FindUseodds(player, mashed, pingame, bestbumper, (spbplace != -1 && player->kartstuff[k_position] == spbplace+1), dontforcespb);
-
-#define SETITEMRESULT(itemnum) \
- for (chance = 0; chance < K_KartGetItemOdds(useodds, itemnum, mashed); chance++) \
- spawnchance[numchoices++] = itemnum
-
- for (i = 1; i < NUMKARTRESULTS; i++)
- SETITEMRESULT(i);
-
-#undef SETITEMRESULT
-
- // Award the player whatever power is rolled
- if (numchoices > 0)
- K_KartGetItemResult(player, spawnchance[P_RandomKey(numchoices)]);
- else
- {
- player->kartstuff[k_itemtype] = KITEM_SAD;
- player->kartstuff[k_itemamount] = 1;
- }
-
- if (P_IsLocalPlayer(player))
- S_StartSound(NULL, ((player->kartstuff[k_roulettetype] == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf)));
-
- player->kartstuff[k_itemblink] = TICRATE;
- player->kartstuff[k_itemblinkmode] = ((player->kartstuff[k_roulettetype] == 1) ? 2 : (mashed ? 1 : 0));
-
- player->kartstuff[k_itemroulette] = 0; // Since we're done, clear the roulette number
- player->kartstuff[k_roulettetype] = 0; // This too
-}
-
-//}
-
-//{ SRB2kart p_user.c Stuff
-
-static fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against)
-{
- fixed_t weight = 5<type)
- {
- case MT_PLAYER:
- if (!mobj->player)
- break;
- if (against->player && !against->player->kartstuff[k_spinouttimer] && mobj->player->kartstuff[k_spinouttimer])
- weight = 0; // Do not bump
- else
- {
- weight = (mobj->player->kartweight)<player->speed > K_GetKartSpeed(mobj->player, false))
- weight += (mobj->player->speed - K_GetKartSpeed(mobj->player, false))/8;
- }
- break;
- case MT_FALLINGROCK:
- if (against->player)
- {
- if (against->player->kartstuff[k_invincibilitytimer]
- || against->player->kartstuff[k_growshrinktimer] > 0)
- weight = 0;
- else
- weight = (against->player->kartweight)<player)
- weight = (against->player->kartweight)<player)
- weight = (against->player->kartweight+3)<player && mobj1->player->playerstate != PST_LIVE)
- || (mobj2->player && mobj2->player->playerstate != PST_LIVE))
- return;
-
- if ((mobj1->player && mobj1->player->kartstuff[k_respawn])
- || (mobj2->player && mobj2->player->kartstuff[k_respawn]))
- return;
-
- // Don't bump if you've recently bumped
- if (mobj1->player && mobj1->player->kartstuff[k_justbumped])
- {
- mobj1->player->kartstuff[k_justbumped] = bumptime;
- return;
- }
-
- if (mobj2->player && mobj2->player->kartstuff[k_justbumped])
- {
- mobj2->player->kartstuff[k_justbumped] = bumptime;
- return;
- }
-
- mass1 = K_GetMobjWeight(mobj1, mobj2);
-
- if (solid == true && mass1 > 0)
- mass2 = mass1;
- else
- mass2 = K_GetMobjWeight(mobj2, mobj1);
-
- momdifx = mobj1->momx - mobj2->momx;
- momdify = mobj1->momy - mobj2->momy;
-
- // if the speed difference is less than this let's assume they're going proportionately faster from each other
- if (P_AproxDistance(momdifx, momdify) < (25*mapobjectscale))
- {
- fixed_t momdiflength = P_AproxDistance(momdifx, momdify);
- fixed_t normalisedx = FixedDiv(momdifx, momdiflength);
- fixed_t normalisedy = FixedDiv(momdify, momdiflength);
- momdifx = FixedMul((25*mapobjectscale), normalisedx);
- momdify = FixedMul((25*mapobjectscale), normalisedy);
- }
-
- // Adds the OTHER player's momentum, so that it reduces the chance of you being "inside" the other object
- distx = (mobj1->x + mobj2->momx) - (mobj2->x + mobj1->momx);
- disty = (mobj1->y + mobj2->momy) - (mobj2->y + mobj1->momy);
-
- { // Don't allow dist to get WAY too low, that it pushes you stupidly huge amounts, or backwards...
- fixed_t dist = P_AproxDistance(distx, disty);
- fixed_t nx = FixedDiv(distx, dist);
- fixed_t ny = FixedDiv(disty, dist);
-
- if (P_AproxDistance(distx, disty) < (3*mobj1->radius)/4)
- {
- distx = FixedMul((3*mobj1->radius)/4, nx);
- disty = FixedMul((3*mobj1->radius)/4, ny);
- }
-
- if (P_AproxDistance(distx, disty) < (3*mobj2->radius)/4)
- {
- distx = FixedMul((3*mobj2->radius)/4, nx);
- disty = FixedMul((3*mobj2->radius)/4, ny);
- }
- }
-
- if (distx == 0 && disty == 0)
- {
- // if there's no distance between the 2, they're directly on top of each other, don't run this
- return;
- }
-
- dot = FixedMul(momdifx, distx) + FixedMul(momdify, disty);
-
- if (dot >= 0)
- {
- // They're moving away from each other
- return;
- }
-
- p = FixedDiv(dot, FixedMul(distx, distx)+FixedMul(disty, disty));
-
- if (bounce == true && mass2 > 0) // Perform a Goomba Bounce.
- mobj1->momz = -mobj1->momz;
- else
- {
- fixed_t newz = mobj1->momz;
- if (mass2 > 0)
- mobj1->momz = mobj2->momz;
- if (mass1 > 0 && solid == false)
- mobj2->momz = newz;
- }
-
- if (mass2 > 0)
- {
- mobj1->momx = mobj1->momx - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), p), distx);
- mobj1->momy = mobj1->momy - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), p), disty);
- }
-
- if (mass1 > 0 && solid == false)
- {
- mobj2->momx = mobj2->momx - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), p), -distx);
- mobj2->momy = mobj2->momy - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), p), -disty);
- }
-
- // Do the bump fx when we've CONFIRMED we can bump.
- S_StartSound(mobj1, sfx_s3k49);
-
- fx = P_SpawnMobj(mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, mobj1->z/2 + mobj2->z/2, MT_BUMP);
- if (mobj1->eflags & MFE_VERTICALFLIP)
- fx->eflags |= MFE_VERTICALFLIP;
- else
- fx->eflags &= ~MFE_VERTICALFLIP;
- P_SetScale(fx, mobj1->scale);
-
- // Because this is done during collision now, rmomx and rmomy need to be recalculated
- // so that friction doesn't immediately decide to stop the player if they're at a standstill
- // Also set justbumped here
- if (mobj1->player)
- {
- mobj1->player->rmomx = mobj1->momx - mobj1->player->cmomx;
- mobj1->player->rmomy = mobj1->momy - mobj1->player->cmomy;
- mobj1->player->kartstuff[k_justbumped] = bumptime;
- if (mobj1->player->kartstuff[k_spinouttimer])
- {
- mobj1->player->kartstuff[k_wipeoutslow] += wipeoutslowtime+1;
- mobj1->player->kartstuff[k_spinouttimer] += wipeoutslowtime+1;
- }
- }
-
- if (mobj2->player)
- {
- mobj2->player->rmomx = mobj2->momx - mobj2->player->cmomx;
- mobj2->player->rmomy = mobj2->momy - mobj2->player->cmomy;
- mobj2->player->kartstuff[k_justbumped] = bumptime;
- if (mobj2->player->kartstuff[k_spinouttimer])
- {
- mobj2->player->kartstuff[k_wipeoutslow] += wipeoutslowtime+1;
- mobj2->player->kartstuff[k_spinouttimer] += wipeoutslowtime+1;
- }
- }
-}
-
-/** \brief Checks that the player is on an offroad subsector for realsies
-
- \param mo player mobj object
-
- \return boolean
-*/
-static UINT8 K_CheckOffroadCollide(mobj_t *mo, sector_t *sec)
-{
- UINT8 i;
- sector_t *sec2;
-
- I_Assert(mo != NULL);
- I_Assert(!P_MobjWasRemoved(mo));
-
- sec2 = P_ThingOnSpecial3DFloor(mo);
-
- for (i = 2; i < 5; i++)
- {
- if ((sec2 && GETSECSPECIAL(sec2->special, 1) == i)
- || (P_IsObjectOnRealGround(mo, sec)
- && GETSECSPECIAL(sec->special, 1) == i))
- return i;
- }
-
- return 0;
-}
-
-/** \brief Updates the Player's offroad value once per frame
-
- \param player player object passed from K_KartPlayerThink
-
- \return void
-*/
-static void K_UpdateOffroad(player_t *player)
-{
- fixed_t kartweight = player->kartweight;
- fixed_t offroad;
- sector_t *nextsector = R_PointInSubsector(
- player->mo->x + player->mo->momx*2, player->mo->y + player->mo->momy*2)->sector;
-
- fixed_t offroadstrength = 0;
-
- if (K_CheckOffroadCollide(player->mo, nextsector) == 2) // Weak Offroad
- offroadstrength = 1;
- else if (K_CheckOffroadCollide(player->mo, nextsector) == 3) // Mid Offroad
- offroadstrength = 2;
- else if (K_CheckOffroadCollide(player->mo, nextsector) == 4) // Strong Offroad
- offroadstrength = 3;
-
- // If you are offroad, a timer starts. Depending on your weight value, the timer increments differently.
- //if ((nextsector->special & 256) && nextsector->special != 768
- // && nextsector->special != 1024 && nextsector->special != 4864)
- if (offroadstrength)
- {
- if (K_CheckOffroadCollide(player->mo, player->mo->subsector->sector) && player->kartstuff[k_offroad] == 0)
- player->kartstuff[k_offroad] = 16;
-
- if (player->kartstuff[k_offroad] > 0)
- {
- // 1872 is the magic number - 35 frames adds up to approximately 65536. 1872/4 = 468/3 = 156
- // A higher kart weight means you can stay offroad for longer without losing speed
- offroad = (1872 + 5*156 - kartweight*156)*offroadstrength;
-
- //if (player->kartstuff[k_growshrinktimer] > 1) // grow slows down half as fast
- // offroad /= 2;
-
- player->kartstuff[k_offroad] += offroad;
- }
-
- if (player->kartstuff[k_offroad] > FRACUNIT*offroadstrength)
- player->kartstuff[k_offroad] = FRACUNIT*offroadstrength;
- }
- else
- player->kartstuff[k_offroad] = 0;
-}
-
-// These have to go earlier than its sisters because of K_RespawnChecker...
-void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master)
-{
- // flipping
- mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP);
- // visibility (usually for hyudoro)
- mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW);
- mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1);
- mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2);
- mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3);
- mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4);
-}
-
-static void K_SpawnDashDustRelease(player_t *player)
-{
- fixed_t newx;
- fixed_t newy;
- mobj_t *dust;
- angle_t travelangle;
- INT32 i;
-
- I_Assert(player != NULL);
- I_Assert(player->mo != NULL);
- I_Assert(!P_MobjWasRemoved(player->mo));
-
- if (!P_IsObjectOnGround(player->mo))
- return;
-
- if (!player->speed && !player->kartstuff[k_startboost])
- return;
-
- travelangle = player->mo->angle;
-
- if (player->kartstuff[k_drift] || player->kartstuff[k_driftend])
- travelangle -= (ANGLE_45/5)*player->kartstuff[k_drift];
-
- for (i = 0; i < 2; i++)
- {
- newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale));
- newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale));
- dust = P_SpawnMobj(newx, newy, player->mo->z, MT_FASTDUST);
-
- P_SetTarget(&dust->target, player->mo);
- dust->angle = travelangle - ((i&1) ? -1 : 1)*ANGLE_45;
- dust->destscale = player->mo->scale;
- P_SetScale(dust, player->mo->scale);
-
- dust->momx = 3*player->mo->momx/5;
- dust->momy = 3*player->mo->momy/5;
- //dust->momz = 3*player->mo->momz/5;
-
- K_MatchGenericExtraFlags(dust, player->mo);
- }
-}
-
-static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the mobj thinker case too!
-{
- mobj_t *sparks;
-
- I_Assert(player != NULL);
- I_Assert(player->mo != NULL);
- I_Assert(!P_MobjWasRemoved(player->mo));
-
- // Position & etc are handled in its thinker, and its spawned invisible.
- // This avoids needing to dupe code if we don't need it.
- sparks = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BRAKEDRIFT);
- P_SetTarget(&sparks->target, player->mo);
- P_SetScale(sparks, (sparks->destscale = player->mo->scale));
- K_MatchGenericExtraFlags(sparks, player->mo);
- sparks->flags2 |= MF2_DONTDRAW;
-}
-
-/** \brief Calculates the respawn timer and drop-boosting
-
- \param player player object passed from K_KartPlayerThink
-
- \return void
-*/
-void K_RespawnChecker(player_t *player)
-{
- ticcmd_t *cmd = &player->cmd;
-
- if (player->spectator)
- return;
-
- if (player->kartstuff[k_respawn] > 1)
- {
- player->kartstuff[k_respawn]--;
- player->mo->momz = 0;
- player->powers[pw_flashing] = 2;
- player->powers[pw_nocontrol] = 2;
- if (leveltime % 8 == 0)
- {
- INT32 i;
- if (!mapreset)
- S_StartSound(player->mo, sfx_s3kcas);
-
- for (i = 0; i < 8; i++)
- {
- mobj_t *mo;
- angle_t newangle;
- fixed_t newx, newy, newz;
-
- newangle = FixedAngle(((360/8)*i)*FRACUNIT);
- newx = player->mo->x + P_ReturnThrustX(player->mo, newangle, 31<mo->y + P_ReturnThrustY(player->mo, newangle, 31<mo->eflags & MFE_VERTICALFLIP)
- newz = player->mo->z + player->mo->height;
- else
- newz = player->mo->z;
-
- mo = P_SpawnMobj(newx, newy, newz, MT_DEZLASER);
- if (mo)
- {
- if (player->mo->eflags & MFE_VERTICALFLIP)
- mo->eflags |= MFE_VERTICALFLIP;
- P_SetTarget(&mo->target, player->mo);
- mo->angle = newangle+ANGLE_90;
- mo->momz = (8*FRACUNIT)*P_MobjFlip(player->mo);
- P_SetScale(mo, (mo->destscale = FRACUNIT));
- }
- }
- }
- }
- else if (player->kartstuff[k_respawn] == 1)
- {
- if (player->kartstuff[k_growshrinktimer] < 0)
- {
- player->mo->scalespeed = mapobjectscale/TICRATE;
- player->mo->destscale = (6*mapobjectscale)/8;
- if (cv_kartdebugshrink.value && !modeattacking && !player->bot)
- player->mo->destscale = (6*player->mo->destscale)/8;
- }
-
- if (!P_IsObjectOnGround(player->mo) && !mapreset)
- {
- player->powers[pw_flashing] = 2;
-
- // Sal: The old behavior was stupid and prone to accidental usage.
- // Let's rip off Mania instead, and turn this into a Drop Dash!
-
- if (cmd->buttons & BT_ACCELERATE)
- player->kartstuff[k_dropdash]++;
- else
- player->kartstuff[k_dropdash] = 0;
-
- if (player->kartstuff[k_dropdash] == TICRATE/4)
- S_StartSound(player->mo, sfx_ddash);
-
- if ((player->kartstuff[k_dropdash] >= TICRATE/4)
- && (player->kartstuff[k_dropdash] & 1))
- player->mo->colorized = true;
- else
- player->mo->colorized = false;
- }
- else
- {
- if ((cmd->buttons & BT_ACCELERATE) && (player->kartstuff[k_dropdash] >= TICRATE/4))
- {
- S_StartSound(player->mo, sfx_s23c);
- player->kartstuff[k_startboost] = 50;
- K_SpawnDashDustRelease(player);
- }
- player->mo->colorized = false;
- player->kartstuff[k_dropdash] = 0;
- player->kartstuff[k_respawn] = 0;
- }
- }
-}
-
-/** \brief Handles the state changing for moving players, moved here to eliminate duplicate code
-
- \param player player data
-
- \return void
-*/
-void K_KartMoveAnimation(player_t *player)
-{
- ticcmd_t *cmd = &player->cmd;
- // Standing frames - S_KART_STND1 S_KART_STND1_L S_KART_STND1_R
- if (player->speed == 0)
- {
- if (cmd->driftturn < 0 && !(player->mo->state >= &states[S_KART_STND1_R] && player->mo->state <= &states[S_KART_STND2_R]))
- P_SetPlayerMobjState(player->mo, S_KART_STND1_R);
- else if (cmd->driftturn > 0 && !(player->mo->state >= &states[S_KART_STND1_L] && player->mo->state <= &states[S_KART_STND2_L]))
- P_SetPlayerMobjState(player->mo, S_KART_STND1_L);
- else if (cmd->driftturn == 0 && !(player->mo->state >= &states[S_KART_STND1] && player->mo->state <= &states[S_KART_STND2]))
- P_SetPlayerMobjState(player->mo, S_KART_STND1);
- }
- // Drifting Left - S_KART_DRIFT1_L
- else if (player->kartstuff[k_drift] > 0 && P_IsObjectOnGround(player->mo))
- {
- if (!(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L]))
- P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L);
- }
- // Drifting Right - S_KART_DRIFT1_R
- else if (player->kartstuff[k_drift] < 0 && P_IsObjectOnGround(player->mo))
- {
- if (!(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R]))
- P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R);
- }
- // Run frames - S_KART_RUN1 S_KART_RUN1_L S_KART_RUN1_R
- else if (player->speed > FixedMul(player->runspeed, player->mo->scale))
- {
- if (cmd->driftturn < 0 && !(player->mo->state >= &states[S_KART_RUN1_R] && player->mo->state <= &states[S_KART_RUN2_R]))
- P_SetPlayerMobjState(player->mo, S_KART_RUN1_R);
- else if (cmd->driftturn > 0 && !(player->mo->state >= &states[S_KART_RUN1_L] && player->mo->state <= &states[S_KART_RUN2_L]))
- P_SetPlayerMobjState(player->mo, S_KART_RUN1_L);
- else if (cmd->driftturn == 0 && !(player->mo->state >= &states[S_KART_RUN1] && player->mo->state <= &states[S_KART_RUN2]))
- P_SetPlayerMobjState(player->mo, S_KART_RUN1);
- }
- // Walk frames - S_KART_WALK1 S_KART_WALK1_L S_KART_WALK1_R
- else if (player->speed <= FixedMul(player->runspeed, player->mo->scale))
- {
- if (cmd->driftturn < 0 && !(player->mo->state >= &states[S_KART_WALK1_R] && player->mo->state <= &states[S_KART_WALK2_R]))
- P_SetPlayerMobjState(player->mo, S_KART_WALK1_R);
- else if (cmd->driftturn > 0 && !(player->mo->state >= &states[S_KART_WALK1_L] && player->mo->state <= &states[S_KART_WALK2_L]))
- P_SetPlayerMobjState(player->mo, S_KART_WALK1_L);
- else if (cmd->driftturn == 0 && !(player->mo->state >= &states[S_KART_WALK1] && player->mo->state <= &states[S_KART_WALK2]))
- P_SetPlayerMobjState(player->mo, S_KART_WALK1);
- }
-}
-
-static void K_TauntVoiceTimers(player_t *player)
-{
- if (!player)
- return;
-
- player->kartstuff[k_tauntvoices] = 6*TICRATE;
- player->kartstuff[k_voices] = 4*TICRATE;
-}
-
-static void K_RegularVoiceTimers(player_t *player)
-{
- if (!player)
- return;
-
- player->kartstuff[k_voices] = 4*TICRATE;
-
- if (player->kartstuff[k_tauntvoices] < 4*TICRATE)
- player->kartstuff[k_tauntvoices] = 4*TICRATE;
-}
-
-static void K_PlayAttackTaunt(mobj_t *source)
-{
- sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons
- boolean tasteful = (!source->player || !source->player->kartstuff[k_tauntvoices]);
-
- if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2))
- S_StartSound(source, sfx_kattk1+pick);
-
- if (!tasteful)
- return;
-
- K_TauntVoiceTimers(source->player);
-}
-
-static void K_PlayBoostTaunt(mobj_t *source)
-{
- sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons
- boolean tasteful = (!source->player || !source->player->kartstuff[k_tauntvoices]);
-
- if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2))
- S_StartSound(source, sfx_kbost1+pick);
-
- if (!tasteful)
- return;
-
- K_TauntVoiceTimers(source->player);
-}
-
-static void K_PlayOvertakeSound(mobj_t *source)
-{
- boolean tasteful = (!source->player || !source->player->kartstuff[k_voices]);
-
- if (!G_RaceGametype()) // Only in race
- return;
-
- // 4 seconds from before race begins, 10 seconds afterwards
- if (leveltime < starttime+(10*TICRATE))
- return;
-
- if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2))
- S_StartSound(source, sfx_kslow);
-
- if (!tasteful)
- return;
-
- K_RegularVoiceTimers(source->player);
-}
-
-static void K_PlayHitEmSound(mobj_t *source)
-{
- if (cv_kartvoices.value)
- S_StartSound(source, sfx_khitem);
- else
- S_StartSound(source, sfx_s1c9); // The only lost gameplay functionality with voices disabled
-
- K_RegularVoiceTimers(source->player);
-}
-
-static void K_PlayPowerGloatSound(mobj_t *source)
-{
- if (cv_kartvoices.value)
- S_StartSound(source, sfx_kgloat);
-
- K_RegularVoiceTimers(source->player);
-}
-
-void K_MomentumToFacing(player_t *player)
-{
- angle_t dangle = player->mo->angle - R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
-
- if (dangle > ANGLE_180)
- dangle = InvAngle(dangle);
-
- // If you aren't on the ground or are moving in too different of a direction don't do this
- if (player->mo->eflags & MFE_JUSTHITFLOOR)
- ; // Just hit floor ALWAYS redirects
- else if (!P_IsObjectOnGround(player->mo) || dangle > ANGLE_90)
- return;
-
- P_Thrust(player->mo, player->mo->angle, player->speed - FixedMul(player->speed, player->mo->friction));
- player->mo->momx = FixedMul(player->mo->momx - player->cmomx, player->mo->friction) + player->cmomx;
- player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy;
-}
-
-// sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be
-static void K_GetKartBoostPower(player_t *player)
-{
- fixed_t boostpower = FRACUNIT;
- fixed_t speedboost = 0, accelboost = 0;
-
- if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow] == 1) // Slow down after you've been bumped
- {
- player->kartstuff[k_boostpower] = player->kartstuff[k_speedboost] = player->kartstuff[k_accelboost] = 0;
- return;
- }
-
- // Offroad is separate, it's difficult to factor it in with a variable value anyway.
- if (!(player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || player->kartstuff[k_sneakertimer])
- && player->kartstuff[k_offroad] >= 0)
- boostpower = FixedDiv(boostpower, player->kartstuff[k_offroad] + FRACUNIT);
-
- if (player->kartstuff[k_itemtype] == KITEM_KITCHENSINK)
- boostpower = max((TICRATE/2), (5*TICRATE)-(player->kartstuff[k_bananadrag]/2))*boostpower/(5*TICRATE);
- else if (player->kartstuff[k_bananadrag] > TICRATE)
- boostpower = 4*boostpower/5;
-
- // Banana drag/offroad dust
- if (boostpower < FRACUNIT
- && player->mo && P_IsObjectOnGround(player->mo)
- && player->speed > 0
- && !player->spectator)
- {
- K_SpawnWipeoutTrail(player->mo, true);
- if (leveltime % 6 == 0)
- S_StartSound(player->mo, sfx_cdfm70);
- }
-
- if (player->kartstuff[k_sneakertimer]) // Sneaker
- {
- switch (gamespeed)
- {
- case 0:
- speedboost = max(speedboost, 53740+768);
- break;
- case 2:
- speedboost = max(speedboost, 17294+768);
- break;
- default:
- speedboost = max(speedboost, 32768);
- break;
- }
- accelboost = max(accelboost, 8*FRACUNIT); // + 800%
- }
-
- if (player->kartstuff[k_invincibilitytimer]) // Invincibility
- {
- speedboost = max(speedboost, 3*FRACUNIT/8); // + 37.5%
- accelboost = max(accelboost, 3*FRACUNIT); // + 300%
- }
-
- if (player->kartstuff[k_growshrinktimer] > 0) // Grow
- {
- speedboost = max(speedboost, FRACUNIT/5); // + 20%
- }
-
- if (player->kartstuff[k_driftboost]) // Drift Boost
- {
- speedboost = max(speedboost, FRACUNIT/4); // + 25%
- accelboost = max(accelboost, 4*FRACUNIT); // + 400%
- }
-
- if (player->kartstuff[k_startboost]) // Startup Boost
- {
- speedboost = max(speedboost, FRACUNIT/4); // + 25%
- accelboost = max(accelboost, 6*FRACUNIT); // + 300%
- }
-
- // don't average them anymore, this would make a small boost and a high boost less useful
- // just take the highest we want instead
-
- player->kartstuff[k_boostpower] = boostpower;
-
- // value smoothing
- if (speedboost > player->kartstuff[k_speedboost])
- player->kartstuff[k_speedboost] = speedboost;
- else
- player->kartstuff[k_speedboost] += (speedboost - player->kartstuff[k_speedboost])/(TICRATE/2);
-
- player->kartstuff[k_accelboost] = accelboost;
-}
-
-fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower)
-{
- fixed_t k_speed = 150;
- fixed_t g_cc = FRACUNIT;
- fixed_t xspd = 3072; // 4.6875 aka 3/64
- UINT8 kartspeed = player->kartspeed;
- fixed_t finalspeed;
-
- if (doboostpower && !player->kartstuff[k_pogospring] && !P_IsObjectOnGround(player->mo))
- return (75*mapobjectscale); // air speed cap
-
- switch (gamespeed)
- {
- case 0:
- g_cc = 53248 + xspd; // 50cc = 81.25 + 4.69 = 85.94%
- break;
- case 2:
- g_cc = 77824 + xspd; // 150cc = 118.75 + 4.69 = 123.44%
- break;
- default:
- g_cc = 65536 + xspd; // 100cc = 100.00 + 4.69 = 104.69%
- break;
- }
-
- if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0)
- kartspeed = 1;
-
- k_speed += kartspeed*3; // 153 - 177
-
- finalspeed = FixedMul(FixedMul(k_speed<<14, g_cc), player->mo->scale);
-
- if (doboostpower)
- return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]);
- return finalspeed;
-}
-
-fixed_t K_GetKartAccel(player_t *player)
-{
- fixed_t k_accel = 32; // 36;
- UINT8 kartspeed = player->kartspeed;
-
- if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0)
- kartspeed = 1;
-
- //k_accel += 3 * (9 - kartspeed); // 36 - 60
- k_accel += 4 * (9 - kartspeed); // 32 - 64
-
- return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]);
-}
-
-UINT16 K_GetKartFlashing(player_t *player)
-{
- UINT16 tics = flashingtics;
- if (G_BattleGametype())
- tics *= 2;
- tics += (flashingtics/8) * (player->kartspeed);
- return tics;
-}
-
-fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove)
-{
- fixed_t accelmax = 4000;
- fixed_t newspeed, oldspeed, finalspeed;
- fixed_t p_speed = K_GetKartSpeed(player, true);
- fixed_t p_accel = K_GetKartAccel(player);
-
- if (!onground) return 0; // If the player isn't on the ground, there is no change in speed
-
- // ACCELCODE!!!1!11!
- oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // FixedMul(P_AproxDistance(player->rmomx, player->rmomy), player->mo->scale);
- newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), ORIG_FRICTION);
-
- if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust
- {
- const fixed_t hscale = mapobjectscale /*+ (mapobjectscale - player->mo->scale)*/;
- const fixed_t minspeed = 24*hscale;
- const fixed_t maxspeed = 28*hscale;
-
- if (newspeed > maxspeed && player->kartstuff[k_pogospring] == 2)
- newspeed = maxspeed;
- if (newspeed < minspeed)
- newspeed = minspeed;
- }
-
- finalspeed = newspeed - oldspeed;
-
- // forwardmove is:
- // 50 while accelerating,
- // 25 while clutching,
- // 0 with no gas, and
- // -25 when only braking.
-
- finalspeed *= forwardmove/25;
- finalspeed /= 2;
-
- if (forwardmove < 0 && finalspeed > FRACUNIT*2)
- return finalspeed/2;
- else if (forwardmove < 0)
- return -FRACUNIT/2;
-
- if (finalspeed < 0)
- finalspeed = 0;
-
- return finalspeed;
-}
-
-void K_DoInstashield(player_t *player)
-{
- mobj_t *layera;
- mobj_t *layerb;
-
- if (player->kartstuff[k_instashield] > 0)
- return;
-
- player->kartstuff[k_instashield] = 15; // length of instashield animation
- S_StartSound(player->mo, sfx_cdpcm9);
-
- layera = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDA);
- P_SetTarget(&layera->target, player->mo);
-
- layerb = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDB);
- P_SetTarget(&layerb->target, player->mo);
-}
-
-void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount)
-{
- statenum_t st;
- mobj_t *pt;
-
- if (!source || !source->mo)
- return;
-
- if (amount == 1)
- st = S_BATTLEPOINT1A;
- else if (amount == 2)
- st = S_BATTLEPOINT2A;
- else if (amount == 3)
- st = S_BATTLEPOINT3A;
- else
- return; // NO STATE!
-
- pt = P_SpawnMobj(source->mo->x, source->mo->y, source->mo->z, MT_BATTLEPOINT);
- P_SetTarget(&pt->target, source->mo);
- P_SetMobjState(pt, st);
- if (victim && victim->skincolor)
- pt->color = victim->skincolor;
- else
- pt->color = source->skincolor;
-}
-
-void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflictor, boolean trapitem)
-{
- UINT8 scoremultiply = 1;
- // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too.
-#ifdef HAVE_BLUA
- boolean force = false; // Used to check if Lua ShouldSpin should get us damaged reguardless of flashtics or heck knows what.
- UINT8 shouldForce = LUAh_ShouldSpin(player, inflictor, source);
- if (P_MobjWasRemoved(player->mo))
- return; // mobj was removed (in theory that shouldn't happen)
- if (shouldForce == 1)
- force = true;
- else if (shouldForce == 2)
- return;
-#else
- static const boolean force = false;
- (void)inflictor; // in case some weirdo doesn't want Lua.
-#endif
-
-
- if (!trapitem && G_BattleGametype())
- {
- if (K_IsPlayerWanted(player))
- scoremultiply = 3;
- else if (player->kartstuff[k_bumper] == 1)
- scoremultiply = 2;
- }
-
- if (player->health <= 0)
- return;
-
- if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0
- || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0
- || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1)))
- {
- if (!force) // if shoulddamage force, we go THROUGH that.
- {
- K_DoInstashield(player);
- return;
- }
- }
-
- if (LUAh_PlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case.
- return;
-
- if (source && source != player->mo && source->player)
- K_PlayHitEmSound(source);
-
- //player->kartstuff[k_sneakertimer] = 0;
- player->kartstuff[k_driftboost] = 0;
-
- player->kartstuff[k_drift] = 0;
- player->kartstuff[k_driftcharge] = 0;
- player->kartstuff[k_pogospring] = 0;
-
- if (G_BattleGametype())
- {
- if (source && source->player && player != source->player)
- {
- P_AddPlayerScore(source->player, scoremultiply);
- K_SpawnBattlePoints(source->player, player, scoremultiply);
- if (!trapitem)
- {
- source->player->kartstuff[k_wanted] -= wantedreduce;
- player->kartstuff[k_wanted] -= (wantedreduce/2);
- }
- }
-
- if (player->kartstuff[k_bumper] > 0)
- {
- if (player->kartstuff[k_bumper] == 1)
- {
- mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!!
- P_SetTarget(&karmahitbox->target, player->mo);
- karmahitbox->destscale = player->mo->scale;
- P_SetScale(karmahitbox, player->mo->scale);
- CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]);
- }
- player->kartstuff[k_bumper]--;
- if (K_IsPlayerWanted(player))
- K_CalculateBattleWanted();
- }
-
- if (!player->kartstuff[k_bumper])
- {
- player->kartstuff[k_comebacktimer] = comebacktime;
- if (player->kartstuff[k_comebackmode] == 2)
- {
- mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE);
- S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound);
- player->kartstuff[k_comebackmode] = 0;
- }
- }
-
- K_CheckBumpers();
- }
-
- player->kartstuff[k_spinouttype] = type;
-
- if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout
- {
- // At spinout, player speed is increased to 1/4 their regular speed, moving them forward
- if (player->speed < K_GetKartSpeed(player, true)/4)
- P_InstaThrust(player->mo, player->mo->angle, FixedMul(K_GetKartSpeed(player, true)/4, player->mo->scale));
- S_StartSound(player->mo, sfx_slip);
- }
-
- player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2;
- player->powers[pw_flashing] = K_GetKartFlashing(player);
-
- if (player->mo->state != &states[S_KART_SPIN])
- P_SetPlayerMobjState(player->mo, S_KART_SPIN);
-
- player->kartstuff[k_instashield] = 15;
- if (cv_kartdebughuddrop.value && !modeattacking)
- K_DropItems(player);
- else
- K_DropHnextList(player);
- return;
-}
-
-static void K_RemoveGrowShrink(player_t *player)
-{
- player->kartstuff[k_growshrinktimer] = 0;
- if (player->kartstuff[k_invincibilitytimer] == 0)
- player->mo->color = player->skincolor;
- player->mo->scalespeed = mapobjectscale/TICRATE;
- player->mo->destscale = mapobjectscale;
- if (cv_kartdebugshrink.value && !modeattacking && !player->bot)
- player->mo->destscale = (6*player->mo->destscale)/8;
- P_RestoreMusic(player);
-}
-
-void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor)
-{
- UINT8 scoremultiply = 1;
- // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too.
-#ifdef HAVE_BLUA
- boolean force = false; // Used to check if Lua ShouldSquish should get us damaged reguardless of flashtics or heck knows what.
- UINT8 shouldForce = LUAh_ShouldSquish(player, inflictor, source);
- if (P_MobjWasRemoved(player->mo))
- return; // mobj was removed (in theory that shouldn't happen)
- if (shouldForce == 1)
- force = true;
- else if (shouldForce == 2)
- return;
-#else
- static const boolean force = false;
- (void)inflictor; // Please stop forgetting to put inflictor in yer functions thank -Lat'
-#endif
-
- if (G_BattleGametype())
- {
- if (K_IsPlayerWanted(player))
- scoremultiply = 3;
- else if (player->kartstuff[k_bumper] == 1)
- scoremultiply = 2;
- }
-
- if (player->health <= 0)
- return;
-
- if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_invincibilitytimer] > 0
- || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0
- || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1)))
- {
- if (!force) // You know the drill by now.
- {
- K_DoInstashield(player);
- return;
- }
- }
-
- if (LUAh_PlayerSquish(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case.
- return;
-
- player->kartstuff[k_sneakertimer] = 0;
- player->kartstuff[k_driftboost] = 0;
-
- player->kartstuff[k_drift] = 0;
- player->kartstuff[k_driftcharge] = 0;
- player->kartstuff[k_pogospring] = 0;
-
- if (G_BattleGametype())
- {
- if (source && source->player && player != source->player)
- {
- P_AddPlayerScore(source->player, scoremultiply);
- K_SpawnBattlePoints(source->player, player, scoremultiply);
- source->player->kartstuff[k_wanted] -= wantedreduce;
- player->kartstuff[k_wanted] -= (wantedreduce/2);
- }
-
- if (player->kartstuff[k_bumper] > 0)
- {
- if (player->kartstuff[k_bumper] == 1)
- {
- mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!!
- P_SetTarget(&karmahitbox->target, player->mo);
- karmahitbox->destscale = player->mo->scale;
- P_SetScale(karmahitbox, player->mo->scale);
- CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]);
- }
- player->kartstuff[k_bumper]--;
- if (K_IsPlayerWanted(player))
- K_CalculateBattleWanted();
- }
-
- if (!player->kartstuff[k_bumper])
- {
- player->kartstuff[k_comebacktimer] = comebacktime;
- if (player->kartstuff[k_comebackmode] == 2)
- {
- mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE);
- S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound);
- player->kartstuff[k_comebackmode] = 0;
- }
- }
-
- K_CheckBumpers();
- }
-
- player->kartstuff[k_squishedtimer] = TICRATE;
-
- // Reduce Shrink timer
- if (player->kartstuff[k_growshrinktimer] < 0)
- {
- player->kartstuff[k_growshrinktimer] += TICRATE;
- if (player->kartstuff[k_growshrinktimer] >= 0)
- K_RemoveGrowShrink(player);
- }
-
- player->powers[pw_flashing] = K_GetKartFlashing(player);
-
- player->mo->flags |= MF_NOCLIP;
-
- if (player->mo->state != &states[S_KART_SQUISH]) // Squash
- P_SetPlayerMobjState(player->mo, S_KART_SQUISH);
-
- P_PlayRinglossSound(player->mo);
-
- player->kartstuff[k_instashield] = 15;
- if (cv_kartdebughuddrop.value && !modeattacking)
- K_DropItems(player);
- else
- K_DropHnextList(player);
- return;
-}
-
-void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A bit of a hack, we just throw the player up higher here and extend their spinout timer
-{
- UINT8 scoremultiply = 1;
-#ifdef HAVE_BLUA
- boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what.
- UINT8 shouldForce = LUAh_ShouldExplode(player, inflictor, source);
- if (P_MobjWasRemoved(player->mo))
- return; // mobj was removed (in theory that shouldn't happen)
- if (shouldForce == 1)
- force = true;
- else if (shouldForce == 2)
- return;
-
-#else
- static const boolean force = false;
-#endif
- if (G_BattleGametype())
- {
- if (K_IsPlayerWanted(player))
- scoremultiply = 3;
- else if (player->kartstuff[k_bumper] == 1)
- scoremultiply = 2;
- }
-
- if (player->health <= 0)
- return;
-
- if (/*player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0 // Explosions should combo, because of SPB and Eggman
- ||*/player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0
- || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1)))
- {
- if (!force) // ShouldDamage can bypass that, again.
- {
- K_DoInstashield(player);
- return;
- }
- }
-
- if (LUAh_PlayerExplode(player, inflictor, source)) // Same thing. Also make sure to let Instashield happen blah blah
- return;
-
- if (source && source != player->mo && source->player)
- K_PlayHitEmSound(source);
-
- player->mo->momz = 18*mapobjectscale;
- player->mo->momx = player->mo->momy = 0;
-
- player->kartstuff[k_sneakertimer] = 0;
- player->kartstuff[k_driftboost] = 0;
-
- player->kartstuff[k_drift] = 0;
- player->kartstuff[k_driftcharge] = 0;
- player->kartstuff[k_pogospring] = 0;
-
- // This is the only part that SHOULDN'T combo :VVVVV
- if (G_BattleGametype() && !(player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0))
- {
- if (source && source->player && player != source->player)
- {
- P_AddPlayerScore(source->player, scoremultiply);
- K_SpawnBattlePoints(source->player, player, scoremultiply);
- source->player->kartstuff[k_wanted] -= wantedreduce;
- player->kartstuff[k_wanted] -= (wantedreduce/2);
- }
-
- if (player->kartstuff[k_bumper] > 0)
- {
- if (player->kartstuff[k_bumper] == 1)
- {
- mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!!
- P_SetTarget(&karmahitbox->target, player->mo);
- karmahitbox->destscale = player->mo->scale;
- P_SetScale(karmahitbox, player->mo->scale);
- CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]);
- }
- player->kartstuff[k_bumper]--;
- if (K_IsPlayerWanted(player))
- K_CalculateBattleWanted();
- }
-
- if (!player->kartstuff[k_bumper])
- {
- player->kartstuff[k_comebacktimer] = comebacktime;
- if (player->kartstuff[k_comebackmode] == 2)
- {
- mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE);
- S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound);
- player->kartstuff[k_comebackmode] = 0;
- }
- }
-
- K_CheckBumpers();
- }
-
- player->kartstuff[k_spinouttype] = 1;
- player->kartstuff[k_spinouttimer] = 2*TICRATE+(TICRATE/2);
-
- player->powers[pw_flashing] = K_GetKartFlashing(player);
-
- if (inflictor && inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1)
- {
- player->kartstuff[k_spinouttimer] = ((3*player->kartstuff[k_spinouttimer])/2)+1;
- player->mo->momz *= 2;
- }
-
- if (player->mo->state != &states[S_KART_SPIN])
- P_SetPlayerMobjState(player->mo, S_KART_SPIN);
-
- P_PlayRinglossSound(player->mo);
-
- if (P_IsLocalPlayer(player))
- {
- quake.intensity = 64*FRACUNIT;
- quake.time = 5;
- }
-
- player->kartstuff[k_instashield] = 15;
- K_DropItems(player);
-
- return;
-}
-
-void K_StealBumper(player_t *player, player_t *victim, boolean force)
-{
- INT32 newbumper;
- angle_t newangle, diff;
- fixed_t newx, newy;
- mobj_t *newmo;
-
- if (!G_BattleGametype())
- return;
-
- if (player->health <= 0 || victim->health <= 0)
- return;
-
- if (!force)
- {
- if (victim->kartstuff[k_bumper] <= 0) // || player->kartstuff[k_bumper] >= cv_kartbumpers.value+2
- return;
-
- if (player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0)
- return;
-
- if (victim->powers[pw_flashing] > 0 || victim->kartstuff[k_squishedtimer] > 0 || victim->kartstuff[k_spinouttimer] > 0
- || victim->kartstuff[k_invincibilitytimer] > 0 || victim->kartstuff[k_growshrinktimer] > 0 || victim->kartstuff[k_hyudorotimer] > 0)
- {
- K_DoInstashield(victim);
- return;
- }
- }
-
- if (netgame && player->kartstuff[k_bumper] <= 0)
- CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]);
-
- newbumper = player->kartstuff[k_bumper];
- if (newbumper <= 1)
- diff = 0;
- else
- diff = FixedAngle(360*FRACUNIT/newbumper);
-
- newangle = player->mo->angle;
- newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, 64*FRACUNIT);
- newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, 64*FRACUNIT);
-
- newmo = P_SpawnMobj(newx, newy, player->mo->z, MT_BATTLEBUMPER);
- newmo->threshold = newbumper;
- P_SetTarget(&newmo->tracer, victim->mo);
- P_SetTarget(&newmo->target, player->mo);
- newmo->angle = (diff * (newbumper-1));
- newmo->color = victim->skincolor;
-
- if (newbumper+1 < 2)
- P_SetMobjState(newmo, S_BATTLEBUMPER3);
- else if (newbumper+1 < 3)
- P_SetMobjState(newmo, S_BATTLEBUMPER2);
- else
- P_SetMobjState(newmo, S_BATTLEBUMPER1);
-
- S_StartSound(player->mo, sfx_3db06);
-
- player->kartstuff[k_bumper]++;
- player->kartstuff[k_comebackpoints] = 0;
- player->powers[pw_flashing] = K_GetKartFlashing(player);
- player->kartstuff[k_comebacktimer] = comebacktime;
-
- /*victim->powers[pw_flashing] = K_GetKartFlashing(victim);
- victim->kartstuff[k_comebacktimer] = comebacktime;*/
-
- victim->kartstuff[k_instashield] = 15;
- if (cv_kartdebughuddrop.value && !modeattacking)
- K_DropItems(victim);
- else
- K_DropHnextList(victim);
- return;
-}
-
-// source is the mobj that originally threw the bomb that exploded etc.
-// Spawns the sphere around the explosion that handles spinout
-void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle, boolean spawncenter, boolean ghostit, mobj_t *source)
-{
- mobj_t *mobj;
- mobj_t *ghost = NULL;
- INT32 i;
- TVector v;
- TVector *res;
- fixed_t finalx, finaly, finalz, dist;
- //mobj_t hoopcenter;
- angle_t degrees, fa, closestangle;
- fixed_t mobjx, mobjy, mobjz;
-
- //hoopcenter.x = x;
- //hoopcenter.y = y;
- //hoopcenter.z = z;
-
- //hoopcenter.z = z - mobjinfo[type].height/2;
-
- degrees = FINEANGLES/number;
-
- closestangle = 0;
-
- // Create the hoop!
- for (i = 0; i < number; i++)
- {
- fa = (i*degrees);
- v[0] = FixedMul(FINECOSINE(fa),radius);
- v[1] = 0;
- v[2] = FixedMul(FINESINE(fa),radius);
- v[3] = FRACUNIT;
-
- res = VectorMatrixMultiply(v, *RotateXMatrix(rotangle));
- M_Memcpy(&v, res, sizeof (v));
- res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle));
- M_Memcpy(&v, res, sizeof (v));
-
- finalx = x + v[0];
- finaly = y + v[1];
- finalz = z + v[2];
-
- mobj = P_SpawnMobj(finalx, finaly, finalz, type);
-
- mobj->z -= mobj->height>>1;
-
- // change angle
- mobj->angle = R_PointToAngle2(mobj->x, mobj->y, x, y);
-
- // change slope
- dist = P_AproxDistance(P_AproxDistance(x - mobj->x, y - mobj->y), z - mobj->z);
-
- if (dist < 1)
- dist = 1;
-
- mobjx = mobj->x;
- mobjy = mobj->y;
- mobjz = mobj->z;
-
- if (ghostit)
- {
- ghost = P_SpawnGhostMobj(mobj);
- P_SetMobjState(mobj, S_NULL);
- mobj = ghost;
- }
-
- if (spawncenter)
- {
- mobj->x = x;
- mobj->y = y;
- mobj->z = z;
- }
-
- mobj->momx = FixedMul(FixedDiv(mobjx - x, dist), FixedDiv(dist, 6*FRACUNIT));
- mobj->momy = FixedMul(FixedDiv(mobjy - y, dist), FixedDiv(dist, 6*FRACUNIT));
- mobj->momz = FixedMul(FixedDiv(mobjz - z, dist), FixedDiv(dist, 6*FRACUNIT));
-
- if (source && !P_MobjWasRemoved(source))
- P_SetTarget(&mobj->target, source);
- }
-}
-
-// Spawns the purely visual explosion
-void K_SpawnMineExplosion(mobj_t *source, UINT8 color)
-{
- INT32 i, radius, height;
- mobj_t *smoldering = P_SpawnMobj(source->x, source->y, source->z, MT_SMOLDERING);
- mobj_t *dust;
- mobj_t *truc;
- INT32 speed, speed2;
-
- smoldering->tics = TICRATE*3;
- radius = source->radius>>FRACBITS;
- height = source->height>>FRACBITS;
-
- if (!color)
- color = SKINCOLOR_KETCHUP;
-
- for (i = 0; i < 32; i++)
- {
- dust = P_SpawnMobj(source->x, source->y, source->z, MT_SMOKE);
- dust->angle = (ANGLE_180/16) * i;
- P_SetScale(dust, source->scale);
- dust->destscale = source->scale*10;
- dust->scalespeed = source->scale/12;
- P_InstaThrust(dust, dust->angle, FixedMul(20*FRACUNIT, source->scale));
-
- truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT,
- source->y + P_RandomRange(-radius, radius)*FRACUNIT,
- source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMEXPLODE);
- P_SetScale(truc, source->scale);
- truc->destscale = source->scale*6;
- truc->scalespeed = source->scale/12;
- speed = FixedMul(10*FRACUNIT, source->scale)>>FRACBITS;
- truc->momx = P_RandomRange(-speed, speed)*FRACUNIT;
- truc->momy = P_RandomRange(-speed, speed)*FRACUNIT;
- speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS;
- truc->momz = P_RandomRange(-speed, speed)*FRACUNIT;
- truc->color = color;
- }
-
- for (i = 0; i < 16; i++)
- {
- dust = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT,
- source->y + P_RandomRange(-radius, radius)*FRACUNIT,
- source->z + P_RandomRange(0, height)*FRACUNIT, MT_SMOKE);
- P_SetScale(dust, source->scale);
- dust->destscale = source->scale*10;
- dust->scalespeed = source->scale/12;
- dust->tics = 30;
- dust->momz = P_RandomRange(FixedMul(3*FRACUNIT, source->scale)>>FRACBITS, FixedMul(7*FRACUNIT, source->scale)>>FRACBITS)*FRACUNIT;
-
- truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT,
- source->y + P_RandomRange(-radius, radius)*FRACUNIT,
- source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMPARTICLE);
- P_SetScale(truc, source->scale);
- truc->destscale = source->scale*5;
- truc->scalespeed = source->scale/12;
- speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS;
- truc->momx = P_RandomRange(-speed, speed)*FRACUNIT;
- truc->momy = P_RandomRange(-speed, speed)*FRACUNIT;
- speed = FixedMul(15*FRACUNIT, source->scale)>>FRACBITS;
- speed2 = FixedMul(45*FRACUNIT, source->scale)>>FRACBITS;
- truc->momz = P_RandomRange(speed, speed2)*FRACUNIT;
- if (P_RandomChance(FRACUNIT/2))
- truc->momz = -truc->momz;
- truc->tics = TICRATE*2;
- truc->color = color;
- }
-}
-
-static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed)
-{
- mobj_t *th;
- fixed_t x, y, z;
- fixed_t finalspeed = speed;
- mobj_t *throwmo;
-
- if (source->player && source->player->speed > K_GetKartSpeed(source->player, false))
- {
- angle_t input = source->angle - an;
- boolean invert = (input > ANGLE_180);
- if (invert)
- input = InvAngle(input);
-
- finalspeed = max(speed, FixedMul(speed, FixedMul(
- FixedDiv(source->player->speed, K_GetKartSpeed(source->player, false)), // Multiply speed to be proportional to your own, boosted maxspeed.
- (((180<x + source->momx + FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT));
- y = source->y + source->momy + FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT));
- z = source->z; // spawn on the ground please
-
- if (P_MobjFlip(source) < 0)
- {
- z = source->z+source->height - mobjinfo[type].height;
- }
-
- th = P_SpawnMobj(x, y, z, type);
-
- th->flags2 |= flags2;
-
- th->threshold = 10;
-
- if (th->info->seesound)
- S_StartSound(source, th->info->seesound);
-
- P_SetTarget(&th->target, source);
-
- if (P_IsObjectOnGround(source))
- {
- // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn
- // This should set it for FOFs
- P_TeleportMove(th, th->x, th->y, th->z);
- // spawn on the ground if the player is on the ground
- if (P_MobjFlip(source) < 0)
- {
- th->z = th->ceilingz - th->height;
- th->eflags |= MFE_VERTICALFLIP;
- }
- else
- th->z = th->floorz;
- }
-
- th->angle = an;
- th->momx = FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT));
- th->momy = FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT));
-
- switch (type)
- {
- case MT_ORBINAUT:
- if (source && source->player)
- th->color = source->player->skincolor;
- else
- th->color = SKINCOLOR_GREY;
- th->movefactor = finalspeed;
- break;
- case MT_JAWZ:
- if (source && source->player)
- th->cvmem = source->player->skincolor;
- else
- th->cvmem = SKINCOLOR_KETCHUP;
- /* FALLTHRU */
- case MT_JAWZ_DUD:
- S_StartSound(th, th->info->activesound);
- /* FALLTHRU */
- case MT_SPB:
- th->movefactor = finalspeed;
- break;
- default:
- break;
- }
-
- x = x + P_ReturnThrustX(source, an, source->radius + th->radius);
- y = y + P_ReturnThrustY(source, an, source->radius + th->radius);
- throwmo = P_SpawnMobj(x, y, z, MT_FIREDITEM);
- throwmo->movecount = 1;
- throwmo->movedir = source->angle - an;
- P_SetTarget(&throwmo->target, source);
-
- return NULL;
-}
-
-static void K_SpawnDriftSparks(player_t *player)
-{
- fixed_t newx;
- fixed_t newy;
- mobj_t *spark;
- angle_t travelangle;
- INT32 i;
-
- I_Assert(player != NULL);
- I_Assert(player->mo != NULL);
- I_Assert(!P_MobjWasRemoved(player->mo));
-
- if (leveltime % 2 == 1)
- return;
-
- if (!P_IsObjectOnGround(player->mo))
- return;
-
- if (!player->kartstuff[k_drift] || player->kartstuff[k_driftcharge] < K_GetKartDriftSparkValue(player))
- return;
-
- travelangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift];
-
- for (i = 0; i < 2; i++)
- {
- newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale));
- newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale));
- spark = P_SpawnMobj(newx, newy, player->mo->z, MT_DRIFTSPARK);
-
- P_SetTarget(&spark->target, player->mo);
- spark->angle = travelangle-(ANGLE_45/5)*player->kartstuff[k_drift];
- spark->destscale = player->mo->scale;
- P_SetScale(spark, player->mo->scale);
-
- spark->momx = player->mo->momx/2;
- spark->momy = player->mo->momy/2;
- //spark->momz = player->mo->momz/2;
-
- if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*4)
- {
- spark->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1)));
- }
- else if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*2)
- {
- if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player)*2)+(24*3))
- spark->color = SKINCOLOR_RASPBERRY; // transition
- else
- spark->color = SKINCOLOR_KETCHUP;
- }
- else
- {
- spark->color = SKINCOLOR_SAPPHIRE;
- }
-
- if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn > 0) // Inward drifts
- || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn < 0))
- {
- if ((player->kartstuff[k_drift] < 0 && (i & 1))
- || (player->kartstuff[k_drift] > 0 && !(i & 1)))
- P_SetMobjState(spark, S_DRIFTSPARK_A1);
- else if ((player->kartstuff[k_drift] < 0 && !(i & 1))
- || (player->kartstuff[k_drift] > 0 && (i & 1)))
- P_SetMobjState(spark, S_DRIFTSPARK_C1);
- }
- else if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn < 0) // Outward drifts
- || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn > 0))
- {
- if ((player->kartstuff[k_drift] < 0 && (i & 1))
- || (player->kartstuff[k_drift] > 0 && !(i & 1)))
- P_SetMobjState(spark, S_DRIFTSPARK_C1);
- else if ((player->kartstuff[k_drift] < 0 && !(i & 1))
- || (player->kartstuff[k_drift] > 0 && (i & 1)))
- P_SetMobjState(spark, S_DRIFTSPARK_A1);
- }
-
- K_MatchGenericExtraFlags(spark, player->mo);
- }
-}
-
-static void K_SpawnAIZDust(player_t *player)
-{
- fixed_t newx;
- fixed_t newy;
- mobj_t *spark;
- angle_t travelangle;
-
- I_Assert(player != NULL);
- I_Assert(player->mo != NULL);
- I_Assert(!P_MobjWasRemoved(player->mo));
-
- if (leveltime % 2 == 1)
- return;
-
- if (!P_IsObjectOnGround(player->mo))
- return;
-
- travelangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
- //S_StartSound(player->mo, sfx_s3k47);
-
- {
- newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale));
- newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale));
- spark = P_SpawnMobj(newx, newy, player->mo->z, MT_AIZDRIFTSTRAT);
-
- spark->angle = travelangle+(player->kartstuff[k_aizdriftstrat]*ANGLE_90);
- P_SetScale(spark, (spark->destscale = (3*player->mo->scale)>>2));
-
- spark->momx = (6*player->mo->momx)/5;
- spark->momy = (6*player->mo->momy)/5;
- //spark->momz = player->mo->momz/2;
-
- K_MatchGenericExtraFlags(spark, player->mo);
- }
-}
-
-void K_SpawnBoostTrail(player_t *player)
-{
- fixed_t newx;
- fixed_t newy;
- fixed_t ground;
- mobj_t *flame;
- angle_t travelangle;
- INT32 i;
-
- I_Assert(player != NULL);
- I_Assert(player->mo != NULL);
- I_Assert(!P_MobjWasRemoved(player->mo));
-
- if (!P_IsObjectOnGround(player->mo)
- || player->kartstuff[k_hyudorotimer] != 0
- || (G_BattleGametype() && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]))
- return;
-
- if (player->mo->eflags & MFE_VERTICALFLIP)
- ground = player->mo->ceilingz - FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale);
- else
- ground = player->mo->floorz;
-
- if (player->kartstuff[k_drift] != 0)
- travelangle = player->mo->angle;
- else
- travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
-
- for (i = 0; i < 2; i++)
- {
- newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
- newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
-#ifdef ESLOPE
- if (player->mo->standingslope)
- {
- ground = P_GetZAt(player->mo->standingslope, newx, newy);
- if (player->mo->eflags & MFE_VERTICALFLIP)
- ground -= FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale);
- }
-#endif
- flame = P_SpawnMobj(newx, newy, ground, MT_SNEAKERTRAIL);
-
- P_SetTarget(&flame->target, player->mo);
- flame->angle = travelangle;
- flame->fuse = TICRATE*2;
- flame->destscale = player->mo->scale;
- P_SetScale(flame, player->mo->scale);
- flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP); // not K_MatchGenericExtraFlags so that a stolen sneaker can be seen
-
- flame->momx = 8;
- P_XYMovement(flame);
- if (P_MobjWasRemoved(flame))
- continue;
-
- if (player->mo->eflags & MFE_VERTICALFLIP)
- {
- if (flame->z + flame->height < flame->ceilingz)
- P_RemoveMobj(flame);
- }
- else if (flame->z > flame->floorz)
- P_RemoveMobj(flame);
- }
-}
-
-void K_SpawnSparkleTrail(mobj_t *mo)
-{
- const INT32 rad = (mo->radius*2)>>FRACBITS;
- mobj_t *sparkle;
- INT32 i;
-
- I_Assert(mo != NULL);
- I_Assert(!P_MobjWasRemoved(mo));
-
- for (i = 0; i < 3; i++)
- {
- fixed_t newx = mo->x + mo->momx + (P_RandomRange(-rad, rad)<y + mo->momy + (P_RandomRange(-rad, rad)<z + mo->momz + (P_RandomRange(0, mo->height>>FRACBITS)<target, mo);
- sparkle->destscale = mo->destscale;
- P_SetScale(sparkle, mo->scale);
- sparkle->eflags = (sparkle->eflags & ~MFE_VERTICALFLIP)|(mo->eflags & MFE_VERTICALFLIP); // not K_MatchGenericExtraFlags so that a stolen invincibility can be seen
- sparkle->color = mo->color;
- //sparkle->colorized = mo->colorized;
- }
-
- P_SetMobjState(sparkle, S_KARTINVULN_LARGE1);
-}
-
-void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent)
-{
- mobj_t *dust;
-
- I_Assert(mo != NULL);
- I_Assert(!P_MobjWasRemoved(mo));
-
- dust = P_SpawnMobj(mo->x + (P_RandomRange(-25,25) * mo->scale), mo->y + (P_RandomRange(-25,25) * mo->scale), mo->z, MT_WIPEOUTTRAIL);
-
- P_SetTarget(&dust->target, mo);
- dust->angle = R_PointToAngle2(0,0,mo->momx,mo->momy);
- dust->destscale = mo->scale;
- P_SetScale(dust, mo->scale);
- dust->eflags = (dust->eflags & ~MFE_VERTICALFLIP)|(mo->eflags & MFE_VERTICALFLIP); // not K_MatchGenericExtraFlags because hyudoro shouldn't be able to wipeout
-
- if (translucent) // offroad effect
- {
- dust->momx = mo->momx/2;
- dust->momy = mo->momy/2;
- dust->momz = mo->momz/2;
- }
-
- if (translucent)
- dust->flags2 |= MF2_SHADOW;
-}
-
-// K_DriftDustHandling
-// Parameters:
-// spawner: The map object that is spawning the drift dust
-// Description: Spawns the drift dust for objects, players use rmomx/y, other objects use regular momx/y.
-// Also plays the drift sound.
-// Other objects should be angled towards where they're trying to go so they don't randomly spawn dust
-// Do note that most of the function won't run in odd intervals of frames
-void K_DriftDustHandling(mobj_t *spawner)
-{
- angle_t anglediff;
- const INT16 spawnrange = spawner->radius>>FRACBITS;
-
- if (!P_IsObjectOnGround(spawner) || leveltime % 2 != 0)
- return;
-
- if (spawner->player)
- {
- if (spawner->player->pflags & PF_SKIDDOWN)
- {
- anglediff = abs((signed)(spawner->angle - spawner->player->frameangle));
- if (leveltime % 6 == 0)
- S_StartSound(spawner, sfx_screec); // repeated here because it doesn't always happen to be within the range when this is the case
- }
- else
- {
- angle_t playerangle = spawner->angle;
-
- if (spawner->player->speed < 5<player->cmd.forwardmove < 0)
- playerangle += ANGLE_180;
-
- anglediff = abs((signed)(playerangle - R_PointToAngle2(0, 0, spawner->player->rmomx, spawner->player->rmomy)));
- }
- }
- else
- {
- if (P_AproxDistance(spawner->momx, spawner->momy) < 5<angle - R_PointToAngle2(0, 0, spawner->momx, spawner->momy)));
- }
-
- if (anglediff > ANGLE_180)
- anglediff = InvAngle(anglediff);
-
- if (anglediff > ANG10*4) // Trying to turn further than 40 degrees
- {
- fixed_t spawnx = P_RandomRange(-spawnrange, spawnrange)<x + spawnx, spawner->y + spawny, spawner->z, MT_DRIFTDUST);
- if (spawner->eflags & MFE_VERTICALFLIP)
- {
- dust->z += spawner->height - dust->height;
- }
- dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange)<scale)/4);
- dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange)<scale)/4);
- dust->momz = P_MobjFlip(spawner) * (P_RandomRange(1, 4) * (spawner->scale));
- P_SetScale(dust, spawner->scale/2);
- dust->destscale = spawner->scale * 3;
- dust->scalespeed = spawner->scale/12;
-
- if (leveltime % 6 == 0)
- S_StartSound(spawner, sfx_screec);
-
- K_MatchGenericExtraFlags(dust, spawner);
- }
-}
-
-static mobj_t *K_FindLastTrailMobj(player_t *player)
-{
- mobj_t *trail;
-
- if (!player || !(trail = player->mo) || !player->mo->hnext || !player->mo->hnext->health)
- return NULL;
-
- while (trail->hnext && !P_MobjWasRemoved(trail->hnext) && trail->hnext->health)
- {
- trail = trail->hnext;
- }
-
- return trail;
-}
-
-static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow)
-{
- mobj_t *mo;
- INT32 dir, PROJSPEED;
- angle_t newangle;
- fixed_t newx, newy, newz;
- mobj_t *throwmo;
-
- if (!player)
- return NULL;
-
- // Figure out projectile speed by game speed
- if (missile && mapthing != MT_BALLHOG) // Trying to keep compatability...
- {
- PROJSPEED = mobjinfo[mapthing].speed;
- if (gamespeed == 0)
- PROJSPEED = FixedMul(PROJSPEED, FRACUNIT-FRACUNIT/4);
- else if (gamespeed == 2)
- PROJSPEED = FixedMul(PROJSPEED, FRACUNIT+FRACUNIT/4);
- PROJSPEED = FixedMul(PROJSPEED, mapobjectscale);
- }
- else
- {
- switch (gamespeed)
- {
- case 0:
- PROJSPEED = 68*mapobjectscale; // Avg Speed is 34
- break;
- case 2:
- PROJSPEED = 96*mapobjectscale; // Avg Speed is 48
- break;
- default:
- PROJSPEED = 82*mapobjectscale; // Avg Speed is 41
- break;
- }
- }
-
- if (altthrow)
- {
- if (altthrow == 2) // Kitchen sink throwing
- {
- if (player->kartstuff[k_throwdir] == 1)
- dir = 3;
- else if (player->kartstuff[k_throwdir] == -1)
- dir = 1;
- else
- dir = 2;
- }
- else
- {
- if (player->kartstuff[k_throwdir] == 1)
- dir = 2;
- else if (player->kartstuff[k_throwdir] == -1)
- dir = -1;
- else
- dir = 1;
- }
- }
- else
- {
- if (player->kartstuff[k_throwdir] != 0)
- dir = player->kartstuff[k_throwdir];
- else
- dir = defaultDir;
- }
-
- if (missile) // Shootables
- {
- if (mapthing == MT_BALLHOG) // Messy
- {
- if (dir == -1)
- {
- // Shoot backward
- mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x06000000, 0, PROJSPEED/4);
- K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x03000000, 0, PROJSPEED/4);
- K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/4);
- K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x03000000, 0, PROJSPEED/4);
- K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x06000000, 0, PROJSPEED/4);
- }
- else
- {
- // Shoot forward
- mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x06000000, 0, PROJSPEED);
- K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x03000000, 0, PROJSPEED);
- K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED);
- K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x03000000, 0, PROJSPEED);
- K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x06000000, 0, PROJSPEED);
- }
- }
- else
- {
- if (dir == -1 && mapthing != MT_SPB)
- {
- // Shoot backward
- mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/2);
- }
- else
- {
- // Shoot forward
- mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED);
- }
- }
- }
- else
- {
- player->kartstuff[k_bananadrag] = 0; // RESET timer, for multiple bananas
-
- if (dir > 0)
- {
- // Shoot forward
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, mapthing);
-
- mo->threshold = 10;
- P_SetTarget(&mo->target, player->mo);
-
- S_StartSound(player->mo, mo->info->seesound);
-
- if (mo)
- {
- angle_t fa = player->mo->angle>>ANGLETOFINESHIFT;
- INT32 HEIGHT = (20 + (dir*10))*mapobjectscale + player->mo->momz;
-
- mo->momx = player->mo->momx + FixedMul(FINECOSINE(fa), (altthrow == 2 ? 2*PROJSPEED/3 : PROJSPEED));
- mo->momy = player->mo->momy + FixedMul(FINESINE(fa), (altthrow == 2 ? 2*PROJSPEED/3 : PROJSPEED));
- mo->momz = P_MobjFlip(player->mo) * HEIGHT;
-
- if (player->mo->eflags & MFE_VERTICALFLIP)
- mo->eflags |= MFE_VERTICALFLIP;
- }
-
- throwmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FIREDITEM);
- P_SetTarget(&throwmo->target, player->mo);
- throwmo->movecount = 0; // above player
- }
- else
- {
- mobj_t *lasttrail = K_FindLastTrailMobj(player);
-
- if (lasttrail)
- {
- newx = lasttrail->x;
- newy = lasttrail->y;
- newz = lasttrail->z;
- }
- else
- {
- // Drop it directly behind you.
- fixed_t dropradius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(mobjinfo[mapthing].radius, mobjinfo[mapthing].radius);
-
- newangle = player->mo->angle;
-
- newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, dropradius);
- newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, dropradius);
- newz = player->mo->z;
- }
-
- mo = P_SpawnMobj(newx, newy, newz, mapthing);
-
- if (P_MobjFlip(player->mo) < 0)
- mo->z = player->mo->z + player->mo->height - mo->height;
-
- mo->threshold = 10;
- P_SetTarget(&mo->target, player->mo);
-
- if (P_IsObjectOnGround(player->mo))
- {
- // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn
- // This should set it for FOFs
- P_TeleportMove(mo, mo->x, mo->y, mo->z);
-
- if (P_MobjFlip(mo) > 0)
- {
- if (mo->floorz > mo->target->z - mo->height)
- {
- mo->z = mo->floorz;
- }
- }
- else
- {
- if (mo->ceilingz < mo->target->z + mo->target->height + mo->height)
- {
- mo->z = mo->ceilingz - mo->height;
- }
- }
- }
-
- if (mo)
- {
- if (player->mo->eflags & MFE_VERTICALFLIP)
- mo->eflags |= MFE_VERTICALFLIP;
- }
- }
- }
-
- return mo;
-}
-
-#define THUNDERRADIUS 320
-
-static void K_DoThunderShield(player_t *player)
-{
- mobj_t *mo;
- int i = 0;
- fixed_t sx;
- fixed_t sy;
- angle_t an;
-
- S_StartSound(player->mo, sfx_zio3);
- //player->kartstuff[k_thunderanim] = 35;
- P_NukeEnemies(player->mo, player->mo, RING_DIST/4);
-
- // spawn vertical bolt
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK);
- P_SetTarget(&mo->target, player->mo);
- P_SetMobjState(mo, S_LZIO11);
- mo->color = SKINCOLOR_TEAL;
- mo->scale = player->mo->scale*3 + (player->mo->scale/2);
-
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK);
- P_SetTarget(&mo->target, player->mo);
- P_SetMobjState(mo, S_LZIO21);
- mo->color = SKINCOLOR_CYAN;
- mo->scale = player->mo->scale*3 + (player->mo->scale/2);
-
- // spawn horizontal bolts;
- for (i=0; i<7; i++)
- {
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK);
- mo->angle = P_RandomRange(0, 359)*ANG1;
- mo->fuse = P_RandomRange(20, 50);
- P_SetTarget(&mo->target, player->mo);
- P_SetMobjState(mo, S_KLIT1);
- }
-
- // spawn the radius thing:
- an = ANGLE_22h;
- for (i=0; i<15; i++)
- {
- sx = player->mo->x + FixedMul((player->mo->scale*THUNDERRADIUS), FINECOSINE((an*i)>>ANGLETOFINESHIFT));
- sy = player->mo->y + FixedMul((player->mo->scale*THUNDERRADIUS), FINESINE((an*i)>>ANGLETOFINESHIFT));
- mo = P_SpawnMobj(sx, sy, player->mo->z, MT_THOK);
- mo-> angle = an*i;
- mo->extravalue1 = THUNDERRADIUS; // Used to know whether we should teleport by radius or something.
- mo->scale = player->mo->scale*3;
- P_SetTarget(&mo->target, player->mo);
- P_SetMobjState(mo, S_KSPARK1);
- }
-}
-
-#undef THUNDERRADIUS
-
-static void K_DoHyudoroSteal(player_t *player)
-{
- INT32 i, numplayers = 0;
- INT32 playerswappable[MAXPLAYERS];
- INT32 stealplayer = -1; // The player that's getting stolen from
- INT32 prandom = 0;
- boolean sink = P_RandomChance(FRACUNIT/64);
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
- && player != &players[i] && !players[i].exiting && !players[i].spectator // Player in-game
-
- // Can steal from this player
- && (G_RaceGametype() //&& players[i].kartstuff[k_position] < player->kartstuff[k_position])
- || (G_BattleGametype() && players[i].kartstuff[k_bumper] > 0))
-
- // Has an item
- && (players[i].kartstuff[k_itemtype]
- && players[i].kartstuff[k_itemamount]
- && !players[i].kartstuff[k_itemheld]
- && !players[i].kartstuff[k_itemblink]))
- {
- playerswappable[numplayers] = i;
- numplayers++;
- }
- }
-
- prandom = P_RandomFixed();
- S_StartSound(player->mo, sfx_s3k92);
-
- if (sink && numplayers > 0 && cv_kitchensink.value) // BEHOLD THE KITCHEN SINK
- {
- player->kartstuff[k_hyudorotimer] = hyudorotime;
- player->kartstuff[k_stealingtimer] = stealtime;
-
- player->kartstuff[k_itemtype] = KITEM_KITCHENSINK;
- player->kartstuff[k_itemamount] = 1;
- player->kartstuff[k_itemheld] = 0;
- return;
- }
- else if ((G_RaceGametype() && player->kartstuff[k_position] == 1) || numplayers == 0) // No-one can be stolen from? Oh well...
- {
- player->kartstuff[k_hyudorotimer] = hyudorotime;
- player->kartstuff[k_stealingtimer] = stealtime;
- return;
- }
- else if (numplayers == 1) // With just 2 players, we just need to set the other player to be the one to steal from
- {
- stealplayer = playerswappable[numplayers-1];
- }
- else if (numplayers > 1) // We need to choose between the available candidates for the 2nd player
- {
- stealplayer = playerswappable[prandom%(numplayers-1)];
- }
-
- if (stealplayer > -1) // Now here's where we do the stealing, has to be done here because we still know the player we're stealing from
- {
- player->kartstuff[k_hyudorotimer] = hyudorotime;
- player->kartstuff[k_stealingtimer] = stealtime;
- players[stealplayer].kartstuff[k_stolentimer] = stealtime;
-
- player->kartstuff[k_itemtype] = players[stealplayer].kartstuff[k_itemtype];
- player->kartstuff[k_itemamount] = players[stealplayer].kartstuff[k_itemamount];
- player->kartstuff[k_itemheld] = 0;
-
- players[stealplayer].kartstuff[k_itemtype] = KITEM_NONE;
- players[stealplayer].kartstuff[k_itemamount] = 0;
- players[stealplayer].kartstuff[k_itemheld] = 0;
-
- if (P_IsLocalPlayer(&players[stealplayer]) && !splitscreen)
- S_StartSound(NULL, sfx_s3k92);
- }
-}
-
-void K_DoSneaker(player_t *player, INT32 type)
-{
- fixed_t intendedboost;
-
- switch (gamespeed)
- {
- case 0:
- intendedboost = 53740+768;
- break;
- case 2:
- intendedboost = 17294+768;
- break;
- default:
- intendedboost = 32768;
- break;
- }
-
- if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3)
- {
- S_StartSound(player->mo, sfx_cdfm01);
- K_SpawnDashDustRelease(player);
- if (intendedboost > player->kartstuff[k_speedboost])
- player->kartstuff[k_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost));
- }
-
- if (!player->kartstuff[k_sneakertimer])
- {
- if (type == 2)
- {
- if (player->mo->hnext)
- {
- mobj_t *cur = player->mo->hnext;
- while (cur && !P_MobjWasRemoved(cur))
- {
- if (!cur->tracer)
- {
- mobj_t *overlay = P_SpawnMobj(cur->x, cur->y, cur->z, MT_BOOSTFLAME);
- P_SetTarget(&overlay->target, cur);
- P_SetTarget(&cur->tracer, overlay);
- P_SetScale(overlay, (overlay->destscale = 3*cur->scale/4));
- }
- cur = cur->hnext;
- }
- }
- }
- else
- {
- mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BOOSTFLAME);
- P_SetTarget(&overlay->target, player->mo);
- P_SetScale(overlay, (overlay->destscale = player->mo->scale));
- }
- }
-
- player->kartstuff[k_sneakertimer] = sneakertime;
-
- if (type != 0)
- {
- player->pflags |= PF_ATTACKDOWN;
- K_PlayBoostTaunt(player->mo);
- }
-}
-
-static void K_DoShrink(player_t *user)
-{
- INT32 i;
-
- S_StartSound(user->mo, sfx_kc46); // Sound the BANG!
- user->pflags |= PF_ATTACKDOWN;
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator || !players[i].mo)
- continue;
- if (&players[i] == user)
- continue;
- if (players[i].kartstuff[k_position] < user->kartstuff[k_position])
- {
- // Don't hit while invulnerable!
- if (!players[i].kartstuff[k_invincibilitytimer]
- && players[i].kartstuff[k_growshrinktimer] <= 0
- && !players[i].kartstuff[k_hyudorotimer])
- {
- // Start shrinking!
- K_DropItems(&players[i]);
- players[i].mo->scalespeed = mapobjectscale/TICRATE;
- players[i].mo->destscale = (6*mapobjectscale)/8;
- if (cv_kartdebugshrink.value && !modeattacking && !players[i].bot)
- players[i].mo->destscale = (6*players[i].mo->destscale)/8;
- players[i].kartstuff[k_growshrinktimer] = -(200+(40*(MAXPLAYERS-players[i].kartstuff[k_position])));
- }
-
- // Grow should get taken away.
- if (players[i].kartstuff[k_growshrinktimer] > 0)
- K_RemoveGrowShrink(&players[i]);
-
- //P_FlashPal(&players[i], PAL_NUKE, 10);
- S_StartSound(players[i].mo, sfx_kc59);
- }
- }
-}
-
-
-void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound)
-{
- const fixed_t vscale = mapobjectscale + (mo->scale - mapobjectscale);
-
- if (mo->player && mo->player->spectator)
- return;
-
- if (mo->eflags & MFE_SPRUNG)
- return;
-
-#ifdef ESLOPE
- mo->standingslope = NULL;
-#endif
-
- mo->eflags |= MFE_SPRUNG;
-
- if (mo->eflags & MFE_VERTICALFLIP)
- vertispeed *= -1;
-
- if (vertispeed == 0)
- {
- fixed_t thrust;
-
- if (mo->player)
- {
- thrust = 3*mo->player->speed/2;
- if (thrust < 48< 72<player->kartstuff[k_pogospring] != 2)
- {
- if (mo->player->kartstuff[k_sneakertimer])
- thrust = FixedMul(thrust, 5*FRACUNIT/4);
- else if (mo->player->kartstuff[k_invincibilitytimer])
- thrust = FixedMul(thrust, 9*FRACUNIT/8);
- }
- }
- else
- {
- thrust = FixedDiv(3*P_AproxDistance(mo->momx, mo->momy)/2, 5*FRACUNIT/2);
- if (thrust < 16< 32<momz = FixedMul(FINESINE(ANGLE_22h>>ANGLETOFINESHIFT), FixedMul(thrust, vscale));
- }
- else
- mo->momz = FixedMul(vertispeed, vscale);
-
- if (sound)
- S_StartSound(mo, (sound == 1 ? sfx_kc2f : sfx_kpogos));
-}
-
-void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source)
-{
- mobj_t *cachenext;
-
-killnext:
- cachenext = banana->hnext;
-
- if (banana->health)
- {
- if (banana->eflags & MFE_VERTICALFLIP)
- banana->z -= banana->height;
- else
- banana->z += banana->height;
-
- S_StartSound(banana, banana->info->deathsound);
- P_KillMobj(banana, inflictor, source);
-
- P_SetObjectMomZ(banana, 8*FRACUNIT, false);
- if (inflictor)
- P_InstaThrust(banana, R_PointToAngle2(inflictor->x, inflictor->y, banana->x, banana->y)+ANGLE_90, 16*FRACUNIT);
- }
-
- if ((banana = cachenext))
- goto killnext;
-}
-
-// Just for firing/dropping items.
-void K_UpdateHnextList(player_t *player, boolean clean)
-{
- mobj_t *work = player->mo, *nextwork;
-
- if (!work)
- return;
-
- nextwork = work->hnext;
-
- while ((work = nextwork) && !P_MobjWasRemoved(work))
- {
- nextwork = work->hnext;
-
- if (!clean && (!work->movedir || work->movedir <= (UINT16)player->kartstuff[k_itemamount]))
- continue;
-
- P_RemoveMobj(work);
- }
-}
-
-// For getting hit!
-void K_DropHnextList(player_t *player)
-{
- mobj_t *work = player->mo, *nextwork, *dropwork;
- INT32 flip;
- mobjtype_t type;
- boolean orbit, ponground, dropall = true;
-
- if (!work)
- return;
-
- flip = P_MobjFlip(player->mo);
- ponground = P_IsObjectOnGround(player->mo);
-
- if (player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD && player->kartstuff[k_itemamount])
- {
- K_DoThunderShield(player);
- player->kartstuff[k_itemamount] = 0;
- player->kartstuff[k_itemtype] = KITEM_NONE;
- player->kartstuff[k_curshield] = 0;
- }
-
- nextwork = work->hnext;
-
- while ((work = nextwork) && !P_MobjWasRemoved(work))
- {
- nextwork = work->hnext;
-
- switch (work->type)
- {
- // Kart orbit items
- case MT_ORBINAUT_SHIELD:
- orbit = true;
- type = MT_ORBINAUT;
- break;
- case MT_JAWZ_SHIELD:
- orbit = true;
- type = MT_JAWZ_DUD;
- break;
- // Kart trailing items
- case MT_BANANA_SHIELD:
- orbit = false;
- type = MT_BANANA;
- break;
- case MT_SSMINE_SHIELD:
- orbit = false;
- dropall = false;
- type = MT_SSMINE;
- break;
- case MT_EGGMANITEM_SHIELD:
- orbit = false;
- type = MT_EGGMANITEM;
- break;
- // intentionally do nothing
- case MT_SINK_SHIELD:
- case MT_ROCKETSNEAKER:
- return;
- default:
- continue;
- }
-
- dropwork = P_SpawnMobj(work->x, work->y, work->z, type);
- P_SetTarget(&dropwork->target, player->mo);
- dropwork->angle = work->angle;
- dropwork->flags2 = work->flags2;
- dropwork->flags |= MF_NOCLIPTHING;
- dropwork->floorz = work->floorz;
- dropwork->ceilingz = work->ceilingz;
-
- if (ponground)
- {
- // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn
- // This should set it for FOFs
- //P_TeleportMove(dropwork, dropwork->x, dropwork->y, dropwork->z); -- handled better by above floorz/ceilingz passing
-
- if (flip == 1)
- {
- if (dropwork->floorz > dropwork->target->z - dropwork->height)
- {
- dropwork->z = dropwork->floorz;
- }
- }
- else
- {
- if (dropwork->ceilingz < dropwork->target->z + dropwork->target->height + dropwork->height)
- {
- dropwork->z = dropwork->ceilingz - dropwork->height;
- }
- }
- }
-
- if (orbit) // splay out
- {
- dropwork->flags2 |= MF2_AMBUSH;
- dropwork->z += flip;
- dropwork->momx = player->mo->momx>>1;
- dropwork->momy = player->mo->momy>>1;
- dropwork->momz = 3*flip*mapobjectscale;
- P_Thrust(dropwork, work->angle - ANGLE_90, 6*mapobjectscale);
- dropwork->movecount = 2;
- dropwork->movedir = work->angle - ANGLE_90;
- P_SetMobjState(dropwork, dropwork->info->deathstate);
- dropwork->tics = -1;
- if (type == MT_JAWZ_DUD)
- dropwork->z += 20*flip*dropwork->scale;
- else
- {
- dropwork->color = work->color;
- dropwork->angle -= ANGLE_90;
- }
- }
- else // plop on the ground
- {
- dropwork->flags &= ~MF_NOCLIPTHING;
- dropwork->threshold = 10;
- }
-
- P_RemoveMobj(work);
- }
-
- {
- // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic...
- P_SetTarget(&player->mo->hnext, NULL);
- player->kartstuff[k_bananadrag] = 0;
- if (player->kartstuff[k_eggmanheld])
- player->kartstuff[k_eggmanheld] = 0;
- else if (player->kartstuff[k_itemheld]
- && (dropall || (--player->kartstuff[k_itemamount] <= 0)))
- {
- player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0;
- player->kartstuff[k_itemtype] = KITEM_NONE;
- }
- }
-}
-
-// For getting EXTRA hit!
-void K_DropItems(player_t *player)
-{
- boolean thunderhack = (player->kartstuff[k_curshield] && player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD);
-
- if (thunderhack)
- player->kartstuff[k_itemtype] = KITEM_NONE;
-
- K_DropHnextList(player);
-
- if (player->mo && player->kartstuff[k_itemamount])
- {
- mobj_t *drop = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FLOATINGITEM);
- P_SetScale(drop, drop->scale>>4);
- drop->destscale = (3*drop->destscale)/2;;
-
- drop->angle = player->mo->angle + ANGLE_90;
- P_Thrust(drop,
- FixedAngle(P_RandomFixed()*180) + player->mo->angle + ANGLE_90,
- 16*mapobjectscale);
- drop->momz = P_MobjFlip(player->mo)*3*mapobjectscale;
-
- drop->threshold = (thunderhack ? KITEM_THUNDERSHIELD : player->kartstuff[k_itemtype]);
- drop->movecount = player->kartstuff[k_itemamount];
-
- drop->flags |= MF_NOCLIPTHING;
- }
-
- K_StripItems(player);
-}
-
-// When an item in the hnext chain dies.
-void K_RepairOrbitChain(mobj_t *orbit)
-{
- mobj_t *cachenext = orbit->hnext;
-
- // First, repair the chain
- if (orbit->hnext && orbit->hnext->health && !P_MobjWasRemoved(orbit->hnext))
- {
- P_SetTarget(&orbit->hnext->hprev, orbit->hprev);
- P_SetTarget(&orbit->hnext, NULL);
- }
-
- if (orbit->hprev && orbit->hprev->health && !P_MobjWasRemoved(orbit->hprev))
- {
- P_SetTarget(&orbit->hprev->hnext, cachenext);
- P_SetTarget(&orbit->hprev, NULL);
- }
-
- // Then recount to make sure item amount is correct
- if (orbit->target && orbit->target->player)
- {
- INT32 num = 0;
-
- mobj_t *cur = orbit->target->hnext;
- mobj_t *prev = NULL;
-
- while (cur && !P_MobjWasRemoved(cur))
- {
- prev = cur;
- cur = cur->hnext;
- if (++num > orbit->target->player->kartstuff[k_itemamount])
- P_RemoveMobj(prev);
- else
- prev->movedir = num;
- }
-
- if (orbit->target->player->kartstuff[k_itemamount] != num)
- orbit->target->player->kartstuff[k_itemamount] = num;
- }
-}
-
-// Move the hnext chain!
-static void K_MoveHeldObjects(player_t *player)
-{
- if (!player->mo)
- return;
-
- if (!player->mo->hnext)
- {
- player->kartstuff[k_bananadrag] = 0;
- if (player->kartstuff[k_eggmanheld])
- player->kartstuff[k_eggmanheld] = 0;
- else if (player->kartstuff[k_itemheld])
- {
- player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0;
- player->kartstuff[k_itemtype] = KITEM_NONE;
- }
- return;
- }
-
- if (P_MobjWasRemoved(player->mo->hnext))
- {
- // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic...
- P_SetTarget(&player->mo->hnext, NULL);
- player->kartstuff[k_bananadrag] = 0;
- if (player->kartstuff[k_eggmanheld])
- player->kartstuff[k_eggmanheld] = 0;
- else if (player->kartstuff[k_itemheld])
- {
- player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0;
- player->kartstuff[k_itemtype] = KITEM_NONE;
- }
- return;
- }
-
- switch (player->mo->hnext->type)
- {
- case MT_ORBINAUT_SHIELD: // Kart orbit items
- case MT_JAWZ_SHIELD:
- {
- mobj_t *cur = player->mo->hnext;
-
- player->kartstuff[k_bananadrag] = 0; // Just to make sure
-
- while (cur && !P_MobjWasRemoved(cur))
- {
- const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); // mobj's distance from its Target, or Radius.
- fixed_t z;
-
- if (!cur->health)
- {
- cur = cur->hnext;
- continue;
- }
-
- cur->color = player->skincolor;
-
- cur->angle -= ANGLE_90;
- cur->angle += FixedAngle(cur->info->speed);
-
- if (cur->extravalue1 < radius)
- cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12);
- if (cur->extravalue1 > radius)
- cur->extravalue1 = radius;
-
- // If the player is on the ceiling, then flip your items as well.
- if (player && player->mo->eflags & MFE_VERTICALFLIP)
- cur->eflags |= MFE_VERTICALFLIP;
- else
- cur->eflags &= ~MFE_VERTICALFLIP;
-
- // Shrink your items if the player shrunk too.
- P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale)));
-
- if (P_MobjFlip(cur) > 0)
- z = player->mo->z;
- else
- z = player->mo->z + player->mo->height - cur->height;
-
- cur->flags |= MF_NOCLIPTHING; // temporarily make them noclip other objects so they can't hit anyone while in the player
- P_TeleportMove(cur, player->mo->x, player->mo->y, z);
- cur->momx = FixedMul(FINECOSINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1);
- cur->momy = FixedMul(FINESINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1);
- cur->flags &= ~MF_NOCLIPTHING;
- if (!P_TryMove(cur, player->mo->x + cur->momx, player->mo->y + cur->momy, true))
- P_SlideMove(cur, true);
- if (P_IsObjectOnGround(player->mo))
- {
- if (P_MobjFlip(cur) > 0)
- {
- if (cur->floorz > player->mo->z - cur->height)
- z = cur->floorz;
- }
- else
- {
- if (cur->ceilingz < player->mo->z + player->mo->height + cur->height)
- z = cur->ceilingz - cur->height;
- }
- }
-
- // Center it during the scale up animation
- z += (FixedMul(mobjinfo[cur->type].height, player->mo->scale - cur->scale)>>1) * P_MobjFlip(cur);
-
- cur->z = z;
- cur->momx = cur->momy = 0;
- cur->angle += ANGLE_90;
-
- cur = cur->hnext;
- }
- }
- break;
- case MT_BANANA_SHIELD: // Kart trailing items
- case MT_SSMINE_SHIELD:
- case MT_EGGMANITEM_SHIELD:
- case MT_SINK_SHIELD:
- {
- mobj_t *cur = player->mo->hnext;
- mobj_t *targ = player->mo;
-
- if (P_IsObjectOnGround(player->mo) && player->speed > 0)
- player->kartstuff[k_bananadrag]++;
-
- while (cur && !P_MobjWasRemoved(cur))
- {
- const fixed_t radius = FixedHypot(targ->radius, targ->radius) + FixedHypot(cur->radius, cur->radius);
- angle_t ang;
- fixed_t targx, targy, targz;
- fixed_t speed, dist;
-
- cur->flags &= ~MF_NOCLIPTHING;
-
- if (!cur->health)
- {
- cur = cur->hnext;
- continue;
- }
-
- if (cur->extravalue1 < radius)
- cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12);
- if (cur->extravalue1 > radius)
- cur->extravalue1 = radius;
-
- if (cur != player->mo->hnext)
- {
- targ = cur->hprev;
- dist = cur->extravalue1/4;
- }
- else
- dist = cur->extravalue1/2;
-
- if (!targ || P_MobjWasRemoved(targ))
- continue;
-
- // Shrink your items if the player shrunk too.
- P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale)));
-
- ang = targ->angle;
- targx = targ->x + P_ReturnThrustX(cur, ang + ANGLE_180, dist);
- targy = targ->y + P_ReturnThrustY(cur, ang + ANGLE_180, dist);
- targz = targ->z;
- speed = FixedMul(R_PointToDist2(cur->x, cur->y, targx, targy), 3*FRACUNIT/4);
- if (P_IsObjectOnGround(targ))
- targz = cur->floorz;
-
- cur->angle = R_PointToAngle2(cur->x, cur->y, targx, targy);
-
- /*if (P_IsObjectOnGround(player->mo) && player->speed > 0 && player->kartstuff[k_bananadrag] > TICRATE
- && P_RandomChance(min(FRACUNIT/2, FixedDiv(player->speed, K_GetKartSpeed(player, false))/2)))
- {
- if (leveltime & 1)
- targz += 8*(2*FRACUNIT)/7;
- else
- targz -= 8*(2*FRACUNIT)/7;
- }*/
-
- if (speed > dist)
- P_InstaThrust(cur, cur->angle, speed-dist);
-
- P_SetObjectMomZ(cur, FixedMul(targz - cur->z, 7*FRACUNIT/8) - gravity, false);
-
- if (R_PointToDist2(cur->x, cur->y, targx, targy) > 768*FRACUNIT)
- P_TeleportMove(cur, targx, targy, cur->z);
-
- cur = cur->hnext;
- }
- }
- break;
- case MT_ROCKETSNEAKER: // Special rocket sneaker stuff
- {
- mobj_t *cur = player->mo->hnext;
- INT32 num = 0;
-
- while (cur && !P_MobjWasRemoved(cur))
- {
- const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius);
- boolean vibrate = ((leveltime & 1) && !cur->tracer);
- angle_t angoffset;
- fixed_t targx, targy, targz;
-
- cur->flags &= ~MF_NOCLIPTHING;
-
- if (player->kartstuff[k_rocketsneakertimer] <= TICRATE && (leveltime & 1))
- cur->flags2 |= MF2_DONTDRAW;
- else
- cur->flags2 &= ~MF2_DONTDRAW;
-
- if (num & 1)
- P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_LVIBRATE : S_ROCKETSNEAKER_L));
- else
- P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_RVIBRATE : S_ROCKETSNEAKER_R));
-
- if (!player->kartstuff[k_rocketsneakertimer] || cur->extravalue2 || !cur->health)
- {
- num = (num+1) % 2;
- cur = cur->hnext;
- continue;
- }
-
- if (cur->extravalue1 < radius)
- cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12);
- if (cur->extravalue1 > radius)
- cur->extravalue1 = radius;
-
- // Shrink your items if the player shrunk too.
- P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale)));
-
-#if 1
- {
- angle_t input = player->frameangle - cur->angle;
- boolean invert = (input > ANGLE_180);
- if (invert)
- input = InvAngle(input);
-
- input = FixedAngle(AngleFixed(input)/4);
- if (invert)
- input = InvAngle(input);
-
- cur->angle = cur->angle + input;
- }
-#else
- cur->angle = player->frameangle;
-#endif
-
- angoffset = ANGLE_90 + (ANGLE_180 * num);
-
- targx = player->mo->x + P_ReturnThrustX(cur, cur->angle + angoffset, cur->extravalue1);
- targy = player->mo->y + P_ReturnThrustY(cur, cur->angle + angoffset, cur->extravalue1);
-
- { // bobbing, copy pasted from my kimokawaiii entry
- const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK);
- targz = (player->mo->z + (player->mo->height/2)) + sine;
- }
-
- if (cur->tracer)
- {
- fixed_t diffx, diffy, diffz;
-
- diffx = targx - cur->x;
- diffy = targy - cur->y;
- diffz = targz - cur->z;
-
- P_TeleportMove(cur->tracer, cur->tracer->x + diffx + P_ReturnThrustX(cur, cur->angle + angoffset, 6*cur->scale),
- cur->tracer->y + diffy + P_ReturnThrustY(cur, cur->angle + angoffset, 6*cur->scale), cur->tracer->z + diffz);
- P_SetScale(cur->tracer, (cur->tracer->destscale = 3*cur->scale/4));
- }
-
- P_TeleportMove(cur, targx, targy, targz);
-
- num = (num+1) % 2;
- cur = cur->hnext;
- }
- }
- break;
- default:
- break;
- }
-}
-
-player_t *K_FindJawzTarget(mobj_t *actor, player_t *source)
-{
- fixed_t best = -1;
- player_t *wtarg = NULL;
- INT32 i;
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- angle_t thisang;
- player_t *player;
-
- if (!playeringame[i])
- continue;
-
- player = &players[i];
-
- if (player->spectator)
- continue; // spectator
-
- if (!player->mo)
- continue;
-
- if (player->mo->health <= 0)
- continue; // dead
-
- // Don't target yourself, stupid.
- if (player == source)
- continue;
-
- // Don't home in on teammates.
- if (G_GametypeHasTeams() && source->ctfteam == player->ctfteam)
- continue;
-
- // Invisible, don't bother
- if (player->kartstuff[k_hyudorotimer])
- continue;
-
- // Find the angle, see who's got the best.
- thisang = actor->angle - R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y);
- if (thisang > ANGLE_180)
- thisang = InvAngle(thisang);
-
- if (thisang > ANGLE_45) // Don't go for people who are behind you
- continue;
-
- // Jawz only go after the person directly ahead of you in race... sort of literally now!
- if (G_RaceGametype())
- {
- if (player->kartstuff[k_position] >= source->kartstuff[k_position]) // Don't pay attention to people behind you
- continue;
- if ((best == -1) || (player->kartstuff[k_position] > best))
- {
- wtarg = player;
- best = player->kartstuff[k_position];
- }
- }
- else
- {
- fixed_t thisdist;
- fixed_t thisavg;
-
- if (player->kartstuff[k_bumper] <= 0)
- continue;
-
- // Z pos too high/low
- if (abs(player->mo->z - (actor->z + actor->momz)) > RING_DIST/8)
- continue;
-
- thisdist = P_AproxDistance(player->mo->x - (actor->x + actor->momx), player->mo->y - (actor->y + actor->momy));
-
- if (thisdist > 2*RING_DIST) // Don't go for people who are too far away
- continue;
-
- thisavg = (AngleFixed(thisang) + thisdist) / 2;
-
- //CONS_Printf("got avg %d from player # %d\n", thisavg>>FRACBITS, i);
-
- if ((best == -1) || (thisavg < best))
- {
- wtarg = player;
- best = thisavg;
- }
- }
- }
-
- return wtarg;
-}
-
-// Engine Sounds.
-static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd)
-{
- const INT32 numsnds = 13;
- INT32 class, s, w; // engine class number
- UINT8 volume = 255;
- fixed_t volumedampen = 0;
- INT32 targetsnd = 0;
- INT32 i;
-
- s = (player->kartspeed-1)/3;
- w = (player->kartweight-1)/3;
-
-#define LOCKSTAT(stat) \
- if (stat < 0) { stat = 0; } \
- if (stat > 2) { stat = 2; }
- LOCKSTAT(s);
- LOCKSTAT(w);
-#undef LOCKSTAT
-
- class = s+(3*w);
-
- // Silence the engines
- if (leveltime < 8 || player->spectator || player->exiting)
- {
- player->kartstuff[k_enginesnd] = 0; // Reset sound number
- return;
- }
-
-#if 0
- if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct!
-#else
- if (leveltime % 8) // .25 seconds of wait time between engine sounds
-#endif
- return;
-
- if ((leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) || (player->kartstuff[k_respawn] == 1)) // Startup boosts
- targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0);
- else
- targetsnd = (((6*cmd->forwardmove)/25) + ((player->speed / mapobjectscale)/5))/2;
-
- if (targetsnd < 0)
- targetsnd = 0;
- if (targetsnd > 12)
- targetsnd = 12;
-
- if (player->kartstuff[k_enginesnd] < targetsnd)
- player->kartstuff[k_enginesnd]++;
- if (player->kartstuff[k_enginesnd] > targetsnd)
- player->kartstuff[k_enginesnd]--;
-
- if (player->kartstuff[k_enginesnd] < 0)
- player->kartstuff[k_enginesnd] = 0;
- if (player->kartstuff[k_enginesnd] > 12)
- player->kartstuff[k_enginesnd] = 12;
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- UINT8 thisvol = 0;
- fixed_t dist;
-
- if (!playeringame[i] || !players[i].mo || players[i].spectator || players[i].exiting)
- continue;
-
- if ((i == displayplayer)
- || (i == secondarydisplayplayer && splitscreen)
- || (i == thirddisplayplayer && splitscreen > 1)
- || (i == fourthdisplayplayer && splitscreen > 2))
- {
- volumedampen += FRACUNIT; // We already know what this is gonna be, let's not waste our time.
- continue;
- }
-
- dist = P_AproxDistance(P_AproxDistance(player->mo->x-players[i].mo->x,
- player->mo->y-players[i].mo->y), player->mo->z-players[i].mo->z) / 2;
-
- dist = FixedDiv(dist, mapobjectscale);
-
- if (dist > 1536<>FRACBITS)) / (((1536<>(FRACBITS+4));
-
- if (thisvol == 0)
- continue;
-
- volumedampen += (thisvol * 257); // 255 * 257 = FRACUNIT
- }
-
- if (volumedampen > FRACUNIT)
- volume = FixedDiv(volume<>FRACBITS;
-
- if (volume <= 0) // Might as well
- return;
-
- S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->kartstuff[k_enginesnd]) + (class*numsnds), volume);
-}
-
-static void K_UpdateInvincibilitySounds(player_t *player)
-{
- INT32 sfxnum = sfx_None;
-
- if (player->mo->health > 0 && !P_IsLocalPlayer(player))
- {
- if (cv_kartinvinsfx.value)
- {
- if (player->kartstuff[k_growshrinktimer] > 0) // Prioritize Grow
- sfxnum = sfx_alarmg;
- else if (player->kartstuff[k_invincibilitytimer] > 0)
- sfxnum = sfx_alarmi;
- }
- else
- {
- if (player->kartstuff[k_growshrinktimer] > 0)
- sfxnum = sfx_kgrow;
- else if (player->kartstuff[k_invincibilitytimer] > 0)
- sfxnum = sfx_kinvnc;
- }
- }
-
- if (sfxnum != sfx_None && !S_SoundPlaying(player->mo, sfxnum))
- S_StartSound(player->mo, sfxnum);
-
-#define STOPTHIS(this) \
- if (sfxnum != this && S_SoundPlaying(player->mo, this)) \
- S_StopSoundByID(player->mo, this);
- STOPTHIS(sfx_alarmi);
- STOPTHIS(sfx_alarmg);
- STOPTHIS(sfx_kinvnc);
- STOPTHIS(sfx_kgrow);
-#undef STOPTHIS
-}
-
-void K_KartPlayerHUDUpdate(player_t *player)
-{
- if (player->kartstuff[k_lapanimation])
- player->kartstuff[k_lapanimation]--;
-
- if (player->kartstuff[k_yougotem])
- player->kartstuff[k_yougotem]--;
-
- if (G_BattleGametype() && (player->exiting || player->kartstuff[k_comebacktimer]))
- {
- if (player->exiting)
- {
- if (player->exiting < 6*TICRATE)
- player->kartstuff[k_cardanimation] += ((164-player->kartstuff[k_cardanimation])/8)+1;
- else if (player->exiting == 6*TICRATE)
- player->kartstuff[k_cardanimation] = 0;
- else if (player->kartstuff[k_cardanimation] < 2*TICRATE)
- player->kartstuff[k_cardanimation]++;
- }
- else
- {
- if (player->kartstuff[k_comebacktimer] < 6*TICRATE)
- player->kartstuff[k_cardanimation] -= ((164-player->kartstuff[k_cardanimation])/8)+1;
- else if (player->kartstuff[k_comebacktimer] < 9*TICRATE)
- player->kartstuff[k_cardanimation] += ((164-player->kartstuff[k_cardanimation])/8)+1;
- }
-
- if (player->kartstuff[k_cardanimation] > 164)
- player->kartstuff[k_cardanimation] = 164;
- if (player->kartstuff[k_cardanimation] < 0)
- player->kartstuff[k_cardanimation] = 0;
- }
- else if (G_RaceGametype() && player->exiting)
- {
- if (player->kartstuff[k_cardanimation] < 2*TICRATE)
- player->kartstuff[k_cardanimation]++;
- }
- else
- player->kartstuff[k_cardanimation] = 0;
-}
-
-/** \brief Decreases various kart timers and powers per frame. Called in P_PlayerThink in p_user.c
-
- \param player player object passed from P_PlayerThink
- \param cmd control input from player
-
- \return void
-*/
-void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
-{
- K_UpdateOffroad(player);
- K_UpdateEngineSounds(player, cmd); // Thanks, VAda!
- K_GetKartBoostPower(player);
-
- // Speed lines
- if ((player->kartstuff[k_sneakertimer] || player->kartstuff[k_driftboost] || player->kartstuff[k_startboost]) && player->speed > 0)
- {
- mobj_t *fast = P_SpawnMobj(player->mo->x + (P_RandomRange(-36,36) * player->mo->scale),
- player->mo->y + (P_RandomRange(-36,36) * player->mo->scale),
- player->mo->z + (player->mo->height/2) + (P_RandomRange(-20,20) * player->mo->scale),
- MT_FASTLINE);
- fast->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
- fast->momx = 3*player->mo->momx/4;
- fast->momy = 3*player->mo->momy/4;
- fast->momz = 3*player->mo->momz/4;
- K_MatchGenericExtraFlags(fast, player->mo);
- }
-
- if (player->playerstate == PST_DEAD || player->kartstuff[k_respawn] > 1) // Ensure these are set correctly here
- {
- player->mo->colorized = false;
- player->mo->color = player->skincolor;
- }
- else if (player->kartstuff[k_eggmanexplode]) // You're gonna diiiiie
- {
- const INT32 flashtime = 4<<(player->kartstuff[k_eggmanexplode]/TICRATE);
- if (player->kartstuff[k_eggmanexplode] == 1 || (player->kartstuff[k_eggmanexplode] % (flashtime/2) != 0))
- {
- player->mo->colorized = false;
- player->mo->color = player->skincolor;
- }
- else if (player->kartstuff[k_eggmanexplode] % flashtime == 0)
- {
- player->mo->colorized = true;
- player->mo->color = SKINCOLOR_BLACK;
- }
- else
- {
- player->mo->colorized = true;
- player->mo->color = SKINCOLOR_CRIMSON;
- }
- }
- else if (player->kartstuff[k_invincibilitytimer]) // setting players to use the star colormap and spawning afterimages
- {
- mobj_t *ghost;
- player->mo->colorized = true;
- ghost = P_SpawnGhostMobj(player->mo);
- ghost->fuse = 4;
- ghost->frame |= FF_FULLBRIGHT;
- }
- else if (player->kartstuff[k_growshrinktimer]) // Ditto, for grow/shrink
- {
- if (player->kartstuff[k_growshrinktimer] % 5 == 0)
- {
- player->mo->colorized = true;
- player->mo->color = (player->kartstuff[k_growshrinktimer] < 0 ? SKINCOLOR_CREAMSICLE : SKINCOLOR_PERIWINKLE);
- }
- else
- {
- player->mo->colorized = false;
- player->mo->color = player->skincolor;
- }
- }
- else
- {
- player->mo->colorized = false;
- }
-
- if (player->kartstuff[k_dashpadcooldown]) // Twinkle Circuit inspired afterimages
- {
- mobj_t *ghost;
- ghost = P_SpawnGhostMobj(player->mo);
- ghost->fuse = player->kartstuff[k_dashpadcooldown]+1;
- ghost->momx = player->mo->momx / (player->kartstuff[k_dashpadcooldown]+1);
- ghost->momy = player->mo->momy / (player->kartstuff[k_dashpadcooldown]+1);
- ghost->momz = player->mo->momz / (player->kartstuff[k_dashpadcooldown]+1);
- player->kartstuff[k_dashpadcooldown]--;
- }
-
- // DKR style camera for boosting
- if (player->kartstuff[k_boostcam] != 0 || player->kartstuff[k_destboostcam] != 0)
- {
- if (player->kartstuff[k_boostcam] < player->kartstuff[k_destboostcam]
- && player->kartstuff[k_destboostcam] != 0)
- {
- player->kartstuff[k_boostcam] += FRACUNIT/(TICRATE/4);
- if (player->kartstuff[k_boostcam] >= player->kartstuff[k_destboostcam])
- player->kartstuff[k_destboostcam] = 0;
- }
- else
- {
- player->kartstuff[k_boostcam] -= FRACUNIT/TICRATE;
- if (player->kartstuff[k_boostcam] < player->kartstuff[k_destboostcam])
- player->kartstuff[k_boostcam] = player->kartstuff[k_destboostcam] = 0;
- }
- //CONS_Printf("cam: %d, dest: %d\n", player->kartstuff[k_boostcam], player->kartstuff[k_destboostcam]);
- }
-
- player->kartstuff[k_timeovercam] = 0;
-
- // Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations.
- if (player->kartstuff[k_spinouttimer] != 0
- || player->kartstuff[k_wipeoutslow] != 0
- || player->kartstuff[k_squishedtimer] != 0)
- {
- player->powers[pw_flashing] = K_GetKartFlashing(player);
- }
- else if (player->powers[pw_flashing] == K_GetKartFlashing(player))
- {
- player->powers[pw_flashing]--;
- }
-
- if (player->kartstuff[k_spinouttimer])
- {
- if ((P_IsObjectOnGround(player->mo) || player->kartstuff[k_spinouttype] == 1)
- && (player->kartstuff[k_sneakertimer] == 0))
- {
- player->kartstuff[k_spinouttimer]--;
- if (player->kartstuff[k_wipeoutslow] > 1)
- player->kartstuff[k_wipeoutslow]--;
- if (player->kartstuff[k_spinouttimer] == 0)
- player->kartstuff[k_spinouttype] = 0; // Reset type
- }
- }
- else
- {
- if (player->kartstuff[k_wipeoutslow] == 1)
- player->mo->friction = ORIG_FRICTION;
- player->kartstuff[k_wipeoutslow] = 0;
- if (!comeback)
- player->kartstuff[k_comebacktimer] = comebacktime;
- else if (player->kartstuff[k_comebacktimer])
- {
- player->kartstuff[k_comebacktimer]--;
- if (P_IsLocalPlayer(player) && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer] <= 0)
- comebackshowninfo = true; // client has already seen the message
- }
- }
-
- /*if (player->kartstuff[k_thunderanim])
- player->kartstuff[k_thunderanim]--;*/
-
- if (player->kartstuff[k_sneakertimer])
- {
- player->kartstuff[k_sneakertimer]--;
- if (player->kartstuff[k_wipeoutslow] > 0 && player->kartstuff[k_wipeoutslow] < wipeoutslowtime+1)
- player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1;
- }
-
- if (player->kartstuff[k_floorboost])
- player->kartstuff[k_floorboost]--;
-
- if (player->kartstuff[k_driftboost])
- player->kartstuff[k_driftboost]--;
-
- if (player->kartstuff[k_startboost])
- player->kartstuff[k_startboost]--;
-
- if (player->kartstuff[k_invincibilitytimer])
- player->kartstuff[k_invincibilitytimer]--;
-
- if (!player->kartstuff[k_respawn] && player->kartstuff[k_growshrinktimer] != 0)
- {
- if (player->kartstuff[k_growshrinktimer] > 0)
- player->kartstuff[k_growshrinktimer]--;
- if (player->kartstuff[k_growshrinktimer] < 0)
- player->kartstuff[k_growshrinktimer]++;
-
- // Back to normal
- if (player->kartstuff[k_growshrinktimer] == 0)
- K_RemoveGrowShrink(player);
- }
-
- if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0
- && player->kartstuff[k_rocketsneakertimer])
- player->kartstuff[k_rocketsneakertimer]--;
-
- if (player->kartstuff[k_hyudorotimer])
- player->kartstuff[k_hyudorotimer]--;
-
- if (player->kartstuff[k_sadtimer])
- player->kartstuff[k_sadtimer]--;
-
- if (player->kartstuff[k_stealingtimer])
- player->kartstuff[k_stealingtimer]--;
-
- if (player->kartstuff[k_stolentimer])
- player->kartstuff[k_stolentimer]--;
-
- if (player->kartstuff[k_squishedtimer])
- player->kartstuff[k_squishedtimer]--;
-
- if (player->kartstuff[k_justbumped])
- player->kartstuff[k_justbumped]--;
-
- // This doesn't go in HUD update because it has potential gameplay ramifications
- if (player->kartstuff[k_itemblink] && player->kartstuff[k_itemblink]-- <= 0)
- {
- player->kartstuff[k_itemblinkmode] = 0;
- player->kartstuff[k_itemblink] = 0;
- }
-
- K_KartPlayerHUDUpdate(player);
-
- if (player->kartstuff[k_voices])
- player->kartstuff[k_voices]--;
-
- if (player->kartstuff[k_tauntvoices])
- player->kartstuff[k_tauntvoices]--;
-
- if (G_BattleGametype() && player->kartstuff[k_bumper] > 0)
- player->kartstuff[k_wanted]++;
-
- if (P_IsObjectOnGround(player->mo))
- player->kartstuff[k_waterskip] = 0;
-
- if (player->kartstuff[k_instashield])
- player->kartstuff[k_instashield]--;
-
- if (player->kartstuff[k_eggmanexplode])
- {
- if (player->spectator || (G_BattleGametype() && !player->kartstuff[k_bumper]))
- player->kartstuff[k_eggmanexplode] = 0;
- else
- {
- player->kartstuff[k_eggmanexplode]--;
- if (player->kartstuff[k_eggmanexplode] <= 0)
- {
- mobj_t *eggsexplode;
- //player->powers[pw_flashing] = 0;
- eggsexplode = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SPBEXPLOSION);
- if (player->kartstuff[k_eggmanblame] >= 0
- && player->kartstuff[k_eggmanblame] < MAXPLAYERS
- && playeringame[player->kartstuff[k_eggmanblame]]
- && !players[player->kartstuff[k_eggmanblame]].spectator
- && players[player->kartstuff[k_eggmanblame]].mo)
- P_SetTarget(&eggsexplode->target, players[player->kartstuff[k_eggmanblame]].mo);
- }
- }
- }
-
- // ???
- /*
- if (player->kartstuff[k_jmp] > 1 && onground)
- {
- S_StartSound(player->mo, sfx_spring);
- P_DoJump(player, false);
- player->mo->momz *= player->kartstuff[k_jmp];
- player->kartstuff[k_jmp] = 0;
- }
- */
-
- if (player->kartstuff[k_comebacktimer])
- player->kartstuff[k_comebackmode] = 0;
-
- if (P_IsObjectOnGround(player->mo) && player->mo->momz <= 0 && player->kartstuff[k_pogospring])
- player->kartstuff[k_pogospring] = 0;
-
- if (cmd->buttons & BT_DRIFT)
- player->kartstuff[k_jmp] = 1;
- else
- player->kartstuff[k_jmp] = 0;
-
- // Respawn Checker
- if (player->kartstuff[k_respawn])
- K_RespawnChecker(player);
-
- // Roulette Code
- K_KartItemRoulette(player, cmd);
-
- // Handle invincibility sfx
- K_UpdateInvincibilitySounds(player); // Also thanks, VAda!
-
- // Plays the music after the starting countdown.
- if (P_IsLocalPlayer(player) && leveltime == (starttime + (TICRATE/2)))
- {
- S_ChangeMusic(mapmusname, mapmusflags, true);
- S_ShowMusicCredit();
- }
-}
-
-void K_KartPlayerAfterThink(player_t *player)
-{
- if (player->kartstuff[k_curshield]
- || player->kartstuff[k_invincibilitytimer]
- || (player->kartstuff[k_growshrinktimer] != 0 && player->kartstuff[k_growshrinktimer] % 5 == 4)) // 4 instead of 0 because this is afterthink!
- {
- player->mo->frame |= FF_FULLBRIGHT;
- }
- else
- {
- if (!(player->mo->state->frame & FF_FULLBRIGHT))
- player->mo->frame &= ~FF_FULLBRIGHT;
- }
-
- // Move held objects (Bananas, Orbinaut, etc)
- K_MoveHeldObjects(player);
-
- // Jawz reticule (seeking)
- if (player->kartstuff[k_itemtype] == KITEM_JAWZ && player->kartstuff[k_itemheld])
- {
- player_t *targ = K_FindJawzTarget(player->mo, player);
- mobj_t *ret;
-
- if (!targ)
- {
- player->kartstuff[k_lastjawztarget] = -1;
- return;
- }
-
- ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE);
- P_SetTarget(&ret->target, targ->mo);
- ret->frame |= ((leveltime % 10) / 2);
- ret->tics = 1;
- ret->color = player->skincolor;
-
- if (targ-players != player->kartstuff[k_lastjawztarget])
- {
- if (P_IsLocalPlayer(player) || P_IsLocalPlayer(targ))
- S_StartSound(NULL, sfx_s3k89);
- else
- S_StartSound(targ->mo, sfx_s3k89);
-
- player->kartstuff[k_lastjawztarget] = targ-players;
- }
- }
- else
- {
- player->kartstuff[k_lastjawztarget] = -1;
- }
-}
-
-// Returns false if this player being placed here causes them to collide with any other player
-// Used in g_game.c for match etc. respawning
-// This does not check along the z because the z is not correctly set for the spawnee at this point
-boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y)
-{
- INT32 i;
- fixed_t p1radius = players[playernum].mo->radius;
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (playernum == i || !playeringame[i] || players[i].spectator || !players[i].mo || players[i].mo->health <= 0
- || players[i].playerstate != PST_LIVE || (players[i].mo->flags & MF_NOCLIP) || (players[i].mo->flags & MF_NOCLIPTHING))
- continue;
-
- if (abs(x - players[i].mo->x) < (p1radius + players[i].mo->radius)
- && abs(y - players[i].mo->y) < (p1radius + players[i].mo->radius))
- {
- return false;
- }
- }
- return true;
-}
-
-// countersteer is how strong the controls are telling us we are turning
-// turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left
-static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer)
-{
- INT16 basedrift, driftangle;
- fixed_t driftweight = player->kartweight*14; // 12
-
- // If they aren't drifting or on the ground this doesn't apply
- if (player->kartstuff[k_drift] == 0 || !P_IsObjectOnGround(player->mo))
- return 0;
-
- if (player->kartstuff[k_driftend] != 0)
- {
- return -266*player->kartstuff[k_drift]; // Drift has ended and we are tweaking their angle back a bit
- }
-
- //basedrift = 90*player->kartstuff[k_drift]; // 450
- //basedrift = 93*player->kartstuff[k_drift] - driftweight*3*player->kartstuff[k_drift]/10; // 447 - 303
- basedrift = 83*player->kartstuff[k_drift] - (driftweight - 14)*player->kartstuff[k_drift]/5; // 415 - 303
- driftangle = abs((252 - driftweight)*player->kartstuff[k_drift]/5);
-
- return basedrift + FixedMul(driftangle, countersteer);
-}
-
-INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
-{
- fixed_t p_maxspeed = FixedMul(K_GetKartSpeed(player, false), 3*FRACUNIT);
- fixed_t adjustangle = FixedDiv((p_maxspeed>>16) - (player->speed>>16), (p_maxspeed>>16) + player->kartweight);
-
- if (player->spectator)
- return turnvalue;
-
- if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo))
- {
- // If we're drifting we have a completely different turning value
- if (player->kartstuff[k_driftend] == 0)
- {
- // 800 is the max set in g_game.c with angleturn
- fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, 800*FRACUNIT);
- turnvalue = K_GetKartDriftValue(player, countersteer);
- }
- else
- turnvalue = (INT16)(turnvalue + K_GetKartDriftValue(player, FRACUNIT));
-
- return turnvalue;
- }
-
- turnvalue = FixedMul(turnvalue, adjustangle); // Weight has a small effect on turning
-
- if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_sneakertimer] || player->kartstuff[k_growshrinktimer] > 0)
- turnvalue = FixedMul(turnvalue, FixedDiv(5*FRACUNIT, 4*FRACUNIT));
-
- return turnvalue;
-}
-
-INT32 K_GetKartDriftSparkValue(player_t *player)
-{
- UINT8 kartspeed = (G_BattleGametype() && player->kartstuff[k_bumper] <= 0)
- ? 1
- : player->kartspeed;
- return (26*4 + kartspeed*2 + (9 - player->kartweight))*8;
-}
-
-static void K_KartDrift(player_t *player, boolean onground)
-{
- fixed_t minspeed = (10 * player->mo->scale);
- INT32 dsone = K_GetKartDriftSparkValue(player);
- INT32 dstwo = dsone*2;
- INT32 dsthree = dstwo*2;
-
- // Drifting is actually straffing + automatic turning.
- // Holding the Jump button will enable drifting.
-
- // Drift Release (Moved here so you can't "chain" drifts)
- if ((player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5)
- // || (player->kartstuff[k_drift] >= 1 && player->kartstuff[k_turndir] != 1) || (player->kartstuff[k_drift] <= -1 && player->kartstuff[k_turndir] != -1))
- && player->kartstuff[k_driftcharge] < dsone
- && onground)
- {
- player->kartstuff[k_driftcharge] = 0;
- }
- else if ((player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5)
- // || (player->kartstuff[k_drift] >= 1 && player->kartstuff[k_turndir] != 1) || (player->kartstuff[k_drift] <= -1 && player->kartstuff[k_turndir] != -1))
- && (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo)
- && onground)
- {
- if (player->kartstuff[k_driftboost] < 20)
- player->kartstuff[k_driftboost] = 20;
- S_StartSound(player->mo, sfx_s23c);
- //K_SpawnDashDustRelease(player);
- player->kartstuff[k_driftcharge] = 0;
- }
- else if ((player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5)
- // || (player->kartstuff[k_drift] >= 1 && player->kartstuff[k_turndir] != 1) || (player->kartstuff[k_drift] <= -1 && player->kartstuff[k_turndir] != -1))
- && player->kartstuff[k_driftcharge] < dsthree
- && onground)
- {
- if (player->kartstuff[k_driftboost] < 50)
- player->kartstuff[k_driftboost] = 50;
- S_StartSound(player->mo, sfx_s23c);
- //K_SpawnDashDustRelease(player);
- player->kartstuff[k_driftcharge] = 0;
- }
- else if ((player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5)
- // || (player->kartstuff[k_drift] >= 1 && player->kartstuff[k_turndir] != 1) || (player->kartstuff[k_drift] <= -1 && player->kartstuff[k_turndir] != -1))
- && player->kartstuff[k_driftcharge] >= dsthree
- && onground)
- {
- if (player->kartstuff[k_driftboost] < 125)
- player->kartstuff[k_driftboost] = 125;
- S_StartSound(player->mo, sfx_s23c);
- //K_SpawnDashDustRelease(player);
- player->kartstuff[k_driftcharge] = 0;
- }
-
- // Drifting: left or right?
- if ((player->cmd.driftturn > 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1
- && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != 1)
- {
- // Starting left drift
- player->kartstuff[k_drift] = 1;
- player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0;
- }
- else if ((player->cmd.driftturn < 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1
- && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != -1)
- {
- // Starting right drift
- player->kartstuff[k_drift] = -1;
- player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0;
- }
- else if (player->kartstuff[k_jmp] == 0) // || player->kartstuff[k_turndir] == 0)
- {
- // drift is not being performed so if we're just finishing set driftend and decrement counters
- if (player->kartstuff[k_drift] > 0)
- {
- player->kartstuff[k_drift]--;
- player->kartstuff[k_driftend] = 1;
- }
- else if (player->kartstuff[k_drift] < 0)
- {
- player->kartstuff[k_drift]++;
- player->kartstuff[k_driftend] = 1;
- }
- else
- player->kartstuff[k_driftend] = 0;
- }
-
-
-
- // Incease/decrease the drift value to continue drifting in that direction
- if (player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_jmp] == 1 && onground && player->kartstuff[k_drift] != 0)
- {
- fixed_t driftadditive = 24;
-
- if (player->kartstuff[k_drift] >= 1) // Drifting to the left
- {
- player->kartstuff[k_drift]++;
- if (player->kartstuff[k_drift] > 5)
- player->kartstuff[k_drift] = 5;
-
- if (player->cmd.driftturn > 0) // Inward
- driftadditive += abs(player->cmd.driftturn)/100;
- if (player->cmd.driftturn < 0) // Outward
- driftadditive -= abs(player->cmd.driftturn)/75;
- }
- else if (player->kartstuff[k_drift] <= -1) // Drifting to the right
- {
- player->kartstuff[k_drift]--;
- if (player->kartstuff[k_drift] < -5)
- player->kartstuff[k_drift] = -5;
-
- if (player->cmd.driftturn < 0) // Inward
- driftadditive += abs(player->cmd.driftturn)/100;
- if (player->cmd.driftturn > 0) // Outward
- driftadditive -= abs(player->cmd.driftturn)/75;
- }
-
- // Disable drift-sparks until you're going fast enough
- if (player->kartstuff[k_getsparks] == 0)
- driftadditive = 0;
- if (player->speed > minspeed*2)
- player->kartstuff[k_getsparks] = 1;
-
- // This spawns the drift sparks
- if (player->kartstuff[k_driftcharge] + driftadditive >= dsone)
- K_SpawnDriftSparks(player);
-
- // Sound whenever you get a different tier of sparks
- if (P_IsLocalPlayer(player) // UGHGHGH...
- && ((player->kartstuff[k_driftcharge] < dsone && player->kartstuff[k_driftcharge]+driftadditive >= dsone)
- || (player->kartstuff[k_driftcharge] < dstwo && player->kartstuff[k_driftcharge]+driftadditive >= dstwo)
- || (player->kartstuff[k_driftcharge] < dsthree && player->kartstuff[k_driftcharge]+driftadditive >= dsthree)))
- {
- //S_StartSound(player->mo, sfx_s3ka2);
- S_StartSoundAtVolume(player->mo, sfx_s3ka2, 192); // Ugh...
- }
-
- player->kartstuff[k_driftcharge] += driftadditive;
- player->kartstuff[k_driftend] = 0;
- }
-
- // Stop drifting
- if (player->kartstuff[k_spinouttimer] > 0 || player->speed < minspeed)
- {
- player->kartstuff[k_drift] = player->kartstuff[k_driftcharge] = 0;
- player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0;
- player->kartstuff[k_getsparks] = 0;
- }
-
- if ((!player->kartstuff[k_sneakertimer])
- || (!player->cmd.driftturn)
- || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0))
- {
- if (!player->kartstuff[k_drift])
- player->kartstuff[k_aizdriftstrat] = 0;
- else
- player->kartstuff[k_aizdriftstrat] = ((player->kartstuff[k_drift] > 0) ? 1 : -1);
- }
- else if (player->kartstuff[k_aizdriftstrat] && !player->kartstuff[k_drift])
- K_SpawnAIZDust(player);
-
- if (player->kartstuff[k_drift]
- && ((player->cmd.buttons & BT_BRAKE)
- || !(player->cmd.buttons & BT_ACCELERATE))
- && P_IsObjectOnGround(player->mo))
- {
- if (!player->kartstuff[k_brakedrift])
- K_SpawnBrakeDriftSparks(player);
- player->kartstuff[k_brakedrift] = 1;
- }
- else
- player->kartstuff[k_brakedrift] = 0;
-}
-//
-// K_KartUpdatePosition
-//
-void K_KartUpdatePosition(player_t *player)
-{
- fixed_t position = 1;
- fixed_t oldposition = player->kartstuff[k_position];
- fixed_t i, ppcd, pncd, ipcd, incd;
- fixed_t pmo, imo;
- mobj_t *mo;
-
- if (player->spectator || !player->mo)
- return;
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator || !players[i].mo)
- continue;
-
- if (G_RaceGametype())
- {
- if ((((players[i].starpostnum) + (numstarposts + 1) * players[i].laps) >
- ((player->starpostnum) + (numstarposts + 1) * player->laps)))
- position++;
- else if (((players[i].starpostnum) + (numstarposts+1)*players[i].laps) ==
- ((player->starpostnum) + (numstarposts+1)*player->laps))
- {
- ppcd = pncd = ipcd = incd = 0;
-
- player->kartstuff[k_prevcheck] = players[i].kartstuff[k_prevcheck] = 0;
- player->kartstuff[k_nextcheck] = players[i].kartstuff[k_nextcheck] = 0;
-
- // This checks every thing on the map, and looks for MT_BOSS3WAYPOINT (the thing we're using for checkpoint wp's, for now)
- for (mo = waypointcap; mo != NULL; mo = mo->tracer)
- {
- pmo = P_AproxDistance(P_AproxDistance( mo->x - player->mo->x,
- mo->y - player->mo->y),
- mo->z - player->mo->z) / FRACUNIT;
- imo = P_AproxDistance(P_AproxDistance( mo->x - players[i].mo->x,
- mo->y - players[i].mo->y),
- mo->z - players[i].mo->z) / FRACUNIT;
-
- if (mo->health == player->starpostnum && (!mo->movecount || mo->movecount == player->laps+1))
- {
- player->kartstuff[k_prevcheck] += pmo;
- ppcd++;
- }
- if (mo->health == (player->starpostnum + 1) && (!mo->movecount || mo->movecount == player->laps+1))
- {
- player->kartstuff[k_nextcheck] += pmo;
- pncd++;
- }
- if (mo->health == players[i].starpostnum && (!mo->movecount || mo->movecount == players[i].laps+1))
- {
- players[i].kartstuff[k_prevcheck] += imo;
- ipcd++;
- }
- if (mo->health == (players[i].starpostnum + 1) && (!mo->movecount || mo->movecount == players[i].laps+1))
- {
- players[i].kartstuff[k_nextcheck] += imo;
- incd++;
- }
- }
-
- if (ppcd > 1) player->kartstuff[k_prevcheck] /= ppcd;
- if (pncd > 1) player->kartstuff[k_nextcheck] /= pncd;
- if (ipcd > 1) players[i].kartstuff[k_prevcheck] /= ipcd;
- if (incd > 1) players[i].kartstuff[k_nextcheck] /= incd;
-
- if ((players[i].kartstuff[k_nextcheck] > 0 || player->kartstuff[k_nextcheck] > 0) && !player->exiting)
- {
- if ((players[i].kartstuff[k_nextcheck] - players[i].kartstuff[k_prevcheck]) <
- (player->kartstuff[k_nextcheck] - player->kartstuff[k_prevcheck]))
- position++;
- }
- else if (!player->exiting)
- {
- if (players[i].kartstuff[k_prevcheck] > player->kartstuff[k_prevcheck])
- position++;
- }
- else
- {
- if (players[i].starposttime < player->starposttime)
- position++;
- }
- }
- }
- else if (G_BattleGametype())
- {
- if (player->exiting) // End of match standings
- {
- if (players[i].marescore > player->marescore) // Only score matters
- position++;
- }
- else
- {
- if (players[i].kartstuff[k_bumper] == player->kartstuff[k_bumper] && players[i].marescore > player->marescore)
- position++;
- else if (players[i].kartstuff[k_bumper] > player->kartstuff[k_bumper])
- position++;
- }
- }
- }
-
- if (leveltime < starttime || oldposition == 0)
- oldposition = position;
-
- if (oldposition != position) // Changed places?
- player->kartstuff[k_positiondelay] = 10; // Position number growth
-
- player->kartstuff[k_position] = position;
-}
-
-//
-// K_StripItems
-//
-void K_StripItems(player_t *player)
-{
- player->kartstuff[k_itemtype] = KITEM_NONE;
- player->kartstuff[k_itemamount] = 0;
- player->kartstuff[k_itemheld] = 0;
-
- player->kartstuff[k_rocketsneakertimer] = 0;
-
- if (!player->kartstuff[k_itemroulette] || player->kartstuff[k_roulettetype] != 2)
- {
- player->kartstuff[k_itemroulette] = 0;
- player->kartstuff[k_roulettetype] = 0;
- }
- player->kartstuff[k_eggmanheld] = 0;
-
- player->kartstuff[k_hyudorotimer] = 0;
- player->kartstuff[k_stealingtimer] = 0;
- player->kartstuff[k_stolentimer] = 0;
-
- player->kartstuff[k_curshield] = 0;
- //player->kartstuff[k_thunderanim] = 0;
- player->kartstuff[k_bananadrag] = 0;
-
- player->kartstuff[k_sadtimer] = 0;
-
- K_UpdateHnextList(player, true);
-}
-
-void K_StripOther(player_t *player)
-{
- player->kartstuff[k_itemroulette] = 0;
- player->kartstuff[k_roulettetype] = 0;
-
- player->kartstuff[k_invincibilitytimer] = 0;
- K_RemoveGrowShrink(player);
-
- if (player->kartstuff[k_eggmanexplode])
- {
- player->kartstuff[k_eggmanexplode] = 0;
- player->kartstuff[k_eggmanblame] = -1;
- }
-}
-
-//
-// K_MoveKartPlayer
-//
-void K_MoveKartPlayer(player_t *player, boolean onground)
-{
- ticcmd_t *cmd = &player->cmd;
- boolean ATTACK_IS_DOWN = ((cmd->buttons & BT_ATTACK) && !(player->pflags & PF_ATTACKDOWN));
- boolean HOLDING_ITEM = (player->kartstuff[k_itemheld] || player->kartstuff[k_eggmanheld]);
- boolean NO_HYUDORO = (player->kartstuff[k_stolentimer] == 0 && player->kartstuff[k_stealingtimer] == 0);
-
- K_KartUpdatePosition(player);
-
- if (!player->exiting)
- {
- if (player->kartstuff[k_oldposition] < player->kartstuff[k_position]) // But first, if you lost a place,
- {
- player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // then the other player taunts.
- K_RegularVoiceTimers(player); // and you can't for a bit
- }
- else if (player->kartstuff[k_oldposition] > player->kartstuff[k_position]) // Otherwise,
- {
- K_PlayOvertakeSound(player->mo); // Say "YOU'RE TOO SLOW!"
- player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // Restore the old position,
- }
- }
-
- if (player->kartstuff[k_positiondelay])
- player->kartstuff[k_positiondelay]--;
-
- if ((player->pflags & PF_ATTACKDOWN) && !(cmd->buttons & BT_ATTACK))
- player->pflags &= ~PF_ATTACKDOWN;
- else if (cmd->buttons & BT_ATTACK)
- player->pflags |= PF_ATTACKDOWN;
-
- if (player && player->mo && player->mo->health > 0 && !player->spectator && !(player->exiting || mapreset)
- && player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && player->kartstuff[k_respawn] == 0)
- {
- // First, the really specific, finicky items that function without the item being directly in your item slot.
- // Karma item dropping
- if (ATTACK_IS_DOWN && player->kartstuff[k_comebackmode] && !player->kartstuff[k_comebacktimer])
- {
- mobj_t *newitem;
-
- if (player->kartstuff[k_comebackmode] == 1)
- {
- newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RANDOMITEM);
- newitem->threshold = 69; // selected "randomly".
- }
- else
- {
- newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM);
- if (player->kartstuff[k_eggmanblame] >= 0
- && player->kartstuff[k_eggmanblame] < MAXPLAYERS
- && playeringame[player->kartstuff[k_eggmanblame]]
- && !players[player->kartstuff[k_eggmanblame]].spectator
- && players[player->kartstuff[k_eggmanblame]].mo)
- P_SetTarget(&newitem->target, players[player->kartstuff[k_eggmanblame]].mo);
- player->kartstuff[k_eggmanblame] = -1;
- }
-
- newitem->flags2 = (player->mo->flags2 & MF2_OBJECTFLIP);
- newitem->fuse = 15*TICRATE; // selected randomly.
-
- player->kartstuff[k_comebackmode] = 0;
- player->kartstuff[k_comebacktimer] = comebacktime;
- S_StartSound(player->mo, sfx_s254);
- }
- // Eggman Monitor exploding
- else if (player->kartstuff[k_eggmanexplode])
- {
- if (ATTACK_IS_DOWN && player->kartstuff[k_eggmanexplode] <= 3*TICRATE && player->kartstuff[k_eggmanexplode] > 1)
- player->kartstuff[k_eggmanexplode] = 1;
- }
- // Eggman Monitor throwing
- else if (ATTACK_IS_DOWN && player->kartstuff[k_eggmanheld])
- {
- K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0);
- K_PlayAttackTaunt(player->mo);
- player->kartstuff[k_eggmanheld] = 0;
- K_UpdateHnextList(player, true);
- }
- // Rocket Sneaker
- else if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO
- && player->kartstuff[k_rocketsneakertimer] > 1)
- {
- K_DoSneaker(player, 2);
- K_PlayBoostTaunt(player->mo);
- player->kartstuff[k_rocketsneakertimer] -= 2*TICRATE;
- if (player->kartstuff[k_rocketsneakertimer] < 1)
- player->kartstuff[k_rocketsneakertimer] = 1;
- }
- else if (player->kartstuff[k_itemamount] <= 0)
- {
- player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0;
- }
- else
- {
- switch (player->kartstuff[k_itemtype])
- {
- case KITEM_SNEAKER:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO)
- {
- K_DoSneaker(player, 1);
- K_PlayBoostTaunt(player->mo);
- player->kartstuff[k_itemamount]--;
- }
- break;
- case KITEM_ROCKETSNEAKER:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO
- && player->kartstuff[k_rocketsneakertimer] == 0)
- {
- INT32 moloop;
- mobj_t *mo = NULL;
- mobj_t *prev = player->mo;
-
- K_PlayBoostTaunt(player->mo);
- //player->kartstuff[k_itemheld] = 1;
- S_StartSound(player->mo, sfx_s3k3a);
-
- //K_DoSneaker(player, 2);
-
- player->kartstuff[k_rocketsneakertimer] = (itemtime*3);
- player->kartstuff[k_itemamount]--;
- K_UpdateHnextList(player, true);
-
- for (moloop = 0; moloop < 2; moloop++)
- {
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ROCKETSNEAKER);
- mo->flags |= MF_NOCLIPTHING;
- mo->angle = player->mo->angle;
- mo->threshold = 10;
- mo->movecount = moloop%2;
- mo->movedir = mo->lastlook = moloop+1;
- P_SetTarget(&mo->target, player->mo);
- P_SetTarget(&mo->hprev, prev);
- P_SetTarget(&prev->hnext, mo);
- prev = mo;
- }
- }
- break;
- case KITEM_INVINCIBILITY:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) // Doesn't hold your item slot hostage normally, so you're free to waste it if you have multiple
- {
- if (!player->kartstuff[k_invincibilitytimer])
- {
- mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INVULNFLASH);
- P_SetTarget(&overlay->target, player->mo);
- overlay->destscale = player->mo->scale;
- P_SetScale(overlay, player->mo->scale);
- }
- player->kartstuff[k_invincibilitytimer] = itemtime+(2*TICRATE); // 10 seconds
- P_RestoreMusic(player);
- if (!P_IsLocalPlayer(player))
- S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmi : sfx_kinvnc));
- K_PlayPowerGloatSound(player->mo);
- player->kartstuff[k_itemamount]--;
- }
- break;
- case KITEM_BANANA:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- INT32 moloop;
- mobj_t *mo;
- mobj_t *prev = player->mo;
-
- //K_PlayAttackTaunt(player->mo);
- player->kartstuff[k_itemheld] = 1;
- S_StartSound(player->mo, sfx_s254);
-
- for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++)
- {
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BANANA_SHIELD);
- if (!mo)
- {
- player->kartstuff[k_itemamount] = moloop;
- break;
- }
- mo->flags |= MF_NOCLIPTHING;
- mo->threshold = 10;
- mo->movecount = player->kartstuff[k_itemamount];
- mo->movedir = moloop+1;
- P_SetTarget(&mo->target, player->mo);
- P_SetTarget(&mo->hprev, prev);
- P_SetTarget(&prev->hnext, mo);
- prev = mo;
- }
- }
- else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Banana x3 thrown
- {
- K_ThrowKartItem(player, false, MT_BANANA, -1, 0);
- K_PlayAttackTaunt(player->mo);
- player->kartstuff[k_itemamount]--;
- K_UpdateHnextList(player, false);
- }
- break;
- case KITEM_EGGMAN:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- mobj_t *mo;
- player->kartstuff[k_itemamount]--;
- player->kartstuff[k_eggmanheld] = 1;
- S_StartSound(player->mo, sfx_s254);
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD);
- if (mo)
- {
- mo->flags |= MF_NOCLIPTHING;
- mo->threshold = 10;
- mo->movecount = 1;
- mo->movedir = 1;
- P_SetTarget(&mo->target, player->mo);
- P_SetTarget(&player->mo->hnext, mo);
- }
- }
- break;
- case KITEM_ORBINAUT:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- angle_t newangle;
- INT32 moloop;
- mobj_t *mo = NULL;
- mobj_t *prev = player->mo;
-
- //K_PlayAttackTaunt(player->mo);
- player->kartstuff[k_itemheld] = 1;
- S_StartSound(player->mo, sfx_s3k3a);
-
- for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++)
- {
- newangle = FixedAngle(((360/player->kartstuff[k_itemamount])*moloop)*FRACUNIT) + ANGLE_90;
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ORBINAUT_SHIELD);
- if (!mo)
- {
- player->kartstuff[k_itemamount] = moloop;
- break;
- }
- mo->flags |= MF_NOCLIPTHING;
- mo->angle = newangle;
- mo->threshold = 10;
- mo->movecount = player->kartstuff[k_itemamount];
- mo->movedir = mo->lastlook = moloop+1;
- mo->color = player->skincolor;
- P_SetTarget(&mo->target, player->mo);
- P_SetTarget(&mo->hprev, prev);
- P_SetTarget(&prev->hnext, mo);
- prev = mo;
- }
- }
- else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Orbinaut x3 thrown
- {
- K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0);
- K_PlayAttackTaunt(player->mo);
- player->kartstuff[k_itemamount]--;
- K_UpdateHnextList(player, false);
- }
- break;
- case KITEM_JAWZ:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- angle_t newangle;
- INT32 moloop;
- mobj_t *mo = NULL;
- mobj_t *prev = player->mo;
-
- //K_PlayAttackTaunt(player->mo);
- player->kartstuff[k_itemheld] = 1;
- S_StartSound(player->mo, sfx_s3k3a);
-
- for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++)
- {
- newangle = FixedAngle(((360/player->kartstuff[k_itemamount])*moloop)*FRACUNIT) + ANGLE_90;
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_JAWZ_SHIELD);
- if (!mo)
- {
- player->kartstuff[k_itemamount] = moloop;
- break;
- }
- mo->flags |= MF_NOCLIPTHING;
- mo->angle = newangle;
- mo->threshold = 10;
- mo->movecount = player->kartstuff[k_itemamount];
- mo->movedir = mo->lastlook = moloop+1;
- P_SetTarget(&mo->target, player->mo);
- P_SetTarget(&mo->hprev, prev);
- P_SetTarget(&prev->hnext, mo);
- prev = mo;
- }
- }
- else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Jawz thrown
- {
- if (player->kartstuff[k_throwdir] == 1 || player->kartstuff[k_throwdir] == 0)
- K_ThrowKartItem(player, true, MT_JAWZ, 1, 0);
- else if (player->kartstuff[k_throwdir] == -1) // Throwing backward gives you a dud that doesn't home in
- K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0);
- K_PlayAttackTaunt(player->mo);
- player->kartstuff[k_itemamount]--;
- K_UpdateHnextList(player, false);
- }
- break;
- case KITEM_MINE:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- mobj_t *mo;
- player->kartstuff[k_itemheld] = 1;
- S_StartSound(player->mo, sfx_s254);
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SSMINE_SHIELD);
- if (mo)
- {
- mo->flags |= MF_NOCLIPTHING;
- mo->threshold = 10;
- mo->movecount = 1;
- mo->movedir = 1;
- P_SetTarget(&mo->target, player->mo);
- P_SetTarget(&player->mo->hnext, mo);
- }
- }
- else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld])
- {
- K_ThrowKartItem(player, false, MT_SSMINE, 1, 1);
- K_PlayAttackTaunt(player->mo);
- player->kartstuff[k_itemamount]--;
- player->kartstuff[k_itemheld] = 0;
- K_UpdateHnextList(player, true);
- }
- break;
- case KITEM_BALLHOG:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- player->kartstuff[k_itemamount]--;
- K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0);
- K_PlayAttackTaunt(player->mo);
- }
- break;
- case KITEM_SPB:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- player->kartstuff[k_itemamount]--;
- K_ThrowKartItem(player, true, MT_SPB, 1, 0);
- K_PlayAttackTaunt(player->mo);
- }
- break;
- case KITEM_GROW:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO
- && player->kartstuff[k_growshrinktimer] <= 0) // Grow holds the item box hostage
- {
- K_PlayPowerGloatSound(player->mo);
- player->mo->scalespeed = mapobjectscale/TICRATE;
- player->mo->destscale = (3*mapobjectscale)/2;
- if (cv_kartdebugshrink.value && !modeattacking && !player->bot)
- player->mo->destscale = (6*player->mo->destscale)/8;
- player->kartstuff[k_growshrinktimer] = itemtime+(4*TICRATE); // 12 seconds
- P_RestoreMusic(player);
- if (!P_IsLocalPlayer(player))
- S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow));
- S_StartSound(player->mo, sfx_kc5a);
- player->kartstuff[k_itemamount]--;
- }
- break;
- case KITEM_SHRINK:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- K_DoShrink(player);
- player->kartstuff[k_itemamount]--;
- K_PlayPowerGloatSound(player->mo);
- }
- break;
- case KITEM_THUNDERSHIELD:
- if (player->kartstuff[k_curshield] != 1)
- {
- mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THUNDERSHIELD);
- P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2));
- P_SetTarget(&shield->target, player->mo);
- S_StartSound(shield, sfx_s3k41);
- player->kartstuff[k_curshield] = 1;
- }
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- K_DoThunderShield(player);
- player->kartstuff[k_itemamount]--;
- K_PlayAttackTaunt(player->mo);
- }
- break;
- case KITEM_HYUDORO:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- player->kartstuff[k_itemamount]--;
- K_DoHyudoroSteal(player); // yes. yes they do.
- }
- break;
- case KITEM_POGOSPRING:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO
- && !player->kartstuff[k_pogospring])
- {
- K_PlayBoostTaunt(player->mo);
- K_DoPogoSpring(player->mo, 32<kartstuff[k_pogospring] = 1;
- player->kartstuff[k_itemamount]--;
- }
- break;
- case KITEM_KITCHENSINK:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
- {
- mobj_t *mo;
- player->kartstuff[k_itemheld] = 1;
- S_StartSound(player->mo, sfx_s254);
- mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SINK_SHIELD);
- if (mo)
- {
- mo->flags |= MF_NOCLIPTHING;
- mo->threshold = 10;
- mo->movecount = 1;
- mo->movedir = 1;
- P_SetTarget(&mo->target, player->mo);
- P_SetTarget(&player->mo->hnext, mo);
- }
- }
- else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Sink thrown
- {
- K_ThrowKartItem(player, false, MT_SINK, 1, 2);
- K_PlayAttackTaunt(player->mo);
- player->kartstuff[k_itemamount]--;
- player->kartstuff[k_itemheld] = 0;
- K_UpdateHnextList(player, true);
- }
- break;
- case KITEM_SAD:
- if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO
- && !player->kartstuff[k_sadtimer])
- {
- player->kartstuff[k_sadtimer] = stealtime;
- player->kartstuff[k_itemamount]--;
- }
- break;
- default:
- break;
- }
- }
-
- // No more!
- if (!player->kartstuff[k_itemamount])
- {
- player->kartstuff[k_itemheld] = 0;
- player->kartstuff[k_itemtype] = KITEM_NONE;
- }
-
- if (player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD)
- player->kartstuff[k_curshield] = 0;
-
- if (player->kartstuff[k_itemtype] == KITEM_SPB
- || player->kartstuff[k_itemtype] == KITEM_SHRINK
- || player->kartstuff[k_growshrinktimer] < 0)
- indirectitemcooldown = 20*TICRATE;
-
- if (player->kartstuff[k_hyudorotimer] > 0)
- {
- if (splitscreen)
- {
- if (leveltime & 1)
- player->mo->flags2 |= MF2_DONTDRAW;
- else
- player->mo->flags2 &= ~MF2_DONTDRAW;
-
- if (player->kartstuff[k_hyudorotimer] >= (1*TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyudorotime-(1*TICRATE/2))
- {
- if (player == &players[secondarydisplayplayer])
- player->mo->eflags |= MFE_DRAWONLYFORP2;
- else if (player == &players[thirddisplayplayer] && splitscreen > 1)
- player->mo->eflags |= MFE_DRAWONLYFORP3;
- else if (player == &players[fourthdisplayplayer] && splitscreen > 2)
- player->mo->eflags |= MFE_DRAWONLYFORP4;
- else if (player == &players[consoleplayer])
- player->mo->eflags |= MFE_DRAWONLYFORP1;
- else
- player->mo->flags2 |= MF2_DONTDRAW;
- }
- else
- player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4);
- }
- else
- {
- if (player == &players[displayplayer]
- || (player != &players[displayplayer] && (player->kartstuff[k_hyudorotimer] < (1*TICRATE/2) || player->kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2))))
- {
- if (leveltime & 1)
- player->mo->flags2 |= MF2_DONTDRAW;
- else
- player->mo->flags2 &= ~MF2_DONTDRAW;
- }
- else
- player->mo->flags2 |= MF2_DONTDRAW;
- }
-
- player->powers[pw_flashing] = player->kartstuff[k_hyudorotimer]; // We'll do this for now, let's people know about the invisible people through subtle hints
- }
- else if (player->kartstuff[k_hyudorotimer] == 0)
- {
- player->mo->flags2 &= ~MF2_DONTDRAW;
- player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4);
- }
-
- if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) // dead in match? you da bomb
- {
- K_DropItems(player); //K_StripItems(player);
- K_StripOther(player);
- player->mo->flags2 |= MF2_SHADOW;
- player->powers[pw_flashing] = player->kartstuff[k_comebacktimer];
- }
- else if (G_RaceGametype() || player->kartstuff[k_bumper] > 0)
- {
- player->mo->flags2 &= ~MF2_SHADOW;
- }
- }
-
- // Friction
- if (player->speed > 0 && cmd->forwardmove == 0 && player->mo->friction == 59392)
- player->mo->friction += 4608;
- if (player->speed > 0 && cmd->forwardmove < 0 && player->mo->friction == 59392)
- player->mo->friction += 1608;
- if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0)
- {
- player->mo->friction += 1228;
-
- if (player->mo->friction > FRACUNIT)
- player->mo->friction = FRACUNIT;
- if (player->mo->friction < 0)
- player->mo->friction = 0;
-
- player->mo->movefactor = FixedDiv(ORIG_FRICTION, player->mo->friction);
-
- if (player->mo->movefactor < FRACUNIT)
- player->mo->movefactor = 19*player->mo->movefactor - 18*FRACUNIT;
- else
- player->mo->movefactor = FRACUNIT; //player->mo->movefactor = ((player->mo->friction - 0xDB34)*(0xA))/0x80;
-
- if (player->mo->movefactor < 32)
- player->mo->movefactor = 32;
- }
- if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow])
- {
- player->mo->friction -= FixedMul(1228, player->kartstuff[k_offroad]);
- if (player->kartstuff[k_wipeoutslow] == 1)
- player->mo->friction -= 4912;
- }
-
- K_KartDrift(player, onground);
-
- // Quick Turning
- // You can't turn your kart when you're not moving.
- // So now it's time to burn some rubber!
- if (player->speed < 2 && leveltime > starttime && cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE && cmd->driftturn != 0)
- {
- if (leveltime % 8 == 0)
- S_StartSound(player->mo, sfx_s224);
- }
-
- // Squishing
- // If a Grow player or a sector crushes you, get flattened instead of being killed.
-
- if (player->kartstuff[k_squishedtimer] <= 0)
- {
- player->mo->flags &= ~MF_NOCLIP;
- }
- else
- {
- player->mo->flags |= MF_NOCLIP;
- player->mo->momx = 0;
- player->mo->momy = 0;
- }
-
- // Play the starting countdown sounds
- if (player == &players[displayplayer]) // Don't play louder in splitscreen
- {
- if ((leveltime == starttime-(3*TICRATE)) || (leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE))
- S_StartSound(NULL, sfx_s3ka7);
- if (leveltime == starttime)
- {
- S_StartSound(NULL, sfx_s3kad);
- S_StopMusic(); // The GO! sound stops the level start ambience
- }
- }
-
- // Start charging once you're given the opportunity.
- if (leveltime >= starttime-(2*TICRATE) && leveltime <= starttime)
- {
- if (cmd->buttons & BT_ACCELERATE)
- player->kartstuff[k_boostcharge]++;
- else
- player->kartstuff[k_boostcharge] = 0;
- }
-
- // Increase your size while charging your engine.
- if (leveltime < starttime+10)
- {
- player->mo->scalespeed = mapobjectscale/12;
- player->mo->destscale = mapobjectscale + (player->kartstuff[k_boostcharge]*131);
- if (cv_kartdebugshrink.value && !modeattacking && !player->bot)
- player->mo->destscale = (6*player->mo->destscale)/8;
- }
-
- // Determine the outcome of your charge.
- if (leveltime > starttime && player->kartstuff[k_boostcharge])
- {
- // Not even trying?
- if (player->kartstuff[k_boostcharge] < 35)
- {
- if (player->kartstuff[k_boostcharge] > 17)
- S_StartSound(player->mo, sfx_cdfm00); // chosen instead of a conventional skid because it's more engine-like
- }
- // Get an instant boost!
- else if (player->kartstuff[k_boostcharge] <= 50)
- {
- player->kartstuff[k_startboost] = (50-player->kartstuff[k_boostcharge])+20;
-
- if (player->kartstuff[k_boostcharge] <= 36)
- {
- player->kartstuff[k_startboost] = 0;
- K_DoSneaker(player, 0);
- player->kartstuff[k_sneakertimer] = 70; // PERFECT BOOST!!
-
- if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) // Let everyone hear this one
- S_StartSound(player->mo, sfx_s25f);
- }
- else
- {
- K_SpawnDashDustRelease(player); // already handled for perfect boosts by K_DoSneaker
- if ((!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) && P_IsLocalPlayer(player))
- {
- if (player->kartstuff[k_boostcharge] <= 40)
- S_StartSound(player->mo, sfx_cdfm01); // You were almost there!
- else
- S_StartSound(player->mo, sfx_s23c); // Nope, better luck next time.
- }
- }
- }
- // You overcharged your engine? Those things are expensive!!!
- else if (player->kartstuff[k_boostcharge] > 50)
- {
- player->powers[pw_nocontrol] = 40;
- //S_StartSound(player->mo, sfx_kc34);
- S_StartSound(player->mo, sfx_s3k83);
- player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse
- }
-
- player->kartstuff[k_boostcharge] = 0;
- }
-}
-
-void K_CalculateBattleWanted(void)
-{
- UINT8 numingame = 0, numplaying = 0, numwanted = 0;
- SINT8 bestbumperplayer = -1, bestbumper = -1;
- SINT8 camppos[MAXPLAYERS]; // who is the biggest camper
- UINT8 ties = 0, nextcamppos = 0;
- boolean setbumper = false;
- UINT8 i, j;
-
- if (!G_BattleGametype())
- {
- for (i = 0; i < 4; i++)
- battlewanted[i] = -1;
- return;
- }
-
- wantedcalcdelay = wantedfrequency;
-
- for (i = 0; i < MAXPLAYERS; i++)
- camppos[i] = -1; // initialize
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- UINT8 position = 1;
-
- if (!playeringame[i] || players[i].spectator) // Not playing
- continue;
-
- if (players[i].exiting) // We're done, don't calculate.
- return;
-
- numplaying++;
-
- if (players[i].kartstuff[k_bumper] <= 0) // Not alive, so don't do anything else
- continue;
-
- numingame++;
-
- if (bestbumper == -1 || players[i].kartstuff[k_bumper] > bestbumper)
- {
- bestbumper = players[i].kartstuff[k_bumper];
- bestbumperplayer = i;
- }
- else if (players[i].kartstuff[k_bumper] == bestbumper)
- bestbumperplayer = -1; // Tie, no one has best bumper.
-
- for (j = 0; j < MAXPLAYERS; j++)
- {
- if (!playeringame[j] || players[j].spectator)
- continue;
- if (players[j].kartstuff[k_bumper] <= 0)
- continue;
- if (j == i)
- continue;
- if (players[j].kartstuff[k_wanted] == players[i].kartstuff[k_wanted] && players[j].marescore > players[i].marescore)
- position++;
- else if (players[j].kartstuff[k_wanted] > players[i].kartstuff[k_wanted])
- position++;
- }
-
- position--; // Make zero based
-
- while (camppos[position] != -1) // Port priority!
- position++;
-
- camppos[position] = i;
- }
-
- if (numplaying <= 2 || (numingame <= 2 && bestbumper == 1)) // In 1v1s then there's no need for WANTED. In bigger netgames, don't show anyone as WANTED when they're equally matched.
- numwanted = 0;
- else
- numwanted = min(4, 1 + ((numingame-2) / 4));
-
- for (i = 0; i < 4; i++)
- {
- if (i+1 > numwanted) // Not enough players for this slot to be wanted!
- battlewanted[i] = -1;
- else if (bestbumperplayer != -1 && !setbumper) // If there's a player who has an untied bumper lead over everyone else, they are the first to be wanted.
- {
- battlewanted[i] = bestbumperplayer;
- setbumper = true; // Don't set twice
- }
- else
- {
- // Don't accidentally set the same player, if the bestbumperplayer is also a huge camper.
- while (bestbumperplayer != -1 && camppos[nextcamppos] != -1
- && bestbumperplayer == camppos[nextcamppos])
- nextcamppos++;
-
- // Do not add *any* more people if there's too many times that are tied with others.
- // This could theoretically happen very easily if people don't hit each other for a while after the start of a match.
- // (I will be sincerely impressed if more than 2 people tie after people start hitting each other though)
-
- if (camppos[nextcamppos] == -1 // Out of entries
- || ties >= (numwanted-i)) // Already counted ties
- {
- battlewanted[i] = -1;
- continue;
- }
-
- if (ties < (numwanted-i))
- {
- ties = 0; // Reset
- for (j = 0; j < 2; j++)
- {
- if (camppos[nextcamppos+(j+1)] == -1) // Nothing beyond, cancel
- break;
- if (players[camppos[nextcamppos]].kartstuff[k_wanted] == players[camppos[nextcamppos+(j+1)]].kartstuff[k_wanted])
- ties++;
- }
- }
-
- if (ties < (numwanted-i)) // Is it still low enough after counting?
- {
- battlewanted[i] = camppos[nextcamppos];
- nextcamppos++;
- }
- else
- battlewanted[i] = -1;
- }
- }
-}
-
-void K_CheckBumpers(void)
-{
- UINT8 i;
- UINT8 numingame = 0;
- SINT8 winnernum = -1;
- INT32 winnerscoreadd = 0;
-
- if (!multiplayer)
- return;
-
- if (!G_BattleGametype())
- return;
-
- if (gameaction == ga_completed)
- return;
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator) // not even in-game
- continue;
-
- if (players[i].exiting) // we're already exiting! stop!
- return;
-
- numingame++;
- winnerscoreadd += players[i].marescore;
-
- if (players[i].kartstuff[k_bumper] <= 0) // if you don't have any bumpers, you're probably not a winner
- continue;
- else if (winnernum > -1) // TWO winners? that's dumb :V
- return;
-
- winnernum = i;
- winnerscoreadd -= players[i].marescore;
- }
-
- if (numingame <= 1)
- return;
-
- if (winnernum > -1 && playeringame[winnernum])
- {
- players[winnernum].marescore += winnerscoreadd;
- CONS_Printf(M_GetText("%s recieved %d point%s for winning!\n"), player_names[winnernum], winnerscoreadd, (winnerscoreadd == 1 ? "" : "s"));
- }
-
- for (i = 0; i < MAXPLAYERS; i++) // This can't go in the earlier loop because winning adds points
- K_KartUpdatePosition(&players[i]);
-
- for (i = 0; i < MAXPLAYERS; i++) // and it can't be merged with this loop because it needs to be all updated before exiting... multi-loops suck...
- P_DoPlayerExit(&players[i]);
-}
-
-void K_CheckSpectateStatus(void)
-{
- UINT8 respawnlist[MAXPLAYERS];
- UINT8 i, numingame = 0, numjoiners = 0;
-
- if (!cv_allowteamchange.value) return;
-
- // Get the number of players in game, and the players to be de-spectated.
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i])
- continue;
-
- if (!players[i].spectator)
- {
- numingame++;
- if (gamestate != GS_LEVEL) // Allow if you're not in a level
- continue;
- if (players[i].exiting) // DON'T allow if anyone's exiting
- return;
- if (numingame < 2 || leveltime < starttime || mapreset) // Allow if the match hasn't started yet
- continue;
- if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in
- return;
- if (G_RaceGametype() && players[i].laps) // DON'T allow if the race is at 2 laps
- return;
- continue;
- }
- else if (!(players[i].pflags & PF_WANTSTOJOIN))
- continue;
-
- respawnlist[numjoiners++] = i;
- }
-
- // literally zero point in going any further if nobody is joining
- if (!numjoiners)
- return;
-
- // Reset the match if you're in an empty server
- if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (numingame < 2 && numingame+numjoiners >= 2))
- {
- S_ChangeMusicInternal("chalng", false); // COME ON
- mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD in the future
- }
-
- // Finally, we can de-spectate everyone!
- for (i = 0; i < numjoiners; i++)
- P_SpectatorJoinGame(&players[respawnlist[i]]);
-}
-
-//}
-
-//{ SRB2kart HUD Code
-
-#define NUMPOSNUMS 10
-#define NUMPOSFRAMES 7 // White, three blues, three reds
-#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple
-
-//{ Patch Definitions
-static patch_t *kp_nodraw;
-
-static patch_t *kp_timesticker;
-static patch_t *kp_timestickerwide;
-static patch_t *kp_lapsticker;
-static patch_t *kp_lapstickerwide;
-static patch_t *kp_lapstickernarrow;
-static patch_t *kp_splitlapflag;
-static patch_t *kp_bumpersticker;
-static patch_t *kp_bumperstickerwide;
-static patch_t *kp_karmasticker;
-static patch_t *kp_splitkarmabomb;
-static patch_t *kp_timeoutsticker;
-
-static patch_t *kp_startcountdown[16];
-static patch_t *kp_racefinish[6];
-
-static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES];
-static patch_t *kp_winnernum[NUMPOSFRAMES];
-
-static patch_t *kp_facenum[MAXPLAYERS+1];
-static patch_t *kp_facehighlight[8];
-
-static patch_t *kp_rankbumper;
-static patch_t *kp_tinybumper[2];
-static patch_t *kp_ranknobumpers;
-
-static patch_t *kp_battlewin;
-static patch_t *kp_battlecool;
-static patch_t *kp_battlelose;
-static patch_t *kp_battlewait;
-static patch_t *kp_battleinfo;
-static patch_t *kp_wanted;
-
-static patch_t *kp_itembg[4];
-static patch_t *kp_itemtimer[2];
-static patch_t *kp_itemmulsticker[2];
-static patch_t *kp_itemx;
-
-static patch_t *kp_sneaker[2];
-static patch_t *kp_rocketsneaker[2];
-static patch_t *kp_invincibility[13];
-static patch_t *kp_banana[2];
-static patch_t *kp_eggman[2];
-static patch_t *kp_orbinaut[5];
-static patch_t *kp_jawz[2];
-static patch_t *kp_mine[2];
-static patch_t *kp_ballhog[2];
-static patch_t *kp_selfpropelledbomb[2];
-static patch_t *kp_grow[2];
-static patch_t *kp_shrink[2];
-static patch_t *kp_thundershield[2];
-static patch_t *kp_hyudoro[2];
-static patch_t *kp_pogospring[2];
-static patch_t *kp_kitchensink[2];
-static patch_t *kp_sadface[2];
-
-static patch_t *kp_check[6];
-
-static patch_t *kp_eggnum[4];
-
-static patch_t *kp_fpview[3];
-static patch_t *kp_inputwheel[5];
-
-static patch_t *kp_challenger[25];
-
-static patch_t *kp_lapanim_lap[7];
-static patch_t *kp_lapanim_final[11];
-static patch_t *kp_lapanim_number[10][3];
-static patch_t *kp_lapanim_emblem[2];
-static patch_t *kp_lapanim_hand[3];
-
-static patch_t *kp_yougotem;
-
-void K_LoadKartHUDGraphics(void)
-{
- INT32 i, j;
- char buffer[9];
-
- // Null Stuff
- kp_nodraw = W_CachePatchName("K_TRNULL", PU_HUDGFX);
-
- // Stickers
- kp_timesticker = W_CachePatchName("K_STTIME", PU_HUDGFX);
- kp_timestickerwide = W_CachePatchName("K_STTIMW", PU_HUDGFX);
- kp_lapsticker = W_CachePatchName("K_STLAPS", PU_HUDGFX);
- kp_lapstickerwide = W_CachePatchName("K_STLAPW", PU_HUDGFX);
- kp_lapstickernarrow = W_CachePatchName("K_STLAPN", PU_HUDGFX);
- kp_splitlapflag = W_CachePatchName("K_SPTLAP", PU_HUDGFX);
- kp_bumpersticker = W_CachePatchName("K_STBALN", PU_HUDGFX);
- kp_bumperstickerwide = W_CachePatchName("K_STBALW", PU_HUDGFX);
- kp_karmasticker = W_CachePatchName("K_STKARM", PU_HUDGFX);
- kp_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX);
- kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX);
-
- // Starting countdown
- kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX);
- kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX);
- kp_startcountdown[2] = W_CachePatchName("K_CNT1A", PU_HUDGFX);
- kp_startcountdown[3] = W_CachePatchName("K_CNTGOA", PU_HUDGFX);
- kp_startcountdown[4] = W_CachePatchName("K_CNT3B", PU_HUDGFX);
- kp_startcountdown[5] = W_CachePatchName("K_CNT2B", PU_HUDGFX);
- kp_startcountdown[6] = W_CachePatchName("K_CNT1B", PU_HUDGFX);
- kp_startcountdown[7] = W_CachePatchName("K_CNTGOB", PU_HUDGFX);
- // Splitscreen
- kp_startcountdown[8] = W_CachePatchName("K_SMC3A", PU_HUDGFX);
- kp_startcountdown[9] = W_CachePatchName("K_SMC2A", PU_HUDGFX);
- kp_startcountdown[10] = W_CachePatchName("K_SMC1A", PU_HUDGFX);
- kp_startcountdown[11] = W_CachePatchName("K_SMCGOA", PU_HUDGFX);
- kp_startcountdown[12] = W_CachePatchName("K_SMC3B", PU_HUDGFX);
- kp_startcountdown[13] = W_CachePatchName("K_SMC2B", PU_HUDGFX);
- kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX);
- kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX);
-
- // Finish
- kp_racefinish[0] = W_CachePatchName("K_FINA", PU_HUDGFX);
- kp_racefinish[1] = W_CachePatchName("K_FINB", PU_HUDGFX);
- // Splitscreen
- kp_racefinish[2] = W_CachePatchName("K_SMFINA", PU_HUDGFX);
- kp_racefinish[3] = W_CachePatchName("K_SMFINB", PU_HUDGFX);
- // 2P splitscreen
- kp_racefinish[4] = W_CachePatchName("K_2PFINA", PU_HUDGFX);
- kp_racefinish[5] = W_CachePatchName("K_2PFINB", PU_HUDGFX);
-
- // Position numbers
- sprintf(buffer, "K_POSNxx");
- for (i = 0; i < NUMPOSNUMS; i++)
- {
- buffer[6] = '0'+i;
- for (j = 0; j < NUMPOSFRAMES; j++)
- {
- //sprintf(buffer, "K_POSN%d%d", i, j);
- buffer[7] = '0'+j;
- kp_positionnum[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
- }
-
- sprintf(buffer, "K_POSNWx");
- for (i = 0; i < NUMWINFRAMES; i++)
- {
- buffer[7] = '0'+i;
- kp_winnernum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- sprintf(buffer, "OPPRNKxx");
- for (i = 0; i <= MAXPLAYERS; i++)
- {
- buffer[6] = '0'+(i/10);
- buffer[7] = '0'+(i%10);
- kp_facenum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- sprintf(buffer, "K_CHILIx");
- for (i = 0; i < 8; i++)
- {
- buffer[7] = '0'+(i+1);
- kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- // Extra ranking icons
- kp_rankbumper = W_CachePatchName("K_BLNICO", PU_HUDGFX);
- kp_tinybumper[0] = W_CachePatchName("K_BLNA", PU_HUDGFX);
- kp_tinybumper[1] = W_CachePatchName("K_BLNB", PU_HUDGFX);
- kp_ranknobumpers = W_CachePatchName("K_NOBLNS", PU_HUDGFX);
-
- // Battle graphics
- kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX);
- kp_battlecool = W_CachePatchName("K_BCOOL", PU_HUDGFX);
- kp_battlelose = W_CachePatchName("K_BLOSE", PU_HUDGFX);
- kp_battlewait = W_CachePatchName("K_BWAIT", PU_HUDGFX);
- kp_battleinfo = W_CachePatchName("K_BINFO", PU_HUDGFX);
- kp_wanted = W_CachePatchName("K_WANTED", PU_HUDGFX);
-
- // Kart Item Windows
- kp_itembg[0] = W_CachePatchName("K_ITBG", PU_HUDGFX);
- kp_itembg[1] = W_CachePatchName("K_ITBGD", PU_HUDGFX);
- kp_itemtimer[0] = W_CachePatchName("K_ITIMER", PU_HUDGFX);
- kp_itemmulsticker[0] = W_CachePatchName("K_ITMUL", PU_HUDGFX);
- kp_itemx = W_CachePatchName("K_ITX", PU_HUDGFX);
-
- kp_sneaker[0] = W_CachePatchName("K_ITSHOE", PU_HUDGFX);
- kp_rocketsneaker[0] = W_CachePatchName("K_ITRSHE", PU_HUDGFX);
-
- sprintf(buffer, "K_ITINVx");
- for (i = 0; i < 7; i++)
- {
- buffer[7] = '1'+i;
- kp_invincibility[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
- kp_banana[0] = W_CachePatchName("K_ITBANA", PU_HUDGFX);
- kp_eggman[0] = W_CachePatchName("K_ITEGGM", PU_HUDGFX);
- sprintf(buffer, "K_ITORBx");
- for (i = 0; i < 4; i++)
- {
- buffer[7] = '1'+i;
- kp_orbinaut[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
- kp_jawz[0] = W_CachePatchName("K_ITJAWZ", PU_HUDGFX);
- kp_mine[0] = W_CachePatchName("K_ITMINE", PU_HUDGFX);
- kp_ballhog[0] = W_CachePatchName("K_ITBHOG", PU_HUDGFX);
- kp_selfpropelledbomb[0] = W_CachePatchName("K_ITSPB", PU_HUDGFX);
- kp_grow[0] = W_CachePatchName("K_ITGROW", PU_HUDGFX);
- kp_shrink[0] = W_CachePatchName("K_ITSHRK", PU_HUDGFX);
- kp_thundershield[0] = W_CachePatchName("K_ITTHNS", PU_HUDGFX);
- kp_hyudoro[0] = W_CachePatchName("K_ITHYUD", PU_HUDGFX);
- kp_pogospring[0] = W_CachePatchName("K_ITPOGO", PU_HUDGFX);
- kp_kitchensink[0] = W_CachePatchName("K_ITSINK", PU_HUDGFX);
- kp_sadface[0] = W_CachePatchName("K_ITSAD", PU_HUDGFX);
-
- // Splitscreen
- kp_itembg[2] = W_CachePatchName("K_ISBG", PU_HUDGFX);
- kp_itembg[3] = W_CachePatchName("K_ISBGD", PU_HUDGFX);
- kp_itemtimer[1] = W_CachePatchName("K_ISIMER", PU_HUDGFX);
- kp_itemmulsticker[1] = W_CachePatchName("K_ISMUL", PU_HUDGFX);
-
- kp_sneaker[1] = W_CachePatchName("K_ISSHOE", PU_HUDGFX);
- kp_rocketsneaker[1] = W_CachePatchName("K_ISRSHE", PU_HUDGFX);
- sprintf(buffer, "K_ISINVx");
- for (i = 0; i < 6; i++)
- {
- buffer[7] = '1'+i;
- kp_invincibility[i+7] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
- kp_banana[1] = W_CachePatchName("K_ISBANA", PU_HUDGFX);
- kp_eggman[1] = W_CachePatchName("K_ISEGGM", PU_HUDGFX);
- kp_orbinaut[4] = W_CachePatchName("K_ISORBN", PU_HUDGFX);
- kp_jawz[1] = W_CachePatchName("K_ISJAWZ", PU_HUDGFX);
- kp_mine[1] = W_CachePatchName("K_ISMINE", PU_HUDGFX);
- kp_ballhog[1] = W_CachePatchName("K_ISBHOG", PU_HUDGFX);
- kp_selfpropelledbomb[1] = W_CachePatchName("K_ISSPB", PU_HUDGFX);
- kp_grow[1] = W_CachePatchName("K_ISGROW", PU_HUDGFX);
- kp_shrink[1] = W_CachePatchName("K_ISSHRK", PU_HUDGFX);
- kp_thundershield[1] = W_CachePatchName("K_ISTHNS", PU_HUDGFX);
- kp_hyudoro[1] = W_CachePatchName("K_ISHYUD", PU_HUDGFX);
- kp_pogospring[1] = W_CachePatchName("K_ISPOGO", PU_HUDGFX);
- kp_kitchensink[1] = W_CachePatchName("K_ISSINK", PU_HUDGFX);
- kp_sadface[1] = W_CachePatchName("K_ISSAD", PU_HUDGFX);
-
- // CHECK indicators
- sprintf(buffer, "K_CHECKx");
- for (i = 0; i < 6; i++)
- {
- buffer[7] = '1'+i;
- kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- // Eggman warning numbers
- sprintf(buffer, "K_EGGNx");
- for (i = 0; i < 4; i++)
- {
- buffer[6] = '0'+i;
- kp_eggnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- // First person mode
- kp_fpview[0] = W_CachePatchName("VIEWA0", PU_HUDGFX);
- kp_fpview[1] = W_CachePatchName("VIEWB0D0", PU_HUDGFX);
- kp_fpview[2] = W_CachePatchName("VIEWC0E0", PU_HUDGFX);
-
- // Input UI Wheel
- sprintf(buffer, "K_WHEELx");
- for (i = 0; i < 5; i++)
- {
- buffer[7] = '0'+i;
- kp_inputwheel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- // HERE COMES A NEW CHALLENGER
- sprintf(buffer, "K_CHALxx");
- for (i = 0; i < 25; i++)
- {
- buffer[6] = '0'+((i+1)/10);
- buffer[7] = '0'+((i+1)%10);
- kp_challenger[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- // Lap start animation
- sprintf(buffer, "K_LAP0x");
- for (i = 0; i < 7; i++)
- {
- buffer[6] = '0'+(i+1);
- kp_lapanim_lap[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- sprintf(buffer, "K_LAPFxx");
- for (i = 0; i < 11; i++)
- {
- buffer[6] = '0'+((i+1)/10);
- buffer[7] = '0'+((i+1)%10);
- kp_lapanim_final[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- sprintf(buffer, "K_LAPNxx");
- for (i = 0; i < 10; i++)
- {
- buffer[6] = '0'+i;
- for (j = 0; j < 3; j++)
- {
- buffer[7] = '0'+(j+1);
- kp_lapanim_number[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
- }
-
- sprintf(buffer, "K_LAPE0x");
- for (i = 0; i < 2; i++)
- {
- buffer[7] = '0'+(i+1);
- kp_lapanim_emblem[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- sprintf(buffer, "K_LAPH0x");
- for (i = 0; i < 3; i++)
- {
- buffer[7] = '0'+(i+1);
- kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
- }
-
- kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX);
-}
-
-// For the item toggle menu
-const char *K_GetItemPatch(UINT8 item, boolean tiny)
-{
- switch (item)
- {
- case KITEM_SNEAKER:
- case KRITEM_TRIPLESNEAKER:
- return (tiny ? "K_ISSHOE" : "K_ITSHOE");
- case KITEM_ROCKETSNEAKER:
- return (tiny ? "K_ISRSHE" : "K_ITRSHE");
- case KITEM_INVINCIBILITY:
- return (tiny ? "K_ISINV1" : "K_ITINV1");
- case KITEM_BANANA:
- case KRITEM_TRIPLEBANANA:
- case KRITEM_TENFOLDBANANA:
- return (tiny ? "K_ISBANA" : "K_ITBANA");
- case KITEM_EGGMAN:
- return (tiny ? "K_ISEGGM" : "K_ITEGGM");
- case KITEM_ORBINAUT:
- return (tiny ? "K_ISORBN" : "K_ITORB1");
- case KITEM_JAWZ:
- case KRITEM_DUALJAWZ:
- return (tiny ? "K_ISJAWZ" : "K_ITJAWZ");
- case KITEM_MINE:
- return (tiny ? "K_ISMINE" : "K_ITMINE");
- case KITEM_BALLHOG:
- return (tiny ? "K_ISBHOG" : "K_ITBHOG");
- case KITEM_SPB:
- return (tiny ? "K_ISSPB" : "K_ITSPB");
- case KITEM_GROW:
- return (tiny ? "K_ISGROW" : "K_ITGROW");
- case KITEM_SHRINK:
- return (tiny ? "K_ISSHRK" : "K_ITSHRK");
- case KITEM_THUNDERSHIELD:
- return (tiny ? "K_ISTHNS" : "K_ITTHNS");
- case KITEM_HYUDORO:
- return (tiny ? "K_ISHYUD" : "K_ITHYUD");
- case KITEM_POGOSPRING:
- return (tiny ? "K_ISPOGO" : "K_ITPOGO");
- case KITEM_KITCHENSINK:
- return (tiny ? "K_ISSINK" : "K_ITSINK");
- case KRITEM_TRIPLEORBINAUT:
- return (tiny ? "K_ISORBN" : "K_ITORB3");
- case KRITEM_QUADORBINAUT:
- return (tiny ? "K_ISORBN" : "K_ITORB4");
- default:
- return (tiny ? "K_ISSAD" : "K_ITSAD");
- }
-}
-
-//}
-
-INT32 ITEM_X, ITEM_Y; // Item Window
-INT32 TIME_X, TIME_Y; // Time Sticker
-INT32 LAPS_X, LAPS_Y; // Lap Sticker
-INT32 SPDM_X, SPDM_Y; // Speedometer
-INT32 POSI_X, POSI_Y; // Position Number
-INT32 FACE_X, FACE_Y; // Top-four Faces
-INT32 STCD_X, STCD_Y; // Starting countdown
-INT32 CHEK_Y; // CHECK graphic
-INT32 MINI_X, MINI_Y; // Minimap
-INT32 WANT_X, WANT_Y; // Battle WANTED poster
-
-static void K_initKartHUD(void)
-{
- /*
- BASEVIDWIDTH = 320
- BASEVIDHEIGHT = 200
-
- Item window graphic is 41 x 33
-
- Time Sticker graphic is 116 x 11
- Time Font is a solid block of (8 x [12) x 14], equal to 96 x 14
- Therefore, timestamp is 116 x 14 altogether
-
- Lap Sticker is 80 x 11
- Lap flag is 22 x 20
- Lap Font is a solid block of (3 x [12) x 14], equal to 36 x 14
- Therefore, lapstamp is 80 x 20 altogether
-
- Position numbers are 43 x 53
-
- Faces are 32 x 32
- Faces draw downscaled at 16 x 16
- Therefore, the allocated space for them is 16 x 67 altogether
-
- ----
-
- ORIGINAL CZ64 SPLITSCREEN:
-
- Item window:
- if (!splitscreen) { ICONX = 139; ICONY = 20; }
- else { ICONX = BASEVIDWIDTH-315; ICONY = 60; }
-
- Time: 236, STRINGY( 12)
- Lap: BASEVIDWIDTH-304, STRINGY(BASEVIDHEIGHT-189)
-
- */
-
- // Single Screen (defaults)
- // Item Window
- ITEM_X = 5; // 5
- ITEM_Y = 5; // 5
- // Level Timer
- TIME_X = BASEVIDWIDTH - 148; // 172
- TIME_Y = 9; // 9
- // Level Laps
- LAPS_X = 9; // 9
- LAPS_Y = BASEVIDHEIGHT - 29; // 171
- // Speedometer
- SPDM_X = 9; // 9
- SPDM_Y = BASEVIDHEIGHT - 45; // 155
- // Position Number
- POSI_X = BASEVIDWIDTH - 9; // 268
- POSI_Y = BASEVIDHEIGHT - 9; // 138
- // Top-Four Faces
- FACE_X = 9; // 9
- FACE_Y = 92; // 92
- // Starting countdown
- STCD_X = BASEVIDWIDTH/2; // 9
- STCD_Y = BASEVIDHEIGHT/2; // 92
- // CHECK graphic
- CHEK_Y = BASEVIDHEIGHT; // 200
- // Minimap
- MINI_X = BASEVIDWIDTH - 50; // 270
- MINI_Y = (BASEVIDHEIGHT/2)-16; // 84
- // Battle WANTED poster
- WANT_X = BASEVIDWIDTH - 55; // 270
- WANT_Y = BASEVIDHEIGHT- 71; // 176
-
- if (splitscreen) // Splitscreen
- {
- ITEM_X = 5;
- ITEM_Y = 3;
-
- LAPS_Y = (BASEVIDHEIGHT/2)-24;
-
- POSI_Y = (BASEVIDHEIGHT/2)- 2;
-
- STCD_Y = BASEVIDHEIGHT/4;
-
- MINI_Y = (BASEVIDHEIGHT/2);
-
- WANT_X = BASEVIDWIDTH-8;
- WANT_Y = (BASEVIDHEIGHT/2)-12;
-
- if (splitscreen > 1) // 3P/4P Small Splitscreen
- {
- ITEM_X = -9;
- ITEM_Y = -8;
-
- LAPS_X = 3;
- LAPS_Y = (BASEVIDHEIGHT/2)-13;
-
- POSI_X = (BASEVIDWIDTH/2)-3;
-
- STCD_X = BASEVIDWIDTH/4;
-
- MINI_X = (3*BASEVIDWIDTH/4);
- MINI_Y = (3*BASEVIDHEIGHT/4);
-
- WANT_X = (BASEVIDWIDTH/2)-8;
-
- if (splitscreen > 2) // 4P-only
- {
- MINI_X = (BASEVIDWIDTH/2);
- MINI_Y = (BASEVIDHEIGHT/2);
- }
- }
- }
-
- if (timeinmap > 113)
- hudtrans = cv_translucenthud.value;
- else if (timeinmap > 105)
- hudtrans = ((((INT32)timeinmap) - 105)*cv_translucenthud.value)/(113-105);
- else
- hudtrans = 0;
-}
-
-INT32 K_calcSplitFlags(INT32 snapflags)
-{
- INT32 splitflags = 0;
-
- if (splitscreen == 0)
- return snapflags;
-
- if (stplyr != &players[displayplayer])
- {
- if (splitscreen == 1 && stplyr == &players[secondarydisplayplayer])
- {
- splitflags |= V_SPLITSCREEN;
- }
- else if (splitscreen > 1)
- {
- if (stplyr == &players[thirddisplayplayer] || stplyr == &players[fourthdisplayplayer])
- splitflags |= V_SPLITSCREEN;
- if (stplyr == &players[secondarydisplayplayer] || stplyr == &players[fourthdisplayplayer])
- splitflags |= V_HORZSCREEN;
- }
- }
-
- if (splitflags & V_SPLITSCREEN)
- snapflags &= ~V_SNAPTOTOP;
- else
- snapflags &= ~V_SNAPTOBOTTOM;
-
- if (splitscreen > 1)
- {
- if (splitflags & V_HORZSCREEN)
- snapflags &= ~V_SNAPTOLEFT;
- else
- snapflags &= ~V_SNAPTORIGHT;
- }
-
- return (splitflags|snapflags);
-}
-
-static void K_drawKartItem(void)
-{
- // ITEM_X = BASEVIDWIDTH-50; // 270
- // ITEM_Y = 24; // 24
-
- // Why write V_DrawScaledPatch calls over and over when they're all the same?
- // Set to 'no item' just in case.
- const UINT8 offset = ((splitscreen > 1) ? 1 : 0);
- patch_t *localpatch = kp_nodraw;
- patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]);
- patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]);
- INT32 splitflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT);
- const INT32 numberdisplaymin = ((!offset && stplyr->kartstuff[k_itemtype] == KITEM_ORBINAUT) ? 5 : 2);
- INT32 itembar = 0;
- UINT8 localcolor = SKINCOLOR_NONE;
- SINT8 colormode = TC_RAINBOW;
- UINT8 *colmap = NULL;
-
- if (stplyr->kartstuff[k_itemroulette])
- {
- if (stplyr->skincolor)
- localcolor = stplyr->skincolor;
-
- switch((stplyr->kartstuff[k_itemroulette] % (14*3)) / 3)
- {
- // Each case is handled in threes, to give three frames of in-game time to see the item on the roulette
- case 0: // Sneaker
- localpatch = kp_sneaker[offset];
- //localcolor = SKINCOLOR_RASPBERRY;
- break;
- case 1: // Banana
- localpatch = kp_banana[offset];
- //localcolor = SKINCOLOR_YELLOW;
- break;
- case 2: // Orbinaut
- localpatch = kp_orbinaut[3+offset];
- //localcolor = SKINCOLOR_STEEL;
- break;
- case 3: // Mine
- localpatch = kp_mine[offset];
- //localcolor = SKINCOLOR_JET;
- break;
- case 4: // Grow
- localpatch = kp_grow[offset];
- //localcolor = SKINCOLOR_TEAL;
- break;
- case 5: // Hyudoro
- localpatch = kp_hyudoro[offset];
- //localcolor = SKINCOLOR_STEEL;
- break;
- case 6: // Rocket Sneaker
- localpatch = kp_rocketsneaker[offset];
- //localcolor = SKINCOLOR_TANGERINE;
- break;
- case 7: // Jawz
- localpatch = kp_jawz[offset];
- //localcolor = SKINCOLOR_JAWZ;
- break;
- case 8: // Self-Propelled Bomb
- localpatch = kp_selfpropelledbomb[offset];
- //localcolor = SKINCOLOR_JET;
- break;
- case 9: // Shrink
- localpatch = kp_shrink[offset];
- //localcolor = SKINCOLOR_ORANGE;
- break;
- case 10: // Invincibility
- localpatch = localinv;
- //localcolor = SKINCOLOR_GREY;
- break;
- case 11: // Eggman Monitor
- localpatch = kp_eggman[offset];
- //localcolor = SKINCOLOR_ROSE;
- break;
- case 12: // Ballhog
- localpatch = kp_ballhog[offset];
- //localcolor = SKINCOLOR_LILAC;
- break;
- case 13: // Thunder Shield
- localpatch = kp_thundershield[offset];
- //localcolor = SKINCOLOR_CYAN;
- break;
- /*case 14: // Pogo Spring
- localpatch = kp_pogospring[offset];
- localcolor = SKINCOLOR_TANGERINE;
- break;
- case 15: // Kitchen Sink
- localpatch = kp_kitchensink[offset];
- localcolor = SKINCOLOR_STEEL;
- break;*/
- default:
- break;
- }
- }
- else
- {
- // I'm doing this a little weird and drawing mostly in reverse order
- // The only actual reason is to make sneakers line up this way in the code below
- // This shouldn't have any actual baring over how it functions
- // Hyudoro is first, because we're drawing it on top of the player's current item
- if (stplyr->kartstuff[k_stolentimer] > 0)
- {
- if (leveltime & 2)
- localpatch = kp_hyudoro[offset];
- else
- localpatch = kp_nodraw;
- }
- else if ((stplyr->kartstuff[k_stealingtimer] > 0) && (leveltime & 2))
- {
- localpatch = kp_hyudoro[offset];
- }
- else if (stplyr->kartstuff[k_eggmanexplode] > 1)
- {
- if (leveltime & 1)
- localpatch = kp_eggman[offset];
- else
- localpatch = kp_nodraw;
- }
- else if (stplyr->kartstuff[k_rocketsneakertimer] > 1)
- {
- itembar = stplyr->kartstuff[k_rocketsneakertimer];
- if (leveltime & 1)
- localpatch = kp_rocketsneaker[offset];
- else
- localpatch = kp_nodraw;
- }
- else if (stplyr->kartstuff[k_growshrinktimer] > 0)
- {
- if (leveltime & 1)
- localpatch = kp_grow[offset];
- else
- localpatch = kp_nodraw;
- }
- else if (stplyr->kartstuff[k_sadtimer] > 0)
- {
- if (leveltime & 2)
- localpatch = kp_sadface[offset];
- else
- localpatch = kp_nodraw;
- }
- else
- {
- if (stplyr->kartstuff[k_itemamount] <= 0)
- return;
-
- switch(stplyr->kartstuff[k_itemtype])
- {
- case KITEM_SNEAKER:
- localpatch = kp_sneaker[offset];
- break;
- case KITEM_ROCKETSNEAKER:
- localpatch = kp_rocketsneaker[offset];
- break;
- case KITEM_INVINCIBILITY:
- localpatch = localinv;
- localbg = kp_itembg[offset+1];
- break;
- case KITEM_BANANA:
- localpatch = kp_banana[offset];
- break;
- case KITEM_EGGMAN:
- localpatch = kp_eggman[offset];
- break;
- case KITEM_ORBINAUT:
- localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->kartstuff[k_itemamount]-1, 3))];
- break;
- case KITEM_JAWZ:
- localpatch = kp_jawz[offset];
- break;
- case KITEM_MINE:
- localpatch = kp_mine[offset];
- break;
- case KITEM_BALLHOG:
- localpatch = kp_ballhog[offset];
- break;
- case KITEM_SPB:
- localpatch = kp_selfpropelledbomb[offset];
- localbg = kp_itembg[offset+1];
- break;
- case KITEM_GROW:
- localpatch = kp_grow[offset];
- break;
- case KITEM_SHRINK:
- localpatch = kp_shrink[offset];
- break;
- case KITEM_THUNDERSHIELD:
- localpatch = kp_thundershield[offset];
- localbg = kp_itembg[offset+1];
- break;
- case KITEM_HYUDORO:
- localpatch = kp_hyudoro[offset];
- break;
- case KITEM_POGOSPRING:
- localpatch = kp_pogospring[offset];
- break;
- case KITEM_KITCHENSINK:
- localpatch = kp_kitchensink[offset];
- break;
- case KITEM_SAD:
- localpatch = kp_sadface[offset];
- break;
- default:
- return;
- }
-
- if (stplyr->kartstuff[k_itemheld] && !(leveltime & 1))
- localpatch = kp_nodraw;
- }
-
- if (stplyr->kartstuff[k_itemblink] && (leveltime & 1))
- {
- colormode = TC_BLINK;
-
- switch (stplyr->kartstuff[k_itemblinkmode])
- {
- case 2:
- localcolor = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1)));
- break;
- case 1:
- localcolor = SKINCOLOR_RED;
- break;
- default:
- localcolor = SKINCOLOR_WHITE;
- break;
- }
- }
- }
-
- if (localcolor != SKINCOLOR_NONE)
- colmap = R_GetTranslationColormap(colormode, localcolor, 0);
-
- V_DrawScaledPatch(ITEM_X, ITEM_Y, V_HUDTRANS|splitflags, localbg);
-
- // Then, the numbers:
- if (stplyr->kartstuff[k_itemamount] >= numberdisplaymin && !stplyr->kartstuff[k_itemroulette])
- {
- V_DrawScaledPatch(ITEM_X, ITEM_Y, V_HUDTRANS|splitflags, kp_itemmulsticker[offset]);
- V_DrawFixedPatch(ITEM_X<kartstuff[k_itemamount]));
- else
- {
- V_DrawScaledPatch(ITEM_X+28, ITEM_Y+41, V_HUDTRANS|splitflags, kp_itemx);
- V_DrawKartString(ITEM_X+38, ITEM_Y+36, V_HUDTRANS|splitflags, va("%d", stplyr->kartstuff[k_itemamount]));
- }
- }
- else
- V_DrawFixedPatch(ITEM_X< 1 ? 12 : 26);
- const INT32 maxl = (itemtime*3) - barlength; // timer's normal highest value
- const INT32 fill = ((itembar*barlength)/maxl);
- const INT32 length = min(barlength, fill);
- const INT32 height = (offset ? 1 : 2);
- const INT32 x = (offset ? 17 : 11), y = (offset ? 27 : 35);
-
- V_DrawScaledPatch(ITEM_X+x, ITEM_Y+y, V_HUDTRANS|splitflags, kp_itemtimer[offset]);
- // The left dark "AA" edge
- V_DrawFill(ITEM_X+x+1, ITEM_Y+y+1, (length == 2 ? 2 : 1), height, 12|splitflags);
- // The bar itself
- if (length > 2)
- {
- V_DrawFill(ITEM_X+x+length, ITEM_Y+y+1, 1, height, 12|splitflags); // the right one
- if (height == 2)
- V_DrawFill(ITEM_X+x+2, ITEM_Y+y+2, length-2, 1, 8|splitflags); // the dulled underside
- V_DrawFill(ITEM_X+x+2, ITEM_Y+y+1, length-2, 1, 120|splitflags); // the shine
- }
- }
-
- // Quick Eggman numbers
- if (stplyr->kartstuff[k_eggmanexplode] > 1 /*&& stplyr->kartstuff[k_eggmanexplode] <= 3*TICRATE*/)
- V_DrawScaledPatch(ITEM_X+17, ITEM_Y+13-offset, V_HUDTRANS|splitflags, kp_eggnum[min(3, G_TicsToSeconds(stplyr->kartstuff[k_eggmanexplode]))]);
-}
-
-void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode)
-{
- // TIME_X = BASEVIDWIDTH-124; // 196
- // TIME_Y = 6; // 6
-
- tic_t worktime;
-
- INT32 splitflags = 0;
- if (!mode)
- {
- splitflags = V_HUDTRANS|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTORIGHT);
- if (cv_timelimit.value && timelimitintics > 0)
- {
- if (drawtime >= timelimitintics)
- drawtime = 0;
- else
- drawtime = timelimitintics - drawtime;
- }
- }
-
- V_DrawScaledPatch(TX, TY, splitflags, ((mode == 2) ? kp_lapstickerwide : kp_timestickerwide));
-
- TX += 33;
-
- worktime = drawtime/(60*TICRATE);
-
- if (mode && !drawtime)
- V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--"));
- else if (worktime < 100) // 99:99:99 only
- {
- // zero minute
- if (worktime < 10)
- {
- V_DrawKartString(TX, TY+3, splitflags, va("0"));
- // minutes time 0 __ __
- V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime));
- }
- // minutes time 0 __ __
- else
- V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime));
-
- // apostrophe location _'__ __
- V_DrawKartString(TX+24, TY+3, splitflags, va("'"));
-
- worktime = (drawtime/TICRATE % 60);
-
- // zero second _ 0_ __
- if (worktime < 10)
- {
- V_DrawKartString(TX+36, TY+3, splitflags, va("0"));
- // seconds time _ _0 __
- V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime));
- }
- // zero second _ 00 __
- else
- V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime));
-
- // quotation mark location _ __"__
- V_DrawKartString(TX+60, TY+3, splitflags, va("\""));
-
- worktime = G_TicsToCentiseconds(drawtime);
-
- // zero tick _ __ 0_
- if (worktime < 10)
- {
- V_DrawKartString(TX+72, TY+3, splitflags, va("0"));
- // tics _ __ _0
- V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime));
- }
- // zero tick _ __ 00
- else
- V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime));
- }
- else if ((drawtime/TICRATE) & 1)
- V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99"));
-
- if (emblemmap && (modeattacking || (mode == 1)) && !demoplayback) // emblem time!
- {
- INT32 workx = TX + 96, worky = TY+18;
- SINT8 curemb = 0;
- patch_t *emblempic[3] = {NULL, NULL, NULL};
- UINT8 *emblemcol[3] = {NULL, NULL, NULL};
-
- emblem_t *emblem = M_GetLevelEmblems(emblemmap);
- while (emblem)
- {
- char targettext[9];
-
- switch (emblem->type)
- {
- case ET_TIME:
- {
- static boolean canplaysound = true;
- tic_t timetoreach = emblem->var;
-
- if (emblem->collected)
- {
- emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE);
- emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE);
- if (++curemb == 3)
- break;
- goto bademblem;
- }
-
- snprintf(targettext, 9, "%i'%02i\"%02i",
- G_TicsToMinutes(timetoreach, false),
- G_TicsToSeconds(timetoreach),
- G_TicsToCentiseconds(timetoreach));
-
- if (!mode)
- {
- if (stplyr->realtime > timetoreach)
- {
- splitflags = (splitflags &~ V_HUDTRANS)|V_HUDTRANSHALF;
- if (canplaysound)
- {
- S_StartSound(NULL, sfx_s3k72); //sfx_s26d); -- you STOLE fizzy lifting drinks
- canplaysound = false;
- }
- }
- else if (!canplaysound)
- canplaysound = true;
- }
-
- targettext[8] = 0;
- }
- break;
- default:
- goto bademblem;
- }
-
- V_DrawRightAlignedString(workx, worky, splitflags, targettext);
- workx -= 67;
- V_DrawSmallScaledPatch(workx + 4, worky, splitflags, W_CachePatchName("NEEDIT", PU_CACHE));
-
- break;
-
- bademblem:
- emblem = M_GetLevelEmblems(-1);
- }
-
- if (!mode)
- splitflags = (splitflags &~ V_HUDTRANSHALF)|V_HUDTRANS;
- while (curemb--)
- {
- workx -= 12;
- V_DrawSmallMappedPatch(workx + 4, worky, splitflags, emblempic[curemb], emblemcol[curemb]);
- }
- }
-}
-
-static void K_DrawKartPositionNum(INT32 num)
-{
- // POSI_X = BASEVIDWIDTH - 51; // 269
- // POSI_Y = BASEVIDHEIGHT- 64; // 136
-
- boolean win = (stplyr->exiting && num == 1);
- INT32 X = POSI_X;
- INT32 W = SHORT(kp_positionnum[0][0]->width);
- fixed_t scale = FRACUNIT;
- patch_t *localpatch = kp_positionnum[0][0];
- INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTORIGHT);
-
- if (stplyr->kartstuff[k_positiondelay] || stplyr->exiting)
- scale *= 2;
- if (splitscreen)
- scale /= 2;
-
- W = FixedMul(W<>FRACBITS;
-
- // Special case for 0
- if (!num)
- {
- V_DrawFixedPatch(X<= 0); // This function does not draw negative numbers
-
- // Draw the number
- while (num)
- {
- if (win) // 1st place winner? You get rainbows!!
- localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3];
- else if (stplyr->laps+1 >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won
- {
- // Alternate frame every three frames
- switch (leveltime % 9)
- {
- case 1: case 2: case 3:
- if (K_IsPlayerLosing(stplyr))
- localpatch = kp_positionnum[num % 10][4];
- else
- localpatch = kp_positionnum[num % 10][1];
- break;
- case 4: case 5: case 6:
- if (K_IsPlayerLosing(stplyr))
- localpatch = kp_positionnum[num % 10][5];
- else
- localpatch = kp_positionnum[num % 10][2];
- break;
- case 7: case 8: case 9:
- if (K_IsPlayerLosing(stplyr))
- localpatch = kp_positionnum[num % 10][6];
- else
- localpatch = kp_positionnum[num % 10][3];
- break;
- default:
- localpatch = kp_positionnum[num % 10][0];
- break;
- }
- }
- else
- localpatch = kp_positionnum[num % 10][0];
-
- V_DrawFixedPatch(X<= lowestposition)
- continue;
-
- rankplayer[ranklines] = i;
- lowestposition = players[i].kartstuff[k_position];
- }
-
- i = rankplayer[ranklines];
-
- completed[i] = true;
-
- if (players+i == stplyr)
- strank = ranklines;
-
- //if (ranklines == 5)
- //break; // Only draw the top 5 players -- we do this a different way now...
-
- ranklines++;
- }
-
- if (ranklines < 5)
- Y -= (9*ranklines);
- else
- Y -= (9*5);
-
- if (G_BattleGametype() || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2)
- {
- i = 0;
- if (ranklines > 5) // could be both...
- ranklines = 5;
- }
- else if (strank+3 > ranklines) // too close to the bottom?
- {
- i = ranklines - 5;
- if (i < 0)
- i = 0;
- }
- else
- {
- i = strank-2;
- ranklines = strank+3;
- }
-
- for (; i < ranklines; i++)
- {
- if (!playeringame[rankplayer[i]]) continue;
- if (players[rankplayer[i]].spectator) continue;
- if (!players[rankplayer[i]].mo) continue;
-
- bumperx = FACE_X+19;
-
- if (players[rankplayer[i]].mo->color)
- {
- colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE);
- if (players[rankplayer[i]].mo->colorized)
- colormap = R_GetTranslationColormap(TC_RAINBOW, players[rankplayer[i]].mo->color, GTC_CACHE);
- else
- colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE);
-
- V_DrawMappedPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, facerankprefix[players[rankplayer[i]].skin], colormap);
- if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] > 0)
- {
- V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[0], colormap);
- for (j = 1; j < players[rankplayer[i]].kartstuff[k_bumper]; j++)
- {
- bumperx += 5;
- V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[1], colormap);
- }
- }
- }
-
- if (i == strank)
- V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]);
-
- if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] <= 0)
- V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SNAPTOLEFT, kp_ranknobumpers);
- else
- {
- INT32 pos = players[rankplayer[i]].kartstuff[k_position];
- if (pos < 0 || pos > MAXPLAYERS)
- pos = 0;
- // Draws the little number over the face
- V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SNAPTOLEFT, kp_facenum[pos]);
- }
-
- Y += 18;
- }
-
- return false;
-}
-
-//
-// HU_DrawTabRankings -- moved here to take advantage of kart stuff!
-//
-void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol)
-{
- INT32 i, rightoffset = 240;
- const UINT8 *colormap;
- INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2;
-
- //this function is designed for 9 or less score lines only
- //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up
-
- V_DrawFill(1-duptweak, 26, dupadjust-2, 1, 0); // Draw a horizontal line because it looks nice!
- if (scorelines > 8)
- {
- V_DrawFill(160, 26, 1, 147, 0); // Draw a vertical line to separate the two sides.
- V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom.
- rightoffset = (BASEVIDWIDTH/2) - 4 - x;
- }
-
- for (i = 0; i < scorelines; i++)
- {
- char strtime[MAXPLAYERNAME+1];
-
- if (players[tab[i].num].spectator || !players[tab[i].num].mo)
- continue; //ignore them.
-
- if (netgame // don't draw it offline
- && tab[i].num != serverplayer)
- HU_drawPing(x + ((i < 8) ? -19 : rightoffset + 13), y+2, playerpingtable[tab[i].num], false);
-
- if (scorelines > 8)
- strlcpy(strtime, tab[i].name, 6);
- else
- STRBUFCPY(strtime, tab[i].name);
-
- V_DrawString(x + 20, y,
- ((tab[i].num == whiteplayer)
- ? hilicol|V_ALLOWLOWERCASE
- : V_ALLOWLOWERCASE),
- strtime);
-
- if (players[tab[i].num].mo->color)
- {
- colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE);
- if (players[tab[i].num].mo->colorized)
- colormap = R_GetTranslationColormap(TC_RAINBOW, players[tab[i].num].mo->color, GTC_CACHE);
- else
- colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE);
-
- V_DrawMappedPatch(x, y-4, 0, facerankprefix[players[tab[i].num].skin], colormap);
- /*if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] > 0) -- not enough space for this
- {
- INT32 bumperx = x+19;
- V_DrawMappedPatch(bumperx-2, y-4, 0, kp_tinybumper[0], colormap);
- for (j = 1; j < players[tab[i].num].kartstuff[k_bumper]; j++)
- {
- bumperx += 5;
- V_DrawMappedPatch(bumperx, y-4, 0, kp_tinybumper[1], colormap);
- }
- }*/
- }
-
- if (tab[i].num == whiteplayer)
- V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]);
-
- if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] <= 0)
- V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers);
- else
- {
- INT32 pos = players[tab[i].num].kartstuff[k_position];
- if (pos < 0 || pos > MAXPLAYERS)
- pos = 0;
- // Draws the little number over the face
- V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]);
- }
-
- if (G_RaceGametype())
- {
-#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time))
- if (players[tab[i].num].exiting)
- V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime));
- else if (players[tab[i].num].pflags & PF_TIMEOVER)
- V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST.");
- else if (circuitmap)
- V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count));
-#undef timestring
- }
- else
- V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count));
-
- y += 18;
- if (i == 7)
- {
- y = 33;
- x = (BASEVIDWIDTH/2) + 4;
- }
- }
-}
-
-static void K_drawKartLaps(void)
-{
- INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT);
-
- if (splitscreen > 1)
- {
- V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_splitlapflag);
-
- if (stplyr->exiting)
- V_DrawString(LAPS_X+13, LAPS_Y+1, V_HUDTRANS|splitflags, "FIN");
- else
- V_DrawString(LAPS_X+13, LAPS_Y+1, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps+1, cv_numlaps.value));
- }
- else
- {
- V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_lapsticker);
-
- if (stplyr->exiting)
- V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, "FIN");
- else
- V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps+1, cv_numlaps.value));
- }
-}
-
-static void K_drawKartSpeedometer(void)
-{
- fixed_t convSpeed;
- INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT);
-
- if (cv_kartspeedometer.value == 1) // Kilometers
- {
- convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale)/FRACUNIT; // 2.172409058
- V_DrawKartString(SPDM_X, SPDM_Y, V_HUDTRANS|splitflags, va("%3d km/h", convSpeed));
- }
- else if (cv_kartspeedometer.value == 2) // Miles
- {
- convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale)/FRACUNIT; // 1.349868774
- V_DrawKartString(SPDM_X, SPDM_Y, V_HUDTRANS|splitflags, va("%3d mph", convSpeed));
- }
- else if (cv_kartspeedometer.value == 3) // Fracunits
- {
- convSpeed = FixedDiv(stplyr->speed, mapobjectscale)/FRACUNIT;
- V_DrawKartString(SPDM_X, SPDM_Y, V_HUDTRANS|splitflags, va("%3d fu/t", convSpeed));
- }
-}
-
-static void K_drawKartBumpersOrKarma(void)
-{
- UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, 0);
- INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT);
-
- if (splitscreen > 1)
- {
- if (stplyr->kartstuff[k_bumper] <= 0)
- {
- V_DrawMappedPatch(LAPS_X, LAPS_Y-1, V_HUDTRANS|splitflags, kp_splitkarmabomb, colormap);
- V_DrawString(LAPS_X+13, LAPS_Y+1, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints]));
- }
- else
- {
- V_DrawMappedPatch(LAPS_X, LAPS_Y-1, V_HUDTRANS|splitflags, kp_rankbumper, colormap);
- V_DrawString(LAPS_X+13, LAPS_Y+1, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value));
- }
- }
- else
- {
- if (stplyr->kartstuff[k_bumper] <= 0)
- {
- V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_karmasticker, colormap);
- V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints]));
- }
- else
- {
- if (stplyr->kartstuff[k_bumper] > 9 && cv_kartbumpers.value > 9)
- V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumperstickerwide, colormap);
- else
- V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumpersticker, colormap);
- V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value));
- }
- }
-}
-
-static fixed_t K_FindCheckX(fixed_t px, fixed_t py, angle_t ang, fixed_t mx, fixed_t my)
-{
- fixed_t dist, x;
- fixed_t range = RING_DIST/3;
- angle_t diff;
-
- range *= gamespeed+1;
-
- dist = abs(R_PointToDist2(px, py, mx, my));
- if (dist > range)
- return -320;
-
- diff = R_PointToAngle2(px, py, mx, my) - ang;
-
- if (diff < ANGLE_90 || diff > ANGLE_270)
- return -320;
- else
- x = (FixedMul(FINETANGENT(((diff+ANGLE_90)>>ANGLETOFINESHIFT) & 4095), 160<>FRACBITS;
-
- if (encoremode)
- x = 320-x;
-
- if (splitscreen > 1)
- x /= 2;
-
- return x;
-}
-
-static void K_drawKartWanted(void)
-{
- UINT8 i, numwanted = 0;
- UINT8 *colormap = NULL;
-
- if (splitscreen) // Can't fit the poster on screen, sadly
- {
- if (K_IsPlayerWanted(stplyr) && leveltime % 10 > 3)
- V_DrawRightAlignedString(WANT_X, WANT_Y, K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_HUDTRANS|V_ORANGEMAP), "WANTED");
- return;
- }
-
- for (i = 0; i < 4; i++)
- {
- if (battlewanted[i] == -1)
- break;
- numwanted++;
- }
-
- if (numwanted <= 0)
- return;
-
- if (battlewanted[0] != -1)
- colormap = R_GetTranslationColormap(0, players[battlewanted[0]].skincolor, GTC_CACHE);
- V_DrawFixedPatch(WANT_X< 1)
- y += 16;
- }
-
- if (players[battlewanted[i]].skincolor)
- {
- colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE);
- V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap);
- }
- }
-}
-
-static void K_drawKartPlayerCheck(void)
-{
- INT32 i;
- UINT8 *colormap;
- INT32 x;
-
- INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM);
-
- if (!stplyr->mo || stplyr->spectator)
- return;
-
- if (stplyr->awayviewtics)
- return;
-
- if (camspin)
- return;
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- UINT8 pnum = 0;
-
- if (&players[i] == stplyr)
- continue;
- if (!playeringame[i] || players[i].spectator)
- continue;
- if (!players[i].mo)
- continue;
-
- if ((players[i].kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2))
- pnum++; // white frames
-
- if (players[i].kartstuff[k_itemtype] == KITEM_GROW || players[i].kartstuff[k_growshrinktimer] > 0)
- pnum += 4;
- else if (players[i].kartstuff[k_itemtype] == KITEM_INVINCIBILITY || players[i].kartstuff[k_invincibilitytimer])
- pnum += 2;
-
- x = K_FindCheckX(stplyr->mo->x, stplyr->mo->y, stplyr->mo->angle, players[i].mo->x, players[i].mo->y);
- if (x <= 320 && x >= 0)
- {
- if (x < 14)
- x = 14;
- else if (x > 306)
- x = 306;
-
- colormap = R_GetTranslationColormap(TC_DEFAULT, players[i].mo->color, 0);
- V_DrawMappedPatch(x, CHEK_Y, V_HUDTRANS|splitflags, kp_check[pnum], colormap);
- }
- }
-}
-
-static void K_drawKartMinimapHead(mobj_t *mo, INT32 x, INT32 y, INT32 flags, patch_t *AutomapPic)
-{
- // amnum xpos & ypos are the icon's speed around the HUD.
- // The number being divided by is for how fast it moves.
- // The higher the number, the slower it moves.
-
- // am xpos & ypos are the icon's starting position. Withouht
- // it, they wouldn't 'spawn' on the top-right side of the HUD.
-
- UINT8 skin = 0;
-
- fixed_t amnumxpos, amnumypos;
- INT32 amxpos, amypos;
-
- node_t *bsp = &nodes[numnodes-1];
- fixed_t maxx, minx, maxy, miny;
-
- fixed_t mapwidth, mapheight;
- fixed_t xoffset, yoffset;
- fixed_t xscale, yscale, zoom;
-
- if (mo->skin)
- skin = ((skin_t*)mo->skin)-skins;
-
- maxx = maxy = INT32_MAX;
- minx = miny = INT32_MIN;
- minx = bsp->bbox[0][BOXLEFT];
- maxx = bsp->bbox[0][BOXRIGHT];
- miny = bsp->bbox[0][BOXBOTTOM];
- maxy = bsp->bbox[0][BOXTOP];
-
- if (bsp->bbox[1][BOXLEFT] < minx)
- minx = bsp->bbox[1][BOXLEFT];
- if (bsp->bbox[1][BOXRIGHT] > maxx)
- maxx = bsp->bbox[1][BOXRIGHT];
- if (bsp->bbox[1][BOXBOTTOM] < miny)
- miny = bsp->bbox[1][BOXBOTTOM];
- if (bsp->bbox[1][BOXTOP] > maxy)
- maxy = bsp->bbox[1][BOXTOP];
-
- // You might be wondering why these are being bitshift here
- // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible...
- // map boundaries and sizes will ALWAYS be whole numbers thankfully
- // later calculations take into consideration that these are actually not in terms of FRACUNIT though
- minx >>= FRACBITS;
- maxx >>= FRACBITS;
- miny >>= FRACBITS;
- maxy >>= FRACBITS;
-
- mapwidth = maxx - minx;
- mapheight = maxy - miny;
-
- // These should always be small enough to be bitshift back right now
- xoffset = (minx + mapwidth/2)<width, mapwidth);
- yscale = FixedDiv(AutomapPic->height, mapheight);
- zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20);
-
- amnumxpos = (FixedMul(mo->x, zoom) - FixedMul(xoffset, zoom));
- amnumypos = -(FixedMul(mo->y, zoom) - FixedMul(yoffset, zoom));
-
- if (encoremode)
- amnumxpos = -amnumxpos;
-
- amxpos = amnumxpos + ((x + AutomapPic->width/2 - (facemmapprefix[skin]->width/2))<height/2 - (facemmapprefix[skin]->height/2))<width/2 + (facemmapprefix[skin]->width/2))<color) // 'default' color
- V_DrawSciencePatch(amxpos, amypos, flags, facemmapprefix[skin], FRACUNIT);
- else
- {
- UINT8 *colormap;
- if (mo->colorized)
- colormap = R_GetTranslationColormap(TC_RAINBOW, mo->color, GTC_CACHE);
- else
- colormap = R_GetTranslationColormap(skin, mo->color, GTC_CACHE);
- V_DrawFixedPatch(amxpos, amypos, FRACUNIT, flags, facemmapprefix[skin], colormap);
- }
-}
-
-static void K_drawKartMinimap(void)
-{
- INT32 lumpnum;
- patch_t *AutomapPic;
- INT32 i = 0;
- INT32 x, y;
- INT32 minimaptrans, splitflags = (splitscreen ? 0 : V_SNAPTORIGHT);
- boolean dop1later = false;
-
- // Draw the HUD only when playing in a level.
- // hu_stuff needs this, unlike st_stuff.
- if (gamestate != GS_LEVEL)
- return;
-
- if (stplyr != &players[displayplayer])
- return;
-
- lumpnum = W_CheckNumForName(va("%sR", G_BuildMapName(gamemap)));
-
- if (lumpnum != -1)
- AutomapPic = W_CachePatchName(va("%sR", G_BuildMapName(gamemap)), PU_HUDGFX);
- else
- return; // no pic, just get outta here
-
- x = MINI_X - (AutomapPic->width/2);
- y = MINI_Y - (AutomapPic->height/2);
-
- if (timeinmap > 105)
- {
- minimaptrans = (splitscreen ? 10 : cv_kartminimap.value);
- if (timeinmap <= 113)
- minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105);
- if (!minimaptrans)
- return;
- }
- else
- return;
-
- minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic);
- else
- V_DrawScaledPatch(x, y, splitflags, AutomapPic);
-
- if (!splitscreen)
- {
- splitflags &= ~minimaptrans;
- splitflags |= V_HUDTRANSHALF;
- }
-
- // let offsets transfer to the heads, too!
- if (encoremode)
- x += SHORT(AutomapPic->leftoffset);
- else
- x -= SHORT(AutomapPic->leftoffset);
- y -= SHORT(AutomapPic->topoffset);
-
- // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen)
- if (ghosts)
- {
- demoghost *g = ghosts;
- while (g)
- {
- K_drawKartMinimapHead(g->mo, x, y, splitflags, AutomapPic);
- g = g->next;
- }
- if (!stplyr->mo || stplyr->spectator) // do we need the latter..?
- return;
- dop1later = true;
- }
- else
- {
- for (i = MAXPLAYERS-1; i >= 0; i--)
- {
- if (!playeringame[i])
- continue;
- if (!players[i].mo || players[i].spectator)
- continue;
-
- if (!splitscreen && i == displayplayer)
- {
- dop1later = true; // Do displayplayer later
- continue;
- }
-
- if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0)
- continue;
- if (players[i].kartstuff[k_hyudorotimer] > 0)
- {
- if (!((players[i].kartstuff[k_hyudorotimer] < 1*TICRATE/2
- || players[i].kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2))
- && !(leveltime & 1)))
- continue;
- }
-
- K_drawKartMinimapHead(players[i].mo, x, y, splitflags, AutomapPic);
- }
- }
-
- if (!dop1later)
- return; // Don't need this
-
- splitflags &= ~V_HUDTRANSHALF;
- splitflags |= V_HUDTRANS;
- K_drawKartMinimapHead(stplyr->mo, x, y, splitflags, AutomapPic);
-}
-
-static void K_drawKartStartCountdown(void)
-{
- INT32 pnum = 0, splitflags = K_calcSplitFlags(0); // 3
-
- if (leveltime >= starttime-(2*TICRATE)) // 2
- pnum++;
- if (leveltime >= starttime-TICRATE) // 1
- pnum++;
- if (leveltime >= starttime) // GO!
- pnum++;
- if ((leveltime % (2*5)) / 5) // blink
- pnum += 4;
- if (splitscreen) // splitscreen
- pnum += 8;
-
- V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]);
-}
-
-static void K_drawKartFinish(void)
-{
- INT32 pnum = 0, splitflags = K_calcSplitFlags(0);
-
- if (!stplyr->kartstuff[k_cardanimation] || stplyr->kartstuff[k_cardanimation] >= 2*TICRATE)
- return;
-
- if ((stplyr->kartstuff[k_cardanimation] % (2*5)) / 5) // blink
- pnum = 1;
-
- if (splitscreen > 1) // 3/4p, stationary FIN
- {
- pnum += 2;
- V_DrawScaledPatch(STCD_X - (SHORT(kp_racefinish[pnum]->width)/2), STCD_Y - (SHORT(kp_racefinish[pnum]->height)/2), splitflags, kp_racefinish[pnum]);
- return;
- }
-
- //else -- 1/2p, scrolling FINISH
- {
- INT32 x, xval;
-
- if (splitscreen) // wide splitscreen
- pnum += 4;
-
- x = ((vid.width<width)<kartstuff[k_cardanimation])*(xval > x ? xval : x))/TICRATE;
-
- if (splitscreen && stplyr == &players[secondarydisplayplayer])
- x = -x;
-
- V_DrawFixedPatch(x + (STCD_X<>1),
- (STCD_Y<height)<<(FRACBITS-1)),
- FRACUNIT,
- splitflags, kp_racefinish[pnum], NULL);
- }
-}
-
-static void K_drawBattleFullscreen(void)
-{
- INT32 x = BASEVIDWIDTH/2;
- INT32 y = -64+(stplyr->kartstuff[k_cardanimation]); // card animation goes from 0 to 164, 164 is the middle of the screen
- INT32 splitflags = V_SNAPTOTOP; // I don't feel like properly supporting non-green resolutions, so you can have a misuse of SNAPTO instead
- fixed_t scale = FRACUNIT;
-
- if (splitscreen)
- {
- if ((splitscreen == 1 && stplyr == &players[secondarydisplayplayer])
- || (splitscreen > 1 && (stplyr == &players[thirddisplayplayer]
- || (stplyr == &players[fourthdisplayplayer] && splitscreen > 2))))
- {
- y = 232-(stplyr->kartstuff[k_cardanimation]/2);
- splitflags = V_SNAPTOBOTTOM;
- }
- else
- y = -32+(stplyr->kartstuff[k_cardanimation]/2);
-
- if (splitscreen > 1)
- {
- scale /= 2;
-
- if (stplyr == &players[secondarydisplayplayer]
- || (stplyr == &players[fourthdisplayplayer] && splitscreen > 2))
- x = 3*BASEVIDWIDTH/4;
- else
- x = BASEVIDWIDTH/4;
- }
- else
- {
- if (stplyr->exiting)
- {
- if (stplyr == &players[secondarydisplayplayer])
- x = BASEVIDWIDTH-96;
- else
- x = 96;
- }
- else
- scale /= 2;
- }
- }
-
- if (stplyr->exiting)
- {
- if (stplyr == &players[displayplayer])
- V_DrawFadeScreen(0xFF00, 16);
- if (stplyr->exiting < 6*TICRATE)
- {
- if (stplyr->kartstuff[k_position] == 1)
- V_DrawFixedPatch(x<kartstuff[k_bumper] <= 0 && stplyr->kartstuff[k_comebacktimer] && comeback && !stplyr->spectator)
- {
- UINT16 t = stplyr->kartstuff[k_comebacktimer]/(10*TICRATE);
- INT32 txoff, adjust = (splitscreen > 1) ? 4 : 6; // normal string is 8, kart string is 12, half of that for ease
- INT32 ty = (BASEVIDHEIGHT/2)+66;
-
- txoff = adjust;
-
- while (t)
- {
- txoff += adjust;
- t /= 10;
- }
-
- if (splitscreen)
- {
- if (splitscreen > 1)
- ty = (BASEVIDHEIGHT/4)+33;
- if ((splitscreen == 1 && stplyr == &players[secondarydisplayplayer])
- || (stplyr == &players[thirddisplayplayer] && splitscreen > 1)
- || (stplyr == &players[fourthdisplayplayer] && splitscreen > 2))
- ty += (BASEVIDHEIGHT/2);
- }
- else
- V_DrawFadeScreen(0xFF00, 16);
-
- if (!comebackshowninfo)
- V_DrawFixedPatch(x< 1)
- V_DrawString(x-txoff, ty, 0, va("%d", stplyr->kartstuff[k_comebacktimer]/TICRATE));
- else
- {
- V_DrawFixedPatch(x<kartstuff[k_comebacktimer]/TICRATE));
- }
- }
-
- if (netgame && !stplyr->spectator && timeinmap > 113) // FREE PLAY?
- {
- UINT8 i;
-
- // check to see if there's anyone else at all
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (i == displayplayer)
- continue;
- if (playeringame[i] && !stplyr->spectator)
- return;
- }
-
- K_drawKartFreePlay(leveltime);
- }
-}
-
-static void K_drawKartFirstPerson(void)
-{
- static INT32 pnum[4], turn[4], drift[4];
- INT32 pn = 0, tn = 0, dr = 0;
- INT32 target = 0, splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM);
- INT32 x = BASEVIDWIDTH/2, y = BASEVIDHEIGHT;
- fixed_t scale;
- UINT8 *colmap = NULL;
- ticcmd_t *cmd = &stplyr->cmd;
-
- if (stplyr->spectator || !stplyr->mo || (stplyr->mo->flags2 & MF2_DONTDRAW))
- return;
-
- if (stplyr == &players[secondarydisplayplayer] && splitscreen)
- { pn = pnum[1]; tn = turn[1]; dr = drift[1]; }
- else if (stplyr == &players[thirddisplayplayer] && splitscreen > 1)
- { pn = pnum[2]; tn = turn[2]; dr = drift[2]; }
- else if (stplyr == &players[fourthdisplayplayer] && splitscreen > 2)
- { pn = pnum[3]; tn = turn[3]; dr = drift[3]; }
- else
- { pn = pnum[0]; tn = turn[0]; dr = drift[0]; }
-
- if (splitscreen)
- {
- y >>= 1;
- if (splitscreen > 1)
- x >>= 1;
- }
-
- {
- if (stplyr->speed < FixedMul(stplyr->runspeed, stplyr->mo->scale) && (leveltime & 1) && !splitscreen)
- y++;
- // the following isn't EXPLICITLY right, it just gets the result we want, but i'm too lazy to look up the right way to do it
- if (stplyr->mo->flags2 & MF2_SHADOW)
- splitflags |= FF_TRANS80;
- else if (stplyr->mo->frame & FF_TRANSMASK)
- splitflags |= (stplyr->mo->frame & FF_TRANSMASK);
- }
-
- if (cmd->driftturn > 400) // strong left turn
- target = 2;
- else if (cmd->driftturn < -400) // strong right turn
- target = -2;
- else if (cmd->driftturn > 0) // weak left turn
- target = 1;
- else if (cmd->driftturn < 0) // weak right turn
- target = -1;
- else // forward
- target = 0;
-
- if (encoremode)
- target = -target;
-
- if (pn < target)
- pn++;
- else if (pn > target)
- pn--;
-
- if (pn < 0)
- splitflags |= V_FLIP; // right turn
-
- target = abs(pn);
- if (target > 2)
- target = 2;
-
- x <<= FRACBITS;
- y <<= FRACBITS;
-
- if (tn != cmd->driftturn/50)
- tn -= (tn - (cmd->driftturn/50))/8;
-
- if (dr != stplyr->kartstuff[k_drift]*16)
- dr -= (dr - (stplyr->kartstuff[k_drift]*16))/8;
-
- if (splitscreen == 1)
- {
- scale = (2*FRACUNIT)/3;
- y += FRACUNIT/(vid.dupx < vid.dupy ? vid.dupx : vid.dupy); // correct a one-pixel gap on the screen view (not the basevid view)
- }
- else if (splitscreen)
- scale = FRACUNIT/2;
- else
- scale = FRACUNIT;
-
- if (stplyr->mo)
- {
- INT32 dsone = K_GetKartDriftSparkValue(stplyr);
- INT32 dstwo = dsone*2;
- INT32 dsthree = dstwo*2;
-
-#ifndef DONTLIKETOASTERSFPTWEAKS
- {
- const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle;
- // yes, the following is correct. no, you do not need to swap the x and y.
- fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2);
- fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT);
-
- if (splitscreen)
- xoffs = FixedMul(xoffs, scale);
-
- xoffs -= (tn)*scale;
- xoffs -= (dr)*scale;
-
- if (stplyr->frameangle == stplyr->mo->angle)
- {
- const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale);
-
- if (mag < FRACUNIT)
- {
- xoffs = FixedMul(xoffs, mag);
- if (!splitscreen)
- yoffs = FixedMul(yoffs, mag);
- }
- }
-
- if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if!
- yoffs += stplyr->mo->momz/3;
-
- if (encoremode)
- x -= xoffs;
- else
- x += xoffs;
- if (!splitscreen)
- y += yoffs;
- }
-
- // drift sparks!
- if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dsthree))
- colmap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), 0);
- else if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dstwo))
- colmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_KETCHUP, 0);
- else if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dsone))
- colmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SAPPHIRE, 0);
- else
-#endif
- // invincibility/grow/shrink!
- if (stplyr->mo->colorized && stplyr->mo->color)
- colmap = R_GetTranslationColormap(TC_RAINBOW, stplyr->mo->color, 0);
- }
-
- V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap);
-
- if (stplyr == &players[secondarydisplayplayer] && splitscreen)
- { pnum[1] = pn; turn[1] = tn; drift[1] = dr; }
- else if (stplyr == &players[thirddisplayplayer] && splitscreen > 1)
- { pnum[2] = pn; turn[2] = tn; drift[2] = dr; }
- else if (stplyr == &players[fourthdisplayplayer] && splitscreen > 2)
- { pnum[3] = pn; turn[3] = tn; drift[3] = dr; }
- else
- { pnum[0] = pn; turn[0] = tn; drift[0] = dr; }
-}
-
-// doesn't need to ever support 4p
-static void K_drawInput(void)
-{
- static INT32 pn = 0;
- INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT);
- INT32 x = BASEVIDWIDTH - 32, y = BASEVIDHEIGHT-24, offs, col;
- const INT32 accent1 = splitflags|colortranslations[stplyr->skincolor][5];
- const INT32 accent2 = splitflags|colortranslations[stplyr->skincolor][9];
- ticcmd_t *cmd = &stplyr->cmd;
-
- if (timeinmap <= 105)
- return;
-
- if (timeinmap < 113)
- {
- INT32 count = ((INT32)(timeinmap) - 105);
- offs = (titledemo ? 128 : 64);
- while (count-- > 0)
- offs >>= 1;
- x += offs;
- }
-
- if (titledemo)
- {
- V_DrawTinyScaledPatch(x-54, 128, splitflags, W_CachePatchName("TTKBANNR", PU_CACHE));
- V_DrawTinyScaledPatch(x-54, 128+25, splitflags, W_CachePatchName("TTKART", PU_CACHE));
- return;
- }
-
-#define BUTTW 8
-#define BUTTH 11
-
-#define drawbutt(xoffs, butt, symb)\
- if (stplyr->cmd.buttons & butt)\
- {\
- offs = 2;\
- col = accent1;\
- }\
- else\
- {\
- offs = 0;\
- col = accent2;\
- V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\
- }\
- V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\
- V_DrawFixedPatch((x+1+(xoffs))<driftturn) // no turn
- target = 0;
- else // turning of multiple strengths!
- {
- target = ((abs(cmd->driftturn) - 1)/125)+1;
- if (target > 4)
- target = 4;
- if (cmd->driftturn < 0)
- target = -target;
- }
-
- if (pn != target)
- {
- if (abs(pn - target) == 1)
- pn = target;
- else if (pn < target)
- pn += 2;
- else //if (pn > target)
- pn -= 2;
- }
-
- if (pn < 0)
- {
- splitflags |= V_FLIP; // right turn
- x--;
- }
-
- target = abs(pn);
- if (target > 4)
- target = 4;
-
- if (!stplyr->skincolor)
- V_DrawFixedPatch(x<skincolor, 0);
- V_DrawFixedPatch(x<kartstuff[k_lapanimation];
- UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, 0);
-
- V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->kartstuff[k_lapanimation]-76)))*FRACUNIT,
- (48 - (32*max(0, progress-76)))*FRACUNIT,
- FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
- (modeattacking ? kp_lapanim_emblem[1] : kp_lapanim_emblem[0]), colormap);
-
- if (stplyr->kartstuff[k_laphand] >= 1 && stplyr->kartstuff[k_laphand] <= 3)
- {
- V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->kartstuff[k_lapanimation]-76)))*FRACUNIT,
- (48 - (32*max(0, progress-76))
- + 4 - abs((signed)((leveltime % 8) - 4)))*FRACUNIT,
- FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
- kp_lapanim_hand[stplyr->kartstuff[k_laphand]-1], NULL);
- }
-
- if (stplyr->laps == (UINT8)(cv_numlaps.value - 1))
- {
- V_DrawFixedPatch((62 - (32*max(0, progress-76)))*FRACUNIT, // 27
- 30*FRACUNIT, // 24
- FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
- kp_lapanim_final[min(progress/2, 10)], NULL);
-
- if (progress/2-12 >= 0)
- {
- V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194
- 30*FRACUNIT, // 24
- FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
- kp_lapanim_lap[min(progress/2-12, 6)], NULL);
- }
- }
- else
- {
- V_DrawFixedPatch((82 - (32*max(0, progress-76)))*FRACUNIT, // 61
- 30*FRACUNIT, // 24
- FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
- kp_lapanim_lap[min(progress/2, 6)], NULL);
-
- if (progress/2-8 >= 0)
- {
- V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194
- 30*FRACUNIT, // 24
- FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
- kp_lapanim_number[(((UINT32)stplyr->laps+1) / 10)][min(progress/2-8, 2)], NULL);
-
- if (progress/2-10 >= 0)
- {
- V_DrawFixedPatch((208 + (32*max(0, progress-76)))*FRACUNIT, // 221
- 30*FRACUNIT, // 24
- FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
- kp_lapanim_number[(((UINT32)stplyr->laps+1) % 10)][min(progress/2-10, 2)], NULL);
- }
- }
- }
-}
-
-void K_drawKartFreePlay(UINT32 flashtime)
-{
- // no splitscreen support because it's not FREE PLAY if you have more than one player in-game
-
- if ((flashtime % TICRATE) < TICRATE/2)
- return;
-
- V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy
- LAPS_Y+3, V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY");
-}
-
-static void K_drawDistributionDebugger(void)
-{
- patch_t *items[NUMKARTRESULTS] = {
- kp_sadface[1],
- kp_sneaker[1],
- kp_rocketsneaker[1],
- kp_invincibility[7],
- kp_banana[1],
- kp_eggman[1],
- kp_orbinaut[4],
- kp_jawz[1],
- kp_mine[1],
- kp_ballhog[1],
- kp_selfpropelledbomb[1],
- kp_grow[1],
- kp_shrink[1],
- kp_thundershield[1],
- kp_hyudoro[1],
- kp_pogospring[1],
- kp_kitchensink[1],
-
- kp_sneaker[1],
- kp_banana[1],
- kp_banana[1],
- kp_orbinaut[4],
- kp_orbinaut[4],
- kp_jawz[1]
- };
- INT32 useodds = 0;
- INT32 pingame = 0, bestbumper = 0;
- INT32 i;
- INT32 x = -9, y = -9;
- boolean dontforcespb = false;
-
- if (stplyr != &players[displayplayer]) // only for p1
- return;
-
- // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator)
- continue;
- pingame++;
- if (players[i].exiting)
- dontforcespb = true;
- if (players[i].kartstuff[k_bumper] > bestbumper)
- bestbumper = players[i].kartstuff[k_bumper];
- }
-
- useodds = K_FindUseodds(stplyr, 0, pingame, bestbumper, (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1), dontforcespb);
-
- for (i = 1; i < NUMKARTRESULTS; i++)
- {
- const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0);
- if (itemodds <= 0)
- continue;
-
- V_DrawScaledPatch(x, y, V_HUDTRANS|V_SNAPTOTOP, items[i]);
- V_DrawThinString(x+11, y+31, V_HUDTRANS|V_SNAPTOTOP, va("%d", itemodds));
-
- // Display amount for multi-items
- if (i >= NUMKARTITEMS)
- {
- INT32 amount;
- switch (i)
- {
- case KRITEM_TENFOLDBANANA:
- amount = 10;
- break;
- case KRITEM_QUADORBINAUT:
- amount = 4;
- break;
- case KRITEM_DUALJAWZ:
- amount = 2;
- break;
- default:
- amount = 3;
- break;
- }
- V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SNAPTOTOP, va("x%d", amount));
- }
-
- x += 32;
- if (x >= 297)
- {
- x = -9;
- y += 32;
- }
- }
-
- V_DrawString(0, 0, V_HUDTRANS|V_SNAPTOTOP, va("USEODDS %d", useodds));
-}
-
-static void K_drawCheckpointDebugger(void)
-{
- if (stplyr != &players[displayplayer]) // only for p1
- return;
-
- if (stplyr->starpostnum >= (numstarposts - (numstarposts/2)))
- V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts));
- else
- V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Skip: %d)", stplyr->starpostnum, numstarposts, ((numstarposts/2) + stplyr->starpostnum)));
- V_DrawString(8, 192, 0, va("Waypoint dist: Prev %d, Next %d", stplyr->kartstuff[k_prevcheck], stplyr->kartstuff[k_nextcheck]));
-}
-
-void K_drawKartHUD(void)
-{
- boolean isfreeplay = false;
-
- // Define the X and Y for each drawn object
- // This is handled by console/menu values
- K_initKartHUD();
-
- // Draw that fun first person HUD! Drawn ASAP so it looks more "real".
- if ((stplyr == &players[displayplayer] && !camera.chase)
- || ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase)
- || ((splitscreen > 1 && stplyr == &players[thirddisplayplayer]) && !camera3.chase)
- || ((splitscreen > 2 && stplyr == &players[fourthdisplayplayer]) && !camera4.chase))
- K_drawKartFirstPerson();
-
- if (splitscreen == 2) // Player 4 in 3P is the minimap :p
- {
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_minimap))
-#endif
- K_drawKartMinimap();
- }
-
- // Draw full screen stuff that turns off the rest of the HUD
- if (mapreset && stplyr == &players[displayplayer])
- {
- K_drawChallengerScreen();
- return;
- }
-
- if ((G_BattleGametype())
- && (stplyr->exiting
- || (stplyr->kartstuff[k_bumper] <= 0
- && stplyr->kartstuff[k_comebacktimer]
- && comeback
- && stplyr->playerstate == PST_LIVE)))
- {
- K_drawBattleFullscreen();
- return;
- }
-
- // Draw the CHECK indicator before the other items, so it's overlapped by everything else
- if (cv_kartcheck.value && !splitscreen && !players[displayplayer].exiting)
- K_drawKartPlayerCheck();
-
- if (splitscreen == 0 && cv_kartminimap.value && !titledemo)
- {
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_minimap))
-#endif
- K_drawKartMinimap(); // 3P splitscreen is handled above
-
- }
-
- // Draw the item window
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_item))
-#endif
- K_drawKartItem();
-
- // Draw WANTED status
- if (G_BattleGametype())
- {
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_wanted))
-#endif
- K_drawKartWanted();
- }
-
- // If not splitscreen, draw...
- if (!splitscreen && !titledemo)
- {
- // Draw the timestamp
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_time))
-#endif
- K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y, gamemap, 0);
-
- if (!modeattacking)
- {
- // The top-four faces on the left
- /*#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_minirankings))
- #endif*/
- isfreeplay = K_drawKartPositionFaces();
- }
- }
-
- if (!stplyr->spectator) // Bottom of the screen elements, don't need in spectate mode
- {
- if (G_RaceGametype()) // Race-only elements
- {
- if (!titledemo)
- {
- // Draw the lap counter
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_gametypeinfo))
-#endif
- K_drawKartLaps();
-
- if (!splitscreen)
- {
- // Draw the speedometer
- // TODO: Make a better speedometer.
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_speedometer))
-#endif
- K_drawKartSpeedometer();
- }
- }
-
- if (isfreeplay)
- ;
- else if (!modeattacking)
- {
- // Draw the numerical position
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_position))
-#endif
- K_DrawKartPositionNum(stplyr->kartstuff[k_position]);
- }
- else //if (!(demoplayback && hu_showscores))
- {
- // Draw the input UI
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_position))
-#endif
- K_drawInput();
- }
- }
- else if (G_BattleGametype()) // Battle-only
- {
- // Draw the hits left!
-#ifdef HAVE_BLUA
- if (LUA_HudEnabled(hud_gametypeinfo))
-#endif
- K_drawKartBumpersOrKarma();
- }
- }
-
- // Draw the countdowns after everything else.
- if (leveltime >= starttime-(3*TICRATE)
- && leveltime < starttime+TICRATE)
- K_drawKartStartCountdown();
- else if (countdown && (!splitscreen || !stplyr->exiting))
- {
- char *countstr = va("%d", countdown/TICRATE);
-
- if (splitscreen > 1)
- V_DrawCenteredString(BASEVIDWIDTH/4, LAPS_Y+1, K_calcSplitFlags(0), countstr);
- else
- {
- INT32 karlen = strlen(countstr)*6; // half of 12
- V_DrawKartString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, K_calcSplitFlags(0), countstr);
- }
- }
-
- // Race overlays
- if (G_RaceGametype())
- {
- if (stplyr->exiting)
- K_drawKartFinish();
- else if (stplyr->kartstuff[k_lapanimation] && !splitscreen)
- K_drawLapStartAnim();
- }
-
- if (modeattacking) // everything after here is MP and debug only
- return;
-
- if (G_BattleGametype() && !splitscreen && (stplyr->kartstuff[k_yougotem] % 2)) // * YOU GOT EM *
- V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem);
-
- // Draw FREE PLAY.
- if (isfreeplay && !stplyr->spectator && timeinmap > 113)
- K_drawKartFreePlay(leveltime);
-
- if (cv_kartdebugdistribution.value)
- K_drawDistributionDebugger();
-
- if (cv_kartdebugcheckpoint.value)
- K_drawCheckpointDebugger();
-
- if (cv_kartdebugnodes.value)
- {
- UINT8 p;
- for (p = 0; p < MAXPLAYERS; p++)
- V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d", p, playernode[p]));
- }
-}
-
-//}
+// SONIC ROBO BLAST 2 KART ~ ZarroTsu
+//-----------------------------------------------------------------------------
+/// \file k_kart.c
+/// \brief SRB2kart general.
+/// All of the SRB2kart-unique stuff.
+
+#include "doomdef.h"
+#include "hu_stuff.h"
+#include "g_game.h"
+#include "m_random.h"
+#include "p_local.h"
+#include "p_slopes.h"
+#include "r_draw.h"
+#include "r_local.h"
+#include "s_sound.h"
+#include "st_stuff.h"
+#include "v_video.h"
+#include "z_zone.h"
+#include "m_misc.h"
+#include "m_cond.h"
+#include "k_kart.h"
+#include "f_finale.h"
+#include "lua_hud.h" // For Lua hud checks
+#include "lua_hook.h" // For MobjDamage and ShouldDamage
+
+// SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H:
+// gamespeed is cc (0 for easy, 1 for normal, 2 for hard)
+// franticitems is Frantic Mode items, bool
+// encoremode is Encore Mode (duh), bool
+// comeback is Battle Mode's karma comeback, also bool
+// battlewanted is an array of the WANTED player nums, -1 for no player in that slot
+// indirectitemcooldown is timer before anyone's allowed another Shrink/SPB
+// mapreset is set when enough players fill an empty server
+// nospectategrief is the players in-game needed to eliminate the person in last
+
+
+//{ SRB2kart Color Code
+
+#define SKIN_RAMP_LENGTH 16
+#define DEFAULT_STARTTRANSCOLOR 160
+#define NUM_PALETTE_ENTRIES 256
+
+// These should be within 14 characters to fit on the character select screen
+const char *KartColor_Names[MAXSKINCOLORS] =
+{
+ "None", // SKINCOLOR_NONE
+ "White", // SKINCOLOR_WHITE
+ "Silver", // SKINCOLOR_SILVER
+ "Grey", // SKINCOLOR_GREY
+ "Nickel", // SKINCOLOR_NICKEL
+ "Black", // SKINCOLOR_BLACK
+ "Fairy", // SKINCOLOR_FAIRY
+ "Popcorn", // SKINCOLOR_POPCORN
+ "Sepia", // SKINCOLOR_SEPIA
+ "Beige", // SKINCOLOR_BEIGE
+ "Brown", // SKINCOLOR_BROWN
+ "Leather", // SKINCOLOR_LEATHER
+ "Salmon", // SKINCOLOR_SALMON
+ "Pink", // SKINCOLOR_PINK
+ "Rose", // SKINCOLOR_ROSE
+ "Brick", // SKINCOLOR_BRICK
+ "Ruby", // SKINCOLOR_RUBY
+ "Raspberry", // SKINCOLOR_RASPBERRY
+ "Cherry", // SKINCOLOR_CHERRY
+ "Red", // SKINCOLOR_RED
+ "Crimson", // SKINCOLOR_CRIMSON
+ "Maroon", // SKINCOLOR_MAROON
+ "Flame", // SKINCOLOR_FLAME
+ "Scarlet", // SKINCOLOR_SCARLET
+ "Ketchup", // SKINCOLOR_KETCHUP
+ "Dawn", // SKINCOLOR_DAWN
+ "Sunset", // SKINCOLOR_SUNSET
+ "Creamsicle", // SKINCOLOR_CREAMSICLE
+ "Orange", // SKINCOLOR_ORANGE
+ "Pumpkin", // SKINCOLOR_PUMPKIN
+ "Rosewood", // SKINCOLOR_ROSEWOOD
+ "Burgundy", // SKINCOLOR_BURGUNDY
+ "Tangerine", // SKINCOLOR_TANGERINE
+ "Peach", // SKINCOLOR_PEACH
+ "Caramel", // SKINCOLOR_CARAMEL
+ "Cream", // SKINCOLOR_CREAM
+ "Gold", // SKINCOLOR_GOLD
+ "Royal", // SKINCOLOR_ROYAL
+ "Bronze", // SKINCOLOR_BRONZE
+ "Copper", // SKINCOLOR_COPPER
+ "Yellow", // SKINCOLOR_YELLOW
+ "Mustard", // SKINCOLOR_MUSTARD
+ "Olive", // SKINCOLOR_OLIVE
+ "Vomit", // SKINCOLOR_VOMIT
+ "Garden", // SKINCOLOR_GARDEN
+ "Lime", // SKINCOLOR_LIME
+ "Handheld", // SKINCOLOR_HANDHELD
+ "Tea", // SKINCOLOR_TEA
+ "Pistachio", // SKINCOLOR_PISTACHIO
+ "Moss", // SKINCOLOR_MOSS
+ "Camouflage", // SKINCOLOR_CAMOUFLAGE
+ "Robo-Hood", // SKINCOLOR_ROBOHOOD
+ "Mint", // SKINCOLOR_MINT
+ "Green", // SKINCOLOR_GREEN
+ "Pinetree", // SKINCOLOR_PINETREE
+ "Emerald", // SKINCOLOR_EMERALD
+ "Swamp", // SKINCOLOR_SWAMP
+ "Dream", // SKINCOLOR_DREAM
+ "Plague", // SKINCOLOR_PLAGUE
+ "Algae", // SKINCOLOR_ALGAE
+ "Caribbean", // SKINCOLOR_CARIBBEAN
+ "Aqua", // SKINCOLOR_AQUA
+ "Teal", // SKINCOLOR_TEAL
+ "Cyan", // SKINCOLOR_CYAN
+ "Jawz", // SKINCOLOR_JAWZ
+ "Cerulean", // SKINCOLOR_CERULEAN
+ "Navy", // SKINCOLOR_NAVY
+ "Platinum", // SKINCOLOR_PLATINUM
+ "Slate", // SKINCOLOR_SLATE
+ "Steel", // SKINCOLOR_STEEL
+ "Rust", // SKINCOLOR_RUST
+ "Jet", // SKINCOLOR_JET
+ "Sapphire", // SKINCOLOR_SAPPHIRE
+ "Periwinkle", // SKINCOLOR_PERIWINKLE
+ "Blue", // SKINCOLOR_BLUE
+ "Blueberry", // SKINCOLOR_BLUEBERRY
+ "Nova", // SKINCOLOR_NOVA
+ "Pastel", // SKINCOLOR_PASTEL
+ "Moonslam", // SKINCOLOR_MOONSLAM
+ "Ultraviolet", // SKINCOLOR_ULTRAVIOLET
+ "Dusk", // SKINCOLOR_DUSK
+ "Bubblegum", // SKINCOLOR_BUBBLEGUM
+ "Purple", // SKINCOLOR_PURPLE
+ "Fuchsia", // SKINCOLOR_FUCHSIA
+ "Toxic", // SKINCOLOR_TOXIC
+ "Mauve", // SKINCOLOR_MAUVE
+ "Lavender", // SKINCOLOR_LAVENDER
+ "Byzantium", // SKINCOLOR_BYZANTIUM
+ "Pomegranate", // SKINCOLOR_POMEGRANATE
+ "Lilac" // SKINCOLOR_LILAC
+};
+
+// Color_Opposite replacement; frame setting has not been changed from 8 for most, should be done later
+const UINT8 KartColor_Opposite[MAXSKINCOLORS*2] =
+{
+ SKINCOLOR_NONE,8, // SKINCOLOR_NONE
+ SKINCOLOR_BLACK,8, // SKINCOLOR_WHITE
+ SKINCOLOR_NICKEL,8, // SKINCOLOR_SILVER
+ SKINCOLOR_GREY,8, // SKINCOLOR_GREY
+ SKINCOLOR_SILVER,8, // SKINCOLOR_NICKEL
+ SKINCOLOR_WHITE,8, // SKINCOLOR_BLACK
+ SKINCOLOR_CAMOUFLAGE,8, // SKINCOLOR_FAIRY
+ SKINCOLOR_BUBBLEGUM,8, // SKINCOLOR_POPCORN
+ SKINCOLOR_LEATHER,6, // SKINCOLOR_SEPIA
+ SKINCOLOR_BROWN,2, // SKINCOLOR_BEIGE
+ SKINCOLOR_BEIGE,8, // SKINCOLOR_BROWN
+ SKINCOLOR_SEPIA,8, // SKINCOLOR_LEATHER
+ SKINCOLOR_TEA,8, // SKINCOLOR_SALMON
+ SKINCOLOR_PISTACHIO,8, // SKINCOLOR_PINK
+ SKINCOLOR_MOSS,8, // SKINCOLOR_ROSE
+ SKINCOLOR_RUST,8, // SKINCOLOR_BRICK
+ SKINCOLOR_SAPPHIRE,8, // SKINCOLOR_RUBY
+ SKINCOLOR_MINT,8, // SKINCOLOR_RASPBERRY
+ SKINCOLOR_HANDHELD,10, // SKINCOLOR_CHERRY
+ SKINCOLOR_GREEN,6, // SKINCOLOR_RED
+ SKINCOLOR_PINETREE,6, // SKINCOLOR_CRIMSON
+ SKINCOLOR_TOXIC,8, // SKINCOLOR_MAROON
+ SKINCOLOR_CARIBBEAN,10, // SKINCOLOR_FLAME
+ SKINCOLOR_ALGAE,10, // SKINCOLOR_SCARLET
+ SKINCOLOR_MUSTARD,10, // SKINCOLOR_KETCHUP
+ SKINCOLOR_DUSK,8, // SKINCOLOR_DAWN
+ SKINCOLOR_MOONSLAM,8, // SKINCOLOR_SUNSET
+ SKINCOLOR_PERIWINKLE,8, // SKINCOLOR_CREAMSICLE
+ SKINCOLOR_BLUE,8, // SKINCOLOR_ORANGE
+ SKINCOLOR_BLUEBERRY,8, // SKINCOLOR_PUMPKIN
+ SKINCOLOR_NAVY,6, // SKINCOLOR_ROSEWOOD
+ SKINCOLOR_JET,8, // SKINCOLOR_BURGUNDY
+ SKINCOLOR_LIME,8, // SKINCOLOR_TANGERINE
+ SKINCOLOR_CYAN,8, // SKINCOLOR_PEACH
+ SKINCOLOR_CERULEAN,8, // SKINCOLOR_CARAMEL
+ SKINCOLOR_COPPER,10, // SKINCOLOR_CREAM
+ SKINCOLOR_SLATE,8, // SKINCOLOR_GOLD
+ SKINCOLOR_PLATINUM,6, // SKINCOLOR_ROYAL
+ SKINCOLOR_STEEL,8, // SKINCOLOR_BRONZE
+ SKINCOLOR_CREAM,6, // SKINCOLOR_COPPER
+ SKINCOLOR_AQUA,8, // SKINCOLOR_YELLOW
+ SKINCOLOR_KETCHUP,8, // SKINCOLOR_MUSTARD
+ SKINCOLOR_TEAL,8, // SKINCOLOR_OLIVE
+ SKINCOLOR_ROBOHOOD,8, // SKINCOLOR_VOMIT
+ SKINCOLOR_LAVENDER,6, // SKINCOLOR_GARDEN
+ SKINCOLOR_TANGERINE,8, // SKINCOLOR_LIME
+ SKINCOLOR_CHERRY,8, // SKINCOLOR_HANDHELD
+ SKINCOLOR_SALMON,8, // SKINCOLOR_TEA
+ SKINCOLOR_PINK,6, // SKINCOLOR_PISTACHIO
+ SKINCOLOR_ROSE,8, // SKINCOLOR_MOSS
+ SKINCOLOR_FAIRY,10, // SKINCOLOR_CAMOUFLAGE
+ SKINCOLOR_VOMIT,8, // SKINCOLOR_ROBOHOOD
+ SKINCOLOR_RASPBERRY,8, // SKINCOLOR_MINT
+ SKINCOLOR_RED,8, // SKINCOLOR_GREEN
+ SKINCOLOR_CRIMSON,8, // SKINCOLOR_PINETREE
+ SKINCOLOR_PURPLE,8, // SKINCOLOR_EMERALD
+ SKINCOLOR_BYZANTIUM,8, // SKINCOLOR_SWAMP
+ SKINCOLOR_POMEGRANATE,8, // SKINCOLOR_DREAM
+ SKINCOLOR_NOVA,8, // SKINCOLOR_PLAGUE
+ SKINCOLOR_SCARLET,10, // SKINCOLOR_ALGAE
+ SKINCOLOR_FLAME,8, // SKINCOLOR_CARIBBEAN
+ SKINCOLOR_YELLOW,8, // SKINCOLOR_AQUA
+ SKINCOLOR_OLIVE,8, // SKINCOLOR_TEAL
+ SKINCOLOR_PEACH,8, // SKINCOLOR_CYAN
+ SKINCOLOR_LILAC,10, // SKINCOLOR_JAWZ
+ SKINCOLOR_CARAMEL,8, // SKINCOLOR_CERULEAN
+ SKINCOLOR_ROSEWOOD,8, // SKINCOLOR_NAVY
+ SKINCOLOR_ROYAL,8, // SKINCOLOR_PLATINUM
+ SKINCOLOR_GOLD,10, // SKINCOLOR_SLATE
+ SKINCOLOR_BRONZE,10, // SKINCOLOR_STEEL
+ SKINCOLOR_BRICK,10, // SKINCOLOR_RUST
+ SKINCOLOR_BURGUNDY,8, // SKINCOLOR_JET
+ SKINCOLOR_RUBY,6, // SKINCOLOR_SAPPHIRE
+ SKINCOLOR_CREAMSICLE,8, // SKINCOLOR_PERIWINKLE
+ SKINCOLOR_ORANGE,8, // SKINCOLOR_BLUE
+ SKINCOLOR_PUMPKIN,8, // SKINCOLOR_BLUEBERRY
+ SKINCOLOR_PLAGUE,10, // SKINCOLOR_NOVA
+ SKINCOLOR_FUCHSIA,11, // SKINCOLOR_PASTEL
+ SKINCOLOR_SUNSET,10, // SKINCOLOR_MOONSLAM
+ SKINCOLOR_MAUVE,10, // SKINCOLOR_ULTRAVIOLET
+ SKINCOLOR_DAWN,6, // SKINCOLOR_DUSK
+ SKINCOLOR_EMERALD,8, // SKINCOLOR_PURPLE
+ SKINCOLOR_PASTEL,11, // SKINCOLOR_FUCHSIA
+ SKINCOLOR_MAROON,8, // SKINCOLOR_TOXIC
+ SKINCOLOR_ULTRAVIOLET,8, // SKINCOLOR_MAUVE
+ SKINCOLOR_GARDEN,6, // SKINCOLOR_LAVENDER
+ SKINCOLOR_SWAMP,8, // SKINCOLOR_BYZANTIUM
+ SKINCOLOR_DREAM,8, // SKINCOLOR_POMEGRANATE
+ SKINCOLOR_JAWZ,6 // SKINCOLOR_LILAC
+};
+
+UINT8 colortranslations[MAXTRANSLATIONS][16] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // SKINCOLOR_NONE
+ {120, 120, 120, 120, 0, 2, 5, 8, 9, 11, 14, 17, 20, 22, 25, 28}, // SKINCOLOR_WHITE
+ { 0, 1, 2, 3, 5, 7, 9, 12, 13, 15, 18, 20, 23, 25, 27, 30}, // SKINCOLOR_SILVER
+ { 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31}, // SKINCOLOR_GREY
+ { 3, 5, 8, 11, 15, 17, 19, 21, 23, 24, 25, 26, 27, 29, 30, 31}, // SKINCOLOR_NICKEL
+ { 4, 7, 11, 15, 20, 22, 24, 27, 28, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_BLACK
+ {120, 120, 121, 121, 122, 123, 10, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_FAIRY
+ {120, 96, 97, 98, 99, 71, 32, 11, 13, 16, 18, 21, 23, 26, 28, 31}, // SKINCOLOR_POPCORN
+ { 0, 1, 3, 5, 7, 9, 34, 36, 38, 40, 42, 44, 60, 61, 62, 63}, // SKINCOLOR_SEPIA
+ {120, 65, 67, 69, 32, 34, 36, 38, 40, 42, 44, 45, 46, 47, 62, 63}, // SKINCOLOR_BEIGE
+ { 67, 70, 73, 76, 48, 49, 51, 53, 54, 56, 58, 59, 61, 63, 29, 30}, // SKINCOLOR_BROWN
+ { 72, 76, 48, 51, 53, 55, 57, 59, 61, 63, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_LEATHER
+ {120, 120, 120, 121, 121, 122, 123, 124, 126, 127, 129, 131, 133, 135, 137, 139}, // SKINCOLOR_SALMON
+ {120, 121, 121, 122, 144, 145, 146, 147, 148, 149, 150, 151, 134, 136, 138, 140}, // SKINCOLOR_PINK
+ {144, 145, 146, 147, 148, 149, 150, 151, 134, 135, 136, 137, 138, 139, 140, 141}, // SKINCOLOR_ROSE
+ { 64, 67, 70, 73, 146, 147, 148, 150, 118, 118, 119, 119, 156, 159, 141, 143}, // SKINCOLOR_BRICK
+ {120, 121, 144, 145, 147, 149, 132, 133, 134, 136, 198, 198, 199, 255, 30, 31}, // SKINCOLOR_RUBY
+ {120, 121, 122, 123, 124, 125, 126, 127, 128, 130, 131, 134, 136, 137, 139, 140}, // SKINCOLOR_RASPBERRY
+ {120, 65, 67, 69, 71, 124, 125, 127, 132, 133, 135, 136, 138, 139, 140, 141}, // SKINCOLOR_CHERRY
+ {122, 123, 124, 126, 129, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142}, // SKINCOLOR_RED
+ {123, 125, 128, 131, 133, 135, 136, 138, 140, 140, 141, 141, 142, 142, 143, 31}, // SKINCOLOR_CRIMSON
+ {123, 124, 126, 128, 132, 135, 137, 27, 28, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_MAROON
+ {120, 97, 112, 113, 113, 85, 87, 126, 149, 150, 151, 252, 253, 254, 255, 29}, // SKINCOLOR_FLAME
+ { 99, 113, 113, 84, 85, 87, 126, 128, 130, 196, 197, 198, 199, 240, 243, 246}, // SKINCOLOR_SCARLET
+ {103, 113, 113, 84, 85, 88, 127, 130, 131, 133, 134, 136, 138, 139, 141, 143}, // SKINCOLOR_KETCHUP
+ {120, 121, 122, 123, 124, 147, 147, 148, 90, 91, 92, 93, 94, 95, 152, 154}, // SKINCOLOR_DAWN
+ { 98, 112, 113, 84, 85, 87, 89, 149, 150, 251, 252, 206, 238, 240, 243, 246}, // SKINCOLOR_SUNSET
+ {120, 120, 80, 80, 81, 82, 83, 83, 84, 85, 86, 88, 89, 91, 93, 95}, // SKINCOLOR_CREAMSICLE
+ { 80, 81, 82, 83, 84, 85, 86, 88, 89, 91, 94, 95, 154, 156, 158, 159}, // SKINCOLOR_ORANGE
+ { 82, 83, 84, 85, 87, 89, 90, 92, 94, 152, 153, 155, 157, 159, 141, 142}, // SKINCOLOR_PUMPKIN
+ { 83, 85, 88, 90, 92, 94, 152, 153, 154, 156, 157, 159, 140, 141, 142, 143}, // SKINCOLOR_ROSEWOOD
+ { 84, 86, 89, 91, 152, 154, 155, 157, 158, 159, 140, 141, 142, 143, 31, 31}, // SKINCOLOR_BURGUNDY
+ { 98, 98, 112, 112, 113, 113, 84, 85, 87, 89, 91, 93, 95, 153, 156, 159}, // SKINCOLOR_TANGERINE
+ {120, 80, 66, 70, 72, 76, 148, 149, 150, 151, 153, 154, 156, 61, 62, 63}, // SKINCOLOR_PEACH
+ { 64, 66, 68, 70, 72, 74, 76, 78, 48, 50, 52, 54, 56, 58, 60, 62}, // SKINCOLOR_CARAMEL
+ {120, 120, 96, 96, 97, 82, 84, 77, 50, 54, 57, 59, 61, 63, 29, 31}, // SKINCOLOR_CREAM
+ {112, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119}, // SKINCOLOR_GOLD
+ { 97, 112, 113, 113, 114, 78, 53, 252, 252, 253, 253, 254, 255, 29, 30, 31}, // SKINCOLOR_ROYAL
+ {112, 113, 114, 115, 116, 117, 118, 119, 156, 157, 158, 159, 141, 141, 142, 143}, // SKINCOLOR_BRONZE
+ {120, 99, 113, 114, 116, 117, 119, 61, 63, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_COPPER
+ { 96, 97, 98, 100, 101, 102, 104, 113, 114, 115, 116, 117, 118, 119, 156, 159}, // SKINCOLOR_YELLOW
+ { 96, 98, 99, 112, 113, 114, 114, 106, 106, 107, 107, 108, 108, 109, 110, 111}, // SKINCOLOR_MUSTARD
+ {105, 105, 105, 106, 106, 107, 107, 108, 108, 109, 109, 110, 110, 111, 111, 31}, // SKINCOLOR_OLIVE
+ {121, 144, 145, 72, 73, 84, 114, 115, 107, 108, 109, 183, 223, 207, 30, 246}, // SKINCOLOR_VOMIT
+ { 98, 99, 112, 101, 113, 114, 106, 179, 180, 180, 181, 182, 183, 173, 174, 175}, // SKINCOLOR_GARDEN
+ { 96, 97, 99, 100, 102, 104, 160, 162, 164, 166, 168, 171, 223, 223, 207, 31}, // SKINCOLOR_LIME
+ { 98, 104, 105, 105, 106, 167, 168, 169, 170, 171, 172, 173, 174, 175, 30, 31}, // SKINCOLOR_HANDHELD
+ {120, 120, 176, 176, 176, 177, 177, 178, 178, 179, 179, 180, 180, 181, 182, 183}, // SKINCOLOR_TEA
+ {120, 120, 176, 176, 177, 177, 178, 179, 165, 166, 167, 168, 169, 170, 171, 172}, // SKINCOLOR_PISTACHIO
+ {178, 178, 178, 179, 179, 180, 181, 182, 183, 172, 172, 173, 173, 174, 174, 175}, // SKINCOLOR_MOSS
+ { 64, 66, 69, 32, 34, 37, 40, 182, 171, 172, 172, 173, 173, 174, 174, 175}, // SKINCOLOR_CAMOUFLAGE
+ {120, 176, 160, 165, 167, 168, 169, 182, 182, 171, 60, 61, 63, 29, 30, 31}, // SKINCOLOR_ROBOHOOD
+ {120, 176, 176, 176, 177, 163, 164, 165, 167, 221, 221, 222, 223, 207, 207, 31}, // SKINCOLOR_MINT
+ {160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175}, // SKINCOLOR_GREEN
+ {161, 163, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 30, 30, 31}, // SKINCOLOR_PINETREE
+ {160, 184, 184, 185, 185, 186, 186, 187, 187, 188, 188, 189, 189, 190, 191, 175}, // SKINCOLOR_EMERALD
+ {160, 184, 185, 186, 187, 188, 189, 190, 191, 191, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_SWAMP
+ {120, 120, 80, 80, 81, 177, 162, 164, 228, 228, 204, 204, 205, 205, 206, 207}, // SKINCOLOR_DREAM
+ {176, 160, 184, 185, 186, 187, 188, 230, 230, 206, 206, 207, 28, 29, 30, 31}, // SKINCOLOR_PLAGUE
+ {208, 209, 210, 211, 213, 220, 216, 167, 168, 188, 188, 189, 190, 191, 30, 31}, // SKINCOLOR_ALGAE
+ {120, 176, 177, 160, 185, 220, 216, 217, 221, 230, 206, 206, 254, 255, 29, 31}, // SKINCOLOR_CARIBBEAN
+ {120, 208, 208, 210, 212, 214, 220, 220, 220, 221, 221, 222, 222, 223, 223, 191}, // SKINCOLOR_AQUA
+ {210, 213, 220, 220, 220, 216, 216, 221, 221, 221, 222, 222, 223, 223, 191, 31}, // SKINCOLOR_TEAL
+ {120, 120, 208, 208, 209, 210, 211, 212, 213, 215, 216, 217, 218, 219, 222, 223}, // SKINCOLOR_CYAN
+ {120, 120, 208, 209, 210, 226, 215, 216, 217, 229, 229, 205, 205, 206, 207, 31}, // SKINCOLOR_JAWZ
+ {208, 209, 211, 213, 215, 216, 216, 217, 217, 218, 218, 219, 205, 206, 207, 207}, // SKINCOLOR_CERULEAN
+ {211, 212, 213, 215, 216, 218, 219, 205, 206, 206, 207, 207, 28, 29, 30, 31}, // SKINCOLOR_NAVY
+ {120, 0, 0, 200, 200, 201, 11, 14, 17, 218, 222, 223, 238, 240, 243, 246}, // SKINCOLOR_PLATINUM
+ {120, 120, 200, 200, 200, 201, 201, 201, 202, 202, 202, 203, 204, 205, 206, 207}, // SKINCOLOR_SLATE
+ {120, 200, 200, 201, 201, 202, 202, 203, 203, 204, 204, 205, 205, 206, 207, 31}, // SKINCOLOR_STEEL
+ { 64, 66, 68, 70, 32, 34, 36, 203, 204, 205, 24, 25, 26, 28, 29, 31}, // SKINCOLOR_RUST
+ {225, 226, 227, 228, 229, 205, 205, 206, 207, 207, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_JET
+ {208, 209, 211, 213, 215, 217, 229, 230, 232, 234, 236, 238, 240, 242, 244, 246}, // SKINCOLOR_SAPPHIRE
+ {120, 120, 224, 225, 226, 202, 227, 228, 229, 230, 231, 233, 235, 237, 239, 241}, // SKINCOLOR_PERIWINKLE
+ {224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 235, 236, 238, 242, 244, 246}, // SKINCOLOR_BLUE
+ {226, 228, 229, 230, 232, 233, 235, 237, 239, 240, 242, 244, 246, 31, 31, 31}, // SKINCOLOR_BLUEBERRY
+ {120, 112, 82, 83, 84, 124, 248, 228, 228, 204, 205, 206, 207, 29, 30, 31}, // SKINCOLOR_NOVA
+ {120, 208, 209, 210, 211, 226, 202, 249, 194, 195, 196, 197, 198, 199, 255, 30}, // SKINCOLOR_PASTEL
+ {120, 224, 201, 226, 202, 249, 250, 196, 197, 198, 199, 140, 141, 142, 143, 31}, // SKINCOLOR_MOONSLAM
+ {120, 64, 81, 122, 192, 249, 203, 221, 221, 219, 219, 223, 223, 191, 191, 31}, // SKINCOLOR_ULTRAVIOLET
+ {121, 145, 192, 249, 250, 251, 204, 204, 205, 205, 206, 206, 207, 29, 30, 31}, // SKINCOLOR_DUSK
+ {120, 96, 64, 121, 67, 144, 123, 192, 193, 194, 195, 196, 197, 198, 199, 30}, // SKINCOLOR_BUBBLEGUM
+ {121, 145, 192, 192, 193, 194, 195, 196, 196, 197, 197, 198, 198, 199, 30, 31}, // SKINCOLOR_PURPLE
+ {120, 122, 124, 125, 126, 150, 196, 197, 198, 198, 199, 199, 240, 242, 244, 246}, // SKINCOLOR_FUCHSIA
+ {120, 120, 176, 176, 177, 6, 8, 10, 249, 250, 196, 197, 198, 199, 143, 31}, // SKINCOLOR_TOXIC
+ { 96, 97, 98, 112, 113, 73, 146, 248, 249, 251, 205, 205, 206, 207, 29, 31}, // SKINCOLOR_MAUVE
+ {121, 145, 192, 248, 249, 250, 251, 252, 252, 253, 253, 254, 254, 255, 30, 31}, // SKINCOLOR_LAVENDER
+ {144, 248, 249, 250, 251, 252, 253, 254, 255, 255, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_BYZANTIUM
+ {144, 145, 146, 147, 148, 149, 150, 251, 251, 252, 252, 253, 254, 255, 29, 30}, // SKINCOLOR_POMEGRANATE
+ {120, 120, 120, 121, 121, 122, 122, 123, 192, 248, 249, 250, 251, 252, 253, 254}, // SKINCOLOR_LILAC
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 96, 100, 104, 113, 116, 119}, // SKINCOLOR_SUPER1
+ {120, 120, 120, 120, 120, 120, 120, 120, 96, 98, 101, 104, 113, 115, 117, 119}, // SKINCOLOR_SUPER2
+ {120, 120, 120, 120, 120, 120, 96, 98, 100, 102, 104, 113, 114, 116, 117, 119}, // SKINCOLOR_SUPER3
+ {120, 120, 120, 120, 96, 97, 99, 100, 102, 104, 113, 114, 115, 116, 117, 119}, // SKINCOLOR_SUPER4
+ {120, 120, 96, 120, 120, 120, 120, 120, 104, 113, 114, 115, 116, 117, 118, 119}, // SKINCOLOR_SUPER5
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 80, 82, 85, 115, 117, 119}, // SKINCOLOR_TSUPER1
+ {120, 120, 120, 120, 120, 120, 120, 120, 80, 81, 83, 85, 115, 116, 117, 119}, // SKINCOLOR_TSUPER2
+ {120, 120, 120, 120, 120, 120, 80, 81, 82, 83, 85, 115, 116, 117, 118, 119}, // SKINCOLOR_TSUPER3
+ {120, 120, 120, 120, 80, 81, 82, 83, 84, 85, 115, 115, 116, 117, 118, 119}, // SKINCOLOR_TSUPER4
+ {120, 120, 80, 80, 81, 82, 83, 84, 85, 115, 115, 116, 117, 117, 118, 119}, // SKINCOLOR_TSUPER5
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 121, 123, 125, 127, 129, 132}, // SKINCOLOR_KSUPER1
+ {120, 120, 120, 120, 120, 120, 120, 120, 121, 122, 124, 125, 127, 128, 130, 132}, // SKINCOLOR_KSUPER2
+ {120, 120, 120, 120, 120, 120, 121, 122, 123, 124, 125, 127, 128, 129, 130, 132}, // SKINCOLOR_KSUPER3
+ {120, 120, 120, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132}, // SKINCOLOR_KSUPER4
+ {120, 120, 121, 121, 122, 123, 124, 125, 126, 126, 127, 128, 129, 130, 131, 132}, // SKINCOLOR_KSUPER5
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 0, 122, 124, 248, 251, 255}, // SKINCOLOR_PSUPER1
+ {120, 120, 120, 120, 120, 120, 120, 120, 0, 121, 122, 124, 248, 250, 252, 255}, // SKINCOLOR_PSUPER2
+ {120, 120, 120, 120, 120, 120, 0, 121, 122, 123, 124, 248, 249, 251, 253, 255}, // SKINCOLOR_PSUPER3
+ {120, 120, 120, 120, 0, 121, 122, 123, 124, 248, 249, 250, 251, 252, 253, 255}, // SKINCOLOR_PSUPER4
+ {120, 120, 0, 121, 122, 123, 124, 248, 248, 249, 250, 251, 252, 253, 254, 255}, // SKINCOLOR_PSUPER5
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 224, 225, 227, 228, 230, 232}, // SKINCOLOR_BSUPER1
+ {120, 120, 120, 120, 120, 120, 120, 120, 224, 225, 226, 227, 228, 229, 230, 232}, // SKINCOLOR_BSUPER2
+ {120, 120, 120, 120, 120, 120, 224, 224, 225, 226, 227, 228, 229, 230, 231, 232}, // SKINCOLOR_BSUPER3
+ {120, 120, 120, 120, 224, 224, 225, 226, 226, 227, 228, 229, 229, 230, 231, 232}, // SKINCOLOR_BSUPER4
+ {120, 120, 224, 224, 225, 225, 226, 227, 227, 228, 228, 229, 230, 230, 231, 232}, // SKINCOLOR_BSUPER5
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 208, 210, 212, 215, 220, 222}, // SKINCOLOR_ASUPER1
+ {120, 120, 120, 120, 120, 120, 120, 120, 208, 209, 211, 213, 215, 220, 221, 223}, // SKINCOLOR_ASUPER2
+ {120, 120, 120, 120, 120, 120, 208, 209, 210, 211, 212, 213, 215, 220, 221, 223}, // SKINCOLOR_ASUPER3
+ {120, 120, 120, 120, 208, 209, 210, 211, 212, 213, 214, 215, 220, 221, 222, 223}, // SKINCOLOR_ASUPER4
+ {120, 120, 208, 208, 209, 210, 211, 211, 212, 213, 214, 215, 220, 221, 222, 223}, // SKINCOLOR_ASUPER5
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 176, 160, 163, 167, 171, 175}, // SKINCOLOR_GSUPER1
+ {120, 120, 120, 120, 120, 120, 120, 120, 176, 176, 160, 163, 166, 169, 172, 175}, // SKINCOLOR_GSUPER2
+ {120, 120, 120, 120, 120, 120, 176, 176, 160, 162, 164, 166, 168, 170, 172, 175}, // SKINCOLOR_GSUPER3
+ {120, 120, 120, 120, 176, 176, 176, 160, 161, 163, 165, 167, 169, 171, 173, 175}, // SKINCOLOR_GSUPER4
+ {120, 120, 176, 176, 176, 160, 161, 163, 164, 166, 167, 169, 170, 172, 173, 175}, // SKINCOLOR_GSUPER5
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120}, // SKINCOLOR_WSUPER1
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 0, 4, 9}, // SKINCOLOR_WSUPER2
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 0, 2, 4, 6, 8, 11}, // SKINCOLOR_WSUPER3
+ {120, 120, 120, 120, 120, 120, 120, 0, 1, 3, 4, 6, 8, 9, 11, 13}, // SKINCOLOR_WSUPER4
+ {120, 120, 120, 120, 0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 15}, // SKINCOLOR_WSUPER5
+ {120, 120, 120, 120, 120, 120, 120, 120, 120, 120, 96, 98, 99, 81, 73, 79}, // SKINCOLOR_CSUPER1
+ {120, 120, 120, 120, 120, 120, 120, 120, 96, 97, 98, 81, 81, 71, 75, 79}, // SKINCOLOR_CSUPER2
+ {120, 120, 120, 120, 120, 120, 96, 97, 98, 99, 81, 81, 70, 73, 76, 79}, // SKINCOLOR_CSUPER3
+ {120, 120, 120, 120, 96, 96, 97, 98, 99, 81, 81, 70, 72, 74, 76, 79}, // SKINCOLOR_CSUPER4
+ {120, 120, 96, 96, 97, 98, 98, 99, 81, 81, 69, 71, 73, 75, 77, 79}, // SKINCOLOR_CSUPER5
+};
+
+// Define for getting accurate color brightness readings according to how the human eye sees them.
+// https://en.wikipedia.org/wiki/Relative_luminance
+// 0.2126 to red
+// 0.7152 to green
+// 0.0722 to blue
+// (See this same define in hw_md2.c!)
+#define SETBRIGHTNESS(brightness,r,g,b) \
+ brightness = (UINT8)(((1063*((UINT16)r)/5000) + (3576*((UINT16)g)/5000) + (361*((UINT16)b)/5000)) / 3)
+
+/** \brief Generates the rainbow colourmaps that are used when a player has the invincibility power
+
+ \param dest_colormap colormap to populate
+ \param skincolor translation color
+*/
+void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor)
+{
+ INT32 i;
+ RGBA_t color;
+ UINT8 brightness;
+ INT32 j;
+ UINT8 colorbrightnesses[16];
+ UINT16 brightdif;
+ INT32 temp;
+
+ // first generate the brightness of all the colours of that skincolour
+ for (i = 0; i < 16; i++)
+ {
+ color = V_GetColor(colortranslations[skincolor][i]);
+ SETBRIGHTNESS(colorbrightnesses[i], color.s.red, color.s.green, color.s.blue);
+ }
+
+ // next, for every colour in the palette, choose the transcolor that has the closest brightness
+ for (i = 0; i < NUM_PALETTE_ENTRIES; i++)
+ {
+ if (i == 0 || i == 31 || i == 120) // pure black and pure white don't change
+ {
+ dest_colormap[i] = (UINT8)i;
+ continue;
+ }
+ color = V_GetColor(i);
+ SETBRIGHTNESS(brightness, color.s.red, color.s.green, color.s.blue);
+ brightdif = 256;
+ for (j = 0; j < 16; j++)
+ {
+ temp = abs((INT16)brightness - (INT16)colorbrightnesses[j]);
+ if (temp < brightdif)
+ {
+ brightdif = (UINT16)temp;
+ dest_colormap[i] = colortranslations[skincolor][j];
+ }
+ }
+ }
+}
+
+#undef SETBRIGHTNESS
+
+/** \brief Generates a translation colormap for Kart, to replace R_GenerateTranslationColormap in r_draw.c
+
+ \param dest_colormap colormap to populate
+ \param skinnum number of skin, TC_DEFAULT or TC_BOSS
+ \param color translation color
+
+ \return void
+*/
+void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color)
+{
+ INT32 i;
+ INT32 starttranscolor;
+
+ // Handle a couple of simple special cases
+ if (skinnum == TC_BOSS
+ || skinnum == TC_ALLWHITE
+ || skinnum == TC_METALSONIC
+ || skinnum == TC_BLINK
+ || color == SKINCOLOR_NONE)
+ {
+ for (i = 0; i < NUM_PALETTE_ENTRIES; i++)
+ {
+ if (skinnum == TC_ALLWHITE)
+ dest_colormap[i] = 0;
+ else if (skinnum == TC_BLINK)
+ dest_colormap[i] = colortranslations[color][3];
+ else
+ dest_colormap[i] = (UINT8)i;
+ }
+
+ // White!
+ if (skinnum == TC_BOSS)
+ dest_colormap[31] = 0;
+ else if (skinnum == TC_METALSONIC)
+ dest_colormap[239] = 0;
+
+ return;
+ }
+ else if (skinnum == TC_RAINBOW)
+ {
+ K_RainbowColormap(dest_colormap, color);
+ return;
+ }
+
+ starttranscolor = (skinnum != TC_DEFAULT) ? skins[skinnum].starttranscolor : DEFAULT_STARTTRANSCOLOR;
+
+ // Fill in the entries of the palette that are fixed
+ for (i = 0; i < starttranscolor; i++)
+ dest_colormap[i] = (UINT8)i;
+
+ for (i = (UINT8)(starttranscolor + 16); i < NUM_PALETTE_ENTRIES; i++)
+ dest_colormap[i] = (UINT8)i;
+
+ // Build the translated ramp
+ for (i = 0; i < SKIN_RAMP_LENGTH; i++)
+ {
+ // Sryder 2017-10-26: What was here before was most definitely not particularly readable, check above for new color translation table
+ dest_colormap[starttranscolor + i] = colortranslations[color][i];
+ }
+}
+
+/** \brief Pulls kart color by name, to replace R_GetColorByName in r_draw.c
+
+ \param name color name
+
+ \return 0
+*/
+UINT8 K_GetKartColorByName(const char *name)
+{
+ UINT8 color = (UINT8)atoi(name);
+ if (color > 0 && color < MAXSKINCOLORS)
+ return color;
+ for (color = 1; color < MAXSKINCOLORS; color++)
+ if (!stricmp(KartColor_Names[color], name))
+ return color;
+ return 0;
+}
+
+//}
+
+//{ SRB2kart Net Variables
+
+void K_RegisterKartStuff(void)
+{
+ CV_RegisterVar(&cv_sneaker);
+ CV_RegisterVar(&cv_rocketsneaker);
+ CV_RegisterVar(&cv_invincibility);
+ CV_RegisterVar(&cv_banana);
+ CV_RegisterVar(&cv_eggmanmonitor);
+ CV_RegisterVar(&cv_orbinaut);
+ CV_RegisterVar(&cv_jawz);
+ CV_RegisterVar(&cv_mine);
+ CV_RegisterVar(&cv_ballhog);
+ CV_RegisterVar(&cv_selfpropelledbomb);
+ CV_RegisterVar(&cv_grow);
+ CV_RegisterVar(&cv_shrink);
+ CV_RegisterVar(&cv_thundershield);
+ CV_RegisterVar(&cv_hyudoro);
+ CV_RegisterVar(&cv_pogospring);
+ CV_RegisterVar(&cv_kitchensink);
+
+ CV_RegisterVar(&cv_triplesneaker);
+ CV_RegisterVar(&cv_triplebanana);
+ CV_RegisterVar(&cv_decabanana);
+ CV_RegisterVar(&cv_tripleorbinaut);
+ CV_RegisterVar(&cv_quadorbinaut);
+ CV_RegisterVar(&cv_dualjawz);
+
+ CV_RegisterVar(&cv_kartminimap);
+ CV_RegisterVar(&cv_kartcheck);
+ CV_RegisterVar(&cv_kartinvinsfx);
+ CV_RegisterVar(&cv_kartspeed);
+ CV_RegisterVar(&cv_kartbumpers);
+ CV_RegisterVar(&cv_kartfrantic);
+ CV_RegisterVar(&cv_kartcomeback);
+ CV_RegisterVar(&cv_kartencore);
+ CV_RegisterVar(&cv_kartvoterulechanges);
+ CV_RegisterVar(&cv_kartspeedometer);
+ CV_RegisterVar(&cv_kartvoices);
+ CV_RegisterVar(&cv_karteliminatelast);
+ CV_RegisterVar(&cv_votetime);
+
+ CV_RegisterVar(&cv_kartdebugitem);
+ CV_RegisterVar(&cv_kartdebugamount);
+ CV_RegisterVar(&cv_kartdebugshrink);
+ CV_RegisterVar(&cv_kartdebugdistribution);
+ CV_RegisterVar(&cv_kartdebughuddrop);
+
+ CV_RegisterVar(&cv_kartdebugcheckpoint);
+ CV_RegisterVar(&cv_kartdebugnodes);
+ CV_RegisterVar(&cv_kartdebugcolorize);
+}
+
+//}
+
+boolean K_IsPlayerLosing(player_t *player)
+{
+ INT32 winningpos = 1;
+ UINT8 i, pcount = 0;
+
+ if (player->kartstuff[k_position] == 1)
+ return false;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+ if (players[i].kartstuff[k_position] > pcount)
+ pcount = players[i].kartstuff[k_position];
+ }
+
+ if (pcount <= 1)
+ return false;
+
+ winningpos = pcount/2;
+ if (pcount % 2) // any remainder?
+ winningpos++;
+
+ return (player->kartstuff[k_position] > winningpos);
+}
+
+boolean K_IsPlayerWanted(player_t *player)
+{
+ UINT8 i;
+ if (!(G_BattleGametype()))
+ return false;
+ for (i = 0; i < 4; i++)
+ {
+ if (battlewanted[i] == -1)
+ break;
+ if (player == &players[battlewanted[i]])
+ return true;
+ }
+ return false;
+}
+
+//{ SRB2kart Roulette Code - Position Based
+
+#define NUMKARTODDS 80
+
+// Less ugly 2D arrays
+static INT32 K_KartItemOddsRace[NUMKARTRESULTS][10] =
+{
+ //P-Odds 0 1 2 3 4 5 6 7 8 9
+ /*Sneaker*/ {20, 0, 0, 4, 6, 7, 0, 0, 0, 0 }, // Sneaker
+ /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 1, 4, 5, 3, 0 }, // Rocket Sneaker
+ /*Invincibility*/ { 0, 0, 0, 0, 0, 1, 4, 6,10, 0 }, // Invincibility
+ /*Banana*/ { 0, 9, 4, 2, 1, 0, 0, 0, 0, 0 }, // Banana
+ /*Eggman Monitor*/ { 0, 3, 2, 1, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor
+ /*Orbinaut*/ { 0, 7, 6, 4, 2, 0, 0, 0, 0, 0 }, // Orbinaut
+ /*Jawz*/ { 0, 0, 3, 2, 1, 1, 0, 0, 0, 0 }, // Jawz
+ /*Mine*/ { 0, 0, 2, 2, 1, 0, 0, 0, 0, 0 }, // Mine
+ /*Ballhog*/ { 0, 0, 0, 2, 1, 0, 0, 0, 0, 0 }, // Ballhog
+ /*Self-Propelled Bomb*/ { 0, 0, 1, 2, 3, 4, 2, 2, 0,20 }, // Self-Propelled Bomb
+ /*Grow*/ { 0, 0, 0, 0, 0, 0, 2, 5, 7, 0 }, // Grow
+ /*Shrink*/ { 0, 0, 0, 0, 0, 0, 0, 2, 0, 0 }, // Shrink
+ /*Thunder Shield*/ { 0, 1, 2, 0, 0, 0, 0, 0, 0, 0 }, // Thunder Shield
+ /*Hyudoro*/ { 0, 0, 0, 0, 1, 2, 1, 0, 0, 0 }, // Hyudoro
+ /*Pogo Spring*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring
+ /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
+ /*Sneaker x3*/ { 0, 0, 0, 0, 3, 7, 9, 2, 0, 0 }, // Sneaker x3
+ /*Banana x3*/ { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0 }, // Banana x3
+ /*Banana x10*/ { 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, // Banana x10
+ /*Orbinaut x3*/ { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, // Orbinaut x3
+ /*Orbinaut x4*/ { 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 }, // Orbinaut x4
+ /*Jawz x2*/ { 0, 0, 0, 1, 2, 0, 0, 0, 0, 0 } // Jawz x2
+};
+
+static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][6] =
+{
+ //P-Odds 0 1 2 3 4 5
+ /*Sneaker*/ { 3, 2, 2, 2, 0, 2 }, // Sneaker
+ /*Rocket Sneaker*/ { 0, 0, 0, 0, 0, 0 }, // Rocket Sneaker
+ /*Invincibility*/ { 0, 1, 2, 3, 4, 2 }, // Invincibility
+ /*Banana*/ { 2, 1, 0, 0, 0, 0 }, // Banana
+ /*Eggman Monitor*/ { 1, 1, 0, 0, 0, 0 }, // Eggman Monitor
+ /*Orbinaut*/ { 6, 2, 1, 0, 0, 0 }, // Orbinaut
+ /*Jawz*/ { 3, 3, 3, 2, 0, 2 }, // Jawz
+ /*Mine*/ { 2, 3, 3, 1, 0, 2 }, // Mine
+ /*Ballhog*/ { 0, 1, 2, 1, 0, 2 }, // Ballhog
+ /*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 0 }, // Self-Propelled Bomb
+ /*Grow*/ { 0, 0, 1, 2, 4, 2 }, // Grow
+ /*Shrink*/ { 0, 0, 0, 0, 0, 0 }, // Shrink
+ /*Thunder Shield*/ { 0, 0, 0, 0, 0, 0 }, // Thunder Shield
+ /*Hyudoro*/ { 1, 1, 0, 0, 0, 0 }, // Hyudoro
+ /*Pogo Spring*/ { 1, 1, 0, 0, 0, 0 }, // Pogo Spring
+ /*Kitchen Sink*/ { 0, 0, 0, 0, 0, 0 }, // Kitchen Sink
+ /*Sneaker x3*/ { 0, 0, 0, 2, 4, 2 }, // Sneaker x3
+ /*Banana x3*/ { 1, 2, 1, 0, 0, 0 }, // Banana x3
+ /*Banana x10*/ { 0, 0, 1, 1, 0, 2 }, // Banana x10
+ /*Orbinaut x3*/ { 0, 1, 2, 1, 0, 0 }, // Orbinaut x3
+ /*Orbinaut x4*/ { 0, 0, 1, 3, 4, 2 }, // Orbinaut x4
+ /*Jawz x2*/ { 0, 0, 1, 2, 4, 2 } // Jawz x2
+};
+
+/** \brief Item Roulette for Kart
+
+ \param player player
+ \param getitem what item we're looking for
+
+ \return void
+*/
+static void K_KartGetItemResult(player_t *player, SINT8 getitem)
+{
+ if (getitem == KITEM_SPB || getitem == KITEM_SHRINK) // Indirect items
+ indirectitemcooldown = 20*TICRATE;
+ if (getitem == KITEM_HYUDORO) // Hyudoro cooldown
+ hyubgone = 5*TICRATE;
+
+ switch (getitem)
+ {
+ // Special roulettes first, then the generic ones are handled by default
+ case KRITEM_TRIPLESNEAKER: // Sneaker x3
+ player->kartstuff[k_itemtype] = KITEM_SNEAKER;
+ player->kartstuff[k_itemamount] = 3;
+ break;
+ case KRITEM_TRIPLEBANANA: // Banana x3
+ player->kartstuff[k_itemtype] = KITEM_BANANA;
+ player->kartstuff[k_itemamount] = 3;
+ break;
+ case KRITEM_TENFOLDBANANA: // Banana x10
+ player->kartstuff[k_itemtype] = KITEM_BANANA;
+ player->kartstuff[k_itemamount] = 10;
+ break;
+ case KRITEM_TRIPLEORBINAUT: // Orbinaut x3
+ player->kartstuff[k_itemtype] = KITEM_ORBINAUT;
+ player->kartstuff[k_itemamount] = 3;
+ break;
+ case KRITEM_QUADORBINAUT: // Orbinaut x4
+ player->kartstuff[k_itemtype] = KITEM_ORBINAUT;
+ player->kartstuff[k_itemamount] = 4;
+ break;
+ case KRITEM_DUALJAWZ: // Jawz x2
+ player->kartstuff[k_itemtype] = KITEM_JAWZ;
+ player->kartstuff[k_itemamount] = 2;
+ break;
+ default:
+ if (getitem <= 0 || getitem >= NUMKARTRESULTS) // Sad (Fallback)
+ {
+ if (getitem != 0)
+ CONS_Printf("ERROR: P_KartGetItemResult - Item roulette gave bad item (%d) :(\n", getitem);
+ player->kartstuff[k_itemtype] = KITEM_SAD;
+ }
+ else
+ player->kartstuff[k_itemtype] = getitem;
+ player->kartstuff[k_itemamount] = 1;
+ break;
+ }
+}
+
+/** \brief Item Roulette for Kart
+
+ \param player player object passed from P_KartPlayerThink
+
+ \return void
+*/
+
+static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush)
+{
+ const INT32 distvar = (64*14);
+ INT32 newodds;
+ INT32 i;
+ UINT8 pingame = 0, pexiting = 0;
+ boolean thunderisout = false;
+ SINT8 first = -1, second = -1;
+ INT32 secondist = 0;
+ boolean itemenabled[NUMKARTRESULTS-1] = {
+ cv_sneaker.value,
+ cv_rocketsneaker.value,
+ cv_invincibility.value,
+ cv_banana.value,
+ cv_eggmanmonitor.value,
+ cv_orbinaut.value,
+ cv_jawz.value,
+ cv_mine.value,
+ cv_ballhog.value,
+ cv_selfpropelledbomb.value,
+ cv_grow.value,
+ cv_shrink.value,
+ cv_thundershield.value,
+ cv_hyudoro.value,
+ cv_pogospring.value,
+ cv_kitchensink.value,
+ cv_triplesneaker.value,
+ cv_triplebanana.value,
+ cv_decabanana.value,
+ cv_tripleorbinaut.value,
+ cv_quadorbinaut.value,
+ cv_dualjawz.value
+ };
+
+ I_Assert(item > KITEM_NONE); // too many off by one scenarioes.
+
+ if (!itemenabled[item-1] && !modeattacking)
+ return 0;
+
+ if (G_BattleGametype())
+ newodds = K_KartItemOddsBattle[item-1][pos];
+ else
+ newodds = K_KartItemOddsRace[item-1][pos];
+
+ // Base multiplication to ALL item odds to simulate fractional precision
+ newodds *= 4;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+
+ if (!G_BattleGametype() || players[i].kartstuff[k_bumper])
+ pingame++;
+
+ if (players[i].exiting)
+ pexiting++;
+
+ if (players[i].mo)
+ {
+ if (players[i].kartstuff[k_itemtype] == KITEM_THUNDERSHIELD)
+ thunderisout = true;
+
+ if (!G_BattleGametype())
+ {
+ if (players[i].kartstuff[k_position] == 1 && first == -1)
+ first = i;
+ if (players[i].kartstuff[k_position] == 2 && second == -1)
+ second = i;
+ }
+ }
+ }
+
+ if (first != -1 && second != -1) // calculate 2nd's distance from 1st, for SPB
+ {
+ secondist = P_AproxDistance(P_AproxDistance(players[first].mo->x - players[second].mo->x,
+ players[first].mo->y - players[second].mo->y),
+ players[first].mo->z - players[second].mo->z) / mapobjectscale;
+ if (franticitems)
+ secondist = (15 * secondist) / 14;
+ secondist = ((28 + (8-pingame)) * secondist) / 28;
+ }
+
+ // POWERITEMODDS handles all of the "frantic item" related functionality, for all of our powerful items.
+ // First, it multiplies it by 2 if franticitems is true; easy-peasy.
+ // Next, it multiplies it again if it's in SPB mode and 2nd needs to apply pressure to 1st.
+ // Then, it multiplies it further if the player count isn't equal to 8.
+ // This is done to make low player count races more interesting and high player count rates more fair.
+ // (2P normal would be about halfway between 8P normal and 8P frantic.)
+ // (This scaling is not done for SPB Rush, so that catchup strength is not weakened.)
+ // Lastly, it *divides* it by your mashed value, which was determined in K_KartItemRoulette, for lesser items needed in a pinch.
+
+#define PLAYERSCALING (8 - (spbrush ? 2 : pingame))
+
+#define POWERITEMODDS(odds) {\
+ if (franticitems) \
+ odds <<= 1; \
+ odds = FixedMul(odds<> FRACBITS; \
+ if (mashed > 0) \
+ odds = FixedDiv(odds<> FRACBITS; \
+}
+
+#define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime)
+
+ switch (item)
+ {
+ case KITEM_ROCKETSNEAKER:
+ case KITEM_JAWZ:
+ case KITEM_BALLHOG:
+ case KRITEM_TRIPLESNEAKER:
+ case KRITEM_TRIPLEBANANA:
+ case KRITEM_TENFOLDBANANA:
+ case KRITEM_TRIPLEORBINAUT:
+ case KRITEM_QUADORBINAUT:
+ case KRITEM_DUALJAWZ:
+ POWERITEMODDS(newodds);
+ break;
+ case KITEM_INVINCIBILITY:
+ case KITEM_MINE:
+ case KITEM_GROW:
+ if (COOLDOWNONSTART)
+ newodds = 0;
+ else
+ POWERITEMODDS(newodds);
+ break;
+ case KITEM_SPB:
+ if (((indirectitemcooldown > 0) || (pexiting > 0) || (secondist/distvar < 3))
+ && (pos != 9)) // Force SPB
+ newodds = 0;
+ else
+ newodds *= min((secondist/distvar)-4, 3); // POWERITEMODDS(newodds);
+ break;
+ case KITEM_SHRINK:
+ if ((indirectitemcooldown > 0) || (pingame-1 <= pexiting) || COOLDOWNONSTART)
+ newodds = 0;
+ else
+ POWERITEMODDS(newodds);
+ break;
+ case KITEM_THUNDERSHIELD:
+ if (thunderisout)
+ newodds = 0;
+ else
+ POWERITEMODDS(newodds);
+ case KITEM_HYUDORO:
+ if ((hyubgone > 0) || COOLDOWNONSTART)
+ newodds = 0;
+ break;
+ default:
+ break;
+ }
+
+#undef POWERITEMODDS
+
+ return newodds;
+}
+
+//{ SRB2kart Roulette Code - Distance Based, no waypoints
+
+static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT32 bestbumper, boolean spbrush, boolean dontforcespb)
+{
+ const INT32 distvar = (64*14);
+ INT32 i;
+ INT32 pdis = 0, useodds = 0;
+ UINT8 disttable[14];
+ UINT8 distlen = 0;
+ boolean oddsvalid[10];
+
+ for (i = 0; i < 10; i++)
+ {
+ INT32 j;
+ boolean available = false;
+
+ if (G_BattleGametype() && i > 5)
+ {
+ oddsvalid[i] = false;
+ break;
+ }
+
+ for (j = 1; j < NUMKARTRESULTS; j++)
+ {
+ if (K_KartGetItemOdds(i, j, mashed, spbrush) > 0)
+ {
+ available = true;
+ break;
+ }
+ }
+
+ oddsvalid[i] = available;
+ }
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i] && !players[i].spectator && players[i].mo
+ && players[i].kartstuff[k_position] < player->kartstuff[k_position])
+ pdis += P_AproxDistance(P_AproxDistance(players[i].mo->x - player->mo->x,
+ players[i].mo->y - player->mo->y),
+ players[i].mo->z - player->mo->z) / mapobjectscale
+ * (pingame - players[i].kartstuff[k_position])
+ / max(1, ((pingame - 1) * (pingame + 1) / 3));
+ }
+
+#define SETUPDISTTABLE(odds, num) \
+ for (i = num; i; --i) disttable[distlen++] = odds
+
+ if (G_BattleGametype()) // Battle Mode
+ {
+ if (oddsvalid[0]) SETUPDISTTABLE(0,1);
+ if (oddsvalid[1]) SETUPDISTTABLE(1,1);
+ if (oddsvalid[2]) SETUPDISTTABLE(2,1);
+ if (oddsvalid[3]) SETUPDISTTABLE(3,1);
+ if (oddsvalid[4]) SETUPDISTTABLE(4,1);
+
+ if (player->kartstuff[k_roulettetype] == 1 && oddsvalid[5]) // 5 is the extreme odds of player-controlled "Karma" items
+ useodds = 5;
+ else
+ {
+ SINT8 wantedpos = (bestbumper-player->kartstuff[k_bumper]); // 0 is the best player's bumper count, 1 is a bumper below best, 2 is two bumpers below, etc
+ if (K_IsPlayerWanted(player))
+ wantedpos++;
+ if (wantedpos > 4) // Don't run off into karma items
+ wantedpos = 4;
+ if (wantedpos < 0) // Don't go below somehow
+ wantedpos = 0;
+ useodds = disttable[(wantedpos * distlen) / 5];
+ }
+ }
+ else
+ {
+ if (oddsvalid[1]) SETUPDISTTABLE(1,1);
+ if (oddsvalid[2]) SETUPDISTTABLE(2,1);
+ if (oddsvalid[3]) SETUPDISTTABLE(3,1);
+ if (oddsvalid[4]) SETUPDISTTABLE(4,2);
+ if (oddsvalid[5]) SETUPDISTTABLE(5,2);
+ if (oddsvalid[6]) SETUPDISTTABLE(6,3);
+ if (oddsvalid[7]) SETUPDISTTABLE(7,3);
+ if (oddsvalid[8]) SETUPDISTTABLE(8,1);
+
+ if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items
+ pdis = (15 * pdis) / 14;
+
+ if (spbrush) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell
+ pdis = (3 * pdis) / 2;
+
+ pdis = ((28 + (8-pingame)) * pdis) / 28;
+
+ if (pingame == 1 && oddsvalid[0]) // Record Attack, or just alone
+ useodds = 0;
+ else if (pdis <= 0) // (64*14) * 0 = 0
+ useodds = disttable[0];
+ else if (player->kartstuff[k_position] == 2 && pdis > (distvar*6)
+ && spbplace == -1 && !indirectitemcooldown && !dontforcespb
+ && oddsvalid[9]) // Force SPB in 2nd
+ useodds = 9;
+ else if (pdis > distvar * ((12 * distlen) / 14)) // (64*14) * 12 = 10752
+ useodds = disttable[distlen-1];
+ else
+ {
+ for (i = 1; i < 13; i++)
+ {
+ if (pdis <= distvar * ((i * distlen) / 14))
+ {
+ useodds = disttable[((i * distlen) / 14)];
+ break;
+ }
+ }
+ }
+ }
+
+#undef SETUPDISTTABLE
+
+ //CONS_Printf("Got useodds %d. (position: %d, distance: %d)\n", useodds, player->kartstuff[k_position], pdis);
+
+ return useodds;
+}
+
+static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
+{
+ INT32 i;
+ UINT8 pingame = 0;
+ UINT8 roulettestop;
+ INT32 useodds = 0;
+ INT32 spawnchance[NUMKARTRESULTS];
+ INT32 totalspawnchance = 0;
+ INT32 bestbumper = 0;
+ fixed_t mashed = 0;
+ boolean dontforcespb = false;
+ boolean spbrush = false;
+
+ // This makes the roulette cycle through items - if this is 0, you shouldn't be here.
+ if (player->kartstuff[k_itemroulette])
+ player->kartstuff[k_itemroulette]++;
+ else
+ return;
+
+ // Gotta check how many players are active at this moment.
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+ pingame++;
+ if (players[i].exiting)
+ dontforcespb = true;
+ if (players[i].kartstuff[k_bumper] > bestbumper)
+ bestbumper = players[i].kartstuff[k_bumper];
+ }
+
+ // This makes the roulette produce the random noises.
+ if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsLocalPlayer(player))
+ {
+#define PLAYROULETTESND S_StartSound(NULL, sfx_itrol1 + ((player->kartstuff[k_itemroulette] / 3) % 8));
+ if (splitscreen)
+ {
+ if (players[displayplayer].kartstuff[k_itemroulette])
+ {
+ if (player == &players[displayplayer])
+ PLAYROULETTESND;
+ }
+ else if (players[secondarydisplayplayer].kartstuff[k_itemroulette])
+ {
+ if (player == &players[secondarydisplayplayer])
+ PLAYROULETTESND;
+ }
+ else if (players[thirddisplayplayer].kartstuff[k_itemroulette] && splitscreen > 1)
+ {
+ if (player == &players[thirddisplayplayer])
+ PLAYROULETTESND;
+ }
+ else if (players[fourthdisplayplayer].kartstuff[k_itemroulette] && splitscreen > 2)
+ {
+ if (player == &players[fourthdisplayplayer])
+ PLAYROULETTESND;
+ }
+ }
+ else
+ PLAYROULETTESND;
+#undef PLAYROULETTESND
+ }
+
+ roulettestop = TICRATE + (3*(pingame - player->kartstuff[k_position]));
+
+ // If the roulette finishes or the player presses BT_ATTACK, stop the roulette and calculate the item.
+ // I'm returning via the exact opposite, however, to forgo having another bracket embed. Same result either way, I think.
+ // Finally, if you get past this check, now you can actually start calculating what item you get.
+ if ((cmd->buttons & BT_ATTACK) && !(player->kartstuff[k_eggmanheld] || player->kartstuff[k_itemheld]) && player->kartstuff[k_itemroulette] >= roulettestop && !modeattacking)
+ {
+ // Mashing reduces your chances for the good items
+ mashed = FixedDiv((player->kartstuff[k_itemroulette])*FRACUNIT, ((TICRATE*3)+roulettestop)*FRACUNIT) - FRACUNIT;
+ }
+ else if (!(player->kartstuff[k_itemroulette] >= (TICRATE*3)))
+ return;
+
+ if (cmd->buttons & BT_ATTACK)
+ player->pflags |= PF_ATTACKDOWN;
+
+ if (player->kartstuff[k_roulettetype] == 2) // Fake items
+ {
+ player->kartstuff[k_eggmanexplode] = 4*TICRATE;
+ //player->kartstuff[k_itemblink] = TICRATE;
+ //player->kartstuff[k_itemblinkmode] = 1;
+ player->kartstuff[k_itemroulette] = 0;
+ player->kartstuff[k_roulettetype] = 0;
+ if (P_IsLocalPlayer(player))
+ S_StartSound(NULL, sfx_itrole);
+ return;
+ }
+
+ if (cv_kartdebugitem.value != 0 && !modeattacking)
+ {
+ K_KartGetItemResult(player, cv_kartdebugitem.value);
+ player->kartstuff[k_itemamount] = cv_kartdebugamount.value;
+ player->kartstuff[k_itemblink] = TICRATE;
+ player->kartstuff[k_itemblinkmode] = 2;
+ player->kartstuff[k_itemroulette] = 0;
+ player->kartstuff[k_roulettetype] = 0;
+ if (P_IsLocalPlayer(player))
+ S_StartSound(NULL, sfx_dbgsal);
+ return;
+ }
+
+ if (G_RaceGametype())
+ spbrush = (spbplace != -1 && player->kartstuff[k_position] == spbplace+1);
+
+ // Initializes existing spawnchance values
+ for (i = 0; i < NUMKARTRESULTS; i++)
+ spawnchance[i] = 0;
+
+ // Split into another function for a debug function below
+ useodds = K_FindUseodds(player, mashed, pingame, bestbumper, spbrush, dontforcespb);
+
+ for (i = 1; i < NUMKARTRESULTS; i++)
+ spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush));
+
+ // Award the player whatever power is rolled
+ if (totalspawnchance > 0)
+ {
+ totalspawnchance = P_RandomKey(totalspawnchance);
+ for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++);
+
+ K_KartGetItemResult(player, i);
+ }
+ else
+ {
+ player->kartstuff[k_itemtype] = KITEM_SAD;
+ player->kartstuff[k_itemamount] = 1;
+ }
+
+ if (P_IsLocalPlayer(player))
+ S_StartSound(NULL, ((player->kartstuff[k_roulettetype] == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf)));
+
+ player->kartstuff[k_itemblink] = TICRATE;
+ player->kartstuff[k_itemblinkmode] = ((player->kartstuff[k_roulettetype] == 1) ? 2 : (mashed ? 1 : 0));
+
+ player->kartstuff[k_itemroulette] = 0; // Since we're done, clear the roulette number
+ player->kartstuff[k_roulettetype] = 0; // This too
+}
+
+//}
+
+//{ SRB2kart p_user.c Stuff
+
+static fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against)
+{
+ fixed_t weight = 5<type)
+ {
+ case MT_PLAYER:
+ if (!mobj->player)
+ break;
+ if (against->player && !against->player->kartstuff[k_spinouttimer] && mobj->player->kartstuff[k_spinouttimer])
+ weight = 0; // Do not bump
+ else
+ {
+ weight = (mobj->player->kartweight)<player->speed > K_GetKartSpeed(mobj->player, false))
+ weight += (mobj->player->speed - K_GetKartSpeed(mobj->player, false))/8;
+ }
+ break;
+ case MT_FALLINGROCK:
+ if (against->player)
+ {
+ if (against->player->kartstuff[k_invincibilitytimer]
+ || against->player->kartstuff[k_growshrinktimer] > 0)
+ weight = 0;
+ else
+ weight = (against->player->kartweight)<player)
+ weight = (against->player->kartweight)<player)
+ weight = (against->player->kartweight+3)<player && mobj1->player->playerstate != PST_LIVE)
+ || (mobj2->player && mobj2->player->playerstate != PST_LIVE))
+ return;
+
+ if ((mobj1->player && mobj1->player->kartstuff[k_respawn])
+ || (mobj2->player && mobj2->player->kartstuff[k_respawn]))
+ return;
+
+ { // Don't bump if you're flashing
+ INT32 flash;
+
+ flash = K_GetKartFlashing(mobj1->player);
+ if (mobj1->player && mobj1->player->powers[pw_flashing] > 0 && mobj1->player->powers[pw_flashing] < flash)
+ {
+ if (mobj1->player->powers[pw_flashing] < flash-1)
+ mobj1->player->powers[pw_flashing]++;
+ return;
+ }
+
+ flash = K_GetKartFlashing(mobj2->player);
+ if (mobj2->player && mobj2->player->powers[pw_flashing] > 0 && mobj2->player->powers[pw_flashing] < flash)
+ {
+ if (mobj2->player->powers[pw_flashing] < flash-1)
+ mobj2->player->powers[pw_flashing]++;
+ return;
+ }
+ }
+
+ // Don't bump if you've recently bumped
+ if (mobj1->player && mobj1->player->kartstuff[k_justbumped])
+ {
+ mobj1->player->kartstuff[k_justbumped] = bumptime;
+ return;
+ }
+
+ if (mobj2->player && mobj2->player->kartstuff[k_justbumped])
+ {
+ mobj2->player->kartstuff[k_justbumped] = bumptime;
+ return;
+ }
+
+ mass1 = K_GetMobjWeight(mobj1, mobj2);
+
+ if (solid == true && mass1 > 0)
+ mass2 = mass1;
+ else
+ mass2 = K_GetMobjWeight(mobj2, mobj1);
+
+ momdifx = mobj1->momx - mobj2->momx;
+ momdify = mobj1->momy - mobj2->momy;
+
+ // Adds the OTHER player's momentum times a bunch, for the best chance of getting the correct direction
+ distx = (mobj1->x + mobj2->momx*3) - (mobj2->x + mobj1->momx*3);
+ disty = (mobj1->y + mobj2->momy*3) - (mobj2->y + mobj1->momy*3);
+
+ if (distx == 0 && disty == 0)
+ // if there's no distance between the 2, they're directly on top of each other, don't run this
+ return;
+
+ { // Normalize distance to the sum of the two objects' radii, since in a perfect world that would be the distance at the point of collision...
+ fixed_t dist = P_AproxDistance(distx, disty);
+ fixed_t nx = FixedDiv(distx, dist);
+ fixed_t ny = FixedDiv(disty, dist);
+
+ dist = dist ? dist : 1;
+ distx = FixedMul(mobj1->radius+mobj2->radius, nx);
+ disty = FixedMul(mobj1->radius+mobj2->radius, ny);
+
+ if (momdifx == 0 && momdify == 0)
+ {
+ // If there's no momentum difference, they're moving at exactly the same rate. Pretend they moved into each other.
+ momdifx = -nx;
+ momdify = -ny;
+ }
+ }
+
+ // if the speed difference is less than this let's assume they're going proportionately faster from each other
+ if (P_AproxDistance(momdifx, momdify) < (25*mapobjectscale))
+ {
+ fixed_t momdiflength = P_AproxDistance(momdifx, momdify);
+ fixed_t normalisedx = FixedDiv(momdifx, momdiflength);
+ fixed_t normalisedy = FixedDiv(momdify, momdiflength);
+ momdifx = FixedMul((25*mapobjectscale), normalisedx);
+ momdify = FixedMul((25*mapobjectscale), normalisedy);
+ }
+
+ dot = FixedMul(momdifx, distx) + FixedMul(momdify, disty);
+
+ if (dot >= 0)
+ {
+ // They're moving away from each other
+ return;
+ }
+
+ force = FixedDiv(dot, FixedMul(distx, distx)+FixedMul(disty, disty));
+
+ if (bounce == true && mass2 > 0) // Perform a Goomba Bounce.
+ mobj1->momz = -mobj1->momz;
+ else
+ {
+ fixed_t newz = mobj1->momz;
+ if (mass2 > 0)
+ mobj1->momz = mobj2->momz;
+ if (mass1 > 0 && solid == false)
+ mobj2->momz = newz;
+ }
+
+ if (mass2 > 0)
+ {
+ mobj1->momx = mobj1->momx - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), distx);
+ mobj1->momy = mobj1->momy - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), disty);
+ }
+
+ if (mass1 > 0 && solid == false)
+ {
+ mobj2->momx = mobj2->momx - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -distx);
+ mobj2->momy = mobj2->momy - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -disty);
+ }
+
+ // Do the bump fx when we've CONFIRMED we can bump.
+ S_StartSound(mobj1, sfx_s3k49);
+
+ fx = P_SpawnMobj(mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, mobj1->z/2 + mobj2->z/2, MT_BUMP);
+ if (mobj1->eflags & MFE_VERTICALFLIP)
+ fx->eflags |= MFE_VERTICALFLIP;
+ else
+ fx->eflags &= ~MFE_VERTICALFLIP;
+ P_SetScale(fx, mobj1->scale);
+
+ // Because this is done during collision now, rmomx and rmomy need to be recalculated
+ // so that friction doesn't immediately decide to stop the player if they're at a standstill
+ // Also set justbumped here
+ if (mobj1->player)
+ {
+ mobj1->player->rmomx = mobj1->momx - mobj1->player->cmomx;
+ mobj1->player->rmomy = mobj1->momy - mobj1->player->cmomy;
+ mobj1->player->kartstuff[k_justbumped] = bumptime;
+ if (mobj1->player->kartstuff[k_spinouttimer])
+ {
+ mobj1->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1;
+ mobj1->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj1->player->kartstuff[k_spinouttimer]);
+ }
+ }
+
+ if (mobj2->player)
+ {
+ mobj2->player->rmomx = mobj2->momx - mobj2->player->cmomx;
+ mobj2->player->rmomy = mobj2->momy - mobj2->player->cmomy;
+ mobj2->player->kartstuff[k_justbumped] = bumptime;
+ if (mobj2->player->kartstuff[k_spinouttimer])
+ {
+ mobj2->player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1;
+ mobj2->player->kartstuff[k_spinouttimer] = max(wipeoutslowtime+1, mobj2->player->kartstuff[k_spinouttimer]);
+ }
+ }
+}
+
+/** \brief Checks that the player is on an offroad subsector for realsies
+
+ \param mo player mobj object
+
+ \return boolean
+*/
+static UINT8 K_CheckOffroadCollide(mobj_t *mo, sector_t *sec)
+{
+ UINT8 i;
+ sector_t *sec2;
+
+ I_Assert(mo != NULL);
+ I_Assert(!P_MobjWasRemoved(mo));
+
+ sec2 = P_ThingOnSpecial3DFloor(mo);
+
+ for (i = 2; i < 5; i++)
+ {
+ if ((sec2 && GETSECSPECIAL(sec2->special, 1) == i)
+ || (P_IsObjectOnRealGround(mo, sec) && GETSECSPECIAL(sec->special, 1) == i))
+ return i-1;
+ }
+
+ return 0;
+}
+
+/** \brief Updates the Player's offroad value once per frame
+
+ \param player player object passed from K_KartPlayerThink
+
+ \return void
+*/
+static void K_UpdateOffroad(player_t *player)
+{
+ fixed_t offroad;
+ sector_t *nextsector = R_PointInSubsector(
+ player->mo->x + player->mo->momx*2, player->mo->y + player->mo->momy*2)->sector;
+ UINT8 offroadstrength = K_CheckOffroadCollide(player->mo, nextsector);
+
+ // If you are in offroad, a timer starts.
+ if (offroadstrength)
+ {
+ if (K_CheckOffroadCollide(player->mo, player->mo->subsector->sector) && player->kartstuff[k_offroad] == 0)
+ player->kartstuff[k_offroad] = (TICRATE/2);
+
+ if (player->kartstuff[k_offroad] > 0)
+ {
+ offroad = (offroadstrength << FRACBITS) / (TICRATE/2);
+
+ //if (player->kartstuff[k_growshrinktimer] > 1) // grow slows down half as fast
+ // offroad /= 2;
+
+ player->kartstuff[k_offroad] += offroad;
+ }
+
+ if (player->kartstuff[k_offroad] > (offroadstrength << FRACBITS))
+ player->kartstuff[k_offroad] = (offroadstrength << FRACBITS);
+ }
+ else
+ player->kartstuff[k_offroad] = 0;
+}
+
+// Adds gravity flipping to an object relative to its master and shifts the z coordinate accordingly.
+void K_FlipFromObject(mobj_t *mo, mobj_t *master)
+{
+ mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP);
+ mo->flags2 = (mo->flags2 & ~MF2_OBJECTFLIP)|(master->flags2 & MF2_OBJECTFLIP);
+
+ if (mo->eflags & MFE_VERTICALFLIP)
+ mo->z += master->height - FixedMul(master->scale, mo->height);
+}
+
+// These have to go earlier than its sisters because of K_RespawnChecker...
+void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master)
+{
+ // flipping
+ // handle z shifting from there too. This is here since there's no reason not to flip us if needed when we do this anyway;
+ K_FlipFromObject(mo, master);
+
+ // visibility (usually for hyudoro)
+ mo->flags2 = (mo->flags2 & ~MF2_DONTDRAW)|(master->flags2 & MF2_DONTDRAW);
+ mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP1)|(master->eflags & MFE_DRAWONLYFORP1);
+ mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP2)|(master->eflags & MFE_DRAWONLYFORP2);
+ mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP3)|(master->eflags & MFE_DRAWONLYFORP3);
+ mo->eflags = (mo->eflags & ~MFE_DRAWONLYFORP4)|(master->eflags & MFE_DRAWONLYFORP4);
+}
+
+static void K_SpawnDashDustRelease(player_t *player)
+{
+ fixed_t newx;
+ fixed_t newy;
+ mobj_t *dust;
+ angle_t travelangle;
+ INT32 i;
+
+ I_Assert(player != NULL);
+ I_Assert(player->mo != NULL);
+ I_Assert(!P_MobjWasRemoved(player->mo));
+
+ if (!P_IsObjectOnGround(player->mo))
+ return;
+
+ if (!player->speed && !player->kartstuff[k_startboost])
+ return;
+
+ travelangle = player->mo->angle;
+
+ if (player->kartstuff[k_drift] || player->kartstuff[k_driftend])
+ travelangle -= (ANGLE_45/5)*player->kartstuff[k_drift];
+
+ for (i = 0; i < 2; i++)
+ {
+ newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale));
+ newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_90, FixedMul(48*FRACUNIT, player->mo->scale));
+ dust = P_SpawnMobj(newx, newy, player->mo->z, MT_FASTDUST);
+
+ P_SetTarget(&dust->target, player->mo);
+ dust->angle = travelangle - ((i&1) ? -1 : 1)*ANGLE_45;
+ dust->destscale = player->mo->scale;
+ P_SetScale(dust, player->mo->scale);
+
+ dust->momx = 3*player->mo->momx/5;
+ dust->momy = 3*player->mo->momy/5;
+ //dust->momz = 3*player->mo->momz/5;
+
+ K_MatchGenericExtraFlags(dust, player->mo);
+ }
+}
+
+static void K_SpawnBrakeDriftSparks(player_t *player) // Be sure to update the mobj thinker case too!
+{
+ mobj_t *sparks;
+
+ I_Assert(player != NULL);
+ I_Assert(player->mo != NULL);
+ I_Assert(!P_MobjWasRemoved(player->mo));
+
+ // Position & etc are handled in its thinker, and its spawned invisible.
+ // This avoids needing to dupe code if we don't need it.
+ sparks = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BRAKEDRIFT);
+ P_SetTarget(&sparks->target, player->mo);
+ P_SetScale(sparks, (sparks->destscale = player->mo->scale));
+ K_MatchGenericExtraFlags(sparks, player->mo);
+ sparks->flags2 |= MF2_DONTDRAW;
+}
+
+/** \brief Calculates the respawn timer and drop-boosting
+
+ \param player player object passed from K_KartPlayerThink
+
+ \return void
+*/
+void K_RespawnChecker(player_t *player)
+{
+ ticcmd_t *cmd = &player->cmd;
+
+ if (player->spectator)
+ return;
+
+ if (player->kartstuff[k_respawn] > 1)
+ {
+ player->kartstuff[k_respawn]--;
+ player->mo->momz = 0;
+ player->powers[pw_flashing] = 2;
+ player->powers[pw_nocontrol] = 2;
+ if (leveltime % 8 == 0)
+ {
+ INT32 i;
+ if (!mapreset)
+ S_StartSound(player->mo, sfx_s3kcas);
+
+ for (i = 0; i < 8; i++)
+ {
+ mobj_t *mo;
+ angle_t newangle;
+ fixed_t newx, newy, newz;
+
+ newangle = FixedAngle(((360/8)*i)*FRACUNIT);
+ newx = player->mo->x + P_ReturnThrustX(player->mo, newangle, 31<mo->y + P_ReturnThrustY(player->mo, newangle, 31<mo->eflags & MFE_VERTICALFLIP)
+ newz = player->mo->z + player->mo->height;
+ else
+ newz = player->mo->z;
+
+ mo = P_SpawnMobj(newx, newy, newz, MT_DEZLASER);
+ if (mo)
+ {
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ mo->eflags |= MFE_VERTICALFLIP;
+ P_SetTarget(&mo->target, player->mo);
+ mo->angle = newangle+ANGLE_90;
+ mo->momz = (8<mo);
+ P_SetScale(mo, (mo->destscale = FRACUNIT));
+ }
+ }
+ }
+ }
+ else if (player->kartstuff[k_respawn] == 1)
+ {
+ if (player->kartstuff[k_growshrinktimer] < 0)
+ {
+ player->mo->scalespeed = mapobjectscale/TICRATE;
+ player->mo->destscale = (6*mapobjectscale)/8;
+ if (cv_kartdebugshrink.value && !modeattacking && !player->bot)
+ player->mo->destscale = (6*player->mo->destscale)/8;
+ }
+
+ if (!P_IsObjectOnGround(player->mo) && !mapreset)
+ {
+ player->powers[pw_flashing] = 2;
+
+ // Sal: The old behavior was stupid and prone to accidental usage.
+ // Let's rip off Mania instead, and turn this into a Drop Dash!
+
+ if (cmd->buttons & BT_ACCELERATE)
+ player->kartstuff[k_dropdash]++;
+ else
+ player->kartstuff[k_dropdash] = 0;
+
+ if (player->kartstuff[k_dropdash] == TICRATE/4)
+ S_StartSound(player->mo, sfx_ddash);
+
+ if ((player->kartstuff[k_dropdash] >= TICRATE/4)
+ && (player->kartstuff[k_dropdash] & 1))
+ player->mo->colorized = true;
+ else
+ player->mo->colorized = false;
+ }
+ else
+ {
+ if ((cmd->buttons & BT_ACCELERATE) && (player->kartstuff[k_dropdash] >= TICRATE/4))
+ {
+ S_StartSound(player->mo, sfx_s23c);
+ player->kartstuff[k_startboost] = 50;
+ K_SpawnDashDustRelease(player);
+ }
+ player->mo->colorized = false;
+ player->kartstuff[k_dropdash] = 0;
+ player->kartstuff[k_respawn] = 0;
+ }
+ }
+}
+
+/** \brief Handles the state changing for moving players, moved here to eliminate duplicate code
+
+ \param player player data
+
+ \return void
+*/
+void K_KartMoveAnimation(player_t *player)
+{
+ ticcmd_t *cmd = &player->cmd;
+ // Standing frames - S_KART_STND1 S_KART_STND1_L S_KART_STND1_R
+ if (player->speed == 0)
+ {
+ if (cmd->driftturn < 0 && !(player->mo->state >= &states[S_KART_STND1_R] && player->mo->state <= &states[S_KART_STND2_R]))
+ P_SetPlayerMobjState(player->mo, S_KART_STND1_R);
+ else if (cmd->driftturn > 0 && !(player->mo->state >= &states[S_KART_STND1_L] && player->mo->state <= &states[S_KART_STND2_L]))
+ P_SetPlayerMobjState(player->mo, S_KART_STND1_L);
+ else if (cmd->driftturn == 0 && !(player->mo->state >= &states[S_KART_STND1] && player->mo->state <= &states[S_KART_STND2]))
+ P_SetPlayerMobjState(player->mo, S_KART_STND1);
+ }
+ // Drifting Left - S_KART_DRIFT1_L
+ else if (player->kartstuff[k_drift] > 0 && P_IsObjectOnGround(player->mo))
+ {
+ if (!(player->mo->state >= &states[S_KART_DRIFT1_L] && player->mo->state <= &states[S_KART_DRIFT2_L]))
+ P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_L);
+ }
+ // Drifting Right - S_KART_DRIFT1_R
+ else if (player->kartstuff[k_drift] < 0 && P_IsObjectOnGround(player->mo))
+ {
+ if (!(player->mo->state >= &states[S_KART_DRIFT1_R] && player->mo->state <= &states[S_KART_DRIFT2_R]))
+ P_SetPlayerMobjState(player->mo, S_KART_DRIFT1_R);
+ }
+ // Run frames - S_KART_RUN1 S_KART_RUN1_L S_KART_RUN1_R
+ else if (player->speed > FixedMul(player->runspeed, player->mo->scale))
+ {
+ if (cmd->driftturn < 0 && !(player->mo->state >= &states[S_KART_RUN1_R] && player->mo->state <= &states[S_KART_RUN2_R]))
+ P_SetPlayerMobjState(player->mo, S_KART_RUN1_R);
+ else if (cmd->driftturn > 0 && !(player->mo->state >= &states[S_KART_RUN1_L] && player->mo->state <= &states[S_KART_RUN2_L]))
+ P_SetPlayerMobjState(player->mo, S_KART_RUN1_L);
+ else if (cmd->driftturn == 0 && !(player->mo->state >= &states[S_KART_RUN1] && player->mo->state <= &states[S_KART_RUN2]))
+ P_SetPlayerMobjState(player->mo, S_KART_RUN1);
+ }
+ // Walk frames - S_KART_WALK1 S_KART_WALK1_L S_KART_WALK1_R
+ else if (player->speed <= FixedMul(player->runspeed, player->mo->scale))
+ {
+ if (cmd->driftturn < 0 && !(player->mo->state >= &states[S_KART_WALK1_R] && player->mo->state <= &states[S_KART_WALK2_R]))
+ P_SetPlayerMobjState(player->mo, S_KART_WALK1_R);
+ else if (cmd->driftturn > 0 && !(player->mo->state >= &states[S_KART_WALK1_L] && player->mo->state <= &states[S_KART_WALK2_L]))
+ P_SetPlayerMobjState(player->mo, S_KART_WALK1_L);
+ else if (cmd->driftturn == 0 && !(player->mo->state >= &states[S_KART_WALK1] && player->mo->state <= &states[S_KART_WALK2]))
+ P_SetPlayerMobjState(player->mo, S_KART_WALK1);
+ }
+}
+
+static void K_TauntVoiceTimers(player_t *player)
+{
+ if (!player)
+ return;
+
+ player->kartstuff[k_tauntvoices] = 6*TICRATE;
+ player->kartstuff[k_voices] = 4*TICRATE;
+}
+
+static void K_RegularVoiceTimers(player_t *player)
+{
+ if (!player)
+ return;
+
+ player->kartstuff[k_voices] = 4*TICRATE;
+
+ if (player->kartstuff[k_tauntvoices] < 4*TICRATE)
+ player->kartstuff[k_tauntvoices] = 4*TICRATE;
+}
+
+void K_PlayAttackTaunt(mobj_t *source)
+{
+ sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons
+ boolean tasteful = (!source->player || !source->player->kartstuff[k_tauntvoices]);
+
+ if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2))
+ S_StartSound(source, sfx_kattk1+pick);
+
+ if (!tasteful)
+ return;
+
+ K_TauntVoiceTimers(source->player);
+}
+
+void K_PlayBoostTaunt(mobj_t *source)
+{
+ sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons
+ boolean tasteful = (!source->player || !source->player->kartstuff[k_tauntvoices]);
+
+ if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2))
+ S_StartSound(source, sfx_kbost1+pick);
+
+ if (!tasteful)
+ return;
+
+ K_TauntVoiceTimers(source->player);
+}
+
+void K_PlayOvertakeSound(mobj_t *source)
+{
+ boolean tasteful = (!source->player || !source->player->kartstuff[k_voices]);
+
+ if (!G_RaceGametype()) // Only in race
+ return;
+
+ // 4 seconds from before race begins, 10 seconds afterwards
+ if (leveltime < starttime+(10*TICRATE))
+ return;
+
+ if (cv_kartvoices.value && (tasteful || cv_kartvoices.value == 2))
+ S_StartSound(source, sfx_kslow);
+
+ if (!tasteful)
+ return;
+
+ K_RegularVoiceTimers(source->player);
+}
+
+void K_PlayHitEmSound(mobj_t *source)
+{
+ if (cv_kartvoices.value)
+ S_StartSound(source, sfx_khitem);
+ else
+ S_StartSound(source, sfx_s1c9); // The only lost gameplay functionality with voices disabled
+
+ K_RegularVoiceTimers(source->player);
+}
+
+void K_PlayPowerGloatSound(mobj_t *source)
+{
+ if (cv_kartvoices.value)
+ S_StartSound(source, sfx_kgloat);
+
+ K_RegularVoiceTimers(source->player);
+}
+
+void K_MomentumToFacing(player_t *player)
+{
+ angle_t dangle = player->mo->angle - R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
+
+ if (dangle > ANGLE_180)
+ dangle = InvAngle(dangle);
+
+ // If you aren't on the ground or are moving in too different of a direction don't do this
+ if (player->mo->eflags & MFE_JUSTHITFLOOR)
+ ; // Just hit floor ALWAYS redirects
+ else if (!P_IsObjectOnGround(player->mo) || dangle > ANGLE_90)
+ return;
+
+ P_Thrust(player->mo, player->mo->angle, player->speed - FixedMul(player->speed, player->mo->friction));
+ player->mo->momx = FixedMul(player->mo->momx - player->cmomx, player->mo->friction) + player->cmomx;
+ player->mo->momy = FixedMul(player->mo->momy - player->cmomy, player->mo->friction) + player->cmomy;
+}
+
+// sets k_boostpower, k_speedboost, and k_accelboost to whatever we need it to be
+static void K_GetKartBoostPower(player_t *player)
+{
+ fixed_t boostpower = FRACUNIT;
+ fixed_t speedboost = 0, accelboost = 0;
+
+ if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow] == 1) // Slow down after you've been bumped
+ {
+ player->kartstuff[k_boostpower] = player->kartstuff[k_speedboost] = player->kartstuff[k_accelboost] = 0;
+ return;
+ }
+
+ // Offroad is separate, it's difficult to factor it in with a variable value anyway.
+ if (!(player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || player->kartstuff[k_sneakertimer])
+ && player->kartstuff[k_offroad] >= 0)
+ boostpower = FixedDiv(boostpower, player->kartstuff[k_offroad] + FRACUNIT);
+
+ if (player->kartstuff[k_bananadrag] > TICRATE)
+ boostpower = (4*boostpower)/5;
+
+ // Banana drag/offroad dust
+ if (boostpower < FRACUNIT
+ && player->mo && P_IsObjectOnGround(player->mo)
+ && player->speed > 0
+ && !player->spectator)
+ {
+ K_SpawnWipeoutTrail(player->mo, true);
+ if (leveltime % 6 == 0)
+ S_StartSound(player->mo, sfx_cdfm70);
+ }
+
+ if (player->kartstuff[k_sneakertimer]) // Sneaker
+ {
+ switch (gamespeed)
+ {
+ case 0:
+ speedboost = max(speedboost, 53740+768);
+ break;
+ case 2:
+ speedboost = max(speedboost, 17294+768);
+ break;
+ default:
+ speedboost = max(speedboost, 32768);
+ break;
+ }
+ accelboost = max(accelboost, 8*FRACUNIT); // + 800%
+ }
+
+ if (player->kartstuff[k_invincibilitytimer]) // Invincibility
+ {
+ speedboost = max(speedboost, 3*FRACUNIT/8); // + 37.5%
+ accelboost = max(accelboost, 3*FRACUNIT); // + 300%
+ }
+
+ if (player->kartstuff[k_growshrinktimer] > 0) // Grow
+ {
+ speedboost = max(speedboost, FRACUNIT/5); // + 20%
+ }
+
+ if (player->kartstuff[k_driftboost]) // Drift Boost
+ {
+ speedboost = max(speedboost, FRACUNIT/4); // + 25%
+ accelboost = max(accelboost, 4*FRACUNIT); // + 400%
+ }
+
+ if (player->kartstuff[k_startboost]) // Startup Boost
+ {
+ speedboost = max(speedboost, FRACUNIT/4); // + 25%
+ accelboost = max(accelboost, 6*FRACUNIT); // + 300%
+ }
+
+ // don't average them anymore, this would make a small boost and a high boost less useful
+ // just take the highest we want instead
+
+ player->kartstuff[k_boostpower] = boostpower;
+
+ // value smoothing
+ if (speedboost > player->kartstuff[k_speedboost])
+ player->kartstuff[k_speedboost] = speedboost;
+ else
+ player->kartstuff[k_speedboost] += (speedboost - player->kartstuff[k_speedboost])/(TICRATE/2);
+
+ player->kartstuff[k_accelboost] = accelboost;
+}
+
+fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower)
+{
+ fixed_t k_speed = 150;
+ fixed_t g_cc = FRACUNIT;
+ fixed_t xspd = 3072; // 4.6875 aka 3/64
+ UINT8 kartspeed = player->kartspeed;
+ fixed_t finalspeed;
+
+ if (doboostpower && !player->kartstuff[k_pogospring] && !P_IsObjectOnGround(player->mo))
+ return (75*mapobjectscale); // air speed cap
+
+ switch (gamespeed)
+ {
+ case 0:
+ g_cc = 53248 + xspd; // 50cc = 81.25 + 4.69 = 85.94%
+ break;
+ case 2:
+ g_cc = 77824 + xspd; // 150cc = 118.75 + 4.69 = 123.44%
+ break;
+ default:
+ g_cc = 65536 + xspd; // 100cc = 100.00 + 4.69 = 104.69%
+ break;
+ }
+
+ if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0)
+ kartspeed = 1;
+
+ k_speed += kartspeed*3; // 153 - 177
+
+ finalspeed = FixedMul(FixedMul(k_speed<<14, g_cc), player->mo->scale);
+
+ if (doboostpower)
+ return FixedMul(finalspeed, player->kartstuff[k_boostpower]+player->kartstuff[k_speedboost]);
+ return finalspeed;
+}
+
+fixed_t K_GetKartAccel(player_t *player)
+{
+ fixed_t k_accel = 32; // 36;
+ UINT8 kartspeed = player->kartspeed;
+
+ if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0)
+ kartspeed = 1;
+
+ //k_accel += 3 * (9 - kartspeed); // 36 - 60
+ k_accel += 4 * (9 - kartspeed); // 32 - 64
+
+ return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]);
+}
+
+UINT16 K_GetKartFlashing(player_t *player)
+{
+ UINT16 tics = flashingtics;
+
+ if (!player)
+ return tics;
+
+ if (G_BattleGametype())
+ tics *= 2;
+
+ tics += (flashingtics/8) * (player->kartspeed);
+
+ return tics;
+}
+
+fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove)
+{
+ fixed_t accelmax = 4000;
+ fixed_t newspeed, oldspeed, finalspeed;
+ fixed_t p_speed = K_GetKartSpeed(player, true);
+ fixed_t p_accel = K_GetKartAccel(player);
+
+ if (!onground) return 0; // If the player isn't on the ground, there is no change in speed
+
+ // ACCELCODE!!!1!11!
+ oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // FixedMul(P_AproxDistance(player->rmomx, player->rmomy), player->mo->scale);
+ newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), ORIG_FRICTION);
+
+ if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust
+ {
+ const fixed_t hscale = mapobjectscale /*+ (mapobjectscale - player->mo->scale)*/;
+ const fixed_t minspeed = 24*hscale;
+ const fixed_t maxspeed = 28*hscale;
+
+ if (newspeed > maxspeed && player->kartstuff[k_pogospring] == 2)
+ newspeed = maxspeed;
+ if (newspeed < minspeed)
+ newspeed = minspeed;
+ }
+
+ finalspeed = newspeed - oldspeed;
+
+ // forwardmove is:
+ // 50 while accelerating,
+ // 25 while clutching,
+ // 0 with no gas, and
+ // -25 when only braking.
+
+ finalspeed *= forwardmove/25;
+ finalspeed /= 2;
+
+ if (forwardmove < 0 && finalspeed > FRACUNIT*2)
+ return finalspeed/2;
+ else if (forwardmove < 0)
+ return -FRACUNIT/2;
+
+ if (finalspeed < 0)
+ finalspeed = 0;
+
+ return finalspeed;
+}
+
+void K_DoInstashield(player_t *player)
+{
+ mobj_t *layera;
+ mobj_t *layerb;
+
+ if (player->kartstuff[k_instashield] > 0)
+ return;
+
+ player->kartstuff[k_instashield] = 15; // length of instashield animation
+ S_StartSound(player->mo, sfx_cdpcm9);
+
+ layera = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDA);
+ P_SetTarget(&layera->target, player->mo);
+
+ layerb = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INSTASHIELDB);
+ P_SetTarget(&layerb->target, player->mo);
+}
+
+void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount)
+{
+ statenum_t st;
+ mobj_t *pt;
+
+ if (!source || !source->mo)
+ return;
+
+ if (amount == 1)
+ st = S_BATTLEPOINT1A;
+ else if (amount == 2)
+ st = S_BATTLEPOINT2A;
+ else if (amount == 3)
+ st = S_BATTLEPOINT3A;
+ else
+ return; // NO STATE!
+
+ pt = P_SpawnMobj(source->mo->x, source->mo->y, source->mo->z, MT_BATTLEPOINT);
+ P_SetTarget(&pt->target, source->mo);
+ P_SetMobjState(pt, st);
+ if (victim && victim->skincolor)
+ pt->color = victim->skincolor;
+ else
+ pt->color = source->skincolor;
+}
+
+void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflictor, boolean trapitem)
+{
+ UINT8 scoremultiply = 1;
+ // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too.
+#ifdef HAVE_BLUA
+ boolean force = false; // Used to check if Lua ShouldSpin should get us damaged reguardless of flashtics or heck knows what.
+ UINT8 shouldForce = LUAh_ShouldSpin(player, inflictor, source);
+ if (P_MobjWasRemoved(player->mo))
+ return; // mobj was removed (in theory that shouldn't happen)
+ if (shouldForce == 1)
+ force = true;
+ else if (shouldForce == 2)
+ return;
+#else
+ static const boolean force = false;
+ (void)inflictor; // in case some weirdo doesn't want Lua.
+#endif
+
+
+ if (!trapitem && G_BattleGametype())
+ {
+ if (K_IsPlayerWanted(player))
+ scoremultiply = 3;
+ else if (player->kartstuff[k_bumper] == 1)
+ scoremultiply = 2;
+ }
+
+ if (player->health <= 0)
+ return;
+
+ if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0
+ || player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0
+ || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1)))
+ {
+ if (!force) // if shoulddamage force, we go THROUGH that.
+ {
+ K_DoInstashield(player);
+ return;
+ }
+ }
+
+#ifdef HAVE_BLUA
+ if (LUAh_PlayerSpin(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case.
+ return;
+#endif
+
+ if (source && source != player->mo && source->player)
+ K_PlayHitEmSound(source);
+
+ //player->kartstuff[k_sneakertimer] = 0;
+ player->kartstuff[k_driftboost] = 0;
+
+ player->kartstuff[k_drift] = 0;
+ player->kartstuff[k_driftcharge] = 0;
+ player->kartstuff[k_pogospring] = 0;
+
+ if (G_BattleGametype())
+ {
+ if (source && source->player && player != source->player)
+ {
+ P_AddPlayerScore(source->player, scoremultiply);
+ K_SpawnBattlePoints(source->player, player, scoremultiply);
+ if (!trapitem)
+ {
+ source->player->kartstuff[k_wanted] -= wantedreduce;
+ player->kartstuff[k_wanted] -= (wantedreduce/2);
+ }
+ }
+
+ if (player->kartstuff[k_bumper] > 0)
+ {
+ if (player->kartstuff[k_bumper] == 1)
+ {
+ mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!!
+ P_SetTarget(&karmahitbox->target, player->mo);
+ karmahitbox->destscale = player->mo->scale;
+ P_SetScale(karmahitbox, player->mo->scale);
+ CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]);
+ }
+ player->kartstuff[k_bumper]--;
+ if (K_IsPlayerWanted(player))
+ K_CalculateBattleWanted();
+ }
+
+ if (!player->kartstuff[k_bumper])
+ {
+ player->kartstuff[k_comebacktimer] = comebacktime;
+ if (player->kartstuff[k_comebackmode] == 2)
+ {
+ mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE);
+ S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound);
+ player->kartstuff[k_comebackmode] = 0;
+ }
+ }
+
+ K_CheckBumpers();
+ }
+
+ player->kartstuff[k_spinouttype] = type;
+
+ if (player->kartstuff[k_spinouttype] <= 0) // type 0 is spinout, type 1 is wipeout
+ {
+ // At spinout, player speed is increased to 1/4 their regular speed, moving them forward
+ if (player->speed < K_GetKartSpeed(player, true)/4)
+ P_InstaThrust(player->mo, player->mo->angle, FixedMul(K_GetKartSpeed(player, true)/4, player->mo->scale));
+ S_StartSound(player->mo, sfx_slip);
+ }
+
+ player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2;
+ player->powers[pw_flashing] = K_GetKartFlashing(player);
+
+ if (player->mo->state != &states[S_KART_SPIN])
+ P_SetPlayerMobjState(player->mo, S_KART_SPIN);
+
+ player->kartstuff[k_instashield] = 15;
+ if (cv_kartdebughuddrop.value && !modeattacking)
+ K_DropItems(player);
+ else
+ K_DropHnextList(player);
+ return;
+}
+
+static void K_RemoveGrowShrink(player_t *player)
+{
+ player->kartstuff[k_growshrinktimer] = 0;
+ player->kartstuff[k_growcancel] = 0;
+
+ if (player->mo && !P_MobjWasRemoved(player->mo))
+ {
+ if (player->kartstuff[k_invincibilitytimer] == 0)
+ player->mo->color = player->skincolor;
+
+ player->mo->scalespeed = mapobjectscale/TICRATE;
+ player->mo->destscale = mapobjectscale;
+ if (cv_kartdebugshrink.value && !modeattacking && !player->bot)
+ player->mo->destscale = (6*player->mo->destscale)/8;
+ }
+
+ P_RestoreMusic(player);
+}
+
+void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor)
+{
+ UINT8 scoremultiply = 1;
+ // PS: Inflictor is unused for all purposes here and is actually only ever relevant to Lua. It may be nil too.
+#ifdef HAVE_BLUA
+ boolean force = false; // Used to check if Lua ShouldSquish should get us damaged reguardless of flashtics or heck knows what.
+ UINT8 shouldForce = LUAh_ShouldSquish(player, inflictor, source);
+ if (P_MobjWasRemoved(player->mo))
+ return; // mobj was removed (in theory that shouldn't happen)
+ if (shouldForce == 1)
+ force = true;
+ else if (shouldForce == 2)
+ return;
+#else
+ static const boolean force = false;
+ (void)inflictor; // Please stop forgetting to put inflictor in yer functions thank -Lat'
+#endif
+
+ if (G_BattleGametype())
+ {
+ if (K_IsPlayerWanted(player))
+ scoremultiply = 3;
+ else if (player->kartstuff[k_bumper] == 1)
+ scoremultiply = 2;
+ }
+
+ if (player->health <= 0)
+ return;
+
+ if (player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_invincibilitytimer] > 0
+ || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0
+ || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1)))
+ {
+ if (!force) // You know the drill by now.
+ {
+ K_DoInstashield(player);
+ return;
+ }
+ }
+
+#ifdef HAVE_BLUA
+ if (LUAh_PlayerSquish(player, inflictor, source)) // Let Lua do its thing or overwrite if it wants to. Make sure to let any possible instashield happen because we didn't get "damaged" in this case.
+ return;
+#endif
+
+ player->kartstuff[k_sneakertimer] = 0;
+ player->kartstuff[k_driftboost] = 0;
+
+ player->kartstuff[k_drift] = 0;
+ player->kartstuff[k_driftcharge] = 0;
+ player->kartstuff[k_pogospring] = 0;
+
+ if (G_BattleGametype())
+ {
+ if (source && source->player && player != source->player)
+ {
+ P_AddPlayerScore(source->player, scoremultiply);
+ K_SpawnBattlePoints(source->player, player, scoremultiply);
+ source->player->kartstuff[k_wanted] -= wantedreduce;
+ player->kartstuff[k_wanted] -= (wantedreduce/2);
+ }
+
+ if (player->kartstuff[k_bumper] > 0)
+ {
+ if (player->kartstuff[k_bumper] == 1)
+ {
+ mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!!
+ P_SetTarget(&karmahitbox->target, player->mo);
+ karmahitbox->destscale = player->mo->scale;
+ P_SetScale(karmahitbox, player->mo->scale);
+ CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]);
+ }
+ player->kartstuff[k_bumper]--;
+ if (K_IsPlayerWanted(player))
+ K_CalculateBattleWanted();
+ }
+
+ if (!player->kartstuff[k_bumper])
+ {
+ player->kartstuff[k_comebacktimer] = comebacktime;
+ if (player->kartstuff[k_comebackmode] == 2)
+ {
+ mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE);
+ S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound);
+ player->kartstuff[k_comebackmode] = 0;
+ }
+ }
+
+ K_CheckBumpers();
+ }
+
+ player->kartstuff[k_squishedtimer] = TICRATE;
+
+ // Reduce Shrink timer
+ if (player->kartstuff[k_growshrinktimer] < 0)
+ {
+ player->kartstuff[k_growshrinktimer] += TICRATE;
+ if (player->kartstuff[k_growshrinktimer] >= 0)
+ K_RemoveGrowShrink(player);
+ }
+
+ player->powers[pw_flashing] = K_GetKartFlashing(player);
+
+ player->mo->flags |= MF_NOCLIP;
+
+ if (player->mo->state != &states[S_KART_SQUISH]) // Squash
+ P_SetPlayerMobjState(player->mo, S_KART_SQUISH);
+
+ P_PlayRinglossSound(player->mo);
+
+ player->kartstuff[k_instashield] = 15;
+ if (cv_kartdebughuddrop.value && !modeattacking)
+ K_DropItems(player);
+ else
+ K_DropHnextList(player);
+ return;
+}
+
+void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A bit of a hack, we just throw the player up higher here and extend their spinout timer
+{
+ UINT8 scoremultiply = 1;
+#ifdef HAVE_BLUA
+ boolean force = false; // Used to check if Lua ShouldExplode should get us damaged reguardless of flashtics or heck knows what.
+ UINT8 shouldForce = LUAh_ShouldExplode(player, inflictor, source);
+
+ if (P_MobjWasRemoved(player->mo))
+ return; // mobj was removed (in theory that shouldn't happen)
+ if (shouldForce == 1)
+ force = true;
+ else if (shouldForce == 2)
+ return;
+
+#else
+ static const boolean force = false;
+#endif
+
+ if (G_BattleGametype())
+ {
+ if (K_IsPlayerWanted(player))
+ scoremultiply = 3;
+ else if (player->kartstuff[k_bumper] == 1)
+ scoremultiply = 2;
+ }
+
+ if (player->health <= 0)
+ return;
+
+ if (/*player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0 // Explosions should combo, because of SPB and Eggman
+ ||*/player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0
+ || (G_BattleGametype() && ((player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]) || player->kartstuff[k_comebackmode] == 1)))
+ {
+ if (!force) // ShouldDamage can bypass that, again.
+ {
+ K_DoInstashield(player);
+ return;
+ }
+ }
+
+#ifdef HAVE_BLUA
+ if (LUAh_PlayerExplode(player, inflictor, source)) // Same thing. Also make sure to let Instashield happen blah blah
+ return;
+#endif
+
+ if (source && source != player->mo && source->player)
+ K_PlayHitEmSound(source);
+
+ player->mo->momz = 18*mapobjectscale*P_MobjFlip(player->mo); // please stop forgetting mobjflip checks!!!!
+ player->mo->momx = player->mo->momy = 0;
+
+ player->kartstuff[k_sneakertimer] = 0;
+ player->kartstuff[k_driftboost] = 0;
+
+ player->kartstuff[k_drift] = 0;
+ player->kartstuff[k_driftcharge] = 0;
+ player->kartstuff[k_pogospring] = 0;
+
+ // This is the only part that SHOULDN'T combo :VVVVV
+ if (G_BattleGametype() && !(player->powers[pw_flashing] > 0 || player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0))
+ {
+ if (source && source->player && player != source->player)
+ {
+ P_AddPlayerScore(source->player, scoremultiply);
+ K_SpawnBattlePoints(source->player, player, scoremultiply);
+ source->player->kartstuff[k_wanted] -= wantedreduce;
+ player->kartstuff[k_wanted] -= (wantedreduce/2);
+ }
+
+ if (player->kartstuff[k_bumper] > 0)
+ {
+ if (player->kartstuff[k_bumper] == 1)
+ {
+ mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!!
+ P_SetTarget(&karmahitbox->target, player->mo);
+ karmahitbox->destscale = player->mo->scale;
+ P_SetScale(karmahitbox, player->mo->scale);
+ CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]);
+ }
+ player->kartstuff[k_bumper]--;
+ if (K_IsPlayerWanted(player))
+ K_CalculateBattleWanted();
+ }
+
+ if (!player->kartstuff[k_bumper])
+ {
+ player->kartstuff[k_comebacktimer] = comebacktime;
+ if (player->kartstuff[k_comebackmode] == 2)
+ {
+ mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE);
+ S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound);
+ player->kartstuff[k_comebackmode] = 0;
+ }
+ }
+
+ K_CheckBumpers();
+ }
+
+ player->kartstuff[k_spinouttype] = 1;
+ player->kartstuff[k_spinouttimer] = (3*TICRATE/2)+2;
+
+ player->powers[pw_flashing] = K_GetKartFlashing(player);
+
+ if (inflictor && inflictor->type == MT_SPBEXPLOSION && inflictor->extravalue1)
+ {
+ player->kartstuff[k_spinouttimer] = ((5*player->kartstuff[k_spinouttimer])/2)+1;
+ player->mo->momz *= 2;
+ }
+
+ if (player->mo->eflags & MFE_UNDERWATER)
+ player->mo->momz = (117 * player->mo->momz) / 200;
+
+ if (player->mo->state != &states[S_KART_SPIN])
+ P_SetPlayerMobjState(player->mo, S_KART_SPIN);
+
+ P_PlayRinglossSound(player->mo);
+
+ if (P_IsLocalPlayer(player))
+ {
+ quake.intensity = 64*FRACUNIT;
+ quake.time = 5;
+ }
+
+ player->kartstuff[k_instashield] = 15;
+ K_DropItems(player);
+
+ return;
+}
+
+void K_StealBumper(player_t *player, player_t *victim, boolean force)
+{
+ INT32 newbumper;
+ angle_t newangle, diff;
+ fixed_t newx, newy;
+ mobj_t *newmo;
+
+ if (!G_BattleGametype())
+ return;
+
+ if (player->health <= 0 || victim->health <= 0)
+ return;
+
+ if (!force)
+ {
+ if (victim->kartstuff[k_bumper] <= 0) // || player->kartstuff[k_bumper] >= cv_kartbumpers.value+2
+ return;
+
+ if (player->kartstuff[k_squishedtimer] > 0 || player->kartstuff[k_spinouttimer] > 0)
+ return;
+
+ if (victim->powers[pw_flashing] > 0 || victim->kartstuff[k_squishedtimer] > 0 || victim->kartstuff[k_spinouttimer] > 0
+ || victim->kartstuff[k_invincibilitytimer] > 0 || victim->kartstuff[k_growshrinktimer] > 0 || victim->kartstuff[k_hyudorotimer] > 0)
+ {
+ K_DoInstashield(victim);
+ return;
+ }
+ }
+
+ if (netgame && player->kartstuff[k_bumper] <= 0)
+ CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]);
+
+ newbumper = player->kartstuff[k_bumper];
+ if (newbumper <= 1)
+ diff = 0;
+ else
+ diff = FixedAngle(360*FRACUNIT/newbumper);
+
+ newangle = player->mo->angle;
+ newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, 64*FRACUNIT);
+ newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, 64*FRACUNIT);
+
+ newmo = P_SpawnMobj(newx, newy, player->mo->z, MT_BATTLEBUMPER);
+ newmo->threshold = newbumper;
+ P_SetTarget(&newmo->tracer, victim->mo);
+ P_SetTarget(&newmo->target, player->mo);
+ newmo->angle = (diff * (newbumper-1));
+ newmo->color = victim->skincolor;
+
+ if (newbumper+1 < 2)
+ P_SetMobjState(newmo, S_BATTLEBUMPER3);
+ else if (newbumper+1 < 3)
+ P_SetMobjState(newmo, S_BATTLEBUMPER2);
+ else
+ P_SetMobjState(newmo, S_BATTLEBUMPER1);
+
+ S_StartSound(player->mo, sfx_3db06);
+
+ player->kartstuff[k_bumper]++;
+ player->kartstuff[k_comebackpoints] = 0;
+ player->powers[pw_flashing] = K_GetKartFlashing(player);
+ player->kartstuff[k_comebacktimer] = comebacktime;
+
+ /*victim->powers[pw_flashing] = K_GetKartFlashing(victim);
+ victim->kartstuff[k_comebacktimer] = comebacktime;*/
+
+ victim->kartstuff[k_instashield] = 15;
+ if (cv_kartdebughuddrop.value && !modeattacking)
+ K_DropItems(victim);
+ else
+ K_DropHnextList(victim);
+ return;
+}
+
+// source is the mobj that originally threw the bomb that exploded etc.
+// Spawns the sphere around the explosion that handles spinout
+void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle, boolean spawncenter, boolean ghostit, mobj_t *source)
+{
+ mobj_t *mobj;
+ mobj_t *ghost = NULL;
+ INT32 i;
+ TVector v;
+ TVector *res;
+ fixed_t finalx, finaly, finalz, dist;
+ //mobj_t hoopcenter;
+ angle_t degrees, fa, closestangle;
+ fixed_t mobjx, mobjy, mobjz;
+
+ //hoopcenter.x = x;
+ //hoopcenter.y = y;
+ //hoopcenter.z = z;
+
+ //hoopcenter.z = z - mobjinfo[type].height/2;
+
+ degrees = FINEANGLES/number;
+
+ closestangle = 0;
+
+ // Create the hoop!
+ for (i = 0; i < number; i++)
+ {
+ fa = (i*degrees);
+ v[0] = FixedMul(FINECOSINE(fa),radius);
+ v[1] = 0;
+ v[2] = FixedMul(FINESINE(fa),radius);
+ v[3] = FRACUNIT;
+
+ res = VectorMatrixMultiply(v, *RotateXMatrix(rotangle));
+ M_Memcpy(&v, res, sizeof (v));
+ res = VectorMatrixMultiply(v, *RotateZMatrix(closestangle));
+ M_Memcpy(&v, res, sizeof (v));
+
+ finalx = x + v[0];
+ finaly = y + v[1];
+ finalz = z + v[2];
+
+ mobj = P_SpawnMobj(finalx, finaly, finalz, type);
+
+ mobj->z -= mobj->height>>1;
+
+ // change angle
+ mobj->angle = R_PointToAngle2(mobj->x, mobj->y, x, y);
+
+ // change slope
+ dist = P_AproxDistance(P_AproxDistance(x - mobj->x, y - mobj->y), z - mobj->z);
+
+ if (dist < 1)
+ dist = 1;
+
+ mobjx = mobj->x;
+ mobjy = mobj->y;
+ mobjz = mobj->z;
+
+ if (ghostit)
+ {
+ ghost = P_SpawnGhostMobj(mobj);
+ P_SetMobjState(mobj, S_NULL);
+ mobj = ghost;
+ }
+
+ if (spawncenter)
+ {
+ mobj->x = x;
+ mobj->y = y;
+ mobj->z = z;
+ }
+
+ mobj->momx = FixedMul(FixedDiv(mobjx - x, dist), FixedDiv(dist, 6*FRACUNIT));
+ mobj->momy = FixedMul(FixedDiv(mobjy - y, dist), FixedDiv(dist, 6*FRACUNIT));
+ mobj->momz = FixedMul(FixedDiv(mobjz - z, dist), FixedDiv(dist, 6*FRACUNIT));
+
+ if (source && !P_MobjWasRemoved(source))
+ P_SetTarget(&mobj->target, source);
+ }
+}
+
+// Spawns the purely visual explosion
+void K_SpawnMineExplosion(mobj_t *source, UINT8 color)
+{
+ mobj_t *dust;
+ mobj_t *truc;
+ INT32 speed, speed2;
+
+ INT32 i, radius, height;
+ mobj_t *smoldering = P_SpawnMobj(source->x, source->y, source->z, MT_SMOLDERING);
+ K_MatchGenericExtraFlags(smoldering, source);
+
+ smoldering->tics = TICRATE*3;
+ radius = source->radius>>FRACBITS;
+ height = source->height>>FRACBITS;
+
+ if (!color)
+ color = SKINCOLOR_KETCHUP;
+
+ for (i = 0; i < 32; i++)
+ {
+ dust = P_SpawnMobj(source->x, source->y, source->z, MT_SMOKE);
+ P_SetMobjState(dust, S_OPAQUESMOKE1);
+ dust->angle = (ANGLE_180/16) * i;
+ P_SetScale(dust, source->scale);
+ dust->destscale = source->scale*10;
+ dust->scalespeed = source->scale/12;
+ P_InstaThrust(dust, dust->angle, FixedMul(20*FRACUNIT, source->scale));
+
+ truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT,
+ source->y + P_RandomRange(-radius, radius)*FRACUNIT,
+ source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMEXPLODE);
+ K_MatchGenericExtraFlags(truc, source);
+ P_SetScale(truc, source->scale);
+ truc->destscale = source->scale*6;
+ truc->scalespeed = source->scale/12;
+ speed = FixedMul(10*FRACUNIT, source->scale)>>FRACBITS;
+ truc->momx = P_RandomRange(-speed, speed)*FRACUNIT;
+ truc->momy = P_RandomRange(-speed, speed)*FRACUNIT;
+ speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS;
+ truc->momz = P_RandomRange(-speed, speed)*FRACUNIT*P_MobjFlip(truc);
+ if (truc->eflags & MFE_UNDERWATER)
+ truc->momz = (117 * truc->momz) / 200;
+ truc->color = color;
+ }
+
+ for (i = 0; i < 16; i++)
+ {
+ dust = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT,
+ source->y + P_RandomRange(-radius, radius)*FRACUNIT,
+ source->z + P_RandomRange(0, height)*FRACUNIT, MT_SMOKE);
+ P_SetMobjState(dust, S_OPAQUESMOKE1);
+ P_SetScale(dust, source->scale);
+ dust->destscale = source->scale*10;
+ dust->scalespeed = source->scale/12;
+ dust->tics = 30;
+ dust->momz = P_RandomRange(FixedMul(3*FRACUNIT, source->scale)>>FRACBITS, FixedMul(7*FRACUNIT, source->scale)>>FRACBITS)*FRACUNIT;
+
+ truc = P_SpawnMobj(source->x + P_RandomRange(-radius, radius)*FRACUNIT,
+ source->y + P_RandomRange(-radius, radius)*FRACUNIT,
+ source->z + P_RandomRange(0, height)*FRACUNIT, MT_BOOMPARTICLE);
+ K_MatchGenericExtraFlags(truc, source);
+ P_SetScale(truc, source->scale);
+ truc->destscale = source->scale*5;
+ truc->scalespeed = source->scale/12;
+ speed = FixedMul(20*FRACUNIT, source->scale)>>FRACBITS;
+ truc->momx = P_RandomRange(-speed, speed)*FRACUNIT;
+ truc->momy = P_RandomRange(-speed, speed)*FRACUNIT;
+ speed = FixedMul(15*FRACUNIT, source->scale)>>FRACBITS;
+ speed2 = FixedMul(45*FRACUNIT, source->scale)>>FRACBITS;
+ truc->momz = P_RandomRange(speed, speed2)*FRACUNIT*P_MobjFlip(truc);
+ if (P_RandomChance(FRACUNIT/2))
+ truc->momz = -truc->momz;
+ if (truc->eflags & MFE_UNDERWATER)
+ truc->momz = (117 * truc->momz) / 200;
+ truc->tics = TICRATE*2;
+ truc->color = color;
+ }
+}
+
+static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, INT32 flags2, fixed_t speed)
+{
+ mobj_t *th;
+ fixed_t x, y, z;
+ fixed_t finalspeed = speed;
+ mobj_t *throwmo;
+
+ if (source->player && source->player->speed > K_GetKartSpeed(source->player, false))
+ {
+ angle_t input = source->angle - an;
+ boolean invert = (input > ANGLE_180);
+ if (invert)
+ input = InvAngle(input);
+
+ finalspeed = max(speed, FixedMul(speed, FixedMul(
+ FixedDiv(source->player->speed, K_GetKartSpeed(source->player, false)), // Multiply speed to be proportional to your own, boosted maxspeed.
+ (((180<x + source->momx + FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT));
+ y = source->y + source->momy + FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT));
+ z = source->z; // spawn on the ground please
+
+ if (P_MobjFlip(source) < 0)
+ {
+ z = source->z+source->height - mobjinfo[type].height;
+ }
+
+ th = P_SpawnMobj(x, y, z, type);
+
+ th->flags2 |= flags2;
+
+ th->threshold = 10;
+
+ if (th->info->seesound)
+ S_StartSound(source, th->info->seesound);
+
+ P_SetTarget(&th->target, source);
+
+ if (P_IsObjectOnGround(source))
+ {
+ // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn
+ // This should set it for FOFs
+ P_TeleportMove(th, th->x, th->y, th->z);
+ // spawn on the ground if the player is on the ground
+ if (P_MobjFlip(source) < 0)
+ {
+ th->z = th->ceilingz - th->height;
+ th->eflags |= MFE_VERTICALFLIP;
+ }
+ else
+ th->z = th->floorz;
+ }
+
+ th->angle = an;
+ th->momx = FixedMul(finalspeed, FINECOSINE(an>>ANGLETOFINESHIFT));
+ th->momy = FixedMul(finalspeed, FINESINE(an>>ANGLETOFINESHIFT));
+
+ switch (type)
+ {
+ case MT_ORBINAUT:
+ if (source && source->player)
+ th->color = source->player->skincolor;
+ else
+ th->color = SKINCOLOR_GREY;
+ th->movefactor = finalspeed;
+ break;
+ case MT_JAWZ:
+ if (source && source->player)
+ {
+ INT32 lasttarg = source->player->kartstuff[k_lastjawztarget];
+ th->cvmem = source->player->skincolor;
+ if ((lasttarg >= 0 && lasttarg < MAXPLAYERS)
+ && playeringame[lasttarg]
+ && !players[lasttarg].spectator
+ && players[lasttarg].mo)
+ {
+ P_SetTarget(&th->tracer, players[lasttarg].mo);
+ }
+ }
+ else
+ th->cvmem = SKINCOLOR_KETCHUP;
+ /* FALLTHRU */
+ case MT_JAWZ_DUD:
+ S_StartSound(th, th->info->activesound);
+ /* FALLTHRU */
+ case MT_SPB:
+ th->movefactor = finalspeed;
+ break;
+ default:
+ break;
+ }
+
+ x = x + P_ReturnThrustX(source, an, source->radius + th->radius);
+ y = y + P_ReturnThrustY(source, an, source->radius + th->radius);
+ throwmo = P_SpawnMobj(x, y, z, MT_FIREDITEM);
+ throwmo->movecount = 1;
+ throwmo->movedir = source->angle - an;
+ P_SetTarget(&throwmo->target, source);
+
+ return NULL;
+}
+
+static void K_SpawnDriftSparks(player_t *player)
+{
+ fixed_t newx;
+ fixed_t newy;
+ mobj_t *spark;
+ angle_t travelangle;
+ INT32 i;
+
+ I_Assert(player != NULL);
+ I_Assert(player->mo != NULL);
+ I_Assert(!P_MobjWasRemoved(player->mo));
+
+ if (leveltime % 2 == 1)
+ return;
+
+ if (!P_IsObjectOnGround(player->mo))
+ return;
+
+ if (!player->kartstuff[k_drift] || player->kartstuff[k_driftcharge] < K_GetKartDriftSparkValue(player))
+ return;
+
+ travelangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift];
+
+ for (i = 0; i < 2; i++)
+ {
+ newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale));
+ newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(32*FRACUNIT, player->mo->scale));
+ spark = P_SpawnMobj(newx, newy, player->mo->z, MT_DRIFTSPARK);
+
+ P_SetTarget(&spark->target, player->mo);
+ spark->angle = travelangle-(ANGLE_45/5)*player->kartstuff[k_drift];
+ spark->destscale = player->mo->scale;
+ P_SetScale(spark, player->mo->scale);
+
+ spark->momx = player->mo->momx/2;
+ spark->momy = player->mo->momy/2;
+ //spark->momz = player->mo->momz/2;
+
+ if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*4)
+ {
+ spark->color = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1)));
+ }
+ else if (player->kartstuff[k_driftcharge] >= K_GetKartDriftSparkValue(player)*2)
+ {
+ if (player->kartstuff[k_driftcharge] <= (K_GetKartDriftSparkValue(player)*2)+(24*3))
+ spark->color = SKINCOLOR_RASPBERRY; // transition
+ else
+ spark->color = SKINCOLOR_KETCHUP;
+ }
+ else
+ {
+ spark->color = SKINCOLOR_SAPPHIRE;
+ }
+
+ if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn > 0) // Inward drifts
+ || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn < 0))
+ {
+ if ((player->kartstuff[k_drift] < 0 && (i & 1))
+ || (player->kartstuff[k_drift] > 0 && !(i & 1)))
+ P_SetMobjState(spark, S_DRIFTSPARK_A1);
+ else if ((player->kartstuff[k_drift] < 0 && !(i & 1))
+ || (player->kartstuff[k_drift] > 0 && (i & 1)))
+ P_SetMobjState(spark, S_DRIFTSPARK_C1);
+ }
+ else if ((player->kartstuff[k_drift] > 0 && player->cmd.driftturn < 0) // Outward drifts
+ || (player->kartstuff[k_drift] < 0 && player->cmd.driftturn > 0))
+ {
+ if ((player->kartstuff[k_drift] < 0 && (i & 1))
+ || (player->kartstuff[k_drift] > 0 && !(i & 1)))
+ P_SetMobjState(spark, S_DRIFTSPARK_C1);
+ else if ((player->kartstuff[k_drift] < 0 && !(i & 1))
+ || (player->kartstuff[k_drift] > 0 && (i & 1)))
+ P_SetMobjState(spark, S_DRIFTSPARK_A1);
+ }
+
+ K_MatchGenericExtraFlags(spark, player->mo);
+ }
+}
+
+static void K_SpawnAIZDust(player_t *player)
+{
+ fixed_t newx;
+ fixed_t newy;
+ mobj_t *spark;
+ angle_t travelangle;
+
+ I_Assert(player != NULL);
+ I_Assert(player->mo != NULL);
+ I_Assert(!P_MobjWasRemoved(player->mo));
+
+ if (leveltime % 2 == 1)
+ return;
+
+ if (!P_IsObjectOnGround(player->mo))
+ return;
+
+ travelangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
+ //S_StartSound(player->mo, sfx_s3k47);
+
+ {
+ newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale));
+ newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle - (player->kartstuff[k_aizdriftstrat]*ANGLE_45), FixedMul(24*FRACUNIT, player->mo->scale));
+ spark = P_SpawnMobj(newx, newy, player->mo->z, MT_AIZDRIFTSTRAT);
+
+ spark->angle = travelangle+(player->kartstuff[k_aizdriftstrat]*ANGLE_90);
+ P_SetScale(spark, (spark->destscale = (3*player->mo->scale)>>2));
+
+ spark->momx = (6*player->mo->momx)/5;
+ spark->momy = (6*player->mo->momy)/5;
+ //spark->momz = player->mo->momz/2;
+
+ K_MatchGenericExtraFlags(spark, player->mo);
+ }
+}
+
+void K_SpawnBoostTrail(player_t *player)
+{
+ fixed_t newx;
+ fixed_t newy;
+ fixed_t ground;
+ mobj_t *flame;
+ angle_t travelangle;
+ INT32 i;
+
+ I_Assert(player != NULL);
+ I_Assert(player->mo != NULL);
+ I_Assert(!P_MobjWasRemoved(player->mo));
+
+ if (!P_IsObjectOnGround(player->mo)
+ || player->kartstuff[k_hyudorotimer] != 0
+ || (G_BattleGametype() && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer]))
+ return;
+
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ ground = player->mo->ceilingz - FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale);
+ else
+ ground = player->mo->floorz;
+
+ if (player->kartstuff[k_drift] != 0)
+ travelangle = player->mo->angle;
+ else
+ travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
+
+ for (i = 0; i < 2; i++)
+ {
+ newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
+ newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
+#ifdef ESLOPE
+ if (player->mo->standingslope)
+ {
+ ground = P_GetZAt(player->mo->standingslope, newx, newy);
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ ground -= FixedMul(mobjinfo[MT_SNEAKERTRAIL].height, player->mo->scale);
+ }
+#endif
+ flame = P_SpawnMobj(newx, newy, ground, MT_SNEAKERTRAIL);
+
+ P_SetTarget(&flame->target, player->mo);
+ flame->angle = travelangle;
+ flame->fuse = TICRATE*2;
+ flame->destscale = player->mo->scale;
+ P_SetScale(flame, player->mo->scale);
+ // not K_MatchGenericExtraFlags so that a stolen sneaker can be seen
+ K_FlipFromObject(flame, player->mo);
+
+ flame->momx = 8;
+ P_XYMovement(flame);
+ if (P_MobjWasRemoved(flame))
+ continue;
+
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ {
+ if (flame->z + flame->height < flame->ceilingz)
+ P_RemoveMobj(flame);
+ }
+ else if (flame->z > flame->floorz)
+ P_RemoveMobj(flame);
+ }
+}
+
+void K_SpawnSparkleTrail(mobj_t *mo)
+{
+ const INT32 rad = (mo->radius*2)>>FRACBITS;
+ mobj_t *sparkle;
+ INT32 i;
+
+ I_Assert(mo != NULL);
+ I_Assert(!P_MobjWasRemoved(mo));
+
+ for (i = 0; i < 3; i++)
+ {
+ fixed_t newx = mo->x + mo->momx + (P_RandomRange(-rad, rad)<y + mo->momy + (P_RandomRange(-rad, rad)<z + mo->momz + (P_RandomRange(0, mo->height>>FRACBITS)<target, mo);
+ sparkle->destscale = mo->destscale;
+ P_SetScale(sparkle, mo->scale);
+ sparkle->color = mo->color;
+ //sparkle->colorized = mo->colorized;
+ }
+
+ P_SetMobjState(sparkle, S_KARTINVULN_LARGE1);
+}
+
+void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent)
+{
+ mobj_t *dust;
+ angle_t aoff;
+
+ I_Assert(mo != NULL);
+ I_Assert(!P_MobjWasRemoved(mo));
+
+ if (mo->player)
+ aoff = (mo->player->frameangle + ANGLE_180);
+ else
+ aoff = (mo->angle + ANGLE_180);
+
+ if ((leveltime / 2) & 1)
+ aoff -= ANGLE_45;
+ else
+ aoff += ANGLE_45;
+
+ dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS),
+ mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)) + (P_RandomRange(-8,8) << FRACBITS),
+ mo->z, MT_WIPEOUTTRAIL);
+
+ P_SetTarget(&dust->target, mo);
+ dust->angle = R_PointToAngle2(0,0,mo->momx,mo->momy);
+ dust->destscale = mo->scale;
+ P_SetScale(dust, mo->scale);
+ K_FlipFromObject(dust, mo);
+
+ if (translucent) // offroad effect
+ {
+ dust->momx = mo->momx/2;
+ dust->momy = mo->momy/2;
+ dust->momz = mo->momz/2;
+ }
+
+ if (translucent)
+ dust->flags2 |= MF2_SHADOW;
+}
+
+// K_DriftDustHandling
+// Parameters:
+// spawner: The map object that is spawning the drift dust
+// Description: Spawns the drift dust for objects, players use rmomx/y, other objects use regular momx/y.
+// Also plays the drift sound.
+// Other objects should be angled towards where they're trying to go so they don't randomly spawn dust
+// Do note that most of the function won't run in odd intervals of frames
+void K_DriftDustHandling(mobj_t *spawner)
+{
+ angle_t anglediff;
+ const INT16 spawnrange = spawner->radius>>FRACBITS;
+
+ if (!P_IsObjectOnGround(spawner) || leveltime % 2 != 0)
+ return;
+
+ if (spawner->player)
+ {
+ if (spawner->player->pflags & PF_SKIDDOWN)
+ {
+ anglediff = abs((signed)(spawner->angle - spawner->player->frameangle));
+ if (leveltime % 6 == 0)
+ S_StartSound(spawner, sfx_screec); // repeated here because it doesn't always happen to be within the range when this is the case
+ }
+ else
+ {
+ angle_t playerangle = spawner->angle;
+
+ if (spawner->player->speed < 5<player->cmd.forwardmove < 0)
+ playerangle += ANGLE_180;
+
+ anglediff = abs((signed)(playerangle - R_PointToAngle2(0, 0, spawner->player->rmomx, spawner->player->rmomy)));
+ }
+ }
+ else
+ {
+ if (P_AproxDistance(spawner->momx, spawner->momy) < 5<angle - R_PointToAngle2(0, 0, spawner->momx, spawner->momy)));
+ }
+
+ if (anglediff > ANGLE_180)
+ anglediff = InvAngle(anglediff);
+
+ if (anglediff > ANG10*4) // Trying to turn further than 40 degrees
+ {
+ fixed_t spawnx = P_RandomRange(-spawnrange, spawnrange)<x + spawnx, spawner->y + spawny, spawner->z, MT_DRIFTDUST);
+ dust->momx = FixedMul(spawner->momx + (P_RandomRange(-speedrange, speedrange)<scale)/4);
+ dust->momy = FixedMul(spawner->momy + (P_RandomRange(-speedrange, speedrange)<scale)/4);
+ dust->momz = P_MobjFlip(spawner) * (P_RandomRange(1, 4) * (spawner->scale));
+ P_SetScale(dust, spawner->scale/2);
+ dust->destscale = spawner->scale * 3;
+ dust->scalespeed = spawner->scale/12;
+
+ if (leveltime % 6 == 0)
+ S_StartSound(spawner, sfx_screec);
+
+ K_MatchGenericExtraFlags(dust, spawner);
+ }
+}
+
+static mobj_t *K_FindLastTrailMobj(player_t *player)
+{
+ mobj_t *trail;
+
+ if (!player || !(trail = player->mo) || !player->mo->hnext || !player->mo->hnext->health)
+ return NULL;
+
+ while (trail->hnext && !P_MobjWasRemoved(trail->hnext) && trail->hnext->health)
+ {
+ trail = trail->hnext;
+ }
+
+ return trail;
+}
+
+static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow)
+{
+ mobj_t *mo;
+ INT32 dir;
+ fixed_t PROJSPEED;
+ angle_t newangle;
+ fixed_t newx, newy, newz;
+ mobj_t *throwmo;
+
+ if (!player)
+ return NULL;
+
+ // Figure out projectile speed by game speed
+ if (missile && mapthing != MT_BALLHOG) // Trying to keep compatability...
+ {
+ PROJSPEED = mobjinfo[mapthing].speed;
+ if (gamespeed == 0)
+ PROJSPEED = FixedMul(PROJSPEED, FRACUNIT-FRACUNIT/4);
+ else if (gamespeed == 2)
+ PROJSPEED = FixedMul(PROJSPEED, FRACUNIT+FRACUNIT/4);
+ PROJSPEED = FixedMul(PROJSPEED, mapobjectscale);
+ }
+ else
+ {
+ switch (gamespeed)
+ {
+ case 0:
+ PROJSPEED = 68*mapobjectscale; // Avg Speed is 34
+ break;
+ case 2:
+ PROJSPEED = 96*mapobjectscale; // Avg Speed is 48
+ break;
+ default:
+ PROJSPEED = 82*mapobjectscale; // Avg Speed is 41
+ break;
+ }
+ }
+
+ if (altthrow)
+ {
+ if (altthrow == 2) // Kitchen sink throwing
+ {
+#if 0
+ if (player->kartstuff[k_throwdir] == 1)
+ dir = 3;
+ else if (player->kartstuff[k_throwdir] == -1)
+ dir = 1;
+ else
+ dir = 2;
+#else
+ if (player->kartstuff[k_throwdir] == 1)
+ dir = 2;
+ else
+ dir = 1;
+#endif
+ }
+ else
+ {
+ if (player->kartstuff[k_throwdir] == 1)
+ dir = 2;
+ else if (player->kartstuff[k_throwdir] == -1)
+ dir = -1;
+ else
+ dir = 1;
+ }
+ }
+ else
+ {
+ if (player->kartstuff[k_throwdir] != 0)
+ dir = player->kartstuff[k_throwdir];
+ else
+ dir = defaultDir;
+ }
+
+ if (missile) // Shootables
+ {
+ if (mapthing == MT_BALLHOG) // Messy
+ {
+ if (dir == -1)
+ {
+ // Shoot backward
+ mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x06000000, 0, PROJSPEED/4);
+ K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 - 0x03000000, 0, PROJSPEED/4);
+ K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/4);
+ K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x03000000, 0, PROJSPEED/4);
+ K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180 + 0x06000000, 0, PROJSPEED/4);
+ }
+ else
+ {
+ // Shoot forward
+ mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x06000000, 0, PROJSPEED);
+ K_SpawnKartMissile(player->mo, mapthing, player->mo->angle - 0x03000000, 0, PROJSPEED);
+ K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED);
+ K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x03000000, 0, PROJSPEED);
+ K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + 0x06000000, 0, PROJSPEED);
+ }
+ }
+ else
+ {
+ if (dir == -1 && mapthing != MT_SPB)
+ {
+ // Shoot backward
+ mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle + ANGLE_180, 0, PROJSPEED/2);
+ }
+ else
+ {
+ // Shoot forward
+ mo = K_SpawnKartMissile(player->mo, mapthing, player->mo->angle, 0, PROJSPEED);
+ }
+ }
+ }
+ else
+ {
+ player->kartstuff[k_bananadrag] = 0; // RESET timer, for multiple bananas
+
+ if (dir > 0)
+ {
+ // Shoot forward
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, mapthing);
+ //K_FlipFromObject(mo, player->mo);
+ // These are really weird so let's make it a very specific case to make SURE it works...
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ {
+ mo->z -= player->mo->height;
+ mo->flags2 |= MF2_OBJECTFLIP;
+ mo->eflags |= MFE_VERTICALFLIP;
+ }
+
+ mo->threshold = 10;
+ P_SetTarget(&mo->target, player->mo);
+
+ S_StartSound(player->mo, mo->info->seesound);
+
+ if (mo)
+ {
+ angle_t fa = player->mo->angle>>ANGLETOFINESHIFT;
+ fixed_t HEIGHT = (20 + (dir*10))*mapobjectscale + (player->mo->momz*P_MobjFlip(player->mo));
+
+ P_SetObjectMomZ(mo, HEIGHT, false);
+ mo->momx = player->mo->momx + FixedMul(FINECOSINE(fa), PROJSPEED*dir);
+ mo->momy = player->mo->momy + FixedMul(FINESINE(fa), PROJSPEED*dir);
+
+ mo->extravalue2 = dir;
+
+ if (mo->eflags & MFE_UNDERWATER)
+ mo->momz = (117 * mo->momz) / 200;
+ }
+
+ // this is the small graphic effect that plops in you when you throw an item:
+ throwmo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FIREDITEM);
+ P_SetTarget(&throwmo->target, player->mo);
+ // Ditto:
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ {
+ throwmo->z -= player->mo->height;
+ throwmo->flags2 |= MF2_OBJECTFLIP;
+ throwmo->eflags |= MFE_VERTICALFLIP;
+ }
+
+ throwmo->movecount = 0; // above player
+ }
+ else
+ {
+ mobj_t *lasttrail = K_FindLastTrailMobj(player);
+
+ if (lasttrail)
+ {
+ newx = lasttrail->x;
+ newy = lasttrail->y;
+ newz = lasttrail->z;
+ }
+ else
+ {
+ // Drop it directly behind you.
+ fixed_t dropradius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(mobjinfo[mapthing].radius, mobjinfo[mapthing].radius);
+
+ newangle = player->mo->angle;
+
+ newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, dropradius);
+ newy = player->mo->y + P_ReturnThrustY(player->mo, newangle + ANGLE_180, dropradius);
+ newz = player->mo->z;
+ }
+
+ mo = P_SpawnMobj(newx, newy, newz, mapthing); // this will never return null because collision isn't processed here
+ K_FlipFromObject(mo, player->mo);
+
+ mo->threshold = 10;
+ P_SetTarget(&mo->target, player->mo);
+
+ if (P_IsObjectOnGround(player->mo))
+ {
+ // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn
+ // This should set it for FOFs
+ P_TeleportMove(mo, mo->x, mo->y, mo->z); // however, THIS can fuck up your day. just absolutely ruin you.
+ if (P_MobjWasRemoved(mo))
+ return NULL;
+
+ if (P_MobjFlip(mo) > 0)
+ {
+ if (mo->floorz > mo->target->z - mo->height)
+ {
+ mo->z = mo->floorz;
+ }
+ }
+ else
+ {
+ if (mo->ceilingz < mo->target->z + mo->target->height + mo->height)
+ {
+ mo->z = mo->ceilingz - mo->height;
+ }
+ }
+ }
+
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ mo->eflags |= MFE_VERTICALFLIP;
+
+ if (mapthing == MT_SSMINE)
+ mo->extravalue1 = 49; // Pads the start-up length from 21 frames to a full 2 seconds
+ }
+ }
+
+ return mo;
+}
+
+void K_PuntMine(mobj_t *thismine, mobj_t *punter)
+{
+ angle_t fa = R_PointToAngle2(0, 0, punter->momx, punter->momy) >> ANGLETOFINESHIFT;
+ fixed_t z = 30*mapobjectscale + punter->momz;
+ fixed_t spd;
+ mobj_t *mine;
+
+ if (!thismine || P_MobjWasRemoved(thismine))
+ return;
+
+ if (thismine->type == MT_SSMINE_SHIELD) // Create a new mine
+ {
+ mine = P_SpawnMobj(thismine->x, thismine->y, thismine->z, MT_SSMINE);
+ P_SetTarget(&mine->target, thismine->target);
+ mine->angle = thismine->angle;
+ mine->flags2 = thismine->flags2;
+ mine->floorz = thismine->floorz;
+ mine->ceilingz = thismine->ceilingz;
+ P_RemoveMobj(thismine);
+ }
+ else
+ mine = thismine;
+
+ if (!mine || P_MobjWasRemoved(mine))
+ return;
+
+ switch (gamespeed)
+ {
+ case 0:
+ spd = 68*mapobjectscale; // Avg Speed is 34
+ break;
+ case 2:
+ spd = 96*mapobjectscale; // Avg Speed is 48
+ break;
+ default:
+ spd = 82*mapobjectscale; // Avg Speed is 41
+ break;
+ }
+
+ mine->flags |= MF_NOCLIPTHING;
+
+ P_SetMobjState(mine, S_SSMINE_AIR1);
+ mine->threshold = 10;
+ mine->extravalue1 = 0;
+ mine->reactiontime = mine->info->reactiontime;
+
+ mine->momx = punter->momx + FixedMul(FINECOSINE(fa), spd);
+ mine->momy = punter->momy + FixedMul(FINESINE(fa), spd);
+ mine->momz = P_MobjFlip(mine) * z;
+
+ mine->flags &= ~MF_NOCLIPTHING;
+}
+
+#define THUNDERRADIUS 320
+
+static void K_DoThunderShield(player_t *player)
+{
+ mobj_t *mo;
+ int i = 0;
+ fixed_t sx;
+ fixed_t sy;
+ angle_t an;
+
+ S_StartSound(player->mo, sfx_zio3);
+ //player->kartstuff[k_thunderanim] = 35;
+ P_NukeEnemies(player->mo, player->mo, RING_DIST/4);
+
+ // spawn vertical bolt
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK);
+ P_SetTarget(&mo->target, player->mo);
+ P_SetMobjState(mo, S_LZIO11);
+ mo->color = SKINCOLOR_TEAL;
+ mo->scale = player->mo->scale*3 + (player->mo->scale/2);
+
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK);
+ P_SetTarget(&mo->target, player->mo);
+ P_SetMobjState(mo, S_LZIO21);
+ mo->color = SKINCOLOR_CYAN;
+ mo->scale = player->mo->scale*3 + (player->mo->scale/2);
+
+ // spawn horizontal bolts;
+ for (i=0; i<7; i++)
+ {
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THOK);
+ mo->angle = P_RandomRange(0, 359)*ANG1;
+ mo->fuse = P_RandomRange(20, 50);
+ P_SetTarget(&mo->target, player->mo);
+ P_SetMobjState(mo, S_KLIT1);
+ }
+
+ // spawn the radius thing:
+ an = ANGLE_22h;
+ for (i=0; i<15; i++)
+ {
+ sx = player->mo->x + FixedMul((player->mo->scale*THUNDERRADIUS), FINECOSINE((an*i)>>ANGLETOFINESHIFT));
+ sy = player->mo->y + FixedMul((player->mo->scale*THUNDERRADIUS), FINESINE((an*i)>>ANGLETOFINESHIFT));
+ mo = P_SpawnMobj(sx, sy, player->mo->z, MT_THOK);
+ mo-> angle = an*i;
+ mo->extravalue1 = THUNDERRADIUS; // Used to know whether we should teleport by radius or something.
+ mo->scale = player->mo->scale*3;
+ P_SetTarget(&mo->target, player->mo);
+ P_SetMobjState(mo, S_KSPARK1);
+ }
+}
+
+#undef THUNDERRADIUS
+
+static void K_DoHyudoroSteal(player_t *player)
+{
+ INT32 i, numplayers = 0;
+ INT32 playerswappable[MAXPLAYERS];
+ INT32 stealplayer = -1; // The player that's getting stolen from
+ INT32 prandom = 0;
+ boolean sink = P_RandomChance(FRACUNIT/64);
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
+ && player != &players[i] && !players[i].exiting && !players[i].spectator // Player in-game
+
+ // Can steal from this player
+ && (G_RaceGametype() //&& players[i].kartstuff[k_position] < player->kartstuff[k_position])
+ || (G_BattleGametype() && players[i].kartstuff[k_bumper] > 0))
+
+ // Has an item
+ && (players[i].kartstuff[k_itemtype]
+ && players[i].kartstuff[k_itemamount]
+ && !players[i].kartstuff[k_itemheld]
+ && !players[i].kartstuff[k_itemblink]))
+ {
+ playerswappable[numplayers] = i;
+ numplayers++;
+ }
+ }
+
+ prandom = P_RandomFixed();
+ S_StartSound(player->mo, sfx_s3k92);
+
+ if (sink && numplayers > 0 && cv_kitchensink.value) // BEHOLD THE KITCHEN SINK
+ {
+ player->kartstuff[k_hyudorotimer] = hyudorotime;
+ player->kartstuff[k_stealingtimer] = stealtime;
+
+ player->kartstuff[k_itemtype] = KITEM_KITCHENSINK;
+ player->kartstuff[k_itemamount] = 1;
+ player->kartstuff[k_itemheld] = 0;
+ return;
+ }
+ else if ((G_RaceGametype() && player->kartstuff[k_position] == 1) || numplayers == 0) // No-one can be stolen from? Oh well...
+ {
+ player->kartstuff[k_hyudorotimer] = hyudorotime;
+ player->kartstuff[k_stealingtimer] = stealtime;
+ return;
+ }
+ else if (numplayers == 1) // With just 2 players, we just need to set the other player to be the one to steal from
+ {
+ stealplayer = playerswappable[numplayers-1];
+ }
+ else if (numplayers > 1) // We need to choose between the available candidates for the 2nd player
+ {
+ stealplayer = playerswappable[prandom%(numplayers-1)];
+ }
+
+ if (stealplayer > -1) // Now here's where we do the stealing, has to be done here because we still know the player we're stealing from
+ {
+ player->kartstuff[k_hyudorotimer] = hyudorotime;
+ player->kartstuff[k_stealingtimer] = stealtime;
+ players[stealplayer].kartstuff[k_stolentimer] = stealtime;
+
+ player->kartstuff[k_itemtype] = players[stealplayer].kartstuff[k_itemtype];
+ player->kartstuff[k_itemamount] = players[stealplayer].kartstuff[k_itemamount];
+ player->kartstuff[k_itemheld] = 0;
+
+ players[stealplayer].kartstuff[k_itemtype] = KITEM_NONE;
+ players[stealplayer].kartstuff[k_itemamount] = 0;
+ players[stealplayer].kartstuff[k_itemheld] = 0;
+
+ if (P_IsLocalPlayer(&players[stealplayer]) && !splitscreen)
+ S_StartSound(NULL, sfx_s3k92);
+ }
+}
+
+void K_DoSneaker(player_t *player, INT32 type)
+{
+ fixed_t intendedboost;
+
+ switch (gamespeed)
+ {
+ case 0:
+ intendedboost = 53740+768;
+ break;
+ case 2:
+ intendedboost = 17294+768;
+ break;
+ default:
+ intendedboost = 32768;
+ break;
+ }
+
+ if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3)
+ {
+ S_StartSound(player->mo, sfx_cdfm01);
+ K_SpawnDashDustRelease(player);
+ if (intendedboost > player->kartstuff[k_speedboost])
+ player->kartstuff[k_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost));
+ }
+
+ if (!player->kartstuff[k_sneakertimer])
+ {
+ if (type == 2)
+ {
+ if (player->mo->hnext)
+ {
+ mobj_t *cur = player->mo->hnext;
+ while (cur && !P_MobjWasRemoved(cur))
+ {
+ if (!cur->tracer)
+ {
+ mobj_t *overlay = P_SpawnMobj(cur->x, cur->y, cur->z, MT_BOOSTFLAME);
+ P_SetTarget(&overlay->target, cur);
+ P_SetTarget(&cur->tracer, overlay);
+ P_SetScale(overlay, (overlay->destscale = 3*cur->scale/4));
+ K_FlipFromObject(overlay, cur);
+ }
+ cur = cur->hnext;
+ }
+ }
+ }
+ else
+ {
+ mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BOOSTFLAME);
+ P_SetTarget(&overlay->target, player->mo);
+ P_SetScale(overlay, (overlay->destscale = player->mo->scale));
+ K_FlipFromObject(overlay, player->mo);
+ }
+ }
+
+ player->kartstuff[k_sneakertimer] = sneakertime;
+
+ if (type != 0)
+ {
+ player->pflags |= PF_ATTACKDOWN;
+ K_PlayBoostTaunt(player->mo);
+ player->powers[pw_flashing] = 0; // Stop flashing after boosting
+ }
+}
+
+static void K_DoShrink(player_t *user)
+{
+ INT32 i;
+
+ S_StartSound(user->mo, sfx_kc46); // Sound the BANG!
+ user->pflags |= PF_ATTACKDOWN;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator || !players[i].mo)
+ continue;
+ if (&players[i] == user)
+ continue;
+ if (players[i].kartstuff[k_position] < user->kartstuff[k_position])
+ {
+ // Don't hit while invulnerable!
+ if (!players[i].kartstuff[k_invincibilitytimer]
+ && players[i].kartstuff[k_growshrinktimer] <= 0
+ && !players[i].kartstuff[k_hyudorotimer])
+ {
+ // Start shrinking!
+ K_DropItems(&players[i]);
+ players[i].kartstuff[k_growshrinktimer] = -(20*TICRATE);
+
+ if (players[i].mo && !P_MobjWasRemoved(players[i].mo))
+ {
+ players[i].mo->scalespeed = mapobjectscale/TICRATE;
+ players[i].mo->destscale = (6*mapobjectscale)/8;
+ if (cv_kartdebugshrink.value && !modeattacking && !players[i].bot)
+ players[i].mo->destscale = (6*players[i].mo->destscale)/8;
+ }
+ }
+
+ // Grow should get taken away.
+ if (players[i].kartstuff[k_growshrinktimer] > 0)
+ K_RemoveGrowShrink(&players[i]);
+
+ //P_FlashPal(&players[i], PAL_NUKE, 10);
+ S_StartSound(players[i].mo, sfx_kc59);
+ }
+ }
+}
+
+
+void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound)
+{
+ const fixed_t vscale = mapobjectscale + (mo->scale - mapobjectscale);
+
+ if (mo->player && mo->player->spectator)
+ return;
+
+ if (mo->eflags & MFE_SPRUNG)
+ return;
+
+#ifdef ESLOPE
+ mo->standingslope = NULL;
+#endif
+
+ mo->eflags |= MFE_SPRUNG;
+
+ if (mo->eflags & MFE_VERTICALFLIP)
+ vertispeed *= -1;
+
+ if (vertispeed == 0)
+ {
+ fixed_t thrust;
+
+ if (mo->player)
+ {
+ thrust = 3*mo->player->speed/2;
+ if (thrust < 48< 72<player->kartstuff[k_pogospring] != 2)
+ {
+ if (mo->player->kartstuff[k_sneakertimer])
+ thrust = FixedMul(thrust, 5*FRACUNIT/4);
+ else if (mo->player->kartstuff[k_invincibilitytimer])
+ thrust = FixedMul(thrust, 9*FRACUNIT/8);
+ }
+ }
+ else
+ {
+ thrust = FixedDiv(3*P_AproxDistance(mo->momx, mo->momy)/2, 5*FRACUNIT/2);
+ if (thrust < 16< 32<momz = P_MobjFlip(mo)*FixedMul(FINESINE(ANGLE_22h>>ANGLETOFINESHIFT), FixedMul(thrust, vscale));
+ }
+ else
+ mo->momz = FixedMul(vertispeed, vscale);
+
+ if (mo->eflags & MFE_UNDERWATER)
+ mo->momz = (117 * mo->momz) / 200;
+
+ if (sound)
+ S_StartSound(mo, (sound == 1 ? sfx_kc2f : sfx_kpogos));
+}
+
+void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source)
+{
+ mobj_t *cachenext;
+
+killnext:
+ cachenext = banana->hnext;
+
+ if (banana->health)
+ {
+ if (banana->eflags & MFE_VERTICALFLIP)
+ banana->z -= banana->height;
+ else
+ banana->z += banana->height;
+
+ S_StartSound(banana, banana->info->deathsound);
+ P_KillMobj(banana, inflictor, source);
+
+ P_SetObjectMomZ(banana, 8*FRACUNIT, false);
+ if (inflictor)
+ P_InstaThrust(banana, R_PointToAngle2(inflictor->x, inflictor->y, banana->x, banana->y)+ANGLE_90, 16*FRACUNIT);
+ }
+
+ if ((banana = cachenext))
+ goto killnext;
+}
+
+// Just for firing/dropping items.
+void K_UpdateHnextList(player_t *player, boolean clean)
+{
+ mobj_t *work = player->mo, *nextwork;
+
+ if (!work)
+ return;
+
+ nextwork = work->hnext;
+
+ while ((work = nextwork) && !P_MobjWasRemoved(work))
+ {
+ nextwork = work->hnext;
+
+ if (!clean && (!work->movedir || work->movedir <= (UINT16)player->kartstuff[k_itemamount]))
+ continue;
+
+ P_RemoveMobj(work);
+ }
+}
+
+// For getting hit!
+void K_DropHnextList(player_t *player)
+{
+ mobj_t *work = player->mo, *nextwork, *dropwork;
+ INT32 flip;
+ mobjtype_t type;
+ boolean orbit, ponground, dropall = true;
+
+ if (!work || P_MobjWasRemoved(work))
+ return;
+
+ flip = P_MobjFlip(player->mo);
+ ponground = P_IsObjectOnGround(player->mo);
+
+ if (player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD && player->kartstuff[k_itemamount])
+ {
+ K_DoThunderShield(player);
+ player->kartstuff[k_itemamount] = 0;
+ player->kartstuff[k_itemtype] = KITEM_NONE;
+ player->kartstuff[k_curshield] = 0;
+ }
+
+ nextwork = work->hnext;
+
+ while ((work = nextwork) && !P_MobjWasRemoved(work))
+ {
+ nextwork = work->hnext;
+
+ switch (work->type)
+ {
+ // Kart orbit items
+ case MT_ORBINAUT_SHIELD:
+ orbit = true;
+ type = MT_ORBINAUT;
+ break;
+ case MT_JAWZ_SHIELD:
+ orbit = true;
+ type = MT_JAWZ_DUD;
+ break;
+ // Kart trailing items
+ case MT_BANANA_SHIELD:
+ orbit = false;
+ type = MT_BANANA;
+ break;
+ case MT_SSMINE_SHIELD:
+ orbit = false;
+ dropall = false;
+ type = MT_SSMINE;
+ break;
+ case MT_EGGMANITEM_SHIELD:
+ orbit = false;
+ type = MT_EGGMANITEM;
+ break;
+ // intentionally do nothing
+ case MT_SINK_SHIELD:
+ case MT_ROCKETSNEAKER:
+ return;
+ default:
+ continue;
+ }
+
+ dropwork = P_SpawnMobj(work->x, work->y, work->z, type);
+ P_SetTarget(&dropwork->target, player->mo);
+ dropwork->angle = work->angle;
+ dropwork->flags2 = work->flags2;
+ dropwork->flags |= MF_NOCLIPTHING;
+ dropwork->floorz = work->floorz;
+ dropwork->ceilingz = work->ceilingz;
+
+ if (ponground)
+ {
+ // floorz and ceilingz aren't properly set to account for FOFs and Polyobjects on spawn
+ // This should set it for FOFs
+ //P_TeleportMove(dropwork, dropwork->x, dropwork->y, dropwork->z); -- handled better by above floorz/ceilingz passing
+
+ if (flip == 1)
+ {
+ if (dropwork->floorz > dropwork->target->z - dropwork->height)
+ {
+ dropwork->z = dropwork->floorz;
+ }
+ }
+ else
+ {
+ if (dropwork->ceilingz < dropwork->target->z + dropwork->target->height + dropwork->height)
+ {
+ dropwork->z = dropwork->ceilingz - dropwork->height;
+ }
+ }
+ }
+
+ if (orbit) // splay out
+ {
+ dropwork->flags2 |= MF2_AMBUSH;
+ dropwork->z += flip;
+ dropwork->momx = player->mo->momx>>1;
+ dropwork->momy = player->mo->momy>>1;
+ dropwork->momz = 3*flip*mapobjectscale;
+ if (dropwork->eflags & MFE_UNDERWATER)
+ dropwork->momz = (117 * dropwork->momz) / 200;
+ P_Thrust(dropwork, work->angle - ANGLE_90, 6*mapobjectscale);
+ dropwork->movecount = 2;
+ dropwork->movedir = work->angle - ANGLE_90;
+ P_SetMobjState(dropwork, dropwork->info->deathstate);
+ dropwork->tics = -1;
+ if (type == MT_JAWZ_DUD)
+ dropwork->z += 20*flip*dropwork->scale;
+ else
+ {
+ dropwork->color = work->color;
+ dropwork->angle -= ANGLE_90;
+ }
+ }
+ else // plop on the ground
+ {
+ dropwork->flags &= ~MF_NOCLIPTHING;
+ dropwork->threshold = 10;
+ }
+
+ P_RemoveMobj(work);
+ }
+
+ {
+ // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic...
+ P_SetTarget(&player->mo->hnext, NULL);
+ player->kartstuff[k_bananadrag] = 0;
+ if (player->kartstuff[k_eggmanheld])
+ player->kartstuff[k_eggmanheld] = 0;
+ else if (player->kartstuff[k_itemheld]
+ && (dropall || (--player->kartstuff[k_itemamount] <= 0)))
+ {
+ player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0;
+ player->kartstuff[k_itemtype] = KITEM_NONE;
+ }
+ }
+}
+
+// For getting EXTRA hit!
+void K_DropItems(player_t *player)
+{
+ boolean thunderhack = (player->kartstuff[k_curshield] && player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD);
+
+ if (thunderhack)
+ player->kartstuff[k_itemtype] = KITEM_NONE;
+
+ K_DropHnextList(player);
+
+ if (player->mo && !P_MobjWasRemoved(player->mo) && player->kartstuff[k_itemamount])
+ {
+ mobj_t *drop = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_FLOATINGITEM);
+ P_SetScale(drop, drop->scale>>4);
+ drop->destscale = (3*drop->destscale)/2;;
+
+ drop->angle = player->mo->angle + ANGLE_90;
+ P_Thrust(drop,
+ FixedAngle(P_RandomFixed()*180) + player->mo->angle + ANGLE_90,
+ 16*mapobjectscale);
+ drop->momz = P_MobjFlip(player->mo)*3*mapobjectscale;
+ if (drop->eflags & MFE_UNDERWATER)
+ drop->momz = (117 * drop->momz) / 200;
+
+ drop->threshold = (thunderhack ? KITEM_THUNDERSHIELD : player->kartstuff[k_itemtype]);
+ drop->movecount = player->kartstuff[k_itemamount];
+
+ drop->flags |= MF_NOCLIPTHING;
+ }
+
+ K_StripItems(player);
+}
+
+// When an item in the hnext chain dies.
+void K_RepairOrbitChain(mobj_t *orbit)
+{
+ mobj_t *cachenext = orbit->hnext;
+
+ // First, repair the chain
+ if (orbit->hnext && orbit->hnext->health && !P_MobjWasRemoved(orbit->hnext))
+ {
+ P_SetTarget(&orbit->hnext->hprev, orbit->hprev);
+ P_SetTarget(&orbit->hnext, NULL);
+ }
+
+ if (orbit->hprev && orbit->hprev->health && !P_MobjWasRemoved(orbit->hprev))
+ {
+ P_SetTarget(&orbit->hprev->hnext, cachenext);
+ P_SetTarget(&orbit->hprev, NULL);
+ }
+
+ // Then recount to make sure item amount is correct
+ if (orbit->target && orbit->target->player)
+ {
+ INT32 num = 0;
+
+ mobj_t *cur = orbit->target->hnext;
+ mobj_t *prev = NULL;
+
+ while (cur && !P_MobjWasRemoved(cur))
+ {
+ prev = cur;
+ cur = cur->hnext;
+ if (++num > orbit->target->player->kartstuff[k_itemamount])
+ P_RemoveMobj(prev);
+ else
+ prev->movedir = num;
+ }
+
+ if (orbit->target->player->kartstuff[k_itemamount] != num)
+ orbit->target->player->kartstuff[k_itemamount] = num;
+ }
+}
+
+// Move the hnext chain!
+static void K_MoveHeldObjects(player_t *player)
+{
+ if (!player->mo)
+ return;
+
+ if (!player->mo->hnext)
+ {
+ player->kartstuff[k_bananadrag] = 0;
+ if (player->kartstuff[k_eggmanheld])
+ player->kartstuff[k_eggmanheld] = 0;
+ else if (player->kartstuff[k_itemheld])
+ {
+ player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0;
+ player->kartstuff[k_itemtype] = KITEM_NONE;
+ }
+ return;
+ }
+
+ if (P_MobjWasRemoved(player->mo->hnext))
+ {
+ // we need this here too because this is done in afterthink - pointers are cleaned up at the START of each tic...
+ P_SetTarget(&player->mo->hnext, NULL);
+ player->kartstuff[k_bananadrag] = 0;
+ if (player->kartstuff[k_eggmanheld])
+ player->kartstuff[k_eggmanheld] = 0;
+ else if (player->kartstuff[k_itemheld])
+ {
+ player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0;
+ player->kartstuff[k_itemtype] = KITEM_NONE;
+ }
+ return;
+ }
+
+ switch (player->mo->hnext->type)
+ {
+ case MT_ORBINAUT_SHIELD: // Kart orbit items
+ case MT_JAWZ_SHIELD:
+ {
+ mobj_t *cur = player->mo->hnext;
+ fixed_t speed = ((8 - min(4, player->kartstuff[k_itemamount])) * cur->info->speed) / 7;
+
+ player->kartstuff[k_bananadrag] = 0; // Just to make sure
+
+ while (cur && !P_MobjWasRemoved(cur))
+ {
+ const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius); // mobj's distance from its Target, or Radius.
+ fixed_t z;
+
+ if (!cur->health)
+ {
+ cur = cur->hnext;
+ continue;
+ }
+
+ cur->color = player->skincolor;
+
+ cur->angle -= ANGLE_90;
+ cur->angle += FixedAngle(speed);
+
+ if (cur->extravalue1 < radius)
+ cur->extravalue1 += P_AproxDistance(cur->extravalue1, radius) / 12;
+ if (cur->extravalue1 > radius)
+ cur->extravalue1 = radius;
+
+ // If the player is on the ceiling, then flip your items as well.
+ if (player && player->mo->eflags & MFE_VERTICALFLIP)
+ cur->eflags |= MFE_VERTICALFLIP;
+ else
+ cur->eflags &= ~MFE_VERTICALFLIP;
+
+ // Shrink your items if the player shrunk too.
+ P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale)));
+
+ if (P_MobjFlip(cur) > 0)
+ z = player->mo->z;
+ else
+ z = player->mo->z + player->mo->height - cur->height;
+
+ cur->flags |= MF_NOCLIPTHING; // temporarily make them noclip other objects so they can't hit anyone while in the player
+ P_TeleportMove(cur, player->mo->x, player->mo->y, z);
+ cur->momx = FixedMul(FINECOSINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1);
+ cur->momy = FixedMul(FINESINE(cur->angle>>ANGLETOFINESHIFT), cur->extravalue1);
+ cur->flags &= ~MF_NOCLIPTHING;
+ if (!P_TryMove(cur, player->mo->x + cur->momx, player->mo->y + cur->momy, true))
+ P_SlideMove(cur, true);
+ if (P_IsObjectOnGround(player->mo))
+ {
+ if (P_MobjFlip(cur) > 0)
+ {
+ if (cur->floorz > player->mo->z - cur->height)
+ z = cur->floorz;
+ }
+ else
+ {
+ if (cur->ceilingz < player->mo->z + player->mo->height + cur->height)
+ z = cur->ceilingz - cur->height;
+ }
+ }
+
+ // Center it during the scale up animation
+ z += (FixedMul(mobjinfo[cur->type].height, player->mo->scale - cur->scale)>>1) * P_MobjFlip(cur);
+
+ cur->z = z;
+ cur->momx = cur->momy = 0;
+ cur->angle += ANGLE_90;
+
+ cur = cur->hnext;
+ }
+ }
+ break;
+ case MT_BANANA_SHIELD: // Kart trailing items
+ case MT_SSMINE_SHIELD:
+ case MT_EGGMANITEM_SHIELD:
+ case MT_SINK_SHIELD:
+ {
+ mobj_t *cur = player->mo->hnext;
+ mobj_t *targ = player->mo;
+
+ if (P_IsObjectOnGround(player->mo) && player->speed > 0)
+ player->kartstuff[k_bananadrag]++;
+
+ while (cur && !P_MobjWasRemoved(cur))
+ {
+ const fixed_t radius = FixedHypot(targ->radius, targ->radius) + FixedHypot(cur->radius, cur->radius);
+ angle_t ang;
+ fixed_t targx, targy, targz;
+ fixed_t speed, dist;
+
+ cur->flags &= ~MF_NOCLIPTHING;
+
+ if (!cur->health)
+ {
+ cur = cur->hnext;
+ continue;
+ }
+
+ if (cur->extravalue1 < radius)
+ cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12);
+ if (cur->extravalue1 > radius)
+ cur->extravalue1 = radius;
+
+ if (cur != player->mo->hnext)
+ {
+ targ = cur->hprev;
+ dist = cur->extravalue1/4;
+ }
+ else
+ dist = cur->extravalue1/2;
+
+ if (!targ || P_MobjWasRemoved(targ))
+ continue;
+
+ // Shrink your items if the player shrunk too.
+ P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale)));
+
+ ang = targ->angle;
+ targx = targ->x + P_ReturnThrustX(cur, ang + ANGLE_180, dist);
+ targy = targ->y + P_ReturnThrustY(cur, ang + ANGLE_180, dist);
+ targz = targ->z;
+
+ speed = FixedMul(R_PointToDist2(cur->x, cur->y, targx, targy), 3*FRACUNIT/4);
+ if (P_IsObjectOnGround(targ))
+ targz = cur->floorz;
+
+ cur->angle = R_PointToAngle2(cur->x, cur->y, targx, targy);
+
+ /*if (P_IsObjectOnGround(player->mo) && player->speed > 0 && player->kartstuff[k_bananadrag] > TICRATE
+ && P_RandomChance(min(FRACUNIT/2, FixedDiv(player->speed, K_GetKartSpeed(player, false))/2)))
+ {
+ if (leveltime & 1)
+ targz += 8*(2*FRACUNIT)/7;
+ else
+ targz -= 8*(2*FRACUNIT)/7;
+ }*/
+
+ if (speed > dist)
+ P_InstaThrust(cur, cur->angle, speed-dist);
+
+ P_SetObjectMomZ(cur, FixedMul(targz - cur->z, 7*FRACUNIT/8) - gravity, false);
+
+ if (R_PointToDist2(cur->x, cur->y, targx, targy) > 768*FRACUNIT)
+ P_TeleportMove(cur, targx, targy, cur->z);
+
+ cur = cur->hnext;
+ }
+ }
+ break;
+ case MT_ROCKETSNEAKER: // Special rocket sneaker stuff
+ {
+ mobj_t *cur = player->mo->hnext;
+ INT32 num = 0;
+
+ while (cur && !P_MobjWasRemoved(cur))
+ {
+ const fixed_t radius = FixedHypot(player->mo->radius, player->mo->radius) + FixedHypot(cur->radius, cur->radius);
+ boolean vibrate = ((leveltime & 1) && !cur->tracer);
+ angle_t angoffset;
+ fixed_t targx, targy, targz;
+
+ cur->flags &= ~MF_NOCLIPTHING;
+
+ if (player->kartstuff[k_rocketsneakertimer] <= TICRATE && (leveltime & 1))
+ cur->flags2 |= MF2_DONTDRAW;
+ else
+ cur->flags2 &= ~MF2_DONTDRAW;
+
+ if (num & 1)
+ P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_LVIBRATE : S_ROCKETSNEAKER_L));
+ else
+ P_SetMobjStateNF(cur, (vibrate ? S_ROCKETSNEAKER_RVIBRATE : S_ROCKETSNEAKER_R));
+
+ if (!player->kartstuff[k_rocketsneakertimer] || cur->extravalue2 || !cur->health)
+ {
+ num = (num+1) % 2;
+ cur = cur->hnext;
+ continue;
+ }
+
+ if (cur->extravalue1 < radius)
+ cur->extravalue1 += FixedMul(P_AproxDistance(cur->extravalue1, radius), FRACUNIT/12);
+ if (cur->extravalue1 > radius)
+ cur->extravalue1 = radius;
+
+ // Shrink your items if the player shrunk too.
+ P_SetScale(cur, (cur->destscale = FixedMul(FixedDiv(cur->extravalue1, radius), player->mo->scale)));
+
+#if 1
+ {
+ angle_t input = player->frameangle - cur->angle;
+ boolean invert = (input > ANGLE_180);
+ if (invert)
+ input = InvAngle(input);
+
+ input = FixedAngle(AngleFixed(input)/4);
+ if (invert)
+ input = InvAngle(input);
+
+ cur->angle = cur->angle + input;
+ }
+#else
+ cur->angle = player->frameangle;
+#endif
+
+ angoffset = ANGLE_90 + (ANGLE_180 * num);
+
+ targx = player->mo->x + P_ReturnThrustX(cur, cur->angle + angoffset, cur->extravalue1);
+ targy = player->mo->y + P_ReturnThrustY(cur, cur->angle + angoffset, cur->extravalue1);
+
+ { // bobbing, copy pasted from my kimokawaiii entry
+ const fixed_t pi = (22<mo->scale, 8 * FINESINE((((2*pi*(4*TICRATE)) * leveltime)>>ANGLETOFINESHIFT) & FINEMASK));
+ targz = (player->mo->z + (player->mo->height/2)) + sine;
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ targz += (player->mo->height/2 - 32*player->mo->scale)*6;
+
+ }
+
+ if (cur->tracer)
+ {
+ fixed_t diffx, diffy, diffz;
+
+ diffx = targx - cur->x;
+ diffy = targy - cur->y;
+ diffz = targz - cur->z;
+
+ P_TeleportMove(cur->tracer, cur->tracer->x + diffx + P_ReturnThrustX(cur, cur->angle + angoffset, 6*cur->scale),
+ cur->tracer->y + diffy + P_ReturnThrustY(cur, cur->angle + angoffset, 6*cur->scale), cur->tracer->z + diffz);
+ P_SetScale(cur->tracer, (cur->tracer->destscale = 3*cur->scale/4));
+ }
+
+ P_TeleportMove(cur, targx, targy, targz);
+ K_FlipFromObject(cur, player->mo); // Update graviflip in real time thanks.
+ num = (num+1) % 2;
+ cur = cur->hnext;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+player_t *K_FindJawzTarget(mobj_t *actor, player_t *source)
+{
+ fixed_t best = -1;
+ player_t *wtarg = NULL;
+ INT32 i;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ angle_t thisang;
+ player_t *player;
+
+ if (!playeringame[i])
+ continue;
+
+ player = &players[i];
+
+ if (player->spectator)
+ continue; // spectator
+
+ if (!player->mo)
+ continue;
+
+ if (player->mo->health <= 0)
+ continue; // dead
+
+ // Don't target yourself, stupid.
+ if (player == source)
+ continue;
+
+ // Don't home in on teammates.
+ if (G_GametypeHasTeams() && source->ctfteam == player->ctfteam)
+ continue;
+
+ // Invisible, don't bother
+ if (player->kartstuff[k_hyudorotimer])
+ continue;
+
+ // Find the angle, see who's got the best.
+ thisang = actor->angle - R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y);
+ if (thisang > ANGLE_180)
+ thisang = InvAngle(thisang);
+
+ // Jawz only go after the person directly ahead of you in race... sort of literally now!
+ if (G_RaceGametype())
+ {
+ // Don't go for people who are behind you
+ if (thisang > ANGLE_67h)
+ continue;
+ // Don't pay attention to people who aren't above your position
+ if (player->kartstuff[k_position] >= source->kartstuff[k_position])
+ continue;
+ if ((best == -1) || (player->kartstuff[k_position] > best))
+ {
+ wtarg = player;
+ best = player->kartstuff[k_position];
+ }
+ }
+ else
+ {
+ fixed_t thisdist;
+ fixed_t thisavg;
+
+ // Don't go for people who are behind you
+ if (thisang > ANGLE_45)
+ continue;
+
+ // Don't pay attention to dead players
+ if (player->kartstuff[k_bumper] <= 0)
+ continue;
+
+ // Z pos too high/low
+ if (abs(player->mo->z - (actor->z + actor->momz)) > RING_DIST/8)
+ continue;
+
+ thisdist = P_AproxDistance(player->mo->x - (actor->x + actor->momx), player->mo->y - (actor->y + actor->momy));
+
+ if (thisdist > 2*RING_DIST) // Don't go for people who are too far away
+ continue;
+
+ thisavg = (AngleFixed(thisang) + thisdist) / 2;
+
+ //CONS_Printf("got avg %d from player # %d\n", thisavg>>FRACBITS, i);
+
+ if ((best == -1) || (thisavg < best))
+ {
+ wtarg = player;
+ best = thisavg;
+ }
+ }
+ }
+
+ return wtarg;
+}
+
+// Engine Sounds.
+static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd)
+{
+ const INT32 numsnds = 13;
+ INT32 class, s, w; // engine class number
+ UINT8 volume = 255;
+ fixed_t volumedampen = 0;
+ INT32 targetsnd = 0;
+ INT32 i;
+
+ s = (player->kartspeed-1)/3;
+ w = (player->kartweight-1)/3;
+
+#define LOCKSTAT(stat) \
+ if (stat < 0) { stat = 0; } \
+ if (stat > 2) { stat = 2; }
+ LOCKSTAT(s);
+ LOCKSTAT(w);
+#undef LOCKSTAT
+
+ class = s+(3*w);
+
+ // Silence the engines
+ if (leveltime < 8 || player->spectator || player->exiting)
+ {
+ player->kartstuff[k_enginesnd] = 0; // Reset sound number
+ return;
+ }
+
+#if 0
+ if ((leveltime % 8) != ((player-players) % 8)) // Per-player offset, to make engines sound distinct!
+#else
+ if (leveltime % 8) // .25 seconds of wait time between engine sounds
+#endif
+ return;
+
+ if ((leveltime >= starttime-(2*TICRATE) && leveltime <= starttime) || (player->kartstuff[k_respawn] == 1)) // Startup boosts
+ targetsnd = ((cmd->buttons & BT_ACCELERATE) ? 12 : 0);
+ else
+ targetsnd = (((6*cmd->forwardmove)/25) + ((player->speed / mapobjectscale)/5))/2;
+
+ if (targetsnd < 0)
+ targetsnd = 0;
+ if (targetsnd > 12)
+ targetsnd = 12;
+
+ if (player->kartstuff[k_enginesnd] < targetsnd)
+ player->kartstuff[k_enginesnd]++;
+ if (player->kartstuff[k_enginesnd] > targetsnd)
+ player->kartstuff[k_enginesnd]--;
+
+ if (player->kartstuff[k_enginesnd] < 0)
+ player->kartstuff[k_enginesnd] = 0;
+ if (player->kartstuff[k_enginesnd] > 12)
+ player->kartstuff[k_enginesnd] = 12;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ UINT8 thisvol = 0;
+ fixed_t dist;
+
+ if (!playeringame[i] || !players[i].mo || players[i].spectator || players[i].exiting)
+ continue;
+
+ if ((i == displayplayer)
+ || (i == secondarydisplayplayer && splitscreen)
+ || (i == thirddisplayplayer && splitscreen > 1)
+ || (i == fourthdisplayplayer && splitscreen > 2))
+ {
+ volumedampen += FRACUNIT; // We already know what this is gonna be, let's not waste our time.
+ continue;
+ }
+
+ dist = P_AproxDistance(P_AproxDistance(player->mo->x-players[i].mo->x,
+ player->mo->y-players[i].mo->y), player->mo->z-players[i].mo->z) / 2;
+
+ dist = FixedDiv(dist, mapobjectscale);
+
+ if (dist > 1536<>FRACBITS)) / (((1536<>(FRACBITS+4));
+
+ if (thisvol == 0)
+ continue;
+
+ volumedampen += (thisvol * 257); // 255 * 257 = FRACUNIT
+ }
+
+ if (volumedampen > FRACUNIT)
+ volume = FixedDiv(volume<>FRACBITS;
+
+ if (volume <= 0) // Might as well
+ return;
+
+ S_StartSoundAtVolume(player->mo, (sfx_krta00 + player->kartstuff[k_enginesnd]) + (class*numsnds), volume);
+}
+
+static void K_UpdateInvincibilitySounds(player_t *player)
+{
+ INT32 sfxnum = sfx_None;
+
+ if (player->mo->health > 0 && !P_IsLocalPlayer(player))
+ {
+ if (cv_kartinvinsfx.value)
+ {
+ if (player->kartstuff[k_growshrinktimer] > 0) // Prioritize Grow
+ sfxnum = sfx_alarmg;
+ else if (player->kartstuff[k_invincibilitytimer] > 0)
+ sfxnum = sfx_alarmi;
+ }
+ else
+ {
+ if (player->kartstuff[k_growshrinktimer] > 0)
+ sfxnum = sfx_kgrow;
+ else if (player->kartstuff[k_invincibilitytimer] > 0)
+ sfxnum = sfx_kinvnc;
+ }
+ }
+
+ if (sfxnum != sfx_None && !S_SoundPlaying(player->mo, sfxnum))
+ S_StartSound(player->mo, sfxnum);
+
+#define STOPTHIS(this) \
+ if (sfxnum != this && S_SoundPlaying(player->mo, this)) \
+ S_StopSoundByID(player->mo, this);
+ STOPTHIS(sfx_alarmi);
+ STOPTHIS(sfx_alarmg);
+ STOPTHIS(sfx_kinvnc);
+ STOPTHIS(sfx_kgrow);
+#undef STOPTHIS
+}
+
+void K_KartPlayerHUDUpdate(player_t *player)
+{
+ if (player->kartstuff[k_lapanimation])
+ player->kartstuff[k_lapanimation]--;
+
+ if (player->kartstuff[k_yougotem])
+ player->kartstuff[k_yougotem]--;
+
+ if (G_BattleGametype() && (player->exiting || player->kartstuff[k_comebacktimer]))
+ {
+ if (player->exiting)
+ {
+ if (player->exiting < 6*TICRATE)
+ player->kartstuff[k_cardanimation] += ((164-player->kartstuff[k_cardanimation])/8)+1;
+ else if (player->exiting == 6*TICRATE)
+ player->kartstuff[k_cardanimation] = 0;
+ else if (player->kartstuff[k_cardanimation] < 2*TICRATE)
+ player->kartstuff[k_cardanimation]++;
+ }
+ else
+ {
+ if (player->kartstuff[k_comebacktimer] < 6*TICRATE)
+ player->kartstuff[k_cardanimation] -= ((164-player->kartstuff[k_cardanimation])/8)+1;
+ else if (player->kartstuff[k_comebacktimer] < 9*TICRATE)
+ player->kartstuff[k_cardanimation] += ((164-player->kartstuff[k_cardanimation])/8)+1;
+ }
+
+ if (player->kartstuff[k_cardanimation] > 164)
+ player->kartstuff[k_cardanimation] = 164;
+ if (player->kartstuff[k_cardanimation] < 0)
+ player->kartstuff[k_cardanimation] = 0;
+ }
+ else if (G_RaceGametype() && player->exiting)
+ {
+ if (player->kartstuff[k_cardanimation] < 2*TICRATE)
+ player->kartstuff[k_cardanimation]++;
+ }
+ else
+ player->kartstuff[k_cardanimation] = 0;
+}
+
+/** \brief Decreases various kart timers and powers per frame. Called in P_PlayerThink in p_user.c
+
+ \param player player object passed from P_PlayerThink
+ \param cmd control input from player
+
+ \return void
+*/
+void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
+{
+ K_UpdateOffroad(player);
+ K_UpdateEngineSounds(player, cmd); // Thanks, VAda!
+ K_GetKartBoostPower(player);
+
+ // Speed lines
+ if ((player->kartstuff[k_sneakertimer] || player->kartstuff[k_driftboost] || player->kartstuff[k_startboost]) && player->speed > 0)
+ {
+ mobj_t *fast = P_SpawnMobj(player->mo->x + (P_RandomRange(-36,36) * player->mo->scale),
+ player->mo->y + (P_RandomRange(-36,36) * player->mo->scale),
+ player->mo->z + (player->mo->height/2) + (P_RandomRange(-20,20) * player->mo->scale),
+ MT_FASTLINE);
+ fast->angle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
+ fast->momx = 3*player->mo->momx/4;
+ fast->momy = 3*player->mo->momy/4;
+ fast->momz = 3*player->mo->momz/4;
+ K_MatchGenericExtraFlags(fast, player->mo);
+ }
+
+ if (player->playerstate == PST_DEAD || player->kartstuff[k_respawn] > 1) // Ensure these are set correctly here
+ {
+ player->mo->colorized = false;
+ player->mo->color = player->skincolor;
+ }
+ else if (player->kartstuff[k_eggmanexplode]) // You're gonna diiiiie
+ {
+ const INT32 flashtime = 4<<(player->kartstuff[k_eggmanexplode]/TICRATE);
+ if (player->kartstuff[k_eggmanexplode] == 1 || (player->kartstuff[k_eggmanexplode] % (flashtime/2) != 0))
+ {
+ player->mo->colorized = false;
+ player->mo->color = player->skincolor;
+ }
+ else if (player->kartstuff[k_eggmanexplode] % flashtime == 0)
+ {
+ player->mo->colorized = true;
+ player->mo->color = SKINCOLOR_BLACK;
+ }
+ else
+ {
+ player->mo->colorized = true;
+ player->mo->color = SKINCOLOR_CRIMSON;
+ }
+ }
+ else if (player->kartstuff[k_invincibilitytimer]) // setting players to use the star colormap and spawning afterimages
+ {
+ mobj_t *ghost;
+ player->mo->colorized = true;
+ ghost = P_SpawnGhostMobj(player->mo);
+ ghost->fuse = 4;
+ ghost->frame |= FF_FULLBRIGHT;
+ }
+ else if (player->kartstuff[k_growshrinktimer]) // Ditto, for grow/shrink
+ {
+ if (player->kartstuff[k_growshrinktimer] % 5 == 0)
+ {
+ player->mo->colorized = true;
+ player->mo->color = (player->kartstuff[k_growshrinktimer] < 0 ? SKINCOLOR_CREAMSICLE : SKINCOLOR_PERIWINKLE);
+ }
+ else
+ {
+ player->mo->colorized = false;
+ player->mo->color = player->skincolor;
+ }
+ }
+ else
+ {
+ player->mo->colorized = false;
+ }
+
+ if (player->kartstuff[k_dashpadcooldown]) // Twinkle Circuit inspired afterimages
+ {
+ mobj_t *ghost;
+ ghost = P_SpawnGhostMobj(player->mo);
+ ghost->fuse = player->kartstuff[k_dashpadcooldown]+1;
+ ghost->momx = player->mo->momx / (player->kartstuff[k_dashpadcooldown]+1);
+ ghost->momy = player->mo->momy / (player->kartstuff[k_dashpadcooldown]+1);
+ ghost->momz = player->mo->momz / (player->kartstuff[k_dashpadcooldown]+1);
+ player->kartstuff[k_dashpadcooldown]--;
+ }
+
+ // DKR style camera for boosting
+ if (player->kartstuff[k_boostcam] != 0 || player->kartstuff[k_destboostcam] != 0)
+ {
+ if (player->kartstuff[k_boostcam] < player->kartstuff[k_destboostcam]
+ && player->kartstuff[k_destboostcam] != 0)
+ {
+ player->kartstuff[k_boostcam] += FRACUNIT/(TICRATE/4);
+ if (player->kartstuff[k_boostcam] >= player->kartstuff[k_destboostcam])
+ player->kartstuff[k_destboostcam] = 0;
+ }
+ else
+ {
+ player->kartstuff[k_boostcam] -= FRACUNIT/TICRATE;
+ if (player->kartstuff[k_boostcam] < player->kartstuff[k_destboostcam])
+ player->kartstuff[k_boostcam] = player->kartstuff[k_destboostcam] = 0;
+ }
+ //CONS_Printf("cam: %d, dest: %d\n", player->kartstuff[k_boostcam], player->kartstuff[k_destboostcam]);
+ }
+
+ player->kartstuff[k_timeovercam] = 0;
+
+ // Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations.
+ if (player->kartstuff[k_spinouttimer] != 0
+ || player->kartstuff[k_wipeoutslow] != 0
+ || player->kartstuff[k_squishedtimer] != 0)
+ {
+ player->powers[pw_flashing] = K_GetKartFlashing(player);
+ }
+ else if (player->powers[pw_flashing] == K_GetKartFlashing(player))
+ {
+ player->powers[pw_flashing]--;
+ }
+
+ if (player->kartstuff[k_spinouttimer])
+ {
+ if ((P_IsObjectOnGround(player->mo) || player->kartstuff[k_spinouttype] == 1)
+ && (player->kartstuff[k_sneakertimer] == 0))
+ {
+ player->kartstuff[k_spinouttimer]--;
+ if (player->kartstuff[k_wipeoutslow] > 1)
+ player->kartstuff[k_wipeoutslow]--;
+ if (player->kartstuff[k_spinouttimer] == 0)
+ player->kartstuff[k_spinouttype] = 0; // Reset type
+ }
+ }
+ else
+ {
+ if (player->kartstuff[k_wipeoutslow] == 1)
+ player->mo->friction = ORIG_FRICTION;
+ player->kartstuff[k_wipeoutslow] = 0;
+ if (!comeback)
+ player->kartstuff[k_comebacktimer] = comebacktime;
+ else if (player->kartstuff[k_comebacktimer])
+ {
+ player->kartstuff[k_comebacktimer]--;
+ if (P_IsLocalPlayer(player) && player->kartstuff[k_bumper] <= 0 && player->kartstuff[k_comebacktimer] <= 0)
+ comebackshowninfo = true; // client has already seen the message
+ }
+ }
+
+ /*if (player->kartstuff[k_thunderanim])
+ player->kartstuff[k_thunderanim]--;*/
+
+ if (player->kartstuff[k_sneakertimer])
+ {
+ player->kartstuff[k_sneakertimer]--;
+ if (player->kartstuff[k_wipeoutslow] > 0 && player->kartstuff[k_wipeoutslow] < wipeoutslowtime+1)
+ player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1;
+ }
+
+ if (player->kartstuff[k_floorboost])
+ player->kartstuff[k_floorboost]--;
+
+ if (player->kartstuff[k_driftboost])
+ player->kartstuff[k_driftboost]--;
+
+ if (player->kartstuff[k_startboost])
+ player->kartstuff[k_startboost]--;
+
+ if (player->kartstuff[k_invincibilitytimer])
+ player->kartstuff[k_invincibilitytimer]--;
+
+ if (!player->kartstuff[k_respawn] && player->kartstuff[k_growshrinktimer] != 0)
+ {
+ if (player->kartstuff[k_growshrinktimer] > 0)
+ player->kartstuff[k_growshrinktimer]--;
+ if (player->kartstuff[k_growshrinktimer] < 0)
+ player->kartstuff[k_growshrinktimer]++;
+
+ // Back to normal
+ if (player->kartstuff[k_growshrinktimer] == 0)
+ K_RemoveGrowShrink(player);
+ }
+
+ if (player->kartstuff[k_stealingtimer] == 0 && player->kartstuff[k_stolentimer] == 0
+ && player->kartstuff[k_rocketsneakertimer])
+ player->kartstuff[k_rocketsneakertimer]--;
+
+ if (player->kartstuff[k_hyudorotimer])
+ player->kartstuff[k_hyudorotimer]--;
+
+ if (player->kartstuff[k_sadtimer])
+ player->kartstuff[k_sadtimer]--;
+
+ if (player->kartstuff[k_stealingtimer])
+ player->kartstuff[k_stealingtimer]--;
+
+ if (player->kartstuff[k_stolentimer])
+ player->kartstuff[k_stolentimer]--;
+
+ if (player->kartstuff[k_squishedtimer])
+ player->kartstuff[k_squishedtimer]--;
+
+ if (player->kartstuff[k_justbumped])
+ player->kartstuff[k_justbumped]--;
+
+ // This doesn't go in HUD update because it has potential gameplay ramifications
+ if (player->kartstuff[k_itemblink] && player->kartstuff[k_itemblink]-- <= 0)
+ {
+ player->kartstuff[k_itemblinkmode] = 0;
+ player->kartstuff[k_itemblink] = 0;
+ }
+
+ K_KartPlayerHUDUpdate(player);
+
+ if (player->kartstuff[k_voices])
+ player->kartstuff[k_voices]--;
+
+ if (player->kartstuff[k_tauntvoices])
+ player->kartstuff[k_tauntvoices]--;
+
+ if (G_BattleGametype() && player->kartstuff[k_bumper] > 0)
+ player->kartstuff[k_wanted]++;
+
+ if (P_IsObjectOnGround(player->mo))
+ player->kartstuff[k_waterskip] = 0;
+
+ if (player->kartstuff[k_instashield])
+ player->kartstuff[k_instashield]--;
+
+ if (player->kartstuff[k_eggmanexplode])
+ {
+ if (player->spectator || (G_BattleGametype() && !player->kartstuff[k_bumper]))
+ player->kartstuff[k_eggmanexplode] = 0;
+ else
+ {
+ player->kartstuff[k_eggmanexplode]--;
+ if (player->kartstuff[k_eggmanexplode] <= 0)
+ {
+ mobj_t *eggsexplode;
+ //player->powers[pw_flashing] = 0;
+ eggsexplode = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SPBEXPLOSION);
+ if (player->kartstuff[k_eggmanblame] >= 0
+ && player->kartstuff[k_eggmanblame] < MAXPLAYERS
+ && playeringame[player->kartstuff[k_eggmanblame]]
+ && !players[player->kartstuff[k_eggmanblame]].spectator
+ && players[player->kartstuff[k_eggmanblame]].mo)
+ P_SetTarget(&eggsexplode->target, players[player->kartstuff[k_eggmanblame]].mo);
+ }
+ }
+ }
+
+ // ???
+ /*
+ if (player->kartstuff[k_jmp] > 1 && onground)
+ {
+ S_StartSound(player->mo, sfx_spring);
+ P_DoJump(player, false);
+ player->mo->momz *= player->kartstuff[k_jmp];
+ player->kartstuff[k_jmp] = 0;
+ }
+ */
+
+ if (player->kartstuff[k_comebacktimer])
+ player->kartstuff[k_comebackmode] = 0;
+
+ if (P_IsObjectOnGround(player->mo) && player->kartstuff[k_pogospring])
+ {
+ if (P_MobjFlip(player->mo)*player->mo->momz <= 0)
+ player->kartstuff[k_pogospring] = 0;
+ }
+
+ if (cmd->buttons & BT_DRIFT)
+ player->kartstuff[k_jmp] = 1;
+ else
+ player->kartstuff[k_jmp] = 0;
+
+ // Respawn Checker
+ if (player->kartstuff[k_respawn])
+ K_RespawnChecker(player);
+
+ // Roulette Code
+ K_KartItemRoulette(player, cmd);
+
+ // Handle invincibility sfx
+ K_UpdateInvincibilitySounds(player); // Also thanks, VAda!
+
+ // Plays the music after the starting countdown.
+ if (P_IsLocalPlayer(player) && leveltime == (starttime + (TICRATE/2)))
+ {
+ S_ChangeMusic(mapmusname, mapmusflags, true);
+ S_ShowMusicCredit();
+ }
+}
+
+void K_KartPlayerAfterThink(player_t *player)
+{
+ if (player->kartstuff[k_curshield]
+ || player->kartstuff[k_invincibilitytimer]
+ || (player->kartstuff[k_growshrinktimer] != 0 && player->kartstuff[k_growshrinktimer] % 5 == 4)) // 4 instead of 0 because this is afterthink!
+ {
+ player->mo->frame |= FF_FULLBRIGHT;
+ }
+ else
+ {
+ if (!(player->mo->state->frame & FF_FULLBRIGHT))
+ player->mo->frame &= ~FF_FULLBRIGHT;
+ }
+
+ // Move held objects (Bananas, Orbinaut, etc)
+ K_MoveHeldObjects(player);
+
+ // Jawz reticule (seeking)
+ if (player->kartstuff[k_itemtype] == KITEM_JAWZ && player->kartstuff[k_itemheld])
+ {
+ INT32 lasttarg = player->kartstuff[k_lastjawztarget];
+ player_t *targ;
+ mobj_t *ret;
+
+ if (player->kartstuff[k_jawztargetdelay] && playeringame[lasttarg] && !players[lasttarg].spectator)
+ {
+ targ = &players[lasttarg];
+ player->kartstuff[k_jawztargetdelay]--;
+ }
+ else
+ targ = K_FindJawzTarget(player->mo, player);
+
+ if (!targ || !targ->mo || P_MobjWasRemoved(targ->mo))
+ {
+ player->kartstuff[k_lastjawztarget] = -1;
+ player->kartstuff[k_jawztargetdelay] = 0;
+ return;
+ }
+
+ ret = P_SpawnMobj(targ->mo->x, targ->mo->y, targ->mo->z, MT_PLAYERRETICULE);
+ P_SetTarget(&ret->target, targ->mo);
+ ret->frame |= ((leveltime % 10) / 2);
+ ret->tics = 1;
+ ret->color = player->skincolor;
+
+ if (targ-players != lasttarg)
+ {
+ if (P_IsLocalPlayer(player) || P_IsLocalPlayer(targ))
+ S_StartSound(NULL, sfx_s3k89);
+ else
+ S_StartSound(targ->mo, sfx_s3k89);
+
+ player->kartstuff[k_lastjawztarget] = targ-players;
+ player->kartstuff[k_jawztargetdelay] = 5;
+ }
+ }
+ else
+ {
+ player->kartstuff[k_lastjawztarget] = -1;
+ player->kartstuff[k_jawztargetdelay] = 0;
+ }
+}
+
+// Returns false if this player being placed here causes them to collide with any other player
+// Used in g_game.c for match etc. respawning
+// This does not check along the z because the z is not correctly set for the spawnee at this point
+boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y)
+{
+ INT32 i;
+ fixed_t p1radius = players[playernum].mo->radius;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playernum == i || !playeringame[i] || players[i].spectator || !players[i].mo || players[i].mo->health <= 0
+ || players[i].playerstate != PST_LIVE || (players[i].mo->flags & MF_NOCLIP) || (players[i].mo->flags & MF_NOCLIPTHING))
+ continue;
+
+ if (abs(x - players[i].mo->x) < (p1radius + players[i].mo->radius)
+ && abs(y - players[i].mo->y) < (p1radius + players[i].mo->radius))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// countersteer is how strong the controls are telling us we are turning
+// turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left
+static INT16 K_GetKartDriftValue(player_t *player, fixed_t countersteer)
+{
+ INT16 basedrift, driftangle;
+ fixed_t driftweight = player->kartweight*14; // 12
+
+ // If they aren't drifting or on the ground this doesn't apply
+ if (player->kartstuff[k_drift] == 0 || !P_IsObjectOnGround(player->mo))
+ return 0;
+
+ if (player->kartstuff[k_driftend] != 0)
+ {
+ return -266*player->kartstuff[k_drift]; // Drift has ended and we are tweaking their angle back a bit
+ }
+
+ //basedrift = 90*player->kartstuff[k_drift]; // 450
+ //basedrift = 93*player->kartstuff[k_drift] - driftweight*3*player->kartstuff[k_drift]/10; // 447 - 303
+ basedrift = 83*player->kartstuff[k_drift] - (driftweight - 14)*player->kartstuff[k_drift]/5; // 415 - 303
+ driftangle = abs((252 - driftweight)*player->kartstuff[k_drift]/5);
+
+ return basedrift + FixedMul(driftangle, countersteer);
+}
+
+INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
+{
+ fixed_t p_maxspeed = FixedMul(K_GetKartSpeed(player, false), 3*FRACUNIT);
+ fixed_t adjustangle = FixedDiv((p_maxspeed>>16) - (player->speed>>16), (p_maxspeed>>16) + player->kartweight);
+
+ if (player->spectator)
+ return turnvalue;
+
+ if (player->kartstuff[k_drift] != 0 && P_IsObjectOnGround(player->mo))
+ {
+ // If we're drifting we have a completely different turning value
+ if (player->kartstuff[k_driftend] == 0)
+ {
+ // 800 is the max set in g_game.c with angleturn
+ fixed_t countersteer = FixedDiv(turnvalue*FRACUNIT, 800*FRACUNIT);
+ turnvalue = K_GetKartDriftValue(player, countersteer);
+ }
+ else
+ turnvalue = (INT16)(turnvalue + K_GetKartDriftValue(player, FRACUNIT));
+
+ return turnvalue;
+ }
+
+ turnvalue = FixedMul(turnvalue, adjustangle); // Weight has a small effect on turning
+
+ if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_sneakertimer] || player->kartstuff[k_growshrinktimer] > 0)
+ turnvalue = FixedMul(turnvalue, FixedDiv(5*FRACUNIT, 4*FRACUNIT));
+
+ return turnvalue;
+}
+
+INT32 K_GetKartDriftSparkValue(player_t *player)
+{
+ UINT8 kartspeed = (G_BattleGametype() && player->kartstuff[k_bumper] <= 0)
+ ? 1
+ : player->kartspeed;
+ return (26*4 + kartspeed*2 + (9 - player->kartweight))*8;
+}
+
+static void K_KartDrift(player_t *player, boolean onground)
+{
+ fixed_t minspeed = (10 * player->mo->scale);
+ INT32 dsone = K_GetKartDriftSparkValue(player);
+ INT32 dstwo = dsone*2;
+ INT32 dsthree = dstwo*2;
+
+ // Drifting is actually straffing + automatic turning.
+ // Holding the Jump button will enable drifting.
+
+ // Drift Release (Moved here so you can't "chain" drifts)
+ if ((player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5)
+ // || (player->kartstuff[k_drift] >= 1 && player->kartstuff[k_turndir] != 1) || (player->kartstuff[k_drift] <= -1 && player->kartstuff[k_turndir] != -1))
+ && player->kartstuff[k_driftcharge] < dsone
+ && onground)
+ {
+ player->kartstuff[k_driftcharge] = 0;
+ }
+ else if ((player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5)
+ // || (player->kartstuff[k_drift] >= 1 && player->kartstuff[k_turndir] != 1) || (player->kartstuff[k_drift] <= -1 && player->kartstuff[k_turndir] != -1))
+ && (player->kartstuff[k_driftcharge] >= dsone && player->kartstuff[k_driftcharge] < dstwo)
+ && onground)
+ {
+ if (player->kartstuff[k_driftboost] < 20)
+ player->kartstuff[k_driftboost] = 20;
+ S_StartSound(player->mo, sfx_s23c);
+ //K_SpawnDashDustRelease(player);
+ player->kartstuff[k_driftcharge] = 0;
+ }
+ else if ((player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5)
+ // || (player->kartstuff[k_drift] >= 1 && player->kartstuff[k_turndir] != 1) || (player->kartstuff[k_drift] <= -1 && player->kartstuff[k_turndir] != -1))
+ && player->kartstuff[k_driftcharge] < dsthree
+ && onground)
+ {
+ if (player->kartstuff[k_driftboost] < 50)
+ player->kartstuff[k_driftboost] = 50;
+ S_StartSound(player->mo, sfx_s23c);
+ //K_SpawnDashDustRelease(player);
+ player->kartstuff[k_driftcharge] = 0;
+ }
+ else if ((player->kartstuff[k_drift] != -5 && player->kartstuff[k_drift] != 5)
+ // || (player->kartstuff[k_drift] >= 1 && player->kartstuff[k_turndir] != 1) || (player->kartstuff[k_drift] <= -1 && player->kartstuff[k_turndir] != -1))
+ && player->kartstuff[k_driftcharge] >= dsthree
+ && onground)
+ {
+ if (player->kartstuff[k_driftboost] < 125)
+ player->kartstuff[k_driftboost] = 125;
+ S_StartSound(player->mo, sfx_s23c);
+ //K_SpawnDashDustRelease(player);
+ player->kartstuff[k_driftcharge] = 0;
+ }
+
+ // Drifting: left or right?
+ if ((player->cmd.driftturn > 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1
+ && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != 1)
+ {
+ // Starting left drift
+ player->kartstuff[k_drift] = 1;
+ player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0;
+ }
+ else if ((player->cmd.driftturn < 0) && player->speed > minspeed && player->kartstuff[k_jmp] == 1
+ && (player->kartstuff[k_drift] == 0 || player->kartstuff[k_driftend] == 1)) // && player->kartstuff[k_drift] != -1)
+ {
+ // Starting right drift
+ player->kartstuff[k_drift] = -1;
+ player->kartstuff[k_driftend] = player->kartstuff[k_driftcharge] = 0;
+ }
+ else if (player->kartstuff[k_jmp] == 0) // || player->kartstuff[k_turndir] == 0)
+ {
+ // drift is not being performed so if we're just finishing set driftend and decrement counters
+ if (player->kartstuff[k_drift] > 0)
+ {
+ player->kartstuff[k_drift]--;
+ player->kartstuff[k_driftend] = 1;
+ }
+ else if (player->kartstuff[k_drift] < 0)
+ {
+ player->kartstuff[k_drift]++;
+ player->kartstuff[k_driftend] = 1;
+ }
+ else
+ player->kartstuff[k_driftend] = 0;
+ }
+
+
+ // Incease/decrease the drift value to continue drifting in that direction
+ if (player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_jmp] == 1 && onground && player->kartstuff[k_drift] != 0)
+ {
+ fixed_t driftadditive = 24;
+
+ if (player->kartstuff[k_drift] >= 1) // Drifting to the left
+ {
+ player->kartstuff[k_drift]++;
+ if (player->kartstuff[k_drift] > 5)
+ player->kartstuff[k_drift] = 5;
+
+ if (player->cmd.driftturn > 0) // Inward
+ driftadditive += abs(player->cmd.driftturn)/100;
+ if (player->cmd.driftturn < 0) // Outward
+ driftadditive -= abs(player->cmd.driftturn)/75;
+ }
+ else if (player->kartstuff[k_drift] <= -1) // Drifting to the right
+ {
+ player->kartstuff[k_drift]--;
+ if (player->kartstuff[k_drift] < -5)
+ player->kartstuff[k_drift] = -5;
+
+ if (player->cmd.driftturn < 0) // Inward
+ driftadditive += abs(player->cmd.driftturn)/100;
+ if (player->cmd.driftturn > 0) // Outward
+ driftadditive -= abs(player->cmd.driftturn)/75;
+ }
+
+ // Disable drift-sparks until you're going fast enough
+ if (player->kartstuff[k_getsparks] == 0 || (player->kartstuff[k_offroad] && !player->kartstuff[k_invincibilitytimer] && !player->kartstuff[k_hyudorotimer] && !player->kartstuff[k_sneakertimer]))
+ driftadditive = 0;
+ if (player->speed > minspeed*2)
+ player->kartstuff[k_getsparks] = 1;
+
+ // This spawns the drift sparks
+ if (player->kartstuff[k_driftcharge] + driftadditive >= dsone)
+ K_SpawnDriftSparks(player);
+
+ // Sound whenever you get a different tier of sparks
+ if (P_IsLocalPlayer(player) // UGHGHGH...
+ && ((player->kartstuff[k_driftcharge] < dsone && player->kartstuff[k_driftcharge]+driftadditive >= dsone)
+ || (player->kartstuff[k_driftcharge] < dstwo && player->kartstuff[k_driftcharge]+driftadditive >= dstwo)
+ || (player->kartstuff[k_driftcharge] < dsthree && player->kartstuff[k_driftcharge]+driftadditive >= dsthree)))
+ {
+ //S_StartSound(player->mo, sfx_s3ka2);
+ S_StartSoundAtVolume(player->mo, sfx_s3ka2, 192); // Ugh...
+ }
+
+ player->kartstuff[k_driftcharge] += driftadditive;
+ player->kartstuff[k_driftend] = 0;
+ }
+
+ // Stop drifting
+ if (player->kartstuff[k_spinouttimer] > 0 || player->speed < minspeed)
+ {
+ player->kartstuff[k_drift] = player->kartstuff[k_driftcharge] = 0;
+ player->kartstuff[k_aizdriftstrat] = player->kartstuff[k_brakedrift] = 0;
+ player->kartstuff[k_getsparks] = 0;
+ }
+
+ if ((!player->kartstuff[k_sneakertimer])
+ || (!player->cmd.driftturn)
+ || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0))
+ {
+ if (!player->kartstuff[k_drift])
+ player->kartstuff[k_aizdriftstrat] = 0;
+ else
+ player->kartstuff[k_aizdriftstrat] = ((player->kartstuff[k_drift] > 0) ? 1 : -1);
+ }
+ else if (player->kartstuff[k_aizdriftstrat] && !player->kartstuff[k_drift])
+ K_SpawnAIZDust(player);
+
+ if (player->kartstuff[k_drift]
+ && ((player->cmd.buttons & BT_BRAKE)
+ || !(player->cmd.buttons & BT_ACCELERATE))
+ && P_IsObjectOnGround(player->mo))
+ {
+ if (!player->kartstuff[k_brakedrift])
+ K_SpawnBrakeDriftSparks(player);
+ player->kartstuff[k_brakedrift] = 1;
+ }
+ else
+ player->kartstuff[k_brakedrift] = 0;
+}
+//
+// K_KartUpdatePosition
+//
+void K_KartUpdatePosition(player_t *player)
+{
+ fixed_t position = 1;
+ fixed_t oldposition = player->kartstuff[k_position];
+ fixed_t i, ppcd, pncd, ipcd, incd;
+ fixed_t pmo, imo;
+ mobj_t *mo;
+
+ if (player->spectator || !player->mo)
+ return;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator || !players[i].mo)
+ continue;
+
+ if (G_RaceGametype())
+ {
+ if ((((players[i].starpostnum) + (numstarposts + 1) * players[i].laps) >
+ ((player->starpostnum) + (numstarposts + 1) * player->laps)))
+ position++;
+ else if (((players[i].starpostnum) + (numstarposts+1)*players[i].laps) ==
+ ((player->starpostnum) + (numstarposts+1)*player->laps))
+ {
+ ppcd = pncd = ipcd = incd = 0;
+
+ player->kartstuff[k_prevcheck] = players[i].kartstuff[k_prevcheck] = 0;
+ player->kartstuff[k_nextcheck] = players[i].kartstuff[k_nextcheck] = 0;
+
+ // This checks every thing on the map, and looks for MT_BOSS3WAYPOINT (the thing we're using for checkpoint wp's, for now)
+ for (mo = waypointcap; mo != NULL; mo = mo->tracer)
+ {
+ pmo = P_AproxDistance(P_AproxDistance( mo->x - player->mo->x,
+ mo->y - player->mo->y),
+ mo->z - player->mo->z) / FRACUNIT;
+ imo = P_AproxDistance(P_AproxDistance( mo->x - players[i].mo->x,
+ mo->y - players[i].mo->y),
+ mo->z - players[i].mo->z) / FRACUNIT;
+
+ if (mo->health == player->starpostnum && (!mo->movecount || mo->movecount == player->laps+1))
+ {
+ player->kartstuff[k_prevcheck] += pmo;
+ ppcd++;
+ }
+ if (mo->health == (player->starpostnum + 1) && (!mo->movecount || mo->movecount == player->laps+1))
+ {
+ player->kartstuff[k_nextcheck] += pmo;
+ pncd++;
+ }
+ if (mo->health == players[i].starpostnum && (!mo->movecount || mo->movecount == players[i].laps+1))
+ {
+ players[i].kartstuff[k_prevcheck] += imo;
+ ipcd++;
+ }
+ if (mo->health == (players[i].starpostnum + 1) && (!mo->movecount || mo->movecount == players[i].laps+1))
+ {
+ players[i].kartstuff[k_nextcheck] += imo;
+ incd++;
+ }
+ }
+
+ if (ppcd > 1) player->kartstuff[k_prevcheck] /= ppcd;
+ if (pncd > 1) player->kartstuff[k_nextcheck] /= pncd;
+ if (ipcd > 1) players[i].kartstuff[k_prevcheck] /= ipcd;
+ if (incd > 1) players[i].kartstuff[k_nextcheck] /= incd;
+
+ if ((players[i].kartstuff[k_nextcheck] > 0 || player->kartstuff[k_nextcheck] > 0) && !player->exiting)
+ {
+ if ((players[i].kartstuff[k_nextcheck] - players[i].kartstuff[k_prevcheck]) <
+ (player->kartstuff[k_nextcheck] - player->kartstuff[k_prevcheck]))
+ position++;
+ }
+ else if (!player->exiting)
+ {
+ if (players[i].kartstuff[k_prevcheck] > player->kartstuff[k_prevcheck])
+ position++;
+ }
+ else
+ {
+ if (players[i].starposttime < player->starposttime)
+ position++;
+ }
+ }
+ }
+ else if (G_BattleGametype())
+ {
+ if (player->exiting) // End of match standings
+ {
+ if (players[i].marescore > player->marescore) // Only score matters
+ position++;
+ }
+ else
+ {
+ if (players[i].kartstuff[k_bumper] == player->kartstuff[k_bumper] && players[i].marescore > player->marescore)
+ position++;
+ else if (players[i].kartstuff[k_bumper] > player->kartstuff[k_bumper])
+ position++;
+ }
+ }
+ }
+
+ if (leveltime < starttime || oldposition == 0)
+ oldposition = position;
+
+ if (oldposition != position) // Changed places?
+ player->kartstuff[k_positiondelay] = 10; // Position number growth
+
+ player->kartstuff[k_position] = position;
+}
+
+//
+// K_StripItems
+//
+void K_StripItems(player_t *player)
+{
+ player->kartstuff[k_itemtype] = KITEM_NONE;
+ player->kartstuff[k_itemamount] = 0;
+ player->kartstuff[k_itemheld] = 0;
+
+ player->kartstuff[k_rocketsneakertimer] = 0;
+
+ if (!player->kartstuff[k_itemroulette] || player->kartstuff[k_roulettetype] != 2)
+ {
+ player->kartstuff[k_itemroulette] = 0;
+ player->kartstuff[k_roulettetype] = 0;
+ }
+ player->kartstuff[k_eggmanheld] = 0;
+
+ player->kartstuff[k_hyudorotimer] = 0;
+ player->kartstuff[k_stealingtimer] = 0;
+ player->kartstuff[k_stolentimer] = 0;
+
+ player->kartstuff[k_curshield] = 0;
+ //player->kartstuff[k_thunderanim] = 0;
+ player->kartstuff[k_bananadrag] = 0;
+
+ player->kartstuff[k_sadtimer] = 0;
+
+ K_UpdateHnextList(player, true);
+}
+
+void K_StripOther(player_t *player)
+{
+ player->kartstuff[k_itemroulette] = 0;
+ player->kartstuff[k_roulettetype] = 0;
+
+ player->kartstuff[k_invincibilitytimer] = 0;
+ K_RemoveGrowShrink(player);
+
+ if (player->kartstuff[k_eggmanexplode])
+ {
+ player->kartstuff[k_eggmanexplode] = 0;
+ player->kartstuff[k_eggmanblame] = -1;
+ }
+}
+
+//
+// K_MoveKartPlayer
+//
+void K_MoveKartPlayer(player_t *player, boolean onground)
+{
+ ticcmd_t *cmd = &player->cmd;
+ boolean ATTACK_IS_DOWN = ((cmd->buttons & BT_ATTACK) && !(player->pflags & PF_ATTACKDOWN));
+ boolean HOLDING_ITEM = (player->kartstuff[k_itemheld] || player->kartstuff[k_eggmanheld]);
+ boolean NO_HYUDORO = (player->kartstuff[k_stolentimer] == 0 && player->kartstuff[k_stealingtimer] == 0);
+
+ K_KartUpdatePosition(player);
+
+ if (!player->exiting)
+ {
+ if (player->kartstuff[k_oldposition] < player->kartstuff[k_position]) // But first, if you lost a place,
+ {
+ player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // then the other player taunts.
+ K_RegularVoiceTimers(player); // and you can't for a bit
+ }
+ else if (player->kartstuff[k_oldposition] > player->kartstuff[k_position]) // Otherwise,
+ {
+ K_PlayOvertakeSound(player->mo); // Say "YOU'RE TOO SLOW!"
+ player->kartstuff[k_oldposition] = player->kartstuff[k_position]; // Restore the old position,
+ }
+ }
+
+ if (player->kartstuff[k_positiondelay])
+ player->kartstuff[k_positiondelay]--;
+
+ if ((player->pflags & PF_ATTACKDOWN) && !(cmd->buttons & BT_ATTACK))
+ player->pflags &= ~PF_ATTACKDOWN;
+ else if (cmd->buttons & BT_ATTACK)
+ player->pflags |= PF_ATTACKDOWN;
+
+ if (player && player->mo && player->mo->health > 0 && !player->spectator && !(player->exiting || mapreset)
+ && player->kartstuff[k_spinouttimer] == 0 && player->kartstuff[k_squishedtimer] == 0 && player->kartstuff[k_respawn] == 0)
+ {
+ // First, the really specific, finicky items that function without the item being directly in your item slot.
+ // Karma item dropping
+ if (ATTACK_IS_DOWN && player->kartstuff[k_comebackmode] && !player->kartstuff[k_comebacktimer])
+ {
+ mobj_t *newitem;
+
+ if (player->kartstuff[k_comebackmode] == 1)
+ {
+ newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_RANDOMITEM);
+ newitem->threshold = 69; // selected "randomly".
+ }
+ else
+ {
+ newitem = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM);
+ if (player->kartstuff[k_eggmanblame] >= 0
+ && player->kartstuff[k_eggmanblame] < MAXPLAYERS
+ && playeringame[player->kartstuff[k_eggmanblame]]
+ && !players[player->kartstuff[k_eggmanblame]].spectator
+ && players[player->kartstuff[k_eggmanblame]].mo)
+ P_SetTarget(&newitem->target, players[player->kartstuff[k_eggmanblame]].mo);
+ player->kartstuff[k_eggmanblame] = -1;
+ }
+
+ newitem->flags2 = (player->mo->flags2 & MF2_OBJECTFLIP);
+ newitem->fuse = 15*TICRATE; // selected randomly.
+
+ player->kartstuff[k_comebackmode] = 0;
+ player->kartstuff[k_comebacktimer] = comebacktime;
+ S_StartSound(player->mo, sfx_s254);
+ }
+ // Eggman Monitor exploding
+ else if (player->kartstuff[k_eggmanexplode])
+ {
+ if (ATTACK_IS_DOWN && player->kartstuff[k_eggmanexplode] <= 3*TICRATE && player->kartstuff[k_eggmanexplode] > 1)
+ player->kartstuff[k_eggmanexplode] = 1;
+ }
+ // Eggman Monitor throwing
+ else if (ATTACK_IS_DOWN && player->kartstuff[k_eggmanheld])
+ {
+ K_ThrowKartItem(player, false, MT_EGGMANITEM, -1, 0);
+ K_PlayAttackTaunt(player->mo);
+ player->kartstuff[k_eggmanheld] = 0;
+ K_UpdateHnextList(player, true);
+ }
+ // Rocket Sneaker
+ else if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO
+ && player->kartstuff[k_rocketsneakertimer] > 1)
+ {
+ K_DoSneaker(player, 2);
+ K_PlayBoostTaunt(player->mo);
+ player->kartstuff[k_rocketsneakertimer] -= 2*TICRATE;
+ if (player->kartstuff[k_rocketsneakertimer] < 1)
+ player->kartstuff[k_rocketsneakertimer] = 1;
+ }
+ // Grow Canceling
+ else if (player->kartstuff[k_growshrinktimer] > 0)
+ {
+ if (cmd->buttons & BT_ATTACK)
+ {
+ player->kartstuff[k_growcancel]++;
+ if (player->kartstuff[k_growcancel] > 26)
+ K_RemoveGrowShrink(player);
+ }
+ else
+ player->kartstuff[k_growcancel] = 0;
+ }
+ else if (player->kartstuff[k_itemamount] <= 0)
+ {
+ player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0;
+ }
+ else
+ {
+ switch (player->kartstuff[k_itemtype])
+ {
+ case KITEM_SNEAKER:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO)
+ {
+ K_DoSneaker(player, 1);
+ K_PlayBoostTaunt(player->mo);
+ player->kartstuff[k_itemamount]--;
+ }
+ break;
+ case KITEM_ROCKETSNEAKER:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO
+ && player->kartstuff[k_rocketsneakertimer] == 0)
+ {
+ INT32 moloop;
+ mobj_t *mo = NULL;
+ mobj_t *prev = player->mo;
+
+ K_PlayBoostTaunt(player->mo);
+ //player->kartstuff[k_itemheld] = 1;
+ S_StartSound(player->mo, sfx_s3k3a);
+
+ //K_DoSneaker(player, 2);
+
+ player->kartstuff[k_rocketsneakertimer] = (itemtime*3);
+ player->kartstuff[k_itemamount]--;
+ K_UpdateHnextList(player, true);
+
+ for (moloop = 0; moloop < 2; moloop++)
+ {
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ROCKETSNEAKER);
+ K_MatchGenericExtraFlags(mo, player->mo);
+ mo->flags |= MF_NOCLIPTHING;
+ mo->angle = player->mo->angle;
+ mo->threshold = 10;
+ mo->movecount = moloop%2;
+ mo->movedir = mo->lastlook = moloop+1;
+ P_SetTarget(&mo->target, player->mo);
+ P_SetTarget(&mo->hprev, prev);
+ P_SetTarget(&prev->hnext, mo);
+ prev = mo;
+ }
+ }
+ break;
+ case KITEM_INVINCIBILITY:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) // Doesn't hold your item slot hostage normally, so you're free to waste it if you have multiple
+ {
+ if (!player->kartstuff[k_invincibilitytimer])
+ {
+ mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INVULNFLASH);
+ P_SetTarget(&overlay->target, player->mo);
+ overlay->destscale = player->mo->scale;
+ P_SetScale(overlay, player->mo->scale);
+ }
+ player->kartstuff[k_invincibilitytimer] = itemtime+(2*TICRATE); // 10 seconds
+ P_RestoreMusic(player);
+ if (!P_IsLocalPlayer(player))
+ S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmi : sfx_kinvnc));
+ K_PlayPowerGloatSound(player->mo);
+ player->kartstuff[k_itemamount]--;
+ }
+ break;
+ case KITEM_BANANA:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ INT32 moloop;
+ mobj_t *mo;
+ mobj_t *prev = player->mo;
+
+ //K_PlayAttackTaunt(player->mo);
+ player->kartstuff[k_itemheld] = 1;
+ S_StartSound(player->mo, sfx_s254);
+
+ for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++)
+ {
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_BANANA_SHIELD);
+ if (!mo)
+ {
+ player->kartstuff[k_itemamount] = moloop;
+ break;
+ }
+ mo->flags |= MF_NOCLIPTHING;
+ mo->threshold = 10;
+ mo->movecount = player->kartstuff[k_itemamount];
+ mo->movedir = moloop+1;
+ P_SetTarget(&mo->target, player->mo);
+ P_SetTarget(&mo->hprev, prev);
+ P_SetTarget(&prev->hnext, mo);
+ prev = mo;
+ }
+ }
+ else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Banana x3 thrown
+ {
+ K_ThrowKartItem(player, false, MT_BANANA, -1, 0);
+ K_PlayAttackTaunt(player->mo);
+ player->kartstuff[k_itemamount]--;
+ K_UpdateHnextList(player, false);
+ }
+ break;
+ case KITEM_EGGMAN:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ mobj_t *mo;
+ player->kartstuff[k_itemamount]--;
+ player->kartstuff[k_eggmanheld] = 1;
+ S_StartSound(player->mo, sfx_s254);
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EGGMANITEM_SHIELD);
+ if (mo)
+ {
+ mo->flags |= MF_NOCLIPTHING;
+ mo->threshold = 10;
+ mo->movecount = 1;
+ mo->movedir = 1;
+ P_SetTarget(&mo->target, player->mo);
+ P_SetTarget(&player->mo->hnext, mo);
+ }
+ }
+ break;
+ case KITEM_ORBINAUT:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ angle_t newangle;
+ INT32 moloop;
+ mobj_t *mo = NULL;
+ mobj_t *prev = player->mo;
+
+ //K_PlayAttackTaunt(player->mo);
+ player->kartstuff[k_itemheld] = 1;
+ S_StartSound(player->mo, sfx_s3k3a);
+
+ for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++)
+ {
+ newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90;
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_ORBINAUT_SHIELD);
+ if (!mo)
+ {
+ player->kartstuff[k_itemamount] = moloop;
+ break;
+ }
+ mo->flags |= MF_NOCLIPTHING;
+ mo->angle = newangle;
+ mo->threshold = 10;
+ mo->movecount = player->kartstuff[k_itemamount];
+ mo->movedir = mo->lastlook = moloop+1;
+ mo->color = player->skincolor;
+ P_SetTarget(&mo->target, player->mo);
+ P_SetTarget(&mo->hprev, prev);
+ P_SetTarget(&prev->hnext, mo);
+ prev = mo;
+ }
+ }
+ else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld]) // Orbinaut x3 thrown
+ {
+ K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0);
+ K_PlayAttackTaunt(player->mo);
+ player->kartstuff[k_itemamount]--;
+ K_UpdateHnextList(player, false);
+ }
+ break;
+ case KITEM_JAWZ:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ angle_t newangle;
+ INT32 moloop;
+ mobj_t *mo = NULL;
+ mobj_t *prev = player->mo;
+
+ //K_PlayAttackTaunt(player->mo);
+ player->kartstuff[k_itemheld] = 1;
+ S_StartSound(player->mo, sfx_s3k3a);
+
+ for (moloop = 0; moloop < player->kartstuff[k_itemamount]; moloop++)
+ {
+ newangle = (player->mo->angle + ANGLE_157h) + FixedAngle(((360 / player->kartstuff[k_itemamount]) * moloop) << FRACBITS) + ANGLE_90;
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_JAWZ_SHIELD);
+ if (!mo)
+ {
+ player->kartstuff[k_itemamount] = moloop;
+ break;
+ }
+ mo->flags |= MF_NOCLIPTHING;
+ mo->angle = newangle;
+ mo->threshold = 10;
+ mo->movecount = player->kartstuff[k_itemamount];
+ mo->movedir = mo->lastlook = moloop+1;
+ P_SetTarget(&mo->target, player->mo);
+ P_SetTarget(&mo->hprev, prev);
+ P_SetTarget(&prev->hnext, mo);
+ prev = mo;
+ }
+ }
+ else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Jawz thrown
+ {
+ if (player->kartstuff[k_throwdir] == 1 || player->kartstuff[k_throwdir] == 0)
+ K_ThrowKartItem(player, true, MT_JAWZ, 1, 0);
+ else if (player->kartstuff[k_throwdir] == -1) // Throwing backward gives you a dud that doesn't home in
+ K_ThrowKartItem(player, true, MT_JAWZ_DUD, -1, 0);
+ K_PlayAttackTaunt(player->mo);
+ player->kartstuff[k_itemamount]--;
+ K_UpdateHnextList(player, false);
+ }
+ break;
+ case KITEM_MINE:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ mobj_t *mo;
+ player->kartstuff[k_itemheld] = 1;
+ S_StartSound(player->mo, sfx_s254);
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SSMINE_SHIELD);
+ if (mo)
+ {
+ mo->flags |= MF_NOCLIPTHING;
+ mo->threshold = 10;
+ mo->movecount = 1;
+ mo->movedir = 1;
+ P_SetTarget(&mo->target, player->mo);
+ P_SetTarget(&player->mo->hnext, mo);
+ }
+ }
+ else if (ATTACK_IS_DOWN && player->kartstuff[k_itemheld])
+ {
+ K_ThrowKartItem(player, false, MT_SSMINE, 1, 1);
+ K_PlayAttackTaunt(player->mo);
+ player->kartstuff[k_itemamount]--;
+ player->kartstuff[k_itemheld] = 0;
+ K_UpdateHnextList(player, true);
+ }
+ break;
+ case KITEM_BALLHOG:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ player->kartstuff[k_itemamount]--;
+ K_ThrowKartItem(player, true, MT_BALLHOG, 1, 0);
+ K_PlayAttackTaunt(player->mo);
+ }
+ break;
+ case KITEM_SPB:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ player->kartstuff[k_itemamount]--;
+ K_ThrowKartItem(player, true, MT_SPB, 1, 0);
+ K_PlayAttackTaunt(player->mo);
+ }
+ break;
+ case KITEM_GROW:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO
+ && player->kartstuff[k_growshrinktimer] <= 0) // Grow holds the item box hostage
+ {
+ K_PlayPowerGloatSound(player->mo);
+ player->mo->scalespeed = mapobjectscale/TICRATE;
+ player->mo->destscale = (3*mapobjectscale)/2;
+ if (cv_kartdebugshrink.value && !modeattacking && !player->bot)
+ player->mo->destscale = (6*player->mo->destscale)/8;
+ player->kartstuff[k_growshrinktimer] = itemtime+(4*TICRATE); // 12 seconds
+ P_RestoreMusic(player);
+ if (!P_IsLocalPlayer(player))
+ S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow));
+ S_StartSound(player->mo, sfx_kc5a);
+ player->kartstuff[k_itemamount]--;
+ }
+ break;
+ case KITEM_SHRINK:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ K_DoShrink(player);
+ player->kartstuff[k_itemamount]--;
+ K_PlayPowerGloatSound(player->mo);
+ }
+ break;
+ case KITEM_THUNDERSHIELD:
+ if (player->kartstuff[k_curshield] != 1)
+ {
+ mobj_t *shield = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_THUNDERSHIELD);
+ P_SetScale(shield, (shield->destscale = (5*shield->destscale)>>2));
+ P_SetTarget(&shield->target, player->mo);
+ S_StartSound(shield, sfx_s3k41);
+ player->kartstuff[k_curshield] = 1;
+ }
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ K_DoThunderShield(player);
+ player->kartstuff[k_itemamount]--;
+ K_PlayAttackTaunt(player->mo);
+ }
+ break;
+ case KITEM_HYUDORO:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ player->kartstuff[k_itemamount]--;
+ K_DoHyudoroSteal(player); // yes. yes they do.
+ }
+ break;
+ case KITEM_POGOSPRING:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && onground && NO_HYUDORO
+ && !player->kartstuff[k_pogospring])
+ {
+ K_PlayBoostTaunt(player->mo);
+ K_DoPogoSpring(player->mo, 32<kartstuff[k_pogospring] = 1;
+ player->kartstuff[k_itemamount]--;
+ }
+ break;
+ case KITEM_KITCHENSINK:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
+ {
+ mobj_t *mo;
+ player->kartstuff[k_itemheld] = 1;
+ S_StartSound(player->mo, sfx_s254);
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SINK_SHIELD);
+ if (mo)
+ {
+ mo->flags |= MF_NOCLIPTHING;
+ mo->threshold = 10;
+ mo->movecount = 1;
+ mo->movedir = 1;
+ P_SetTarget(&mo->target, player->mo);
+ P_SetTarget(&player->mo->hnext, mo);
+ }
+ }
+ else if (ATTACK_IS_DOWN && HOLDING_ITEM && player->kartstuff[k_itemheld]) // Sink thrown
+ {
+ K_ThrowKartItem(player, false, MT_SINK, 1, 2);
+ K_PlayAttackTaunt(player->mo);
+ player->kartstuff[k_itemamount]--;
+ player->kartstuff[k_itemheld] = 0;
+ K_UpdateHnextList(player, true);
+ }
+ break;
+ case KITEM_SAD:
+ if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO
+ && !player->kartstuff[k_sadtimer])
+ {
+ player->kartstuff[k_sadtimer] = stealtime;
+ player->kartstuff[k_itemamount]--;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ // No more!
+ if (!player->kartstuff[k_itemamount])
+ {
+ player->kartstuff[k_itemheld] = 0;
+ player->kartstuff[k_itemtype] = KITEM_NONE;
+ }
+
+ if (player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD)
+ player->kartstuff[k_curshield] = 0;
+
+ if (player->kartstuff[k_growshrinktimer] <= 0)
+ player->kartstuff[k_growcancel] = 0;
+
+ if (player->kartstuff[k_itemtype] == KITEM_SPB
+ || player->kartstuff[k_itemtype] == KITEM_SHRINK
+ || player->kartstuff[k_growshrinktimer] < 0)
+ indirectitemcooldown = 20*TICRATE;
+
+ if (player->kartstuff[k_hyudorotimer] > 0)
+ {
+ if (splitscreen)
+ {
+ if (leveltime & 1)
+ player->mo->flags2 |= MF2_DONTDRAW;
+ else
+ player->mo->flags2 &= ~MF2_DONTDRAW;
+
+ if (player->kartstuff[k_hyudorotimer] >= (1*TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyudorotime-(1*TICRATE/2))
+ {
+ if (player == &players[secondarydisplayplayer])
+ player->mo->eflags |= MFE_DRAWONLYFORP2;
+ else if (player == &players[thirddisplayplayer] && splitscreen > 1)
+ player->mo->eflags |= MFE_DRAWONLYFORP3;
+ else if (player == &players[fourthdisplayplayer] && splitscreen > 2)
+ player->mo->eflags |= MFE_DRAWONLYFORP4;
+ else if (player == &players[consoleplayer])
+ player->mo->eflags |= MFE_DRAWONLYFORP1;
+ else
+ player->mo->flags2 |= MF2_DONTDRAW;
+ }
+ else
+ player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4);
+ }
+ else
+ {
+ if (player == &players[displayplayer]
+ || (player != &players[displayplayer] && (player->kartstuff[k_hyudorotimer] < (1*TICRATE/2) || player->kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2))))
+ {
+ if (leveltime & 1)
+ player->mo->flags2 |= MF2_DONTDRAW;
+ else
+ player->mo->flags2 &= ~MF2_DONTDRAW;
+ }
+ else
+ player->mo->flags2 |= MF2_DONTDRAW;
+ }
+
+ player->powers[pw_flashing] = player->kartstuff[k_hyudorotimer]; // We'll do this for now, let's people know about the invisible people through subtle hints
+ }
+ else if (player->kartstuff[k_hyudorotimer] == 0)
+ {
+ player->mo->flags2 &= ~MF2_DONTDRAW;
+ player->mo->eflags &= ~(MFE_DRAWONLYFORP1|MFE_DRAWONLYFORP2|MFE_DRAWONLYFORP3|MFE_DRAWONLYFORP4);
+ }
+
+ if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0) // dead in match? you da bomb
+ {
+ K_DropItems(player); //K_StripItems(player);
+ K_StripOther(player);
+ player->mo->flags2 |= MF2_SHADOW;
+ player->powers[pw_flashing] = player->kartstuff[k_comebacktimer];
+ }
+ else if (G_RaceGametype() || player->kartstuff[k_bumper] > 0)
+ {
+ player->mo->flags2 &= ~MF2_SHADOW;
+ }
+ }
+
+ if (onground)
+ {
+ // Friction
+ if (!player->kartstuff[k_offroad])
+ {
+ if (player->speed > 0 && cmd->forwardmove == 0 && player->mo->friction == 59392)
+ player->mo->friction += 4608;
+ if (player->speed > 0 && cmd->forwardmove < 0 && player->mo->friction == 59392)
+ player->mo->friction += 1608;
+ }
+
+ // Karma ice physics
+ if (G_BattleGametype() && player->kartstuff[k_bumper] <= 0)
+ {
+ player->mo->friction += 1228;
+
+ if (player->mo->friction > FRACUNIT)
+ player->mo->friction = FRACUNIT;
+ if (player->mo->friction < 0)
+ player->mo->friction = 0;
+
+ player->mo->movefactor = FixedDiv(ORIG_FRICTION, player->mo->friction);
+
+ if (player->mo->movefactor < FRACUNIT)
+ player->mo->movefactor = 19*player->mo->movefactor - 18*FRACUNIT;
+ else
+ player->mo->movefactor = FRACUNIT; //player->mo->movefactor = ((player->mo->friction - 0xDB34)*(0xA))/0x80;
+
+ if (player->mo->movefactor < 32)
+ player->mo->movefactor = 32;
+ }
+
+ // Wipeout slowdown
+ if (player->kartstuff[k_spinouttimer] && player->kartstuff[k_wipeoutslow])
+ {
+ if (player->kartstuff[k_offroad])
+ player->mo->friction -= 4912;
+ if (player->kartstuff[k_wipeoutslow] == 1)
+ player->mo->friction -= 9824;
+ }
+ }
+
+ K_KartDrift(player, onground);
+
+ // Quick Turning
+ // You can't turn your kart when you're not moving.
+ // So now it's time to burn some rubber!
+ if (player->speed < 2 && leveltime > starttime && cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE && cmd->driftturn != 0)
+ {
+ if (leveltime % 8 == 0)
+ S_StartSound(player->mo, sfx_s224);
+ }
+
+ // Squishing
+ // If a Grow player or a sector crushes you, get flattened instead of being killed.
+
+ if (player->kartstuff[k_squishedtimer] <= 0)
+ {
+ player->mo->flags &= ~MF_NOCLIP;
+ }
+ else
+ {
+ player->mo->flags |= MF_NOCLIP;
+ player->mo->momx = 0;
+ player->mo->momy = 0;
+ }
+
+ // Play the starting countdown sounds
+ if (player == &players[displayplayer]) // Don't play louder in splitscreen
+ {
+ if ((leveltime == starttime-(3*TICRATE)) || (leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE))
+ S_StartSound(NULL, sfx_s3ka7);
+ if (leveltime == starttime)
+ {
+ S_StartSound(NULL, sfx_s3kad);
+ S_StopMusic(); // The GO! sound stops the level start ambience
+ }
+ }
+
+ // Start charging once you're given the opportunity.
+ if (leveltime >= starttime-(2*TICRATE) && leveltime <= starttime)
+ {
+ if (cmd->buttons & BT_ACCELERATE)
+ {
+ if (player->kartstuff[k_boostcharge] == 0)
+ player->kartstuff[k_boostcharge] = cmd->latency;
+
+ player->kartstuff[k_boostcharge]++;
+ }
+ else
+ player->kartstuff[k_boostcharge] = 0;
+ }
+
+ // Increase your size while charging your engine.
+ if (leveltime < starttime+10)
+ {
+ player->mo->scalespeed = mapobjectscale/12;
+ player->mo->destscale = mapobjectscale + (player->kartstuff[k_boostcharge]*131);
+ if (cv_kartdebugshrink.value && !modeattacking && !player->bot)
+ player->mo->destscale = (6*player->mo->destscale)/8;
+ }
+
+ // Determine the outcome of your charge.
+ if (leveltime > starttime && player->kartstuff[k_boostcharge])
+ {
+ // Not even trying?
+ if (player->kartstuff[k_boostcharge] < 35)
+ {
+ if (player->kartstuff[k_boostcharge] > 17)
+ S_StartSound(player->mo, sfx_cdfm00); // chosen instead of a conventional skid because it's more engine-like
+ }
+ // Get an instant boost!
+ else if (player->kartstuff[k_boostcharge] <= 50)
+ {
+ player->kartstuff[k_startboost] = (50-player->kartstuff[k_boostcharge])+20;
+
+ if (player->kartstuff[k_boostcharge] <= 36)
+ {
+ player->kartstuff[k_startboost] = 0;
+ K_DoSneaker(player, 0);
+ player->kartstuff[k_sneakertimer] = 70; // PERFECT BOOST!!
+
+ if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) // Let everyone hear this one
+ S_StartSound(player->mo, sfx_s25f);
+ }
+ else
+ {
+ K_SpawnDashDustRelease(player); // already handled for perfect boosts by K_DoSneaker
+ if ((!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) && P_IsLocalPlayer(player))
+ {
+ if (player->kartstuff[k_boostcharge] <= 40)
+ S_StartSound(player->mo, sfx_cdfm01); // You were almost there!
+ else
+ S_StartSound(player->mo, sfx_s23c); // Nope, better luck next time.
+ }
+ }
+ }
+ // You overcharged your engine? Those things are expensive!!!
+ else if (player->kartstuff[k_boostcharge] > 50)
+ {
+ player->powers[pw_nocontrol] = 40;
+ //S_StartSound(player->mo, sfx_kc34);
+ S_StartSound(player->mo, sfx_s3k83);
+ player->pflags |= PF_SKIDDOWN; // cheeky pflag reuse
+ }
+
+ player->kartstuff[k_boostcharge] = 0;
+ }
+}
+
+void K_CalculateBattleWanted(void)
+{
+ UINT8 numingame = 0, numplaying = 0, numwanted = 0;
+ SINT8 bestbumperplayer = -1, bestbumper = -1;
+ SINT8 camppos[MAXPLAYERS]; // who is the biggest camper
+ UINT8 ties = 0, nextcamppos = 0;
+ boolean setbumper = false;
+ UINT8 i, j;
+
+ if (!G_BattleGametype())
+ {
+ for (i = 0; i < 4; i++)
+ battlewanted[i] = -1;
+ return;
+ }
+
+ wantedcalcdelay = wantedfrequency;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ camppos[i] = -1; // initialize
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ UINT8 position = 1;
+
+ if (!playeringame[i] || players[i].spectator) // Not playing
+ continue;
+
+ if (players[i].exiting) // We're done, don't calculate.
+ return;
+
+ numplaying++;
+
+ if (players[i].kartstuff[k_bumper] <= 0) // Not alive, so don't do anything else
+ continue;
+
+ numingame++;
+
+ if (bestbumper == -1 || players[i].kartstuff[k_bumper] > bestbumper)
+ {
+ bestbumper = players[i].kartstuff[k_bumper];
+ bestbumperplayer = i;
+ }
+ else if (players[i].kartstuff[k_bumper] == bestbumper)
+ bestbumperplayer = -1; // Tie, no one has best bumper.
+
+ for (j = 0; j < MAXPLAYERS; j++)
+ {
+ if (!playeringame[j] || players[j].spectator)
+ continue;
+ if (players[j].kartstuff[k_bumper] <= 0)
+ continue;
+ if (j == i)
+ continue;
+ if (players[j].kartstuff[k_wanted] == players[i].kartstuff[k_wanted] && players[j].marescore > players[i].marescore)
+ position++;
+ else if (players[j].kartstuff[k_wanted] > players[i].kartstuff[k_wanted])
+ position++;
+ }
+
+ position--; // Make zero based
+
+ while (camppos[position] != -1) // Port priority!
+ position++;
+
+ camppos[position] = i;
+ }
+
+ if (numplaying <= 2 || (numingame <= 2 && bestbumper == 1)) // In 1v1s then there's no need for WANTED. In bigger netgames, don't show anyone as WANTED when they're equally matched.
+ numwanted = 0;
+ else
+ numwanted = min(4, 1 + ((numingame-2) / 4));
+
+ for (i = 0; i < 4; i++)
+ {
+ if (i+1 > numwanted) // Not enough players for this slot to be wanted!
+ battlewanted[i] = -1;
+ else if (bestbumperplayer != -1 && !setbumper) // If there's a player who has an untied bumper lead over everyone else, they are the first to be wanted.
+ {
+ battlewanted[i] = bestbumperplayer;
+ setbumper = true; // Don't set twice
+ }
+ else
+ {
+ // Don't accidentally set the same player, if the bestbumperplayer is also a huge camper.
+ while (bestbumperplayer != -1 && camppos[nextcamppos] != -1
+ && bestbumperplayer == camppos[nextcamppos])
+ nextcamppos++;
+
+ // Do not add *any* more people if there's too many times that are tied with others.
+ // This could theoretically happen very easily if people don't hit each other for a while after the start of a match.
+ // (I will be sincerely impressed if more than 2 people tie after people start hitting each other though)
+
+ if (camppos[nextcamppos] == -1 // Out of entries
+ || ties >= (numwanted-i)) // Already counted ties
+ {
+ battlewanted[i] = -1;
+ continue;
+ }
+
+ if (ties < (numwanted-i))
+ {
+ ties = 0; // Reset
+ for (j = 0; j < 2; j++)
+ {
+ if (camppos[nextcamppos+(j+1)] == -1) // Nothing beyond, cancel
+ break;
+ if (players[camppos[nextcamppos]].kartstuff[k_wanted] == players[camppos[nextcamppos+(j+1)]].kartstuff[k_wanted])
+ ties++;
+ }
+ }
+
+ if (ties < (numwanted-i)) // Is it still low enough after counting?
+ {
+ battlewanted[i] = camppos[nextcamppos];
+ nextcamppos++;
+ }
+ else
+ battlewanted[i] = -1;
+ }
+ }
+}
+
+void K_CheckBumpers(void)
+{
+ UINT8 i;
+ UINT8 numingame = 0;
+ SINT8 winnernum = -1;
+ INT32 winnerscoreadd = 0;
+
+ if (!multiplayer)
+ return;
+
+ if (!G_BattleGametype())
+ return;
+
+ if (gameaction == ga_completed)
+ return;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator) // not even in-game
+ continue;
+
+ if (players[i].exiting) // we're already exiting! stop!
+ return;
+
+ numingame++;
+ winnerscoreadd += players[i].marescore;
+
+ if (players[i].kartstuff[k_bumper] <= 0) // if you don't have any bumpers, you're probably not a winner
+ continue;
+ else if (winnernum > -1) // TWO winners? that's dumb :V
+ return;
+
+ winnernum = i;
+ winnerscoreadd -= players[i].marescore;
+ }
+
+ if (numingame <= 1)
+ return;
+
+ if (winnernum > -1 && playeringame[winnernum])
+ {
+ players[winnernum].marescore += winnerscoreadd;
+ CONS_Printf(M_GetText("%s recieved %d point%s for winning!\n"), player_names[winnernum], winnerscoreadd, (winnerscoreadd == 1 ? "" : "s"));
+ }
+
+ for (i = 0; i < MAXPLAYERS; i++) // This can't go in the earlier loop because winning adds points
+ K_KartUpdatePosition(&players[i]);
+
+ for (i = 0; i < MAXPLAYERS; i++) // and it can't be merged with this loop because it needs to be all updated before exiting... multi-loops suck...
+ P_DoPlayerExit(&players[i]);
+}
+
+void K_CheckSpectateStatus(void)
+{
+ UINT8 respawnlist[MAXPLAYERS];
+ UINT8 i, j, numingame = 0, numjoiners = 0;
+
+ // Maintain spectate wait timer
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i])
+ continue;
+ if (players[i].spectator && (players[i].pflags & PF_WANTSTOJOIN))
+ players[i].kartstuff[k_spectatewait]++;
+ else
+ players[i].kartstuff[k_spectatewait] = 0;
+ }
+
+ // No one's allowed to join
+ if (!cv_allowteamchange.value)
+ return;
+
+ // Get the number of players in game, and the players to be de-spectated.
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i])
+ continue;
+
+ if (!players[i].spectator)
+ {
+ numingame++;
+ if (cv_ingamecap.value && numingame >= cv_ingamecap.value) // DON'T allow if you've hit the in-game player cap
+ return;
+ if (gamestate != GS_LEVEL) // Allow if you're not in a level
+ continue;
+ if (players[i].exiting) // DON'T allow if anyone's exiting
+ return;
+ if (numingame < 2 || leveltime < starttime || mapreset) // Allow if the match hasn't started yet
+ continue;
+ if (leveltime > (starttime + 20*TICRATE)) // DON'T allow if the match is 20 seconds in
+ return;
+ if (G_RaceGametype() && players[i].laps) // DON'T allow if the race is at 2 laps
+ return;
+ continue;
+ }
+ else if (!(players[i].pflags & PF_WANTSTOJOIN))
+ continue;
+
+ respawnlist[numjoiners++] = i;
+ }
+
+ // literally zero point in going any further if nobody is joining
+ if (!numjoiners)
+ return;
+
+ // Organize by spectate wait timer
+ if (cv_ingamecap.value)
+ {
+ UINT8 oldrespawnlist[MAXPLAYERS];
+ memcpy(oldrespawnlist, respawnlist, numjoiners);
+ for (i = 0; i < numjoiners; i++)
+ {
+ UINT8 pos = 0;
+ INT32 ispecwait = players[oldrespawnlist[i]].kartstuff[k_spectatewait];
+
+ for (j = 0; j < numjoiners; j++)
+ {
+ INT32 jspecwait = players[oldrespawnlist[j]].kartstuff[k_spectatewait];
+ if (j == i)
+ continue;
+ if (jspecwait > ispecwait)
+ pos++;
+ else if (jspecwait == ispecwait && j < i)
+ pos++;
+ }
+
+ respawnlist[pos] = oldrespawnlist[i];
+ }
+ }
+
+ // Finally, we can de-spectate everyone!
+ for (i = 0; i < numjoiners; i++)
+ {
+ if (cv_ingamecap.value && numingame+i >= cv_ingamecap.value) // Hit the in-game player cap while adding people?
+ break;
+ P_SpectatorJoinGame(&players[respawnlist[i]]);
+ }
+
+ // Reset the match if you're in an empty server
+ if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (numingame < 2 && numingame+i >= 2)) // use previous i value
+ {
+ S_ChangeMusicInternal("chalng", false); // COME ON
+ mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD
+ }
+}
+
+//}
+
+//{ SRB2kart HUD Code
+
+#define NUMPOSNUMS 10
+#define NUMPOSFRAMES 7 // White, three blues, three reds
+#define NUMWINFRAMES 6 // Red, yellow, green, cyan, blue, purple
+
+//{ Patch Definitions
+static patch_t *kp_nodraw;
+
+static patch_t *kp_timesticker;
+static patch_t *kp_timestickerwide;
+static patch_t *kp_lapsticker;
+static patch_t *kp_lapstickerwide;
+static patch_t *kp_lapstickernarrow;
+static patch_t *kp_splitlapflag;
+static patch_t *kp_bumpersticker;
+static patch_t *kp_bumperstickerwide;
+static patch_t *kp_karmasticker;
+static patch_t *kp_splitkarmabomb;
+static patch_t *kp_timeoutsticker;
+
+static patch_t *kp_startcountdown[16];
+static patch_t *kp_racefinish[6];
+
+static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES];
+static patch_t *kp_winnernum[NUMPOSFRAMES];
+
+static patch_t *kp_facenum[MAXPLAYERS+1];
+static patch_t *kp_facehighlight[8];
+
+static patch_t *kp_rankbumper;
+static patch_t *kp_tinybumper[2];
+static patch_t *kp_ranknobumpers;
+
+static patch_t *kp_battlewin;
+static patch_t *kp_battlecool;
+static patch_t *kp_battlelose;
+static patch_t *kp_battlewait;
+static patch_t *kp_battleinfo;
+static patch_t *kp_wanted;
+static patch_t *kp_wantedsplit;
+static patch_t *kp_wantedreticle;
+
+static patch_t *kp_itembg[4];
+static patch_t *kp_itemtimer[2];
+static patch_t *kp_itemmulsticker[2];
+static patch_t *kp_itemx;
+
+static patch_t *kp_sneaker[2];
+static patch_t *kp_rocketsneaker[2];
+static patch_t *kp_invincibility[13];
+static patch_t *kp_banana[2];
+static patch_t *kp_eggman[2];
+static patch_t *kp_orbinaut[5];
+static patch_t *kp_jawz[2];
+static patch_t *kp_mine[2];
+static patch_t *kp_ballhog[2];
+static patch_t *kp_selfpropelledbomb[2];
+static patch_t *kp_grow[2];
+static patch_t *kp_shrink[2];
+static patch_t *kp_thundershield[2];
+static patch_t *kp_hyudoro[2];
+static patch_t *kp_pogospring[2];
+static patch_t *kp_kitchensink[2];
+static patch_t *kp_sadface[2];
+
+static patch_t *kp_check[6];
+
+static patch_t *kp_eggnum[4];
+
+static patch_t *kp_fpview[3];
+static patch_t *kp_inputwheel[5];
+
+static patch_t *kp_challenger[25];
+
+static patch_t *kp_lapanim_lap[7];
+static patch_t *kp_lapanim_final[11];
+static patch_t *kp_lapanim_number[10][3];
+static patch_t *kp_lapanim_emblem[2];
+static patch_t *kp_lapanim_hand[3];
+
+static patch_t *kp_yougotem;
+
+void K_LoadKartHUDGraphics(void)
+{
+ INT32 i, j;
+ char buffer[9];
+
+ // Null Stuff
+ kp_nodraw = W_CachePatchName("K_TRNULL", PU_HUDGFX);
+
+ // Stickers
+ kp_timesticker = W_CachePatchName("K_STTIME", PU_HUDGFX);
+ kp_timestickerwide = W_CachePatchName("K_STTIMW", PU_HUDGFX);
+ kp_lapsticker = W_CachePatchName("K_STLAPS", PU_HUDGFX);
+ kp_lapstickerwide = W_CachePatchName("K_STLAPW", PU_HUDGFX);
+ kp_lapstickernarrow = W_CachePatchName("K_STLAPN", PU_HUDGFX);
+ kp_splitlapflag = W_CachePatchName("K_SPTLAP", PU_HUDGFX);
+ kp_bumpersticker = W_CachePatchName("K_STBALN", PU_HUDGFX);
+ kp_bumperstickerwide = W_CachePatchName("K_STBALW", PU_HUDGFX);
+ kp_karmasticker = W_CachePatchName("K_STKARM", PU_HUDGFX);
+ kp_splitkarmabomb = W_CachePatchName("K_SPTKRM", PU_HUDGFX);
+ kp_timeoutsticker = W_CachePatchName("K_STTOUT", PU_HUDGFX);
+
+ // Starting countdown
+ kp_startcountdown[0] = W_CachePatchName("K_CNT3A", PU_HUDGFX);
+ kp_startcountdown[1] = W_CachePatchName("K_CNT2A", PU_HUDGFX);
+ kp_startcountdown[2] = W_CachePatchName("K_CNT1A", PU_HUDGFX);
+ kp_startcountdown[3] = W_CachePatchName("K_CNTGOA", PU_HUDGFX);
+ kp_startcountdown[4] = W_CachePatchName("K_CNT3B", PU_HUDGFX);
+ kp_startcountdown[5] = W_CachePatchName("K_CNT2B", PU_HUDGFX);
+ kp_startcountdown[6] = W_CachePatchName("K_CNT1B", PU_HUDGFX);
+ kp_startcountdown[7] = W_CachePatchName("K_CNTGOB", PU_HUDGFX);
+ // Splitscreen
+ kp_startcountdown[8] = W_CachePatchName("K_SMC3A", PU_HUDGFX);
+ kp_startcountdown[9] = W_CachePatchName("K_SMC2A", PU_HUDGFX);
+ kp_startcountdown[10] = W_CachePatchName("K_SMC1A", PU_HUDGFX);
+ kp_startcountdown[11] = W_CachePatchName("K_SMCGOA", PU_HUDGFX);
+ kp_startcountdown[12] = W_CachePatchName("K_SMC3B", PU_HUDGFX);
+ kp_startcountdown[13] = W_CachePatchName("K_SMC2B", PU_HUDGFX);
+ kp_startcountdown[14] = W_CachePatchName("K_SMC1B", PU_HUDGFX);
+ kp_startcountdown[15] = W_CachePatchName("K_SMCGOB", PU_HUDGFX);
+
+ // Finish
+ kp_racefinish[0] = W_CachePatchName("K_FINA", PU_HUDGFX);
+ kp_racefinish[1] = W_CachePatchName("K_FINB", PU_HUDGFX);
+ // Splitscreen
+ kp_racefinish[2] = W_CachePatchName("K_SMFINA", PU_HUDGFX);
+ kp_racefinish[3] = W_CachePatchName("K_SMFINB", PU_HUDGFX);
+ // 2P splitscreen
+ kp_racefinish[4] = W_CachePatchName("K_2PFINA", PU_HUDGFX);
+ kp_racefinish[5] = W_CachePatchName("K_2PFINB", PU_HUDGFX);
+
+ // Position numbers
+ sprintf(buffer, "K_POSNxx");
+ for (i = 0; i < NUMPOSNUMS; i++)
+ {
+ buffer[6] = '0'+i;
+ for (j = 0; j < NUMPOSFRAMES; j++)
+ {
+ //sprintf(buffer, "K_POSN%d%d", i, j);
+ buffer[7] = '0'+j;
+ kp_positionnum[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+ }
+
+ sprintf(buffer, "K_POSNWx");
+ for (i = 0; i < NUMWINFRAMES; i++)
+ {
+ buffer[7] = '0'+i;
+ kp_winnernum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ sprintf(buffer, "OPPRNKxx");
+ for (i = 0; i <= MAXPLAYERS; i++)
+ {
+ buffer[6] = '0'+(i/10);
+ buffer[7] = '0'+(i%10);
+ kp_facenum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ sprintf(buffer, "K_CHILIx");
+ for (i = 0; i < 8; i++)
+ {
+ buffer[7] = '0'+(i+1);
+ kp_facehighlight[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ // Extra ranking icons
+ kp_rankbumper = W_CachePatchName("K_BLNICO", PU_HUDGFX);
+ kp_tinybumper[0] = W_CachePatchName("K_BLNA", PU_HUDGFX);
+ kp_tinybumper[1] = W_CachePatchName("K_BLNB", PU_HUDGFX);
+ kp_ranknobumpers = W_CachePatchName("K_NOBLNS", PU_HUDGFX);
+
+ // Battle graphics
+ kp_battlewin = W_CachePatchName("K_BWIN", PU_HUDGFX);
+ kp_battlecool = W_CachePatchName("K_BCOOL", PU_HUDGFX);
+ kp_battlelose = W_CachePatchName("K_BLOSE", PU_HUDGFX);
+ kp_battlewait = W_CachePatchName("K_BWAIT", PU_HUDGFX);
+ kp_battleinfo = W_CachePatchName("K_BINFO", PU_HUDGFX);
+ kp_wanted = W_CachePatchName("K_WANTED", PU_HUDGFX);
+ kp_wantedsplit = W_CachePatchName("4PWANTED", PU_HUDGFX);
+ kp_wantedreticle = W_CachePatchName("MMAPWANT", PU_HUDGFX);
+
+ // Kart Item Windows
+ kp_itembg[0] = W_CachePatchName("K_ITBG", PU_HUDGFX);
+ kp_itembg[1] = W_CachePatchName("K_ITBGD", PU_HUDGFX);
+ kp_itemtimer[0] = W_CachePatchName("K_ITIMER", PU_HUDGFX);
+ kp_itemmulsticker[0] = W_CachePatchName("K_ITMUL", PU_HUDGFX);
+ kp_itemx = W_CachePatchName("K_ITX", PU_HUDGFX);
+
+ kp_sneaker[0] = W_CachePatchName("K_ITSHOE", PU_HUDGFX);
+ kp_rocketsneaker[0] = W_CachePatchName("K_ITRSHE", PU_HUDGFX);
+
+ sprintf(buffer, "K_ITINVx");
+ for (i = 0; i < 7; i++)
+ {
+ buffer[7] = '1'+i;
+ kp_invincibility[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+ kp_banana[0] = W_CachePatchName("K_ITBANA", PU_HUDGFX);
+ kp_eggman[0] = W_CachePatchName("K_ITEGGM", PU_HUDGFX);
+ sprintf(buffer, "K_ITORBx");
+ for (i = 0; i < 4; i++)
+ {
+ buffer[7] = '1'+i;
+ kp_orbinaut[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+ kp_jawz[0] = W_CachePatchName("K_ITJAWZ", PU_HUDGFX);
+ kp_mine[0] = W_CachePatchName("K_ITMINE", PU_HUDGFX);
+ kp_ballhog[0] = W_CachePatchName("K_ITBHOG", PU_HUDGFX);
+ kp_selfpropelledbomb[0] = W_CachePatchName("K_ITSPB", PU_HUDGFX);
+ kp_grow[0] = W_CachePatchName("K_ITGROW", PU_HUDGFX);
+ kp_shrink[0] = W_CachePatchName("K_ITSHRK", PU_HUDGFX);
+ kp_thundershield[0] = W_CachePatchName("K_ITTHNS", PU_HUDGFX);
+ kp_hyudoro[0] = W_CachePatchName("K_ITHYUD", PU_HUDGFX);
+ kp_pogospring[0] = W_CachePatchName("K_ITPOGO", PU_HUDGFX);
+ kp_kitchensink[0] = W_CachePatchName("K_ITSINK", PU_HUDGFX);
+ kp_sadface[0] = W_CachePatchName("K_ITSAD", PU_HUDGFX);
+
+ // Splitscreen
+ kp_itembg[2] = W_CachePatchName("K_ISBG", PU_HUDGFX);
+ kp_itembg[3] = W_CachePatchName("K_ISBGD", PU_HUDGFX);
+ kp_itemtimer[1] = W_CachePatchName("K_ISIMER", PU_HUDGFX);
+ kp_itemmulsticker[1] = W_CachePatchName("K_ISMUL", PU_HUDGFX);
+
+ kp_sneaker[1] = W_CachePatchName("K_ISSHOE", PU_HUDGFX);
+ kp_rocketsneaker[1] = W_CachePatchName("K_ISRSHE", PU_HUDGFX);
+ sprintf(buffer, "K_ISINVx");
+ for (i = 0; i < 6; i++)
+ {
+ buffer[7] = '1'+i;
+ kp_invincibility[i+7] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+ kp_banana[1] = W_CachePatchName("K_ISBANA", PU_HUDGFX);
+ kp_eggman[1] = W_CachePatchName("K_ISEGGM", PU_HUDGFX);
+ kp_orbinaut[4] = W_CachePatchName("K_ISORBN", PU_HUDGFX);
+ kp_jawz[1] = W_CachePatchName("K_ISJAWZ", PU_HUDGFX);
+ kp_mine[1] = W_CachePatchName("K_ISMINE", PU_HUDGFX);
+ kp_ballhog[1] = W_CachePatchName("K_ISBHOG", PU_HUDGFX);
+ kp_selfpropelledbomb[1] = W_CachePatchName("K_ISSPB", PU_HUDGFX);
+ kp_grow[1] = W_CachePatchName("K_ISGROW", PU_HUDGFX);
+ kp_shrink[1] = W_CachePatchName("K_ISSHRK", PU_HUDGFX);
+ kp_thundershield[1] = W_CachePatchName("K_ISTHNS", PU_HUDGFX);
+ kp_hyudoro[1] = W_CachePatchName("K_ISHYUD", PU_HUDGFX);
+ kp_pogospring[1] = W_CachePatchName("K_ISPOGO", PU_HUDGFX);
+ kp_kitchensink[1] = W_CachePatchName("K_ISSINK", PU_HUDGFX);
+ kp_sadface[1] = W_CachePatchName("K_ISSAD", PU_HUDGFX);
+
+ // CHECK indicators
+ sprintf(buffer, "K_CHECKx");
+ for (i = 0; i < 6; i++)
+ {
+ buffer[7] = '1'+i;
+ kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ // Eggman warning numbers
+ sprintf(buffer, "K_EGGNx");
+ for (i = 0; i < 4; i++)
+ {
+ buffer[6] = '0'+i;
+ kp_eggnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ // First person mode
+ kp_fpview[0] = W_CachePatchName("VIEWA0", PU_HUDGFX);
+ kp_fpview[1] = W_CachePatchName("VIEWB0D0", PU_HUDGFX);
+ kp_fpview[2] = W_CachePatchName("VIEWC0E0", PU_HUDGFX);
+
+ // Input UI Wheel
+ sprintf(buffer, "K_WHEELx");
+ for (i = 0; i < 5; i++)
+ {
+ buffer[7] = '0'+i;
+ kp_inputwheel[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ // HERE COMES A NEW CHALLENGER
+ sprintf(buffer, "K_CHALxx");
+ for (i = 0; i < 25; i++)
+ {
+ buffer[6] = '0'+((i+1)/10);
+ buffer[7] = '0'+((i+1)%10);
+ kp_challenger[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ // Lap start animation
+ sprintf(buffer, "K_LAP0x");
+ for (i = 0; i < 7; i++)
+ {
+ buffer[6] = '0'+(i+1);
+ kp_lapanim_lap[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ sprintf(buffer, "K_LAPFxx");
+ for (i = 0; i < 11; i++)
+ {
+ buffer[6] = '0'+((i+1)/10);
+ buffer[7] = '0'+((i+1)%10);
+ kp_lapanim_final[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ sprintf(buffer, "K_LAPNxx");
+ for (i = 0; i < 10; i++)
+ {
+ buffer[6] = '0'+i;
+ for (j = 0; j < 3; j++)
+ {
+ buffer[7] = '0'+(j+1);
+ kp_lapanim_number[i][j] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+ }
+
+ sprintf(buffer, "K_LAPE0x");
+ for (i = 0; i < 2; i++)
+ {
+ buffer[7] = '0'+(i+1);
+ kp_lapanim_emblem[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ sprintf(buffer, "K_LAPH0x");
+ for (i = 0; i < 3; i++)
+ {
+ buffer[7] = '0'+(i+1);
+ kp_lapanim_hand[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX);
+ }
+
+ kp_yougotem = (patch_t *) W_CachePatchName("YOUGOTEM", PU_HUDGFX);
+}
+
+// For the item toggle menu
+const char *K_GetItemPatch(UINT8 item, boolean tiny)
+{
+ switch (item)
+ {
+ case KITEM_SNEAKER:
+ case KRITEM_TRIPLESNEAKER:
+ return (tiny ? "K_ISSHOE" : "K_ITSHOE");
+ case KITEM_ROCKETSNEAKER:
+ return (tiny ? "K_ISRSHE" : "K_ITRSHE");
+ case KITEM_INVINCIBILITY:
+ return (tiny ? "K_ISINV1" : "K_ITINV1");
+ case KITEM_BANANA:
+ case KRITEM_TRIPLEBANANA:
+ case KRITEM_TENFOLDBANANA:
+ return (tiny ? "K_ISBANA" : "K_ITBANA");
+ case KITEM_EGGMAN:
+ return (tiny ? "K_ISEGGM" : "K_ITEGGM");
+ case KITEM_ORBINAUT:
+ return (tiny ? "K_ISORBN" : "K_ITORB1");
+ case KITEM_JAWZ:
+ case KRITEM_DUALJAWZ:
+ return (tiny ? "K_ISJAWZ" : "K_ITJAWZ");
+ case KITEM_MINE:
+ return (tiny ? "K_ISMINE" : "K_ITMINE");
+ case KITEM_BALLHOG:
+ return (tiny ? "K_ISBHOG" : "K_ITBHOG");
+ case KITEM_SPB:
+ return (tiny ? "K_ISSPB" : "K_ITSPB");
+ case KITEM_GROW:
+ return (tiny ? "K_ISGROW" : "K_ITGROW");
+ case KITEM_SHRINK:
+ return (tiny ? "K_ISSHRK" : "K_ITSHRK");
+ case KITEM_THUNDERSHIELD:
+ return (tiny ? "K_ISTHNS" : "K_ITTHNS");
+ case KITEM_HYUDORO:
+ return (tiny ? "K_ISHYUD" : "K_ITHYUD");
+ case KITEM_POGOSPRING:
+ return (tiny ? "K_ISPOGO" : "K_ITPOGO");
+ case KITEM_KITCHENSINK:
+ return (tiny ? "K_ISSINK" : "K_ITSINK");
+ case KRITEM_TRIPLEORBINAUT:
+ return (tiny ? "K_ISORBN" : "K_ITORB3");
+ case KRITEM_QUADORBINAUT:
+ return (tiny ? "K_ISORBN" : "K_ITORB4");
+ default:
+ return (tiny ? "K_ISSAD" : "K_ITSAD");
+ }
+}
+
+//}
+
+INT32 ITEM_X, ITEM_Y; // Item Window
+INT32 TIME_X, TIME_Y; // Time Sticker
+INT32 LAPS_X, LAPS_Y; // Lap Sticker
+INT32 SPDM_X, SPDM_Y; // Speedometer
+INT32 POSI_X, POSI_Y; // Position Number
+INT32 FACE_X, FACE_Y; // Top-four Faces
+INT32 STCD_X, STCD_Y; // Starting countdown
+INT32 CHEK_Y; // CHECK graphic
+INT32 MINI_X, MINI_Y; // Minimap
+INT32 WANT_X, WANT_Y; // Battle WANTED poster
+
+// This is for the P2 and P4 side of splitscreen. Then we'll flip P1's and P2's to the bottom with V_SPLITSCREEN.
+INT32 ITEM2_X, ITEM2_Y;
+INT32 LAPS2_X, LAPS2_Y;
+INT32 POSI2_X, POSI2_Y;
+
+
+static void K_initKartHUD(void)
+{
+ /*
+ BASEVIDWIDTH = 320
+ BASEVIDHEIGHT = 200
+
+ Item window graphic is 41 x 33
+
+ Time Sticker graphic is 116 x 11
+ Time Font is a solid block of (8 x [12) x 14], equal to 96 x 14
+ Therefore, timestamp is 116 x 14 altogether
+
+ Lap Sticker is 80 x 11
+ Lap flag is 22 x 20
+ Lap Font is a solid block of (3 x [12) x 14], equal to 36 x 14
+ Therefore, lapstamp is 80 x 20 altogether
+
+ Position numbers are 43 x 53
+
+ Faces are 32 x 32
+ Faces draw downscaled at 16 x 16
+ Therefore, the allocated space for them is 16 x 67 altogether
+
+ ----
+
+ ORIGINAL CZ64 SPLITSCREEN:
+
+ Item window:
+ if (!splitscreen) { ICONX = 139; ICONY = 20; }
+ else { ICONX = BASEVIDWIDTH-315; ICONY = 60; }
+
+ Time: 236, STRINGY( 12)
+ Lap: BASEVIDWIDTH-304, STRINGY(BASEVIDHEIGHT-189)
+
+ */
+
+ // Single Screen (defaults)
+ // Item Window
+ ITEM_X = 5; // 5
+ ITEM_Y = 5; // 5
+ // Level Timer
+ TIME_X = BASEVIDWIDTH - 148; // 172
+ TIME_Y = 9; // 9
+ // Level Laps
+ LAPS_X = 9; // 9
+ LAPS_Y = BASEVIDHEIGHT - 29; // 171
+ // Speedometer
+ SPDM_X = 9; // 9
+ SPDM_Y = BASEVIDHEIGHT - 45; // 155
+ // Position Number
+ POSI_X = BASEVIDWIDTH - 9; // 268
+ POSI_Y = BASEVIDHEIGHT - 9; // 138
+ // Top-Four Faces
+ FACE_X = 9; // 9
+ FACE_Y = 92; // 92
+ // Starting countdown
+ STCD_X = BASEVIDWIDTH/2; // 9
+ STCD_Y = BASEVIDHEIGHT/2; // 92
+ // CHECK graphic
+ CHEK_Y = BASEVIDHEIGHT; // 200
+ // Minimap
+ MINI_X = BASEVIDWIDTH - 50; // 270
+ MINI_Y = (BASEVIDHEIGHT/2)-16; // 84
+ // Battle WANTED poster
+ WANT_X = BASEVIDWIDTH - 55; // 270
+ WANT_Y = BASEVIDHEIGHT- 71; // 176
+
+ if (splitscreen) // Splitscreen
+ {
+ ITEM_X = 5;
+ ITEM_Y = 3;
+
+ LAPS_Y = (BASEVIDHEIGHT/2)-24;
+
+ POSI_Y = (BASEVIDHEIGHT/2)- 2;
+
+ STCD_Y = BASEVIDHEIGHT/4;
+
+ MINI_Y = (BASEVIDHEIGHT/2);
+
+ if (splitscreen > 1) // 3P/4P Small Splitscreen
+ {
+ // 1P (top left)
+ ITEM_X = -9;
+ ITEM_Y = -8;
+
+ LAPS_X = 3;
+ LAPS_Y = (BASEVIDHEIGHT/2)-13;
+
+ POSI_X = 24;
+ POSI_Y = (BASEVIDHEIGHT/2)- 16;
+
+ // 2P (top right)
+ ITEM2_X = BASEVIDWIDTH-39;
+ ITEM2_Y = -8;
+
+ LAPS2_X = BASEVIDWIDTH-40;
+ LAPS2_Y = (BASEVIDHEIGHT/2)-13;
+
+ POSI2_X = BASEVIDWIDTH -4;
+ POSI2_Y = (BASEVIDHEIGHT/2)- 16;
+
+ // Reminder that 3P and 4P are just 1P and 2P splitscreen'd to the bottom.
+
+ STCD_X = BASEVIDWIDTH/4;
+
+ MINI_X = (3*BASEVIDWIDTH/4);
+ MINI_Y = (3*BASEVIDHEIGHT/4);
+
+ if (splitscreen > 2) // 4P-only
+ {
+ MINI_X = (BASEVIDWIDTH/2);
+ MINI_Y = (BASEVIDHEIGHT/2);
+ }
+ }
+ }
+
+ if (timeinmap > 113)
+ hudtrans = cv_translucenthud.value;
+ else if (timeinmap > 105)
+ hudtrans = ((((INT32)timeinmap) - 105)*cv_translucenthud.value)/(113-105);
+ else
+ hudtrans = 0;
+}
+
+INT32 K_calcSplitFlags(INT32 snapflags)
+{
+ INT32 splitflags = 0;
+
+ if (splitscreen == 0)
+ return snapflags;
+
+ if (stplyr != &players[displayplayer])
+ {
+ if (splitscreen == 1 && stplyr == &players[secondarydisplayplayer])
+ {
+ splitflags |= V_SPLITSCREEN;
+ }
+ else if (splitscreen > 1)
+ {
+ if (stplyr == &players[thirddisplayplayer] || stplyr == &players[fourthdisplayplayer])
+ splitflags |= V_SPLITSCREEN;
+ if (stplyr == &players[secondarydisplayplayer] || stplyr == &players[fourthdisplayplayer])
+ splitflags |= V_HORZSCREEN;
+ }
+ }
+
+ if (splitflags & V_SPLITSCREEN)
+ snapflags &= ~V_SNAPTOTOP;
+ else
+ snapflags &= ~V_SNAPTOBOTTOM;
+
+ if (splitscreen > 1)
+ {
+ if (splitflags & V_HORZSCREEN)
+ snapflags &= ~V_SNAPTOLEFT;
+ else
+ snapflags &= ~V_SNAPTORIGHT;
+ }
+
+ return (splitflags|snapflags);
+}
+
+static void K_drawKartItem(void)
+{
+ // ITEM_X = BASEVIDWIDTH-50; // 270
+ // ITEM_Y = 24; // 24
+
+ // Why write V_DrawScaledPatch calls over and over when they're all the same?
+ // Set to 'no item' just in case.
+ const UINT8 offset = ((splitscreen > 1) ? 1 : 0);
+ patch_t *localpatch = kp_nodraw;
+ patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]);
+ patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]);
+ INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags...
+ //INT32 splitflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT);
+ const INT32 numberdisplaymin = ((!offset && stplyr->kartstuff[k_itemtype] == KITEM_ORBINAUT) ? 5 : 2);
+ INT32 itembar = 0;
+ INT32 maxl = 0; // itembar's normal highest value
+ const INT32 barlength = (splitscreen > 1 ? 12 : 26);
+ UINT8 localcolor = SKINCOLOR_NONE;
+ SINT8 colormode = TC_RAINBOW;
+ UINT8 *colmap = NULL;
+ boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff
+
+ if (stplyr->kartstuff[k_itemroulette])
+ {
+ if (stplyr->skincolor)
+ localcolor = stplyr->skincolor;
+
+ switch((stplyr->kartstuff[k_itemroulette] % (14*3)) / 3)
+ {
+ // Each case is handled in threes, to give three frames of in-game time to see the item on the roulette
+ case 0: // Sneaker
+ localpatch = kp_sneaker[offset];
+ //localcolor = SKINCOLOR_RASPBERRY;
+ break;
+ case 1: // Banana
+ localpatch = kp_banana[offset];
+ //localcolor = SKINCOLOR_YELLOW;
+ break;
+ case 2: // Orbinaut
+ localpatch = kp_orbinaut[3+offset];
+ //localcolor = SKINCOLOR_STEEL;
+ break;
+ case 3: // Mine
+ localpatch = kp_mine[offset];
+ //localcolor = SKINCOLOR_JET;
+ break;
+ case 4: // Grow
+ localpatch = kp_grow[offset];
+ //localcolor = SKINCOLOR_TEAL;
+ break;
+ case 5: // Hyudoro
+ localpatch = kp_hyudoro[offset];
+ //localcolor = SKINCOLOR_STEEL;
+ break;
+ case 6: // Rocket Sneaker
+ localpatch = kp_rocketsneaker[offset];
+ //localcolor = SKINCOLOR_TANGERINE;
+ break;
+ case 7: // Jawz
+ localpatch = kp_jawz[offset];
+ //localcolor = SKINCOLOR_JAWZ;
+ break;
+ case 8: // Self-Propelled Bomb
+ localpatch = kp_selfpropelledbomb[offset];
+ //localcolor = SKINCOLOR_JET;
+ break;
+ case 9: // Shrink
+ localpatch = kp_shrink[offset];
+ //localcolor = SKINCOLOR_ORANGE;
+ break;
+ case 10: // Invincibility
+ localpatch = localinv;
+ //localcolor = SKINCOLOR_GREY;
+ break;
+ case 11: // Eggman Monitor
+ localpatch = kp_eggman[offset];
+ //localcolor = SKINCOLOR_ROSE;
+ break;
+ case 12: // Ballhog
+ localpatch = kp_ballhog[offset];
+ //localcolor = SKINCOLOR_LILAC;
+ break;
+ case 13: // Thunder Shield
+ localpatch = kp_thundershield[offset];
+ //localcolor = SKINCOLOR_CYAN;
+ break;
+ /*case 14: // Pogo Spring
+ localpatch = kp_pogospring[offset];
+ localcolor = SKINCOLOR_TANGERINE;
+ break;
+ case 15: // Kitchen Sink
+ localpatch = kp_kitchensink[offset];
+ localcolor = SKINCOLOR_STEEL;
+ break;*/
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // I'm doing this a little weird and drawing mostly in reverse order
+ // The only actual reason is to make sneakers line up this way in the code below
+ // This shouldn't have any actual baring over how it functions
+ // Hyudoro is first, because we're drawing it on top of the player's current item
+ if (stplyr->kartstuff[k_stolentimer] > 0)
+ {
+ if (leveltime & 2)
+ localpatch = kp_hyudoro[offset];
+ else
+ localpatch = kp_nodraw;
+ }
+ else if ((stplyr->kartstuff[k_stealingtimer] > 0) && (leveltime & 2))
+ {
+ localpatch = kp_hyudoro[offset];
+ }
+ else if (stplyr->kartstuff[k_eggmanexplode] > 1)
+ {
+ if (leveltime & 1)
+ localpatch = kp_eggman[offset];
+ else
+ localpatch = kp_nodraw;
+ }
+ else if (stplyr->kartstuff[k_rocketsneakertimer] > 1)
+ {
+ itembar = stplyr->kartstuff[k_rocketsneakertimer];
+ maxl = (itemtime*3) - barlength;
+
+ if (leveltime & 1)
+ localpatch = kp_rocketsneaker[offset];
+ else
+ localpatch = kp_nodraw;
+ }
+ else if (stplyr->kartstuff[k_growshrinktimer] > 0)
+ {
+ if (stplyr->kartstuff[k_growcancel])
+ {
+ itembar = stplyr->kartstuff[k_growcancel];
+ maxl = 26;
+ }
+
+ if (leveltime & 1)
+ localpatch = kp_grow[offset];
+ else
+ localpatch = kp_nodraw;
+ }
+ else if (stplyr->kartstuff[k_sadtimer] > 0)
+ {
+ if (leveltime & 2)
+ localpatch = kp_sadface[offset];
+ else
+ localpatch = kp_nodraw;
+ }
+ else
+ {
+ if (stplyr->kartstuff[k_itemamount] <= 0)
+ return;
+
+ switch(stplyr->kartstuff[k_itemtype])
+ {
+ case KITEM_SNEAKER:
+ localpatch = kp_sneaker[offset];
+ break;
+ case KITEM_ROCKETSNEAKER:
+ localpatch = kp_rocketsneaker[offset];
+ break;
+ case KITEM_INVINCIBILITY:
+ localpatch = localinv;
+ localbg = kp_itembg[offset+1];
+ break;
+ case KITEM_BANANA:
+ localpatch = kp_banana[offset];
+ break;
+ case KITEM_EGGMAN:
+ localpatch = kp_eggman[offset];
+ break;
+ case KITEM_ORBINAUT:
+ localpatch = kp_orbinaut[(offset ? 4 : min(stplyr->kartstuff[k_itemamount]-1, 3))];
+ break;
+ case KITEM_JAWZ:
+ localpatch = kp_jawz[offset];
+ break;
+ case KITEM_MINE:
+ localpatch = kp_mine[offset];
+ break;
+ case KITEM_BALLHOG:
+ localpatch = kp_ballhog[offset];
+ break;
+ case KITEM_SPB:
+ localpatch = kp_selfpropelledbomb[offset];
+ localbg = kp_itembg[offset+1];
+ break;
+ case KITEM_GROW:
+ localpatch = kp_grow[offset];
+ break;
+ case KITEM_SHRINK:
+ localpatch = kp_shrink[offset];
+ break;
+ case KITEM_THUNDERSHIELD:
+ localpatch = kp_thundershield[offset];
+ localbg = kp_itembg[offset+1];
+ break;
+ case KITEM_HYUDORO:
+ localpatch = kp_hyudoro[offset];
+ break;
+ case KITEM_POGOSPRING:
+ localpatch = kp_pogospring[offset];
+ break;
+ case KITEM_KITCHENSINK:
+ localpatch = kp_kitchensink[offset];
+ break;
+ case KITEM_SAD:
+ localpatch = kp_sadface[offset];
+ break;
+ default:
+ return;
+ }
+
+ if (stplyr->kartstuff[k_itemheld] && !(leveltime & 1))
+ localpatch = kp_nodraw;
+ }
+
+ if (stplyr->kartstuff[k_itemblink] && (leveltime & 1))
+ {
+ colormode = TC_BLINK;
+
+ switch (stplyr->kartstuff[k_itemblinkmode])
+ {
+ case 2:
+ localcolor = (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1)));
+ break;
+ case 1:
+ localcolor = SKINCOLOR_RED;
+ break;
+ default:
+ localcolor = SKINCOLOR_WHITE;
+ break;
+ }
+ }
+ }
+
+ // pain and suffering defined below
+ if (splitscreen < 2) // don't change shit for THIS splitscreen.
+ {
+ fx = ITEM_X;
+ fy = ITEM_Y;
+ fflags = K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOLEFT);
+ }
+ else // now we're having a fun game.
+ {
+ if (stplyr == &players[displayplayer] || stplyr == &players[thirddisplayplayer]) // If we are P1 or P3...
+ {
+ fx = ITEM_X;
+ fy = ITEM_Y;
+ fflags = V_SNAPTOLEFT|((stplyr == &players[thirddisplayplayer]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P3 to the bottom.
+ }
+ else // else, that means we're P2 or P4.
+ {
+ fx = ITEM2_X;
+ fy = ITEM2_Y;
+ fflags = V_SNAPTORIGHT|((stplyr == &players[fourthdisplayplayer]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P4 to the bottom
+ flipamount = true;
+ }
+ }
+
+ if (localcolor != SKINCOLOR_NONE)
+ colmap = R_GetTranslationColormap(colormode, localcolor, GTC_CACHE);
+
+ V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, localbg);
+
+ // Then, the numbers:
+ if (stplyr->kartstuff[k_itemamount] >= numberdisplaymin && !stplyr->kartstuff[k_itemroulette])
+ {
+ V_DrawScaledPatch(fx + (flipamount ? 48 : 0), fy, V_HUDTRANS|fflags|(flipamount ? V_FLIP : 0), kp_itemmulsticker[offset]); // flip this graphic for p2 and p4 in split and shift it.
+ V_DrawFixedPatch(fx<kartstuff[k_itemamount]));
+ else
+ V_DrawString(fx+24, fy+31, V_ALLOWLOWERCASE|V_HUDTRANS|fflags, va("x%d", stplyr->kartstuff[k_itemamount]));
+ else
+ {
+ V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|fflags, kp_itemx);
+ V_DrawKartString(fx+38, fy+36, V_HUDTRANS|fflags, va("%d", stplyr->kartstuff[k_itemamount]));
+ }
+ }
+ else
+ V_DrawFixedPatch(fx< 2)
+ {
+ V_DrawFill(fx+x+length, fy+y+1, 1, height, 12|fflags); // the right one
+ if (height == 2)
+ V_DrawFill(fx+x+2, fy+y+2, length-2, 1, 8|fflags); // the dulled underside
+ V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 120|fflags); // the shine
+ }
+ }
+
+ // Quick Eggman numbers
+ if (stplyr->kartstuff[k_eggmanexplode] > 1 /*&& stplyr->kartstuff[k_eggmanexplode] <= 3*TICRATE*/)
+ V_DrawScaledPatch(fx+17, fy+13-offset, V_HUDTRANS|fflags, kp_eggnum[min(3, G_TicsToSeconds(stplyr->kartstuff[k_eggmanexplode]))]);
+
+}
+
+void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode)
+{
+ // TIME_X = BASEVIDWIDTH-124; // 196
+ // TIME_Y = 6; // 6
+
+ tic_t worktime;
+
+ INT32 splitflags = 0;
+ if (!mode)
+ {
+ splitflags = V_HUDTRANS|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTORIGHT);
+ if (cv_timelimit.value && timelimitintics > 0)
+ {
+ if (drawtime >= timelimitintics)
+ drawtime = 0;
+ else
+ drawtime = timelimitintics - drawtime;
+ }
+ }
+
+ V_DrawScaledPatch(TX, TY, splitflags, ((mode == 2) ? kp_lapstickerwide : kp_timestickerwide));
+
+ TX += 33;
+
+ worktime = drawtime/(60*TICRATE);
+
+ if (mode && !drawtime)
+ V_DrawKartString(TX, TY+3, splitflags, va("--'--\"--"));
+ else if (worktime < 100) // 99:99:99 only
+ {
+ // zero minute
+ if (worktime < 10)
+ {
+ V_DrawKartString(TX, TY+3, splitflags, va("0"));
+ // minutes time 0 __ __
+ V_DrawKartString(TX+12, TY+3, splitflags, va("%d", worktime));
+ }
+ // minutes time 0 __ __
+ else
+ V_DrawKartString(TX, TY+3, splitflags, va("%d", worktime));
+
+ // apostrophe location _'__ __
+ V_DrawKartString(TX+24, TY+3, splitflags, va("'"));
+
+ worktime = (drawtime/TICRATE % 60);
+
+ // zero second _ 0_ __
+ if (worktime < 10)
+ {
+ V_DrawKartString(TX+36, TY+3, splitflags, va("0"));
+ // seconds time _ _0 __
+ V_DrawKartString(TX+48, TY+3, splitflags, va("%d", worktime));
+ }
+ // zero second _ 00 __
+ else
+ V_DrawKartString(TX+36, TY+3, splitflags, va("%d", worktime));
+
+ // quotation mark location _ __"__
+ V_DrawKartString(TX+60, TY+3, splitflags, va("\""));
+
+ worktime = G_TicsToCentiseconds(drawtime);
+
+ // zero tick _ __ 0_
+ if (worktime < 10)
+ {
+ V_DrawKartString(TX+72, TY+3, splitflags, va("0"));
+ // tics _ __ _0
+ V_DrawKartString(TX+84, TY+3, splitflags, va("%d", worktime));
+ }
+ // zero tick _ __ 00
+ else
+ V_DrawKartString(TX+72, TY+3, splitflags, va("%d", worktime));
+ }
+ else if ((drawtime/TICRATE) & 1)
+ V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99"));
+
+ if (emblemmap && (modeattacking || (mode == 1)) && !demoplayback) // emblem time!
+ {
+ INT32 workx = TX + 96, worky = TY+18;
+ SINT8 curemb = 0;
+ patch_t *emblempic[3] = {NULL, NULL, NULL};
+ UINT8 *emblemcol[3] = {NULL, NULL, NULL};
+
+ emblem_t *emblem = M_GetLevelEmblems(emblemmap);
+ while (emblem)
+ {
+ char targettext[9];
+
+ switch (emblem->type)
+ {
+ case ET_TIME:
+ {
+ static boolean canplaysound = true;
+ tic_t timetoreach = emblem->var;
+
+ if (emblem->collected)
+ {
+ emblempic[curemb] = W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE);
+ emblemcol[curemb] = R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE);
+ if (++curemb == 3)
+ break;
+ goto bademblem;
+ }
+
+ snprintf(targettext, 9, "%i'%02i\"%02i",
+ G_TicsToMinutes(timetoreach, false),
+ G_TicsToSeconds(timetoreach),
+ G_TicsToCentiseconds(timetoreach));
+
+ if (!mode)
+ {
+ if (stplyr->realtime > timetoreach)
+ {
+ splitflags = (splitflags &~ V_HUDTRANS)|V_HUDTRANSHALF;
+ if (canplaysound)
+ {
+ S_StartSound(NULL, sfx_s3k72); //sfx_s26d); -- you STOLE fizzy lifting drinks
+ canplaysound = false;
+ }
+ }
+ else if (!canplaysound)
+ canplaysound = true;
+ }
+
+ targettext[8] = 0;
+ }
+ break;
+ default:
+ goto bademblem;
+ }
+
+ V_DrawRightAlignedString(workx, worky, splitflags, targettext);
+ workx -= 67;
+ V_DrawSmallScaledPatch(workx + 4, worky, splitflags, W_CachePatchName("NEEDIT", PU_CACHE));
+
+ break;
+
+ bademblem:
+ emblem = M_GetLevelEmblems(-1);
+ }
+
+ if (!mode)
+ splitflags = (splitflags &~ V_HUDTRANSHALF)|V_HUDTRANS;
+ while (curemb--)
+ {
+ workx -= 12;
+ V_DrawSmallMappedPatch(workx + 4, worky, splitflags, emblempic[curemb], emblemcol[curemb]);
+ }
+ }
+}
+
+static void K_DrawKartPositionNum(INT32 num)
+{
+ // POSI_X = BASEVIDWIDTH - 51; // 269
+ // POSI_Y = BASEVIDHEIGHT- 64; // 136
+
+ boolean win = (stplyr->exiting && num == 1);
+ //INT32 X = POSI_X;
+ INT32 W = SHORT(kp_positionnum[0][0]->width);
+ fixed_t scale = FRACUNIT;
+ patch_t *localpatch = kp_positionnum[0][0];
+ //INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTORIGHT);
+ INT32 fx = 0, fy = 0, fflags = 0;
+ boolean flipdraw = false; // flip the order we draw it in for MORE splitscreen bs. fun.
+ boolean flipvdraw = false; // used only for 2p splitscreen so overtaking doesn't make 1P's position fly off the screen.
+ boolean overtake = false;
+
+ if (stplyr->kartstuff[k_positiondelay] || stplyr->exiting)
+ {
+ scale *= 2;
+ overtake = true; // this is used for splitscreen stuff in conjunction with flipdraw.
+ }
+ if (splitscreen)
+ scale /= 2;
+
+ W = FixedMul(W<>FRACBITS;
+
+ // pain and suffering defined below
+ if (!splitscreen)
+ {
+ fx = POSI_X;
+ fy = BASEVIDHEIGHT - 8;
+ fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT;
+ }
+ else if (splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different.
+ {
+ fx = POSI_X;
+ if (stplyr == &players[displayplayer]) // for player 1: display this at the top right, above the minimap.
+ {
+ fy = 30;
+ fflags = V_SNAPTOTOP|V_SNAPTORIGHT;
+ if (overtake)
+ flipvdraw = true; // make sure overtaking doesn't explode us
+ }
+ else // if we're not p1, that means we're p2. display this at the bottom right, below the minimap.
+ {
+ fy = BASEVIDHEIGHT - 8;
+ fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT;
+ }
+ }
+ else
+ {
+ if (stplyr == &players[displayplayer] || stplyr == &players[thirddisplayplayer]) // If we are P1 or P3...
+ {
+ fx = POSI_X;
+ fy = POSI_Y;
+ fflags = V_SNAPTOLEFT|((stplyr == &players[thirddisplayplayer]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom.
+ flipdraw = true;
+ if (num && num >= 10)
+ fx += W; // this seems dumb, but we need to do this in order for positions above 10 going off screen.
+ }
+ else // else, that means we're P2 or P4.
+ {
+ fx = POSI2_X;
+ fy = POSI2_Y;
+ fflags = V_SNAPTORIGHT|((stplyr == &players[fourthdisplayplayer]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom
+ }
+ }
+
+ // Special case for 0
+ if (!num)
+ {
+ V_DrawFixedPatch(fx<= 0); // This function does not draw negative numbers
+
+ // Draw the number
+ while (num)
+ {
+ if (win) // 1st place winner? You get rainbows!!
+ localpatch = kp_winnernum[(leveltime % (NUMWINFRAMES*3)) / 3];
+ else if (stplyr->laps+1 >= cv_numlaps.value || stplyr->exiting) // Check for the final lap, or won
+ {
+ // Alternate frame every three frames
+ switch (leveltime % 9)
+ {
+ case 1: case 2: case 3:
+ if (K_IsPlayerLosing(stplyr))
+ localpatch = kp_positionnum[num % 10][4];
+ else
+ localpatch = kp_positionnum[num % 10][1];
+ break;
+ case 4: case 5: case 6:
+ if (K_IsPlayerLosing(stplyr))
+ localpatch = kp_positionnum[num % 10][5];
+ else
+ localpatch = kp_positionnum[num % 10][2];
+ break;
+ case 7: case 8: case 9:
+ if (K_IsPlayerLosing(stplyr))
+ localpatch = kp_positionnum[num % 10][6];
+ else
+ localpatch = kp_positionnum[num % 10][3];
+ break;
+ default:
+ localpatch = kp_positionnum[num % 10][0];
+ break;
+ }
+ }
+ else
+ localpatch = kp_positionnum[num % 10][0];
+
+ V_DrawFixedPatch((fx<width)*scale/2) : 0), (fy<height)*scale/2) : 0), scale, V_HUDTRANSHALF|fflags, localpatch, NULL);
+ // ^ if we overtake as p1 or p3 in splitscren, we shift it so that it doesn't go off screen.
+ // ^ if we overtake as p1 in 2p splits, shift vertically so that this doesn't happen either.
+
+ fx -= W;
+ num /= 10;
+ }
+}
+
+static boolean K_drawKartPositionFaces(void)
+{
+ // FACE_X = 15; // 15
+ // FACE_Y = 72; // 72
+
+ INT32 Y = FACE_Y+9; // +9 to offset where it's being drawn if there are more than one
+ INT32 i, j, ranklines, strank = -1;
+ boolean completed[MAXPLAYERS];
+ INT32 rankplayer[MAXPLAYERS];
+ INT32 bumperx, numplayersingame = 0;
+ UINT8 *colormap;
+
+ ranklines = 0;
+ memset(completed, 0, sizeof (completed));
+ memset(rankplayer, 0, sizeof (rankplayer));
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ rankplayer[i] = -1;
+
+ if (!playeringame[i] || players[i].spectator || !players[i].mo)
+ continue;
+
+ numplayersingame++;
+ }
+
+ if (numplayersingame <= 1)
+ return true;
+
+#ifdef HAVE_BLUA
+ if (!LUA_HudEnabled(hud_minirankings))
+ return false; // Don't proceed but still return true for free play above if HUD is disabled.
+#endif
+
+ for (j = 0; j < numplayersingame; j++)
+ {
+ UINT8 lowestposition = MAXPLAYERS+1;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (completed[i] || !playeringame[i] || players[i].spectator || !players[i].mo)
+ continue;
+
+ if (players[i].kartstuff[k_position] >= lowestposition)
+ continue;
+
+ rankplayer[ranklines] = i;
+ lowestposition = players[i].kartstuff[k_position];
+ }
+
+ i = rankplayer[ranklines];
+
+ completed[i] = true;
+
+ if (players+i == stplyr)
+ strank = ranklines;
+
+ //if (ranklines == 5)
+ //break; // Only draw the top 5 players -- we do this a different way now...
+
+ ranklines++;
+ }
+
+ if (ranklines < 5)
+ Y -= (9*ranklines);
+ else
+ Y -= (9*5);
+
+ if (G_BattleGametype() || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2)
+ {
+ i = 0;
+ if (ranklines > 5) // could be both...
+ ranklines = 5;
+ }
+ else if (strank+3 > ranklines) // too close to the bottom?
+ {
+ i = ranklines - 5;
+ if (i < 0)
+ i = 0;
+ }
+ else
+ {
+ i = strank-2;
+ ranklines = strank+3;
+ }
+
+ for (; i < ranklines; i++)
+ {
+ if (!playeringame[rankplayer[i]]) continue;
+ if (players[rankplayer[i]].spectator) continue;
+ if (!players[rankplayer[i]].mo) continue;
+
+ bumperx = FACE_X+19;
+
+ if (players[rankplayer[i]].mo->color)
+ {
+ colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE);
+ if (players[rankplayer[i]].mo->colorized)
+ colormap = R_GetTranslationColormap(TC_RAINBOW, players[rankplayer[i]].mo->color, GTC_CACHE);
+ else
+ colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE);
+
+ V_DrawMappedPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, facerankprefix[players[rankplayer[i]].skin], colormap);
+
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_battlebumpers))
+ {
+#endif
+ if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] > 0)
+ {
+ V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[0], colormap);
+ for (j = 1; j < players[rankplayer[i]].kartstuff[k_bumper]; j++)
+ {
+ bumperx += 5;
+ V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_tinybumper[1], colormap);
+ }
+ }
+#ifdef HAVE_BLUA
+ } // A new level of stupidity: checking if lua is enabled to close a bracket. :Fascinating:
+#endif
+ }
+
+ if (i == strank)
+ V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]);
+
+ if (G_BattleGametype() && players[rankplayer[i]].kartstuff[k_bumper] <= 0)
+ V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SNAPTOLEFT, kp_ranknobumpers);
+ else
+ {
+ INT32 pos = players[rankplayer[i]].kartstuff[k_position];
+ if (pos < 0 || pos > MAXPLAYERS)
+ pos = 0;
+ // Draws the little number over the face
+ V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SNAPTOLEFT, kp_facenum[pos]);
+ }
+
+ Y += 18;
+ }
+
+ return false;
+}
+
+//
+// HU_DrawTabRankings -- moved here to take advantage of kart stuff!
+//
+void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol)
+{
+ INT32 i, rightoffset = 240;
+ const UINT8 *colormap;
+ INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2;
+
+ //this function is designed for 9 or less score lines only
+ //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up
+
+ V_DrawFill(1-duptweak, 26, dupadjust-2, 1, 0); // Draw a horizontal line because it looks nice!
+ if (scorelines > 8)
+ {
+ V_DrawFill(160, 26, 1, 147, 0); // Draw a vertical line to separate the two sides.
+ V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom.
+ rightoffset = (BASEVIDWIDTH/2) - 4 - x;
+ }
+
+ for (i = 0; i < scorelines; i++)
+ {
+ char strtime[MAXPLAYERNAME+1];
+
+ if (players[tab[i].num].spectator || !players[tab[i].num].mo)
+ continue; //ignore them.
+
+ if (netgame // don't draw it offline
+ && tab[i].num != serverplayer)
+ HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0);
+
+ STRBUFCPY(strtime, tab[i].name);
+
+ if (scorelines > 8)
+ V_DrawThinString(x + 20, y, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime);
+ else
+ V_DrawString(x + 20, y, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime);
+
+ if (players[tab[i].num].mo->color)
+ {
+ colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE);
+ if (players[tab[i].num].mo->colorized)
+ colormap = R_GetTranslationColormap(TC_RAINBOW, players[tab[i].num].mo->color, GTC_CACHE);
+ else
+ colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo->color, GTC_CACHE);
+
+ V_DrawMappedPatch(x, y-4, 0, facerankprefix[players[tab[i].num].skin], colormap);
+ /*if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] > 0) -- not enough space for this
+ {
+ INT32 bumperx = x+19;
+ V_DrawMappedPatch(bumperx-2, y-4, 0, kp_tinybumper[0], colormap);
+ for (j = 1; j < players[tab[i].num].kartstuff[k_bumper]; j++)
+ {
+ bumperx += 5;
+ V_DrawMappedPatch(bumperx, y-4, 0, kp_tinybumper[1], colormap);
+ }
+ }*/
+ }
+
+ if (tab[i].num == whiteplayer)
+ V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]);
+
+ if (G_BattleGametype() && players[tab[i].num].kartstuff[k_bumper] <= 0)
+ V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers);
+ else
+ {
+ INT32 pos = players[tab[i].num].kartstuff[k_position];
+ if (pos < 0 || pos > MAXPLAYERS)
+ pos = 0;
+ // Draws the little number over the face
+ V_DrawScaledPatch(x-5, y+6, 0, kp_facenum[pos]);
+ }
+
+ if (G_RaceGametype())
+ {
+#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time))
+ if (scorelines > 8)
+ {
+ if (players[tab[i].num].exiting)
+ V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime));
+ else if (players[tab[i].num].pflags & PF_TIMEOVER)
+ V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, "NO CONTEST.");
+ else if (circuitmap)
+ V_DrawRightAlignedThinString(x+rightoffset, y-1, V_6WIDTHSPACE, va("Lap %d", tab[i].count));
+ }
+ else
+ {
+ if (players[tab[i].num].exiting)
+ V_DrawRightAlignedString(x+rightoffset, y, hilicol, timestring(players[tab[i].num].realtime));
+ else if (players[tab[i].num].pflags & PF_TIMEOVER)
+ V_DrawRightAlignedThinString(x+rightoffset, y-1, 0, "NO CONTEST.");
+ else if (circuitmap)
+ V_DrawRightAlignedString(x+rightoffset, y, 0, va("Lap %d", tab[i].count));
+ }
+#undef timestring
+ }
+ else
+ V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count));
+
+ y += 18;
+ if (i == 7)
+ {
+ y = 33;
+ x = (BASEVIDWIDTH/2) + 4;
+ }
+ }
+}
+
+static void K_drawKartLaps(void)
+{
+ INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT);
+ INT32 fx = 0, fy = 0, fflags = 0; // stuff for 3p / 4p splitscreen.
+ boolean flipstring = false; // used for 3p or 4p
+ INT32 stringw = 0; // used with the above
+
+ if (splitscreen > 1)
+ {
+
+ // pain and suffering defined below
+ if (splitscreen < 2) // don't change shit for THIS splitscreen.
+ {
+ fx = LAPS_X;
+ fy = LAPS_Y;
+ fflags = splitflags;
+ }
+ else
+ {
+ if (stplyr == &players[displayplayer] || stplyr == &players[thirddisplayplayer]) // If we are P1 or P3...
+ {
+ fx = LAPS_X;
+ fy = LAPS_Y;
+ fflags = V_SNAPTOLEFT|((stplyr == &players[thirddisplayplayer]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom.
+ }
+ else // else, that means we're P2 or P4.
+ {
+ fx = LAPS2_X;
+ fy = LAPS2_Y;
+ fflags = V_SNAPTORIGHT|((stplyr == &players[fourthdisplayplayer]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom
+ flipstring = true; // make the string right aligned and other shit
+ }
+ }
+
+
+
+ if (stplyr->exiting) // draw stuff as god intended.
+ {
+ V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, kp_splitlapflag);
+ V_DrawString(fx+13, fy+1, V_HUDTRANS|fflags, "FIN");
+ }
+ else // take flipstring into account here since we may have more laps than just 10
+ if (flipstring)
+ {
+ stringw = V_StringWidth(va("%d/%d", stplyr->laps+1, cv_numlaps.value), 0);
+
+ V_DrawScaledPatch(BASEVIDWIDTH-stringw-16, fy, V_HUDTRANS|fflags, kp_splitlapflag);
+ V_DrawRightAlignedString(BASEVIDWIDTH-3, fy+1, V_HUDTRANS|fflags, va("%d/%d", stplyr->laps+1, cv_numlaps.value));
+ }
+ else // draw stuff NORMALLY.
+ {
+ V_DrawScaledPatch(fx, fy, V_HUDTRANS|fflags, kp_splitlapflag);
+ V_DrawString(fx+13, fy+1, V_HUDTRANS|fflags, va("%d/%d", stplyr->laps+1, cv_numlaps.value));
+ }
+ }
+ else
+ {
+ V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_lapsticker);
+
+ if (stplyr->exiting)
+ V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, "FIN");
+ else
+ V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps+1, cv_numlaps.value));
+ }
+}
+
+static void K_drawKartSpeedometer(void)
+{
+ fixed_t convSpeed;
+ INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT);
+
+ if (cv_kartspeedometer.value == 1) // Kilometers
+ {
+ convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale)/FRACUNIT; // 2.172409058
+ V_DrawKartString(SPDM_X, SPDM_Y, V_HUDTRANS|splitflags, va("%3d km/h", convSpeed));
+ }
+ else if (cv_kartspeedometer.value == 2) // Miles
+ {
+ convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale)/FRACUNIT; // 1.349868774
+ V_DrawKartString(SPDM_X, SPDM_Y, V_HUDTRANS|splitflags, va("%3d mph", convSpeed));
+ }
+ else if (cv_kartspeedometer.value == 3) // Fracunits
+ {
+ convSpeed = FixedDiv(stplyr->speed, mapobjectscale)/FRACUNIT;
+ V_DrawKartString(SPDM_X, SPDM_Y, V_HUDTRANS|splitflags, va("%3d fu/t", convSpeed));
+ }
+}
+
+static void K_drawKartBumpersOrKarma(void)
+{
+ UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE);
+ INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT);
+ INT32 fx = 0, fy = 0, fflags = 0;
+ boolean flipstring = false; // same as laps, used for splitscreen
+ INT32 stringw = 0; // used with the above
+
+ if (splitscreen > 1)
+ {
+
+ // we will reuse lap coords here since it's essentially the same shit.
+
+ if (stplyr == &players[displayplayer] || stplyr == &players[thirddisplayplayer]) // If we are P1 or P3...
+ {
+ fx = LAPS_X;
+ fy = LAPS_Y;
+ fflags = V_SNAPTOLEFT|((stplyr == &players[thirddisplayplayer]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P3 to the bottom.
+ }
+ else // else, that means we're P2 or P4.
+ {
+ fx = LAPS2_X;
+ fy = LAPS2_Y;
+ fflags = V_SNAPTORIGHT|((stplyr == &players[fourthdisplayplayer]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom
+ flipstring = true;
+ }
+
+ if (stplyr->kartstuff[k_bumper] <= 0)
+ {
+ V_DrawMappedPatch(fx, fy-1, V_HUDTRANS|fflags, kp_splitkarmabomb, colormap);
+ V_DrawString(fx+13, fy+1, V_HUDTRANS|fflags, va("%d/2", stplyr->kartstuff[k_comebackpoints]));
+ }
+ else // the above doesn't need to account for weird stuff since the max amount of karma necessary is always 2 ^^^^
+ {
+ if (flipstring) // for p2 and p4, assume we can have more than 10 bumpers. It's retarded but who knows.
+ {
+ stringw = V_StringWidth(va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value), 0);
+
+ V_DrawMappedPatch(BASEVIDWIDTH-stringw-16, fy-1, V_HUDTRANS|fflags, kp_rankbumper, colormap);
+ V_DrawRightAlignedString(BASEVIDWIDTH-3, fy+1, V_HUDTRANS|fflags, va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value));
+ }
+ else // draw bumpers normally.
+ {
+ V_DrawMappedPatch(fx, fy-1, V_HUDTRANS|fflags, kp_rankbumper, colormap);
+ V_DrawString(fx+13, fy+1, V_HUDTRANS|fflags, va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value));
+ }
+ }
+ }
+ else
+ {
+ if (stplyr->kartstuff[k_bumper] <= 0)
+ {
+ V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_karmasticker, colormap);
+ V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/2", stplyr->kartstuff[k_comebackpoints]));
+ }
+ else
+ {
+ if (stplyr->kartstuff[k_bumper] > 9 && cv_kartbumpers.value > 9)
+ V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumperstickerwide, colormap);
+ else
+ V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|splitflags, kp_bumpersticker, colormap);
+ V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->kartstuff[k_bumper], cv_kartbumpers.value));
+ }
+ }
+}
+
+static fixed_t K_FindCheckX(fixed_t px, fixed_t py, angle_t ang, fixed_t mx, fixed_t my)
+{
+ fixed_t dist, x;
+ fixed_t range = RING_DIST/3;
+ angle_t diff;
+
+ range *= gamespeed+1;
+
+ dist = abs(R_PointToDist2(px, py, mx, my));
+ if (dist > range)
+ return -320;
+
+ diff = R_PointToAngle2(px, py, mx, my) - ang;
+
+ if (diff < ANGLE_90 || diff > ANGLE_270)
+ return -320;
+ else
+ x = (FixedMul(FINETANGENT(((diff+ANGLE_90)>>ANGLETOFINESHIFT) & 4095), 160<>FRACBITS;
+
+ if (encoremode)
+ x = 320-x;
+
+ if (splitscreen > 1)
+ x /= 2;
+
+ return x;
+}
+
+static void K_drawKartWanted(void)
+{
+ UINT8 i, numwanted = 0;
+ UINT8 *colormap = NULL;
+ INT32 basex = 0, basey = 0;
+
+ if (stplyr != &players[displayplayer])
+ return;
+
+ for (i = 0; i < 4; i++)
+ {
+ if (battlewanted[i] == -1)
+ break;
+ numwanted++;
+ }
+
+ if (numwanted <= 0)
+ return;
+
+ // set X/Y coords depending on splitscreen.
+ if (splitscreen < 3) // 1P and 2P use the same code.
+ {
+ basex = WANT_X;
+ basey = WANT_Y;
+ if (splitscreen == 2)
+ {
+ basey += 16; // slight adjust for 3P
+ basex -= 6;
+ }
+ }
+ else if (splitscreen == 3) // 4P splitscreen...
+ {
+ basex = BASEVIDWIDTH/2 - (SHORT(kp_wantedsplit->width)/2); // center on screen
+ basey = BASEVIDHEIGHT - 55;
+ //basey2 = 4;
+ }
+
+ if (battlewanted[0] != -1)
+ colormap = R_GetTranslationColormap(0, players[battlewanted[0]].skincolor, GTC_CACHE);
+ V_DrawFixedPatch(basex< 1 ? kp_wantedsplit : kp_wanted), colormap);
+ /*if (basey2)
+ V_DrawFixedPatch(basex< 1 ? 13 : 8), y = basey+(splitscreen > 1 ? 16 : 21);
+ fixed_t scale = FRACUNIT/2;
+ player_t *p = &players[battlewanted[i]];
+
+ if (battlewanted[i] == -1)
+ break;
+
+ if (numwanted == 1)
+ scale = FRACUNIT;
+ else
+ {
+ if (i & 1)
+ x += 16;
+ if (i > 1)
+ y += 16;
+ }
+
+ if (players[battlewanted[i]].skincolor)
+ {
+ colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE);
+ V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap);
+ /*if (basey2) // again with 4p stuff
+ V_DrawFixedPatch(x<skin] : facerankprefix[p->skin]), colormap);*/
+ }
+ }
+}
+
+static void K_drawKartPlayerCheck(void)
+{
+ INT32 i;
+ UINT8 *colormap;
+ INT32 x;
+
+ INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM);
+
+ if (!stplyr->mo || stplyr->spectator)
+ return;
+
+ if (stplyr->awayviewtics)
+ return;
+
+ if (camspin)
+ return;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ UINT8 pnum = 0;
+
+ if (&players[i] == stplyr)
+ continue;
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+ if (!players[i].mo)
+ continue;
+
+ if ((players[i].kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2))
+ pnum++; // white frames
+
+ if (players[i].kartstuff[k_itemtype] == KITEM_GROW || players[i].kartstuff[k_growshrinktimer] > 0)
+ pnum += 4;
+ else if (players[i].kartstuff[k_itemtype] == KITEM_INVINCIBILITY || players[i].kartstuff[k_invincibilitytimer])
+ pnum += 2;
+
+ x = K_FindCheckX(stplyr->mo->x, stplyr->mo->y, stplyr->mo->angle, players[i].mo->x, players[i].mo->y);
+ if (x <= 320 && x >= 0)
+ {
+ if (x < 14)
+ x = 14;
+ else if (x > 306)
+ x = 306;
+
+ colormap = R_GetTranslationColormap(TC_DEFAULT, players[i].mo->color, GTC_CACHE);
+ V_DrawMappedPatch(x, CHEK_Y, V_HUDTRANS|splitflags, kp_check[pnum], colormap);
+ }
+ }
+}
+
+static void K_drawKartMinimapHead(mobj_t *mo, INT32 x, INT32 y, INT32 flags, patch_t *AutomapPic)
+{
+ // amnum xpos & ypos are the icon's speed around the HUD.
+ // The number being divided by is for how fast it moves.
+ // The higher the number, the slower it moves.
+
+ // am xpos & ypos are the icon's starting position. Withouht
+ // it, they wouldn't 'spawn' on the top-right side of the HUD.
+
+ UINT8 skin = 0;
+
+ fixed_t amnumxpos, amnumypos;
+ INT32 amxpos, amypos;
+
+ node_t *bsp = &nodes[numnodes-1];
+ fixed_t maxx, minx, maxy, miny;
+
+ fixed_t mapwidth, mapheight;
+ fixed_t xoffset, yoffset;
+ fixed_t xscale, yscale, zoom;
+
+ if (mo->skin)
+ skin = ((skin_t*)mo->skin)-skins;
+
+ maxx = maxy = INT32_MAX;
+ minx = miny = INT32_MIN;
+ minx = bsp->bbox[0][BOXLEFT];
+ maxx = bsp->bbox[0][BOXRIGHT];
+ miny = bsp->bbox[0][BOXBOTTOM];
+ maxy = bsp->bbox[0][BOXTOP];
+
+ if (bsp->bbox[1][BOXLEFT] < minx)
+ minx = bsp->bbox[1][BOXLEFT];
+ if (bsp->bbox[1][BOXRIGHT] > maxx)
+ maxx = bsp->bbox[1][BOXRIGHT];
+ if (bsp->bbox[1][BOXBOTTOM] < miny)
+ miny = bsp->bbox[1][BOXBOTTOM];
+ if (bsp->bbox[1][BOXTOP] > maxy)
+ maxy = bsp->bbox[1][BOXTOP];
+
+ // You might be wondering why these are being bitshift here
+ // it's because mapwidth and height would otherwise overflow for maps larger than half the size possible...
+ // map boundaries and sizes will ALWAYS be whole numbers thankfully
+ // later calculations take into consideration that these are actually not in terms of FRACUNIT though
+ minx >>= FRACBITS;
+ maxx >>= FRACBITS;
+ miny >>= FRACBITS;
+ maxy >>= FRACBITS;
+
+ mapwidth = maxx - minx;
+ mapheight = maxy - miny;
+
+ // These should always be small enough to be bitshift back right now
+ xoffset = (minx + mapwidth/2)<width, mapwidth);
+ yscale = FixedDiv(AutomapPic->height, mapheight);
+ zoom = FixedMul(min(xscale, yscale), FRACUNIT-FRACUNIT/20);
+
+ amnumxpos = (FixedMul(mo->x, zoom) - FixedMul(xoffset, zoom));
+ amnumypos = -(FixedMul(mo->y, zoom) - FixedMul(yoffset, zoom));
+
+ if (encoremode)
+ amnumxpos = -amnumxpos;
+
+ amxpos = amnumxpos + ((x + AutomapPic->width/2 - (facemmapprefix[skin]->width/2))<height/2 - (facemmapprefix[skin]->height/2))<width/2 + (facemmapprefix[skin]->width/2))<color) // 'default' color
+ V_DrawSciencePatch(amxpos, amypos, flags, facemmapprefix[skin], FRACUNIT);
+ else
+ {
+ UINT8 *colormap;
+ if (mo->colorized)
+ colormap = R_GetTranslationColormap(TC_RAINBOW, mo->color, GTC_CACHE);
+ else
+ colormap = R_GetTranslationColormap(skin, mo->color, GTC_CACHE);
+ V_DrawFixedPatch(amxpos, amypos, FRACUNIT, flags, facemmapprefix[skin], colormap);
+ if (mo->player
+ && ((G_RaceGametype() && mo->player->kartstuff[k_position] == spbplace)
+ || (G_BattleGametype() && K_IsPlayerWanted(mo->player))))
+ {
+ V_DrawFixedPatch(amxpos - (4<width/2);
+ y = MINI_Y - (AutomapPic->height/2);
+
+ if (timeinmap > 105)
+ {
+ minimaptrans = cv_kartminimap.value;
+ if (timeinmap <= 113)
+ minimaptrans = ((((INT32)timeinmap) - 105)*minimaptrans)/(113-105);
+ if (!minimaptrans)
+ return;
+ }
+ else
+ return;
+
+ minimaptrans = ((10-minimaptrans)<width), y, splitflags|V_FLIP, AutomapPic);
+ else
+ V_DrawScaledPatch(x, y, splitflags, AutomapPic);
+
+ if (!(splitscreen == 2))
+ {
+ splitflags &= ~minimaptrans;
+ splitflags |= V_HUDTRANSHALF;
+ }
+
+ // let offsets transfer to the heads, too!
+ if (encoremode)
+ x += SHORT(AutomapPic->leftoffset);
+ else
+ x -= SHORT(AutomapPic->leftoffset);
+ y -= SHORT(AutomapPic->topoffset);
+
+ // initialize
+ for (i = 0; i < 4; i++)
+ localplayers[i] = -1;
+
+ // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen)
+ if (ghosts)
+ {
+ demoghost *g = ghosts;
+ while (g)
+ {
+ K_drawKartMinimapHead(g->mo, x, y, splitflags, AutomapPic);
+ g = g->next;
+ }
+
+ if (!stplyr->mo || stplyr->spectator) // do we need the latter..?
+ return;
+
+ localplayers[numlocalplayers] = stplyr-players;
+ numlocalplayers++;
+ }
+ else
+ {
+ for (i = MAXPLAYERS-1; i >= 0; i--)
+ {
+ if (!playeringame[i])
+ continue;
+ if (!players[i].mo || players[i].spectator)
+ continue;
+
+ if (i != displayplayer || splitscreen)
+ {
+ if (G_BattleGametype() && players[i].kartstuff[k_bumper] <= 0)
+ continue;
+
+ if (players[i].kartstuff[k_hyudorotimer] > 0)
+ {
+ if (!((players[i].kartstuff[k_hyudorotimer] < 1*TICRATE/2
+ || players[i].kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2))
+ && !(leveltime & 1)))
+ continue;
+ }
+ }
+
+ if (i == displayplayer || i == secondarydisplayplayer || i == thirddisplayplayer || i == fourthdisplayplayer)
+ {
+ // Draw display players on top of everything else
+ localplayers[numlocalplayers] = i;
+ numlocalplayers++;
+ continue;
+ }
+
+ K_drawKartMinimapHead(players[i].mo, x, y, splitflags, AutomapPic);
+ }
+ }
+
+ // draw our local players here, opaque.
+ splitflags &= ~V_HUDTRANSHALF;
+ splitflags |= V_HUDTRANS;
+
+ for (i = 0; i < numlocalplayers; i++)
+ {
+ if (i == -1)
+ continue; // this doesn't interest us
+ K_drawKartMinimapHead(players[localplayers[i]].mo, x, y, splitflags, AutomapPic);
+ }
+}
+
+static void K_drawKartStartCountdown(void)
+{
+ INT32 pnum = 0, splitflags = K_calcSplitFlags(0); // 3
+
+ if (leveltime >= starttime-(2*TICRATE)) // 2
+ pnum++;
+ if (leveltime >= starttime-TICRATE) // 1
+ pnum++;
+ if (leveltime >= starttime) // GO!
+ pnum++;
+ if ((leveltime % (2*5)) / 5) // blink
+ pnum += 4;
+ if (splitscreen) // splitscreen
+ pnum += 8;
+
+ V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), splitflags, kp_startcountdown[pnum]);
+}
+
+static void K_drawKartFinish(void)
+{
+ INT32 pnum = 0, splitflags = K_calcSplitFlags(0);
+
+ if (!stplyr->kartstuff[k_cardanimation] || stplyr->kartstuff[k_cardanimation] >= 2*TICRATE)
+ return;
+
+ if ((stplyr->kartstuff[k_cardanimation] % (2*5)) / 5) // blink
+ pnum = 1;
+
+ if (splitscreen > 1) // 3/4p, stationary FIN
+ {
+ pnum += 2;
+ V_DrawScaledPatch(STCD_X - (SHORT(kp_racefinish[pnum]->width)/2), STCD_Y - (SHORT(kp_racefinish[pnum]->height)/2), splitflags, kp_racefinish[pnum]);
+ return;
+ }
+
+ //else -- 1/2p, scrolling FINISH
+ {
+ INT32 x, xval;
+
+ if (splitscreen) // wide splitscreen
+ pnum += 4;
+
+ x = ((vid.width<width)<kartstuff[k_cardanimation])*(xval > x ? xval : x))/TICRATE;
+
+ if (splitscreen && stplyr == &players[secondarydisplayplayer])
+ x = -x;
+
+ V_DrawFixedPatch(x + (STCD_X<>1),
+ (STCD_Y<height)<<(FRACBITS-1)),
+ FRACUNIT,
+ splitflags, kp_racefinish[pnum], NULL);
+ }
+}
+
+static void K_drawBattleFullscreen(void)
+{
+ INT32 x = BASEVIDWIDTH/2;
+ INT32 y = -64+(stplyr->kartstuff[k_cardanimation]); // card animation goes from 0 to 164, 164 is the middle of the screen
+ INT32 splitflags = V_SNAPTOTOP; // I don't feel like properly supporting non-green resolutions, so you can have a misuse of SNAPTO instead
+ fixed_t scale = FRACUNIT;
+
+ if (splitscreen)
+ {
+ if ((splitscreen == 1 && stplyr == &players[secondarydisplayplayer])
+ || (splitscreen > 1 && (stplyr == &players[thirddisplayplayer]
+ || (stplyr == &players[fourthdisplayplayer] && splitscreen > 2))))
+ {
+ y = 232-(stplyr->kartstuff[k_cardanimation]/2);
+ splitflags = V_SNAPTOBOTTOM;
+ }
+ else
+ y = -32+(stplyr->kartstuff[k_cardanimation]/2);
+
+ if (splitscreen > 1)
+ {
+ scale /= 2;
+
+ if (stplyr == &players[secondarydisplayplayer]
+ || (stplyr == &players[fourthdisplayplayer] && splitscreen > 2))
+ x = 3*BASEVIDWIDTH/4;
+ else
+ x = BASEVIDWIDTH/4;
+ }
+ else
+ {
+ if (stplyr->exiting)
+ {
+ if (stplyr == &players[secondarydisplayplayer])
+ x = BASEVIDWIDTH-96;
+ else
+ x = 96;
+ }
+ else
+ scale /= 2;
+ }
+ }
+
+ if (stplyr->exiting)
+ {
+ if (stplyr == &players[displayplayer])
+ V_DrawFadeScreen(0xFF00, 16);
+ if (stplyr->exiting < 6*TICRATE && !stplyr->spectator)
+ {
+ if (stplyr->kartstuff[k_position] == 1)
+ V_DrawFixedPatch(x<kartstuff[k_bumper] <= 0 && stplyr->kartstuff[k_comebacktimer] && comeback && !stplyr->spectator)
+ {
+ UINT16 t = stplyr->kartstuff[k_comebacktimer]/(10*TICRATE);
+ INT32 txoff, adjust = (splitscreen > 1) ? 4 : 6; // normal string is 8, kart string is 12, half of that for ease
+ INT32 ty = (BASEVIDHEIGHT/2)+66;
+
+ txoff = adjust;
+
+ while (t)
+ {
+ txoff += adjust;
+ t /= 10;
+ }
+
+ if (splitscreen)
+ {
+ if (splitscreen > 1)
+ ty = (BASEVIDHEIGHT/4)+33;
+ if ((splitscreen == 1 && stplyr == &players[secondarydisplayplayer])
+ || (stplyr == &players[thirddisplayplayer] && splitscreen > 1)
+ || (stplyr == &players[fourthdisplayplayer] && splitscreen > 2))
+ ty += (BASEVIDHEIGHT/2);
+ }
+ else
+ V_DrawFadeScreen(0xFF00, 16);
+
+ if (!comebackshowninfo)
+ V_DrawFixedPatch(x< 1)
+ V_DrawString(x-txoff, ty, 0, va("%d", stplyr->kartstuff[k_comebacktimer]/TICRATE));
+ else
+ {
+ V_DrawFixedPatch(x<kartstuff[k_comebacktimer]/TICRATE));
+ }
+ }
+
+ if (netgame && !stplyr->spectator && timeinmap > 113) // FREE PLAY?
+ {
+ UINT8 i;
+
+ // check to see if there's anyone else at all
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (i == displayplayer)
+ continue;
+ if (playeringame[i] && !stplyr->spectator)
+ return;
+ }
+
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_freeplay))
+#endif
+ K_drawKartFreePlay(leveltime);
+ }
+}
+
+static void K_drawKartFirstPerson(void)
+{
+ static INT32 pnum[4], turn[4], drift[4];
+ INT32 pn = 0, tn = 0, dr = 0;
+ INT32 target = 0, splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM);
+ INT32 x = BASEVIDWIDTH/2, y = BASEVIDHEIGHT;
+ fixed_t scale;
+ UINT8 *colmap = NULL;
+ ticcmd_t *cmd = &stplyr->cmd;
+
+ if (stplyr->spectator || !stplyr->mo || (stplyr->mo->flags2 & MF2_DONTDRAW))
+ return;
+
+ if (stplyr == &players[secondarydisplayplayer] && splitscreen)
+ { pn = pnum[1]; tn = turn[1]; dr = drift[1]; }
+ else if (stplyr == &players[thirddisplayplayer] && splitscreen > 1)
+ { pn = pnum[2]; tn = turn[2]; dr = drift[2]; }
+ else if (stplyr == &players[fourthdisplayplayer] && splitscreen > 2)
+ { pn = pnum[3]; tn = turn[3]; dr = drift[3]; }
+ else
+ { pn = pnum[0]; tn = turn[0]; dr = drift[0]; }
+
+ if (splitscreen)
+ {
+ y >>= 1;
+ if (splitscreen > 1)
+ x >>= 1;
+ }
+
+ {
+ if (stplyr->speed < FixedMul(stplyr->runspeed, stplyr->mo->scale) && (leveltime & 1) && !splitscreen)
+ y++;
+ // the following isn't EXPLICITLY right, it just gets the result we want, but i'm too lazy to look up the right way to do it
+ if (stplyr->mo->flags2 & MF2_SHADOW)
+ splitflags |= FF_TRANS80;
+ else if (stplyr->mo->frame & FF_TRANSMASK)
+ splitflags |= (stplyr->mo->frame & FF_TRANSMASK);
+ }
+
+ if (cmd->driftturn > 400) // strong left turn
+ target = 2;
+ else if (cmd->driftturn < -400) // strong right turn
+ target = -2;
+ else if (cmd->driftturn > 0) // weak left turn
+ target = 1;
+ else if (cmd->driftturn < 0) // weak right turn
+ target = -1;
+ else // forward
+ target = 0;
+
+ if (encoremode)
+ target = -target;
+
+ if (pn < target)
+ pn++;
+ else if (pn > target)
+ pn--;
+
+ if (pn < 0)
+ splitflags |= V_FLIP; // right turn
+
+ target = abs(pn);
+ if (target > 2)
+ target = 2;
+
+ x <<= FRACBITS;
+ y <<= FRACBITS;
+
+ if (tn != cmd->driftturn/50)
+ tn -= (tn - (cmd->driftturn/50))/8;
+
+ if (dr != stplyr->kartstuff[k_drift]*16)
+ dr -= (dr - (stplyr->kartstuff[k_drift]*16))/8;
+
+ if (splitscreen == 1)
+ {
+ scale = (2*FRACUNIT)/3;
+ y += FRACUNIT/(vid.dupx < vid.dupy ? vid.dupx : vid.dupy); // correct a one-pixel gap on the screen view (not the basevid view)
+ }
+ else if (splitscreen)
+ scale = FRACUNIT/2;
+ else
+ scale = FRACUNIT;
+
+ if (stplyr->mo)
+ {
+ INT32 dsone = K_GetKartDriftSparkValue(stplyr);
+ INT32 dstwo = dsone*2;
+ INT32 dsthree = dstwo*2;
+
+#ifndef DONTLIKETOASTERSFPTWEAKS
+ {
+ const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->frameangle;
+ // yes, the following is correct. no, you do not need to swap the x and y.
+ fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2);
+ fixed_t yoffs = -(P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT) - 4*FRACUNIT);
+
+ if (splitscreen)
+ xoffs = FixedMul(xoffs, scale);
+
+ xoffs -= (tn)*scale;
+ xoffs -= (dr)*scale;
+
+ if (stplyr->frameangle == stplyr->mo->angle)
+ {
+ const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale);
+
+ if (mag < FRACUNIT)
+ {
+ xoffs = FixedMul(xoffs, mag);
+ if (!splitscreen)
+ yoffs = FixedMul(yoffs, mag);
+ }
+ }
+
+ if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if!
+ yoffs += stplyr->mo->momz/3;
+
+ if (encoremode)
+ x -= xoffs;
+ else
+ x += xoffs;
+ if (!splitscreen)
+ y += yoffs;
+ }
+
+ // drift sparks!
+ if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dsthree))
+ colmap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), GTC_CACHE);
+ else if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dstwo))
+ colmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_KETCHUP, GTC_CACHE);
+ else if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dsone))
+ colmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SAPPHIRE, GTC_CACHE);
+ else
+#endif
+ // invincibility/grow/shrink!
+ if (stplyr->mo->colorized && stplyr->mo->color)
+ colmap = R_GetTranslationColormap(TC_RAINBOW, stplyr->mo->color, GTC_CACHE);
+ }
+
+ V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap);
+
+ if (stplyr == &players[secondarydisplayplayer] && splitscreen)
+ { pnum[1] = pn; turn[1] = tn; drift[1] = dr; }
+ else if (stplyr == &players[thirddisplayplayer] && splitscreen > 1)
+ { pnum[2] = pn; turn[2] = tn; drift[2] = dr; }
+ else if (stplyr == &players[fourthdisplayplayer] && splitscreen > 2)
+ { pnum[3] = pn; turn[3] = tn; drift[3] = dr; }
+ else
+ { pnum[0] = pn; turn[0] = tn; drift[0] = dr; }
+}
+
+// doesn't need to ever support 4p
+static void K_drawInput(void)
+{
+ static INT32 pn = 0;
+ INT32 target = 0, splitflags = (V_SNAPTOBOTTOM|V_SNAPTORIGHT);
+ INT32 x = BASEVIDWIDTH - 32, y = BASEVIDHEIGHT-24, offs, col;
+ const INT32 accent1 = splitflags|colortranslations[stplyr->skincolor][5];
+ const INT32 accent2 = splitflags|colortranslations[stplyr->skincolor][9];
+ ticcmd_t *cmd = &stplyr->cmd;
+
+ if (timeinmap <= 105)
+ return;
+
+ if (timeinmap < 113)
+ {
+ INT32 count = ((INT32)(timeinmap) - 105);
+ offs = (titledemo ? 128 : 64);
+ while (count-- > 0)
+ offs >>= 1;
+ x += offs;
+ }
+
+ if (titledemo)
+ {
+ V_DrawTinyScaledPatch(x-54, 128, splitflags, W_CachePatchName("TTKBANNR", PU_CACHE));
+ V_DrawTinyScaledPatch(x-54, 128+25, splitflags, W_CachePatchName("TTKART", PU_CACHE));
+ return;
+ }
+
+#define BUTTW 8
+#define BUTTH 11
+
+#define drawbutt(xoffs, butt, symb)\
+ if (stplyr->cmd.buttons & butt)\
+ {\
+ offs = 2;\
+ col = accent1;\
+ }\
+ else\
+ {\
+ offs = 0;\
+ col = accent2;\
+ V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\
+ }\
+ V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\
+ V_DrawFixedPatch((x+1+(xoffs))<driftturn) // no turn
+ target = 0;
+ else // turning of multiple strengths!
+ {
+ target = ((abs(cmd->driftturn) - 1)/125)+1;
+ if (target > 4)
+ target = 4;
+ if (cmd->driftturn < 0)
+ target = -target;
+ }
+
+ if (pn != target)
+ {
+ if (abs(pn - target) == 1)
+ pn = target;
+ else if (pn < target)
+ pn += 2;
+ else //if (pn > target)
+ pn -= 2;
+ }
+
+ if (pn < 0)
+ {
+ splitflags |= V_FLIP; // right turn
+ x--;
+ }
+
+ target = abs(pn);
+ if (target > 4)
+ target = 4;
+
+ if (!stplyr->skincolor)
+ V_DrawFixedPatch(x<skincolor, GTC_CACHE);
+ V_DrawFixedPatch(x<kartstuff[k_lapanimation];
+ UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE);
+
+ V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->kartstuff[k_lapanimation]-76)))*FRACUNIT,
+ (48 - (32*max(0, progress-76)))*FRACUNIT,
+ FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
+ (modeattacking ? kp_lapanim_emblem[1] : kp_lapanim_emblem[0]), colormap);
+
+ if (stplyr->kartstuff[k_laphand] >= 1 && stplyr->kartstuff[k_laphand] <= 3)
+ {
+ V_DrawFixedPatch((BASEVIDWIDTH/2 + (32*max(0, stplyr->kartstuff[k_lapanimation]-76)))*FRACUNIT,
+ (48 - (32*max(0, progress-76))
+ + 4 - abs((signed)((leveltime % 8) - 4)))*FRACUNIT,
+ FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
+ kp_lapanim_hand[stplyr->kartstuff[k_laphand]-1], NULL);
+ }
+
+ if (stplyr->laps == (UINT8)(cv_numlaps.value - 1))
+ {
+ V_DrawFixedPatch((62 - (32*max(0, progress-76)))*FRACUNIT, // 27
+ 30*FRACUNIT, // 24
+ FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
+ kp_lapanim_final[min(progress/2, 10)], NULL);
+
+ if (progress/2-12 >= 0)
+ {
+ V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194
+ 30*FRACUNIT, // 24
+ FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
+ kp_lapanim_lap[min(progress/2-12, 6)], NULL);
+ }
+ }
+ else
+ {
+ V_DrawFixedPatch((82 - (32*max(0, progress-76)))*FRACUNIT, // 61
+ 30*FRACUNIT, // 24
+ FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
+ kp_lapanim_lap[min(progress/2, 6)], NULL);
+
+ if (progress/2-8 >= 0)
+ {
+ V_DrawFixedPatch((188 + (32*max(0, progress-76)))*FRACUNIT, // 194
+ 30*FRACUNIT, // 24
+ FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
+ kp_lapanim_number[(((UINT32)stplyr->laps+1) / 10)][min(progress/2-8, 2)], NULL);
+
+ if (progress/2-10 >= 0)
+ {
+ V_DrawFixedPatch((208 + (32*max(0, progress-76)))*FRACUNIT, // 221
+ 30*FRACUNIT, // 24
+ FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
+ kp_lapanim_number[(((UINT32)stplyr->laps+1) % 10)][min(progress/2-10, 2)], NULL);
+ }
+ }
+ }
+}
+
+void K_drawKartFreePlay(UINT32 flashtime)
+{
+ // no splitscreen support because it's not FREE PLAY if you have more than one player in-game
+
+ if ((flashtime % TICRATE) < TICRATE/2)
+ return;
+
+ V_DrawKartString((BASEVIDWIDTH - (LAPS_X+1)) - (12*9), // mirror the laps thingy
+ LAPS_Y+3, V_SNAPTOBOTTOM|V_SNAPTORIGHT, "FREE PLAY");
+}
+
+static void K_drawDistributionDebugger(void)
+{
+ patch_t *items[NUMKARTRESULTS] = {
+ kp_sadface[1],
+ kp_sneaker[1],
+ kp_rocketsneaker[1],
+ kp_invincibility[7],
+ kp_banana[1],
+ kp_eggman[1],
+ kp_orbinaut[4],
+ kp_jawz[1],
+ kp_mine[1],
+ kp_ballhog[1],
+ kp_selfpropelledbomb[1],
+ kp_grow[1],
+ kp_shrink[1],
+ kp_thundershield[1],
+ kp_hyudoro[1],
+ kp_pogospring[1],
+ kp_kitchensink[1],
+
+ kp_sneaker[1],
+ kp_banana[1],
+ kp_banana[1],
+ kp_orbinaut[4],
+ kp_orbinaut[4],
+ kp_jawz[1]
+ };
+ INT32 useodds = 0;
+ INT32 pingame = 0, bestbumper = 0;
+ INT32 i;
+ INT32 x = -9, y = -9;
+ boolean dontforcespb = false;
+ boolean spbrush = false;
+
+ if (stplyr != &players[displayplayer]) // only for p1
+ return;
+
+ // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+ pingame++;
+ if (players[i].exiting)
+ dontforcespb = true;
+ if (players[i].kartstuff[k_bumper] > bestbumper)
+ bestbumper = players[i].kartstuff[k_bumper];
+ }
+
+ if (G_RaceGametype())
+ spbrush = (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1);
+
+ useodds = K_FindUseodds(stplyr, 0, pingame, bestbumper, spbrush, dontforcespb);
+
+ for (i = 1; i < NUMKARTRESULTS; i++)
+ {
+ const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush);
+ if (itemodds <= 0)
+ continue;
+
+ V_DrawScaledPatch(x, y, V_HUDTRANS|V_SNAPTOTOP, items[i]);
+ V_DrawThinString(x+11, y+31, V_HUDTRANS|V_SNAPTOTOP, va("%d", itemodds));
+
+ // Display amount for multi-items
+ if (i >= NUMKARTITEMS)
+ {
+ INT32 amount;
+ switch (i)
+ {
+ case KRITEM_TENFOLDBANANA:
+ amount = 10;
+ break;
+ case KRITEM_QUADORBINAUT:
+ amount = 4;
+ break;
+ case KRITEM_DUALJAWZ:
+ amount = 2;
+ break;
+ default:
+ amount = 3;
+ break;
+ }
+ V_DrawString(x+24, y+31, V_ALLOWLOWERCASE|V_HUDTRANS|V_SNAPTOTOP, va("x%d", amount));
+ }
+
+ x += 32;
+ if (x >= 297)
+ {
+ x = -9;
+ y += 32;
+ }
+ }
+
+ V_DrawString(0, 0, V_HUDTRANS|V_SNAPTOTOP, va("USEODDS %d", useodds));
+}
+
+static void K_drawCheckpointDebugger(void)
+{
+ if (stplyr != &players[displayplayer]) // only for p1
+ return;
+
+ if (stplyr->starpostnum >= (numstarposts - (numstarposts/2)))
+ V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Can finish)", stplyr->starpostnum, numstarposts));
+ else
+ V_DrawString(8, 184, 0, va("Checkpoint: %d / %d (Skip: %d)", stplyr->starpostnum, numstarposts, ((numstarposts/2) + stplyr->starpostnum)));
+ V_DrawString(8, 192, 0, va("Waypoint dist: Prev %d, Next %d", stplyr->kartstuff[k_prevcheck], stplyr->kartstuff[k_nextcheck]));
+}
+
+void K_drawKartHUD(void)
+{
+ boolean isfreeplay = false;
+ boolean battlefullscreen = false;
+
+ // Define the X and Y for each drawn object
+ // This is handled by console/menu values
+ K_initKartHUD();
+
+ // Draw that fun first person HUD! Drawn ASAP so it looks more "real".
+ if ((stplyr == &players[displayplayer] && !camera.chase)
+ || ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase)
+ || ((splitscreen > 1 && stplyr == &players[thirddisplayplayer]) && !camera3.chase)
+ || ((splitscreen > 2 && stplyr == &players[fourthdisplayplayer]) && !camera4.chase))
+ K_drawKartFirstPerson();
+
+ // Draw full screen stuff that turns off the rest of the HUD
+ if (mapreset && stplyr == &players[displayplayer])
+ {
+ K_drawChallengerScreen();
+ return;
+ }
+
+ battlefullscreen = ((G_BattleGametype())
+ && (stplyr->exiting
+ || (stplyr->kartstuff[k_bumper] <= 0
+ && stplyr->kartstuff[k_comebacktimer]
+ && comeback
+ && stplyr->playerstate == PST_LIVE)));
+
+ if (!battlefullscreen || splitscreen)
+ {
+ // Draw the CHECK indicator before the other items, so it's overlapped by everything else
+ if (cv_kartcheck.value && !splitscreen && !players[displayplayer].exiting)
+ K_drawKartPlayerCheck();
+
+ // Draw WANTED status
+ if (G_BattleGametype())
+ {
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_wanted))
+#endif
+ K_drawKartWanted();
+ }
+
+ if (cv_kartminimap.value && !titledemo)
+ {
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_minimap))
+#endif
+ K_drawKartMinimap();
+ }
+ }
+
+ if (battlefullscreen)
+ {
+ K_drawBattleFullscreen();
+ return;
+ }
+
+ // Draw the item window
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_item))
+#endif
+ K_drawKartItem();
+
+ // If not splitscreen, draw...
+ if (!splitscreen && !titledemo)
+ {
+ // Draw the timestamp
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_time))
+#endif
+ K_drawKartTimestamp(stplyr->realtime, TIME_X, TIME_Y, gamemap, 0);
+
+ if (!modeattacking)
+ {
+ // The top-four faces on the left
+ /*#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_minirankings))
+ #endif*/
+ isfreeplay = K_drawKartPositionFaces();
+ }
+ }
+
+ if (!stplyr->spectator) // Bottom of the screen elements, don't need in spectate mode
+ {
+ if (G_RaceGametype()) // Race-only elements
+ {
+ if (!titledemo)
+ {
+ // Draw the lap counter
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_gametypeinfo))
+#endif
+ K_drawKartLaps();
+
+ if (!splitscreen)
+ {
+ // Draw the speedometer
+ // TODO: Make a better speedometer.
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_speedometer))
+#endif
+ K_drawKartSpeedometer();
+ }
+ }
+
+ if (isfreeplay)
+ ;
+ else if (!modeattacking)
+ {
+ // Draw the numerical position
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_position))
+#endif
+ K_DrawKartPositionNum(stplyr->kartstuff[k_position]);
+ }
+ else //if (!(demoplayback && hu_showscores))
+ {
+ // Draw the input UI
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_position))
+#endif
+ K_drawInput();
+ }
+ }
+ else if (G_BattleGametype()) // Battle-only
+ {
+ // Draw the hits left!
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_gametypeinfo))
+#endif
+ K_drawKartBumpersOrKarma();
+ }
+ }
+
+ // Draw the countdowns after everything else.
+ if (leveltime >= starttime-(3*TICRATE)
+ && leveltime < starttime+TICRATE)
+ K_drawKartStartCountdown();
+ else if (countdown && (!splitscreen || !stplyr->exiting))
+ {
+ char *countstr = va("%d", countdown/TICRATE);
+
+ if (splitscreen > 1)
+ V_DrawCenteredString(BASEVIDWIDTH/4, LAPS_Y+1, K_calcSplitFlags(0), countstr);
+ else
+ {
+ INT32 karlen = strlen(countstr)*6; // half of 12
+ V_DrawKartString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, K_calcSplitFlags(0), countstr);
+ }
+ }
+
+ // Race overlays
+ if (G_RaceGametype())
+ {
+ if (stplyr->exiting)
+ K_drawKartFinish();
+ else if (stplyr->kartstuff[k_lapanimation] && !splitscreen)
+ K_drawLapStartAnim();
+ }
+
+ if (modeattacking) // everything after here is MP and debug only
+ return;
+
+ if (G_BattleGametype() && !splitscreen && (stplyr->kartstuff[k_yougotem] % 2)) // * YOU GOT EM *
+ V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem);
+
+ // Draw FREE PLAY.
+ if (isfreeplay && !stplyr->spectator && timeinmap > 113)
+ {
+#ifdef HAVE_BLUA
+ if (LUA_HudEnabled(hud_freeplay))
+#endif
+ K_drawKartFreePlay(leveltime);
+ }
+
+ if (cv_kartdebugdistribution.value)
+ K_drawDistributionDebugger();
+
+ if (cv_kartdebugcheckpoint.value)
+ K_drawCheckpointDebugger();
+
+ if (cv_kartdebugnodes.value)
+ {
+ UINT8 p;
+ for (p = 0; p < MAXPLAYERS; p++)
+ V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d (%dl)", p, playernode[p], players[p].cmd.latency));
+ }
+
+ if (cv_kartdebugcolorize.value && stplyr->mo && stplyr->mo->skin)
+ {
+ INT32 x = 0, y = 0;
+ UINT8 c;
+
+ for (c = 1; c < MAXSKINCOLORS; c++)
+ {
+ UINT8 *cm = R_GetTranslationColormap(TC_RAINBOW, c, GTC_CACHE);
+ V_DrawFixedPatch(x<>1, 0, facewantprefix[stplyr->skin], cm);
+
+ x += 16;
+ if (x > BASEVIDWIDTH-16)
+ {
+ x = 0;
+ y += 16;
+ }
+ }
+ }
+}
+
+//}
diff --git a/src/k_kart.h b/src/k_kart.h
index fed490db..2ba5d1bd 100644
--- a/src/k_kart.h
+++ b/src/k_kart.h
@@ -1,74 +1,85 @@
-// SONIC ROBO BLAST 2 KART ~ ZarroTsu
-//-----------------------------------------------------------------------------
-/// \file k_kart.h
-/// \brief SRB2kart stuff.
-
-#ifndef __K_KART__
-#define __K_KART__
-
-#include "doomdef.h"
-#include "d_player.h" // Need for player_t
-
-UINT8 colortranslations[MAXSKINCOLORS][16];
-extern const char *KartColor_Names[MAXSKINCOLORS];
-extern const UINT8 KartColor_Opposite[MAXSKINCOLORS*2];
-void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor);
-void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color);
-UINT8 K_GetKartColorByName(const char *name);
-
-void K_RegisterKartStuff(void);
-
-boolean K_IsPlayerLosing(player_t *player);
-boolean K_IsPlayerWanted(player_t *player);
-void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid);
-void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master);
-void K_RespawnChecker(player_t *player);
-void K_KartMoveAnimation(player_t *player);
-void K_KartPlayerHUDUpdate(player_t *player);
-void K_KartPlayerThink(player_t *player, ticcmd_t *cmd);
-void K_KartPlayerAfterThink(player_t *player);
-void K_DoInstashield(player_t *player);
-void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount);
-void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflictor, boolean trapitem);
-void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor);
-void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor);
-void K_StealBumper(player_t *player, player_t *victim, boolean force);
-void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle, boolean spawncenter, boolean ghostit, mobj_t *source);
-void K_SpawnMineExplosion(mobj_t *source, UINT8 color);
-void K_SpawnBoostTrail(player_t *player);
-void K_SpawnSparkleTrail(mobj_t *mo);
-void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent);
-void K_DriftDustHandling(mobj_t *spawner);
-void K_DoSneaker(player_t *player, INT32 type);
-void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound);
-void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source);
-void K_UpdateHnextList(player_t *player, boolean clean);
-void K_DropHnextList(player_t *player);
-void K_RepairOrbitChain(mobj_t *orbit);
-player_t *K_FindJawzTarget(mobj_t *actor, player_t *source);
-boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y);
-INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue);
-INT32 K_GetKartDriftSparkValue(player_t *player);
-void K_KartUpdatePosition(player_t *player);
-void K_DropItems(player_t *player);
-void K_StripItems(player_t *player);
-void K_StripOther(player_t *player);
-void K_MomentumToFacing(player_t *player);
-fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower);
-fixed_t K_GetKartAccel(player_t *player);
-UINT16 K_GetKartFlashing(player_t *player);
-fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove);
-void K_MoveKartPlayer(player_t *player, boolean onground);
-void K_CalculateBattleWanted(void);
-void K_CheckBumpers(void);
-void K_CheckSpectateStatus(void);
-
-const char *K_GetItemPatch(UINT8 item, boolean tiny);
-INT32 K_calcSplitFlags(INT32 snapflags);
-void K_LoadKartHUDGraphics(void);
-void K_drawKartHUD(void);
-void K_drawKartFreePlay(UINT32 flashtime);
-void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode);
-
-// =========================================================================
-#endif // __K_KART__
+// SONIC ROBO BLAST 2 KART ~ ZarroTsu
+//-----------------------------------------------------------------------------
+/// \file k_kart.h
+/// \brief SRB2kart stuff.
+
+#ifndef __K_KART__
+#define __K_KART__
+
+#include "doomdef.h"
+#include "d_player.h" // Need for player_t
+
+#define KART_FULLTURN 800
+
+UINT8 colortranslations[MAXTRANSLATIONS][16];
+extern const char *KartColor_Names[MAXSKINCOLORS];
+extern const UINT8 KartColor_Opposite[MAXSKINCOLORS*2];
+void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor);
+void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color);
+UINT8 K_GetKartColorByName(const char *name);
+
+void K_RegisterKartStuff(void);
+
+boolean K_IsPlayerLosing(player_t *player);
+boolean K_IsPlayerWanted(player_t *player);
+void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid);
+void K_FlipFromObject(mobj_t *mo, mobj_t *master);
+void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master);
+void K_RespawnChecker(player_t *player);
+void K_KartMoveAnimation(player_t *player);
+void K_KartPlayerHUDUpdate(player_t *player);
+void K_KartPlayerThink(player_t *player, ticcmd_t *cmd);
+void K_KartPlayerAfterThink(player_t *player);
+void K_DoInstashield(player_t *player);
+void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount);
+void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflictor, boolean trapitem);
+void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor);
+void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor);
+void K_StealBumper(player_t *player, player_t *victim, boolean force);
+void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle, boolean spawncenter, boolean ghostit, mobj_t *source);
+void K_SpawnMineExplosion(mobj_t *source, UINT8 color);
+void K_SpawnBoostTrail(player_t *player);
+void K_SpawnSparkleTrail(mobj_t *mo);
+void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent);
+void K_DriftDustHandling(mobj_t *spawner);
+void K_PuntMine(mobj_t *mine, mobj_t *punter);
+void K_DoSneaker(player_t *player, INT32 type);
+void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound);
+void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source);
+void K_UpdateHnextList(player_t *player, boolean clean);
+void K_DropHnextList(player_t *player);
+void K_RepairOrbitChain(mobj_t *orbit);
+player_t *K_FindJawzTarget(mobj_t *actor, player_t *source);
+boolean K_CheckPlayersRespawnColliding(INT32 playernum, fixed_t x, fixed_t y);
+INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue);
+INT32 K_GetKartDriftSparkValue(player_t *player);
+void K_KartUpdatePosition(player_t *player);
+void K_DropItems(player_t *player);
+void K_StripItems(player_t *player);
+void K_StripOther(player_t *player);
+void K_MomentumToFacing(player_t *player);
+fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower);
+fixed_t K_GetKartAccel(player_t *player);
+UINT16 K_GetKartFlashing(player_t *player);
+fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove);
+void K_MoveKartPlayer(player_t *player, boolean onground);
+void K_CalculateBattleWanted(void);
+void K_CheckBumpers(void);
+void K_CheckSpectateStatus(void);
+
+// sound stuff for lua
+void K_PlayAttackTaunt(mobj_t *source);
+void K_PlayBoostTaunt(mobj_t *source);
+void K_PlayOvertakeSound(mobj_t *source);
+void K_PlayHitEmSound(mobj_t *source);
+void K_PlayPowerGloatSound(mobj_t *source);
+
+const char *K_GetItemPatch(UINT8 item, boolean tiny);
+INT32 K_calcSplitFlags(INT32 snapflags);
+void K_LoadKartHUDGraphics(void);
+void K_drawKartHUD(void);
+void K_drawKartFreePlay(UINT32 flashtime);
+void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode);
+
+// =========================================================================
+#endif // __K_KART__
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index c862ec26..dde57c2d 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -31,9 +31,10 @@
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
+#include "lua_hook.h" // hook_cmd_running
-#define NOHUD if (hud_running) return luaL_error(L, "HUD rendering code should not call this function!");
-
+#define NOHUD if (hud_running) return luaL_error(L, "HUD rendering code should not call this function!"); else if (hook_cmd_running) return luaL_error(L, "CMD Building code should not call this function!");
+// Yes technically cmd hook isn't a hud but whatever, this avoids having 2 defines for virtually the same thing.
boolean luaL_checkboolean(lua_State *L, int narg) {
luaL_checktype(L, narg, LUA_TBOOLEAN);
@@ -631,19 +632,6 @@ static int lib_pCheckSolidLava(lua_State *L)
return 1;
}
-static int lib_pCanRunOnWater(lua_State *L)
-{
- player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
- ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR));
- //HUDSAFE
- if (!player)
- return LUA_ErrInvalid(L, "player_t");
- if (!rover)
- return LUA_ErrInvalid(L, "ffloor_t");
- lua_pushboolean(L, P_CanRunOnWater(player, rover));
- return 1;
-}
-
static int lib_pSpawnShadowMobj(lua_State *L)
{
mobj_t *caster = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@@ -677,16 +665,6 @@ static int lib_pGetPlayerSpinHeight(lua_State *L)
return 1;
}
-static int lib_pGetPlayerControlDirection(lua_State *L)
-{
- player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
- //HUDSAFE
- if (!player)
- return LUA_ErrInvalid(L, "player_t");
- lua_pushinteger(L, P_GetPlayerControlDirection(player));
- return 1;
-}
-
static int lib_pAddPlayerScore(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@@ -2141,6 +2119,72 @@ static int lib_gTicsToMilliseconds(lua_State *L)
// K_KART
////////////
+// Seriously, why weren't those exposed before?
+static int lib_kAttackSound(lua_State *L)
+{
+ mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+ NOHUD
+ if (!mobj->player)
+ return luaL_error(L, "K_PlayAttackTaunt: mobj_t isn't a player object."); //Nothing bad would happen if we let it run the func, but telling why it ain't doing anything is helpful.
+ K_PlayAttackTaunt(mobj);
+ return 0;
+}
+
+static int lib_kBoostSound(lua_State *L)
+{
+ mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+ NOHUD
+ if (!mobj->player)
+ return luaL_error(L, "K_PlayBoostTaunt: mobj_t isn't a player object."); //Nothing bad would happen if we let it run the func, but telling why it ain't doing anything is helpful.
+ K_PlayBoostTaunt(mobj);
+ return 0;
+}
+
+static int lib_kOvertakeSound(lua_State *L)
+{
+ mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+ NOHUD
+ if (!mobj->player)
+ return luaL_error(L, "K_PlayOvertakeSound: mobj_t isn't a player object."); //Nothing bad would happen if we let it run the func, but telling why it ain't doing anything is helpful.
+ K_PlayOvertakeSound(mobj);
+ return 0;
+}
+
+static int lib_kHitEmSound(lua_State *L)
+{
+ mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+ NOHUD
+ if (!mobj->player)
+ return luaL_error(L, "K_PlayHitEmSound: mobj_t isn't a player object."); //Nothing bad would happen if we let it run the func, but telling why it ain't doing anything is helpful.
+ K_PlayHitEmSound(mobj);
+ return 0;
+}
+
+static int lib_kGloatSound(lua_State *L)
+{
+ mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+ NOHUD
+ if (!mobj->player)
+ return luaL_error(L, "K_PlayPowerGloatSound: mobj_t isn't a player object."); //Nothing bad would happen if we let it run the func, but telling why it ain't doing anything is helpful.
+ K_PlayPowerGloatSound(mobj);
+ return 0;
+}
+
+static int lib_kLossSound(lua_State *L)
+{
+ mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); // let's require a mobj for consistency with the other functions
+ sfxenum_t sfx_id;
+ NOHUD
+ if (!mobj->player)
+ return luaL_error(L, "K_PlayLossSound: mobj_t isn't a player object.");
+
+ sfx_id = ((skin_t *)mobj->skin)->soundsid[S_sfx[sfx_klose].skinsound];
+ S_StartSound(mobj, sfx_id);
+ return 0;
+}
+
+// Note: Pain, Death and Victory are already exposed.
+
static int lib_kGetKartColorByName(lua_State *L)
{
const char *name = luaL_checkstring(L, 1);
@@ -2570,13 +2614,11 @@ static luaL_Reg lib[] = {
{"P_InsideANonSolidFFloor",lib_pInsideANonSolidFFloor},
{"P_CheckDeathPitCollide",lib_pCheckDeathPitCollide},
{"P_CheckSolidLava",lib_pCheckSolidLava},
- {"P_CanRunOnWater",lib_pCanRunOnWater},
{"P_SpawnShadowMobj",lib_pSpawnShadowMobj},
// p_user
{"P_GetPlayerHeight",lib_pGetPlayerHeight},
{"P_GetPlayerSpinHeight",lib_pGetPlayerSpinHeight},
- {"P_GetPlayerControlDirection",lib_pGetPlayerControlDirection},
{"P_AddPlayerScore",lib_pAddPlayerScore},
{"P_PlayerInPain",lib_pPlayerInPain},
{"P_DoPlayerPain",lib_pDoPlayerPain},
@@ -2708,6 +2750,12 @@ static luaL_Reg lib[] = {
{"G_TicsToMilliseconds",lib_gTicsToMilliseconds},
// k_kart
+ {"K_PlayAttackTaunt", lib_kAttackSound},
+ {"K_PlayBoostTaunt", lib_kBoostSound},
+ {"K_PlayPowerGloatSund", lib_kGloatSound},
+ {"K_PlayOvertakeSound", lib_kOvertakeSound},
+ {"K_PlayLossSound", lib_kLossSound},
+ {"K_PlayHitEmSound", lib_kHitEmSound},
{"K_GetKartColorByName",lib_kGetKartColorByName},
{"K_IsPlayerLosing",lib_kIsPlayerLosing},
{"K_IsPlayerWanted",lib_kIsPlayerWanted},
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 2c9cd346..126e7e40 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -50,11 +50,14 @@ enum hook {
hook_PlayerSpin, //SRB2KART
hook_PlayerExplode, //SRB2KART
hook_PlayerSquish, //SRB2KART
+ hook_PlayerCmd, //SRB2KART
hook_MAX // last hook
};
extern const char *const hookNames[];
+extern boolean hook_cmd_running; // This is used by PlayerCmd and lua_playerlib to prevent anything from being wirtten to player while we run PlayerCmd.
+
void LUAh_MapChange(INT16 mapnumber); // Hook for map change (before load)
void LUAh_MapLoad(void); // Hook for map load
void LUAh_PlayerJoin(int playernum); // Hook for Got_AddPlayer
@@ -93,4 +96,7 @@ UINT8 LUAh_ShouldSquish(player_t *player, mobj_t *inflictor, mobj_t *source); //
boolean LUAh_PlayerSpin(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Hook for K_SpinPlayer. Allows Lua to execute code and/or overwrite its behavior.
boolean LUAh_PlayerExplode(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Hook for K_ExplodePlayer. Allows Lua to execute code and/or overwrite its behavior.
boolean LUAh_PlayerSquish(player_t *player, mobj_t *inflictor, mobj_t *source); // SRB2KART: Hook for K_SquishPlayer. Allows Lua to execute code and/or overwrite its behavior.
+
+boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Allows to write to player cmd before the game does anything with them.
+
#endif
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 13ad03d3..5a95877e 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -61,6 +61,7 @@ const char *const hookNames[hook_MAX+1] = {
"PlayerSpin",
"PlayerExplode",
"PlayerSquish",
+ "PlayerCmd",
NULL
};
@@ -877,6 +878,47 @@ boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd)
return hooked;
}
+// Hook for G_BuildTicCmd
+boolean hook_cmd_running = false;
+boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd)
+{
+ hook_p hookp;
+ boolean hooked = false;
+ if (!gL || !(hooksAvailable[hook_PlayerCmd/8] & (1<<(hook_PlayerCmd%8))))
+ return false;
+
+ lua_settop(gL, 0);
+
+ hook_cmd_running = true;
+ for (hookp = roothook; hookp; hookp = hookp->next)
+ if (hookp->type == hook_PlayerCmd)
+ {
+ if (lua_gettop(gL) == 0)
+ {
+ LUA_PushUserdata(gL, player, META_PLAYER);
+ LUA_PushUserdata(gL, cmd, META_TICCMD);
+ }
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -3);
+ lua_pushvalue(gL, -3);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!hookp->error || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ hookp->error = true;
+ continue;
+ }
+ if (lua_toboolean(gL, -1))
+ hooked = true;
+ lua_pop(gL, 1);
+ }
+
+ hook_cmd_running = false;
+ lua_settop(gL, 0);
+ return hooked;
+}
+
// Hook for B_BuildTailsTiccmd by skin name
boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
{
diff --git a/src/lua_hud.h b/src/lua_hud.h
index 3216ab15..4fbbbace 100644
--- a/src/lua_hud.h
+++ b/src/lua_hud.h
@@ -20,8 +20,10 @@ enum hud {
hud_item,
hud_position,
hud_minirankings, // Rankings to the left
+ hud_battlebumpers, // mini rankings battle bumpers.
hud_wanted,
hud_speedometer,
+ hud_freeplay,
hud_rankings, // Tab rankings
hud_MAX
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 353aebb2..fb6814b2 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -34,6 +34,8 @@ static UINT8 hud_enabled[(hud_MAX/8)+1];
static UINT8 hudAvailable; // hud hooks field
+static UINT8 camnum = 1;
+
// must match enum hud in lua_hud.h
static const char *const hud_disable_options[] = {
"stagetitle",
@@ -45,8 +47,10 @@ static const char *const hud_disable_options[] = {
"item",
"position",
"minirankings", // Gametype rankings to the left
+ "battlerankingsbumpers", // bumper drawer for battle. Useful if you want to make a custom battle gamemode without bumpers being involved.
"wanted",
"speedometer",
+ "freeplay",
"rankings",
NULL};
@@ -132,7 +136,8 @@ enum cameraf {
camera_height,
camera_momx,
camera_momy,
- camera_momz
+ camera_momz,
+ camera_pnum
};
@@ -151,6 +156,7 @@ static const char *const camera_opt[] = {
"momx",
"momy",
"momz",
+ "pnum",
NULL};
static int lib_getHudInfo(lua_State *L)
@@ -306,6 +312,9 @@ static int camera_get(lua_State *L)
case camera_momz:
lua_pushinteger(L, cam->momz);
break;
+ case camera_pnum:
+ lua_pushinteger(L, camnum);
+ break;
}
return 1;
}
@@ -401,6 +410,24 @@ static int libd_drawPaddedNum(lua_State *L)
return 0;
}
+
+static int libd_drawPingNum(lua_State *L)
+{
+ INT32 x, y, flags, num;
+ const UINT8 *colormap = NULL;
+ HUDONLY
+ x = luaL_checkinteger(L, 1);
+ y = luaL_checkinteger(L, 2);
+ num = luaL_checkinteger(L, 3);
+ flags = luaL_optinteger(L, 4, 0);
+ flags &= ~V_PARAMMASK; // Don't let crashes happen.
+ if (!lua_isnoneornil(L, 5))
+ colormap = *((UINT8 **)luaL_checkudata(L, 5, META_COLORMAP));
+
+ V_DrawPingNum(x, y, flags, num, colormap);
+ return 0;
+}
+
static int libd_drawFill(lua_State *L)
{
INT32 x = luaL_optinteger(L, 1, 0);
@@ -482,6 +509,20 @@ static int libd_drawString(lua_State *L)
return 0;
}
+static int libd_drawKartString(lua_State *L)
+{
+ fixed_t x = luaL_checkinteger(L, 1);
+ fixed_t y = luaL_checkinteger(L, 2);
+ const char *str = luaL_checkstring(L, 3);
+ INT32 flags = luaL_optinteger(L, 4, V_ALLOWLOWERCASE);
+
+ flags &= ~V_PARAMMASK; // Don't let crashes happen.
+
+ HUDONLY
+ V_DrawKartString(x, y, flags, str);
+ return 0;
+}
+
static int libd_stringWidth(lua_State *L)
{
const char *str = luaL_checkstring(L, 1);
@@ -590,9 +631,11 @@ static luaL_Reg lib_draw[] = {
{"drawScaled", libd_drawScaled},
{"drawNum", libd_drawNum},
{"drawPaddedNum", libd_drawPaddedNum},
+ {"drawPingNum", libd_drawPingNum},
{"drawFill", libd_drawFill},
{"fadeScreen", libd_fadeScreen},
{"drawString", libd_drawString},
+ {"drawKartString", libd_drawKartString},
{"stringWidth", libd_stringWidth},
{"getColormap", libd_getColormap},
{"width", libd_width},
@@ -755,13 +798,25 @@ void LUAh_GameHUD(player_t *stplayr)
LUA_PushUserdata(gL, stplayr, META_PLAYER);
if (splitscreen > 2 && stplayr == &players[fourthdisplayplayer])
+ {
LUA_PushUserdata(gL, &camera4, META_CAMERA);
+ camnum = 4;
+ }
else if (splitscreen > 1 && stplayr == &players[thirddisplayplayer])
+ {
LUA_PushUserdata(gL, &camera3, META_CAMERA);
+ camnum = 3;
+ }
else if (splitscreen && stplayr == &players[secondarydisplayplayer])
+ {
LUA_PushUserdata(gL, &camera2, META_CAMERA);
+ camnum = 2;
+ }
else
+ {
LUA_PushUserdata(gL, &camera, META_CAMERA);
+ camnum = 1;
+ }
lua_pushnil(gL);
while (lua_next(gL, -5) != 0) {
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 9b22170f..d8659a7e 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -22,6 +22,7 @@
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
+#include "lua_hook.h" // cmd errors
boolean LUA_CallAction(const char *action, mobj_t *actor);
state_t *astate;
@@ -169,6 +170,8 @@ static int lib_setState(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter states in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter states in BuildCMD code!");
// clear the state to start with, in case of missing table elements
memset(state,0,sizeof(state_t));
@@ -378,6 +381,8 @@ static int state_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter states in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter states in BuildCMD code!");
if (fastcmp(field,"sprite")) {
value = luaL_checknumber(L, 3);
@@ -466,6 +471,8 @@ static int lib_setMobjInfo(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter mobjinfo in BuildCMD code!");
// clear the mobjinfo to start with, in case of missing table elements
memset(info,0,sizeof(mobjinfo_t));
@@ -633,6 +640,8 @@ static int mobjinfo_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter mobjinfo in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter mobjinfo in BuildCMD code!");
I_Assert(info != NULL);
I_Assert(info >= mobjinfo);
@@ -755,6 +764,8 @@ static int lib_setSfxInfo(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter sfxinfo in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter sfxinfo in BuildCMD code!");
lua_pushnil(L);
while (lua_next(L, 1)) {
@@ -830,6 +841,8 @@ static int sfxinfo_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter S_sfx in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter S_sfx in BuildCMD code!");
I_Assert(sfx != NULL);
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 7599b261..0bb9a99d 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -24,6 +24,7 @@
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
+#include "lua_hook.h" // cmd errors
#include "dehacked.h"
#include "fastcmp.h"
@@ -484,6 +485,8 @@ static int sector_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter sector_t in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter sector_t in BuildCMD code!");
switch(field)
{
@@ -1174,6 +1177,8 @@ static int ffloor_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter ffloor_t in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter ffloor_t in BuildCMD code!");
switch(field)
{
@@ -1303,6 +1308,8 @@ static int slope_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter pslope_t in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter pslope_t in BuildCMD code!");
switch(field) // todo: reorganize this shit
{
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 34aba0a3..b56538d0 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -21,6 +21,7 @@
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
+#include "lua_hook.h" // cmd errors
static const char *const array_opt[] ={"iterate",NULL};
@@ -391,6 +392,9 @@ static int mobj_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter mobj_t in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter mobj_t in BuildCMD code!");
+
switch(field)
{
case mobj_valid:
@@ -756,6 +760,8 @@ static int mapthing_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter mapthing_t in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter mapthing_t in BuildCMD code!");
if(fastcmp(field,"x"))
mt->x = (INT16)luaL_checkinteger(L, 3);
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 067124ba..73d5ecbc 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -21,6 +21,7 @@
#include "lua_script.h"
#include "lua_libs.h"
#include "lua_hud.h" // hud_running errors
+#include "lua_hook.h" // hook_cmd_running
static int lib_iteratePlayers(lua_State *L)
{
@@ -324,6 +325,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->bot);
else if (fastcmp(field,"jointime"))
lua_pushinteger(L, plr->jointime);
+ else if (fastcmp(field,"splitscreenindex"))
+ lua_pushinteger(L, plr->splitscreenindex);
#ifdef HWRENDER
else if (fastcmp(field,"fovadd"))
lua_pushfixed(L, plr->fovadd);
@@ -356,6 +359,9 @@ static int player_set(lua_State *L)
if (hud_running)
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter player_t in BuildCMD code!");
+
if (fastcmp(field,"mo")) {
mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
plr->mo->player = NULL; // remove player pointer from old mobj
@@ -609,6 +615,8 @@ static int player_set(lua_State *L)
return NOSET;
else if (fastcmp(field,"jointime"))
plr->jointime = (tic_t)luaL_checkinteger(L, 3);
+ else if (fastcmp(field,"splitscreenindex"))
+ return NOSET;
#ifdef HWRENDER
else if (fastcmp(field,"fovadd"))
plr->fovadd = luaL_checkfixed(L, 3);
@@ -667,6 +675,8 @@ static int power_set(lua_State *L)
return luaL_error(L, LUA_QL("powertype_t") " cannot be %u", p);
if (hud_running)
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter player_t in BuildCMD code!");
powers[p] = i;
return 0;
}
@@ -699,6 +709,8 @@ static int kartstuff_set(lua_State *L)
return luaL_error(L, LUA_QL("kartstufftype_t") " cannot be %u", ks);
if (hud_running)
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
+ if (hook_cmd_running)
+ return luaL_error(L, "Do not alter player_t in BuildCMD code!");
kartstuff[ks] = i;
return 0;
}
@@ -731,6 +743,8 @@ static int ticcmd_get(lua_State *L)
lua_pushinteger(L, cmd->buttons);
else if (fastcmp(field,"driftturn"))
lua_pushinteger(L, cmd->driftturn);
+ else if (fastcmp(field,"latency"))
+ lua_pushinteger(L, cmd->latency);
else
return NOFIELD;
diff --git a/src/lua_script.c b/src/lua_script.c
index 34a26052..28f02ca3 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -212,6 +212,9 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump)
LUA_LoadFile(&f, name); // actually load file!
+ // Okay, we've modified the game beyond the point of no return.
+ G_SetGameModified(multiplayer, true);
+
free(name);
Z_Free(f.data);
}
@@ -1017,7 +1020,7 @@ void LUA_Archive(void)
for (i = 0; i < MAXPLAYERS; i++)
{
- if (!playeringame[i])
+ if (!playeringame[i] && i > 0) // NEVER skip player 0, this is for dedi servs.
continue;
// all players in game will be archived, even if they just add a 0.
ArchiveExtVars(&players[i], "player");
@@ -1053,7 +1056,7 @@ void LUA_UnArchive(void)
for (i = 0; i < MAXPLAYERS; i++)
{
- if (!playeringame[i])
+ if (!playeringame[i] && i > 0) // same here, this is to synch dediservs properly.
continue;
UnArchiveExtVars(&players[i]);
}
diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index 8fdd92a5..f44e97af 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -30,24 +30,10 @@ enum skin {
skin_facerank,
skin_facewant,
skin_facemmap,
- skin_ability,
- skin_ability2,
- skin_thokitem,
- skin_spinitem,
- skin_revitem,
- skin_actionspd,
- skin_mindash,
- skin_maxdash,
// SRB2kart
skin_kartspeed,
skin_kartweight,
//
- skin_normalspeed,
- skin_runspeed,
- skin_thrustfactor,
- skin_accelstart,
- skin_acceleration,
- skin_jumpfactor,
skin_starttranscolor,
skin_prefcolor,
skin_highresscale,
@@ -64,24 +50,10 @@ static const char *const skin_opt[] = {
"facerank",
"facewant",
"facemmap",
- "ability",
- "ability2",
- "thokitem",
- "spinitem",
- "revitem",
- "actionspd",
- "mindash",
- "maxdash",
// SRB2kart
"kartspeed",
"kartweight",
//
- "normalspeed",
- "runspeed",
- "thrustfactor",
- "accelstart",
- "acceleration",
- "jumpfactor",
"starttranscolor",
"prefcolor",
"highresscale",
@@ -139,30 +111,6 @@ static int skin_get(lua_State *L)
break;
lua_pushlstring(L, skin->facemmap, i);
break;
- case skin_ability:
- lua_pushinteger(L, skin->ability);
- break;
- case skin_ability2:
- lua_pushinteger(L, skin->ability2);
- break;
- case skin_thokitem:
- lua_pushinteger(L, skin->thokitem);
- break;
- case skin_spinitem:
- lua_pushinteger(L, skin->spinitem);
- break;
- case skin_revitem:
- lua_pushinteger(L, skin->revitem);
- break;
- case skin_actionspd:
- lua_pushfixed(L, skin->actionspd);
- break;
- case skin_mindash:
- lua_pushfixed(L, skin->mindash);
- break;
- case skin_maxdash:
- lua_pushfixed(L, skin->maxdash);
- break;
// SRB2kart
case skin_kartspeed:
lua_pushinteger(L, skin->kartspeed);
@@ -171,24 +119,6 @@ static int skin_get(lua_State *L)
lua_pushinteger(L, skin->kartweight);
break;
//
- case skin_normalspeed:
- lua_pushfixed(L, skin->normalspeed);
- break;
- case skin_runspeed:
- lua_pushfixed(L, skin->runspeed);
- break;
- case skin_thrustfactor:
- lua_pushinteger(L, skin->thrustfactor);
- break;
- case skin_accelstart:
- lua_pushinteger(L, skin->accelstart);
- break;
- case skin_acceleration:
- lua_pushinteger(L, skin->acceleration);
- break;
- case skin_jumpfactor:
- lua_pushfixed(L, skin->jumpfactor);
- break;
case skin_starttranscolor:
lua_pushinteger(L, skin->starttranscolor);
break;
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 9c53f901..2fc2e85c 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -121,7 +121,7 @@ static UINT8 cheatf_devmode(void)
S_StartSound(0, sfx_itemup);
// Just unlock all the things and turn on -debug and console devmode.
- G_SetGameModified(false);
+ G_SetGameModified(false, false); // might need to revist the latter later
for (i = 0; i < MAXUNLOCKABLES; i++)
unlockables[i].unlocked = true;
devparm = true;
@@ -295,7 +295,7 @@ void Command_CheatNoClip_f(void)
plyr->pflags ^= PF_NOCLIP;
CONS_Printf(M_GetText("No Clipping %s\n"), plyr->pflags & PF_NOCLIP ? M_GetText("On") : M_GetText("Off"));
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
}
void Command_CheatGod_f(void)
@@ -310,7 +310,7 @@ void Command_CheatGod_f(void)
plyr->pflags ^= PF_GODMODE;
CONS_Printf(M_GetText("Sissy Mode %s\n"), plyr->pflags & PF_GODMODE ? M_GetText("On") : M_GetText("Off"));
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
}
void Command_CheatNoTarget_f(void)
@@ -325,7 +325,7 @@ void Command_CheatNoTarget_f(void)
plyr->pflags ^= PF_INVIS;
CONS_Printf(M_GetText("SEP Field %s\n"), plyr->pflags & PF_INVIS ? M_GetText("On") : M_GetText("Off"));
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
}
void Command_Scale_f(void)
@@ -727,7 +727,7 @@ void Command_Devmode_f(void)
return;
}
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
}
/*void Command_Setrings_f(void)
@@ -1130,12 +1130,12 @@ void OP_ObjectplaceMovement(player_t *player)
P_TeleportMove(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z);
player->mo->momx = player->mo->momy = 0;
}
- if (cmd->sidemove != 0)
+ /*if (cmd->sidemove != 0) -- was disabled in practice anyways, since sidemove was suppressed
{
P_Thrust(player->mo, player->mo->angle-ANGLE_90, (cmd->sidemove*FRACUNIT/MAXPLMOVE)*cv_speed.value);
P_TeleportMove(player->mo, player->mo->x+player->mo->momx, player->mo->y+player->mo->momy, player->mo->z);
player->mo->momx = player->mo->momy = 0;
- }
+ }*/
if (player->mo->z > player->mo->ceilingz - player->mo->height)
player->mo->z = player->mo->ceilingz - player->mo->height;
@@ -1267,7 +1267,7 @@ void Command_ObjectPlace_f(void)
REQUIRE_SINGLEPLAYER;
REQUIRE_NOULTIMATE;
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
// Entering objectplace?
if (!objectplacing)
diff --git a/src/m_cond.c b/src/m_cond.c
index 35eccd1c..b777e7d2 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -385,8 +385,7 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(boolean force)
char cechoText[992] = "";
UINT8 cechoLines = 0;
- if (modifiedgame && !savemoddata
- && !force) // SRB2Kart: for enabling unlocks online in modified servers
+ if (majormods && !force) // SRB2Kart: for enabling unlocks online in modified servers
return false;
M_CheckUnlockConditions();
diff --git a/src/m_menu.c b/src/m_menu.c
index 2241e826..3ad076ff 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -274,14 +274,13 @@ static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef;
#ifndef NONET
static void M_StartServerMenu(INT32 choice);
static void M_ConnectMenu(INT32 choice);
-#endif
-static void M_StartOfflineServerMenu(INT32 choice);
-static void M_StartServer(INT32 choice);
-#ifndef NONET
+static void M_ConnectMenuModChecks(INT32 choice);
static void M_Refresh(INT32 choice);
static void M_Connect(INT32 choice);
static void M_ChooseRoom(INT32 choice);
#endif
+static void M_StartOfflineServerMenu(INT32 choice);
+static void M_StartServer(INT32 choice);
static void M_SetupMultiPlayer(INT32 choice);
static void M_SetupMultiPlayer2(INT32 choice);
static void M_SetupMultiPlayer3(INT32 choice);
@@ -400,6 +399,8 @@ static void Dummystaff_OnChange(void);
// CONSOLE VARIABLES AND THEIR POSSIBLE VALUES GO HERE.
// ==========================================================================
+consvar_t cv_showfocuslost = {"showfocuslost", "Yes", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL };
+
static CV_PossibleValue_t map_cons_t[] = {
{0,"MIN"},
{NUMMAPS, "MAX"},
@@ -439,11 +440,11 @@ static CV_PossibleValue_t serversort_cons_t[] = {
{1,"Modified State"},
{2,"Most Players"},
{3,"Least Players"},
- {4,"Max Players"},
+ {4,"Max Player Slots"},
{5,"Gametype"},
{0,NULL}
};
-consvar_t cv_serversort = {"serversort", "Ping", CV_HIDEN | CV_CALL, serversort_cons_t, M_SortServerList, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_serversort = {"serversort", "Ping", CV_CALL, serversort_cons_t, M_SortServerList, 0, NULL, NULL, 0, 0, NULL};
// autorecord demos for time attack
static consvar_t cv_autorecord = {"autorecord", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -911,41 +912,7 @@ static menuitem_t SP_LevelStatsMenu[] =
// External files modify this menu, so we can't call it static.
// And I'm too lazy to go through and rename it everywhere. ARRGH!
#define M_ChoosePlayer NULL
-menuitem_t PlayerMenu[32] =
-{
- {IT_CALL, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
- {IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0}
-};
+menuitem_t PlayerMenu[MAXSKINS];
// -----------------------------------
// Multiplayer and all of its submenus
@@ -969,11 +936,11 @@ static menuitem_t MP_MainMenu[] =
{IT_HEADER, NULL, "Join a game", NULL, 132-24},
#ifndef NONET
- {IT_STRING|IT_CALL, NULL, "Internet server browser...",M_ConnectMenu, 142-24},
+ {IT_STRING|IT_CALL, NULL, "Internet server browser...",M_ConnectMenuModChecks, 142-24},
{IT_STRING|IT_KEYHANDLER, NULL, "Specify IPv4 address:", M_HandleConnectIP, 150-24},
#else
- {IT_GRAYEDOUT, NULL, "Internet server browser...",M_ConnectMenu, 142-24},
- {IT_GRAYEDOUT, NULL, "Specify IPv4 address:", M_HandleConnectIP, 150-24},
+ {IT_GRAYEDOUT, NULL, "Internet server browser...",NULL, 142-24},
+ {IT_GRAYEDOUT, NULL, "Specify IPv4 address:", NULL, 150-24},
#endif
//{IT_HEADER, NULL, "Player setup", NULL, 80},
//{IT_STRING|IT_CALL, NULL, "Name, character, color...", M_SetupMultiPlayer, 90},
@@ -983,14 +950,15 @@ static menuitem_t MP_MainMenu[] =
static menuitem_t MP_ServerMenu[] =
{
- {IT_STRING|IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 10},
- {IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 20},
- {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 30},
+ {IT_STRING|IT_CVAR, NULL, "Max. Player Count", &cv_maxplayers, 0},
+ {IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 10},
+ {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 20},
+ {IT_STRING|IT_CVAR|IT_CV_PASSWORD, NULL, "Password", &cv_dummyjoinpassword, 44},
- {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68},
- {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78},
+ {IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 68},
+ {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78},
- {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 130},
+ {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 130},
};
#endif
@@ -1284,7 +1252,7 @@ static menuitem_t OP_OpenGLOptionsMenu[] =
{IT_SUBMENU|IT_STRING, NULL, "Fog...", &OP_OpenGLFogDef, 10},
{IT_SUBMENU|IT_STRING, NULL, "Gamma...", &OP_OpenGLColorDef, 20},
- {IT_STRING|IT_CVAR, NULL, "Field of View", &cv_grfov, 35},
+ {IT_STRING|IT_CVAR, NULL, "Field of View", &cv_fov, 35},
{IT_STRING|IT_CVAR, NULL, "Quality", &cv_scr_depth, 45},
{IT_STRING|IT_CVAR, NULL, "Texture Filter", &cv_grfiltermode, 55},
{IT_STRING|IT_CVAR, NULL, "Anisotropic", &cv_granisotropicmode, 65},
@@ -1351,6 +1319,9 @@ static menuitem_t OP_SoundOptionsMenu[] =
{IT_STRING|IT_CVAR, NULL, "Powerup Warning", &cv_kartinvinsfx, 95},
{IT_KEYHANDLER|IT_STRING, NULL, "Sound Test", M_HandleSoundTest, 110},
+
+ {IT_STRING|IT_CVAR, NULL, "Play Music While Unfocused", &cv_playmusicifunfocused, 125},
+ {IT_STRING|IT_CVAR, NULL, "Play SFX While Unfocused", &cv_playsoundifunfocused, 135},
};
/*static menuitem_t OP_DataOptionsMenu[] =
@@ -1425,7 +1396,7 @@ static menuitem_t OP_HUDOptionsMenu[] =
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
NULL, "HUD Visibility", &cv_translucenthud, 20},
- {IT_STRING | IT_SUBMENU, NULL, "Online chat options...",&OP_ChatOptionsDef, 35},
+ {IT_STRING | IT_SUBMENU, NULL, "Online HUD options...",&OP_ChatOptionsDef, 35},
{IT_STRING | IT_CVAR, NULL, "Background Glass", &cons_backcolor, 45},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
@@ -1437,8 +1408,11 @@ static menuitem_t OP_HUDOptionsMenu[] =
// highlight info - (GOOD HIGHLIGHT, WARNING HIGHLIGHT) - 105 (see M_DrawHUDOptions)
{IT_STRING | IT_CVAR, NULL, "Console Text Size", &cv_constextsize, 120},
+
+ {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 135},
};
+// Ok it's still called chatoptions but we'll put ping display in here to be clean
static menuitem_t OP_ChatOptionsMenu[] =
{
// will ANYONE who doesn't know how to use the console want to touch this one?
@@ -1452,6 +1426,8 @@ static menuitem_t OP_ChatOptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Chat Background Tint", &cv_chatbacktint, 50},
{IT_STRING | IT_CVAR, NULL, "Message Fadeout Time", &cv_chattime, 60},
{IT_STRING | IT_CVAR, NULL, "Spam Protection", &cv_chatspamprotection, 70},
+
+ {IT_STRING | IT_CVAR, NULL, "Local ping display", &cv_showping, 90}, // shows ping next to framerate if we want to.
};
static menuitem_t OP_GameOptionsMenu[] =
@@ -1504,15 +1480,16 @@ static menuitem_t OP_AdvServerOptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 40},
{IT_STRING | IT_CVAR, NULL, "Ping limit (ms)", &cv_maxping, 50},
- {IT_STRING | IT_CVAR, NULL, "Connection timeout (tics)", &cv_nettimeout, 60},
- {IT_STRING | IT_CVAR, NULL, "Join timeout (tics)", &cv_jointimeout, 70},
+ {IT_STRING | IT_CVAR, NULL, "Ping timeout (s)", &cv_pingtimeout, 60},
+ {IT_STRING | IT_CVAR, NULL, "Connection timeout (tics)", &cv_nettimeout, 70},
+ {IT_STRING | IT_CVAR, NULL, "Join timeout (tics)", &cv_jointimeout, 80},
- {IT_STRING | IT_CVAR, NULL, "Max. file transfer send (KB)", &cv_maxsend, 90},
- {IT_STRING | IT_CVAR, NULL, "File transfer packet rate", &cv_downloadspeed, 100},
+ {IT_STRING | IT_CVAR, NULL, "Max. file transfer send (KB)", &cv_maxsend, 100},
+ {IT_STRING | IT_CVAR, NULL, "File transfer packet rate", &cv_downloadspeed, 110},
- {IT_STRING | IT_CVAR, NULL, "Log join addresses", &cv_showjoinaddress, 120},
- {IT_STRING | IT_CVAR, NULL, "Log resyncs", &cv_blamecfail, 130},
- {IT_STRING | IT_CVAR, NULL, "Log file transfers", &cv_noticedownload, 140},
+ {IT_STRING | IT_CVAR, NULL, "Log join addresses", &cv_showjoinaddress, 130},
+ {IT_STRING | IT_CVAR, NULL, "Log resyncs", &cv_blamecfail, 140},
+ {IT_STRING | IT_CVAR, NULL, "Log file transfers", &cv_noticedownload, 150},
};
#endif
@@ -1865,7 +1842,6 @@ static menu_t SP_NightsGhostDef =
NULL
};*/
-#ifndef NONET
// Multiplayer
menu_t MP_MainDef =
{
@@ -1876,12 +1852,18 @@ menu_t MP_MainDef =
M_DrawMPMainMenu,
42, 30,
0,
- M_CancelConnect
-};
-menu_t MP_ServerDef = MAPICONMENUSTYLE("M_MULTI", MP_ServerMenu, &MP_MainDef);
-#endif
-menu_t MP_OfflineServerDef = MAPICONMENUSTYLE("M_MULTI", MP_OfflineServerMenu, &MP_MainDef);
#ifndef NONET
+ M_CancelConnect
+#else
+ NULL
+#endif
+};
+
+menu_t MP_OfflineServerDef = MAPICONMENUSTYLE("M_MULTI", MP_OfflineServerMenu, &MP_MainDef);
+
+#ifndef NONET
+menu_t MP_ServerDef = MAPICONMENUSTYLE("M_MULTI", MP_ServerMenu, &MP_MainDef);
+
menu_t MP_ConnectDef =
{
"M_MULTI",
@@ -2409,6 +2391,9 @@ static void M_NextOpt(void)
{
INT16 oldItemOn = itemOn; // prevent infinite loop
+ if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
+ ((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value = 0;
+
do
{
if (itemOn + 1 > currentMenu->numitems - 1)
@@ -2422,6 +2407,9 @@ static void M_PrevOpt(void)
{
INT16 oldItemOn = itemOn; // prevent infinite loop
+ if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
+ ((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value = 0;
+
do
{
if (!itemOn)
@@ -2584,7 +2572,7 @@ boolean M_Responder(event_t *ev)
return false;
else if (ch == gamecontrol[gc_systemmenu][0] || ch == gamecontrol[gc_systemmenu][1]) // allow remappable ESC key
ch = KEY_ESCAPE;
- else if (ch == gamecontrol[gc_accelerate][0] || ch == gamecontrol[gc_accelerate][1])
+ else if ((ch == gamecontrol[gc_accelerate][0] || ch == gamecontrol[gc_accelerate][1]) && ch >= KEY_MOUSE1)
ch = KEY_ENTER;
// F-Keys
@@ -2662,7 +2650,7 @@ boolean M_Responder(event_t *ev)
return false;
}
- if (ch == gamecontrol[gc_brake][0] || ch == gamecontrol[gc_brake][1]) // do this here, otherwise brake opens the menu mid-game
+ if ((ch == gamecontrol[gc_brake][0] || ch == gamecontrol[gc_brake][1]) && ch >= KEY_MOUSE1) // do this here, otherwise brake opens the menu mid-game
ch = KEY_ESCAPE;
routine = currentMenu->menuitems[itemOn].itemaction;
@@ -2708,8 +2696,11 @@ boolean M_Responder(event_t *ev)
// BP: one of the more big hack i have never made
if (routine && (currentMenu->menuitems[itemOn].status & IT_TYPE) == IT_CVAR)
{
- if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING)
+ if ((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_STRING || (currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
{
+ if (ch == KEY_TAB && (currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_PASSWORD)
+ ((consvar_t *)currentMenu->menuitems[itemOn].itemaction)->value ^= 1;
+
if (shiftdown && ch >= 32 && ch <= 127)
ch = shiftxform[ch];
if (M_ChangeStringCvar(ch))
@@ -2773,10 +2764,10 @@ boolean M_Responder(event_t *ev)
|| (currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_SUBMENU)
&& (currentMenu->menuitems[itemOn].status & IT_CALLTYPE))
{
- if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && modifiedgame && !savemoddata)
+ if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && majormods)
{
S_StartSound(NULL, sfx_menu1);
- M_StartMessage(M_GetText("This cannot be done with add-ons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
+ M_StartMessage(M_GetText("This cannot be done with complex add-ons\nor in a cheated game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
return true;
}
}
@@ -2910,7 +2901,7 @@ void M_Drawer(void)
}
// focus lost notification goes on top of everything, even the former everything
- if (window_notinfocus)
+ if (window_notinfocus && cv_showfocuslost.value)
{
M_DrawTextBox((BASEVIDWIDTH/2) - (60), (BASEVIDHEIGHT/2) - (16), 13, 2);
if (gamestate == GS_LEVEL && (P_AutoPause() || paused))
@@ -3188,6 +3179,8 @@ void M_Ticker(void)
//
void M_Init(void)
{
+ UINT8 i;
+
COM_AddCommand("manual", Command_Manual_f);
CV_RegisterVar(&cv_nextmap);
@@ -3199,7 +3192,6 @@ void M_Init(void)
return;
// Menu hacks
- CV_RegisterVar(&cv_splitplayers);
CV_RegisterVar(&cv_dummymenuplayer);
CV_RegisterVar(&cv_dummyteam);
CV_RegisterVar(&cv_dummyspectate);
@@ -3235,6 +3227,15 @@ void M_Init(void)
quitmsg[QUIT3MSG5] = M_GetText("You'll be back to play soon, though...\n...right?\n\n(Press 'Y' to quit)");
quitmsg[QUIT3MSG6] = M_GetText("Aww, is Eggman's Nightclub too\ndifficult for you?\n\n(Press 'Y' to quit)");
+ // Setup PlayerMenu table
+ for (i = 0; i < MAXSKINS; i++)
+ {
+ PlayerMenu[i].status = (i == 0 ? IT_CALL : IT_DISABLED);
+ PlayerMenu[i].patch = PlayerMenu[i].text = NULL;
+ PlayerMenu[i].itemaction = M_ChoosePlayer;
+ PlayerMenu[i].alphaKey = 0;
+ }
+
#ifdef HWRENDER
// Permanently hide some options based on render mode
if (rendermode == render_soft)
@@ -3473,7 +3474,7 @@ static void M_DrawMapEmblems(INT32 mapnum, INT32 x, INT32 y)
if (emblem->collected)
V_DrawSmallMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE),
- R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
+ R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_MENUCACHE));
else
V_DrawSmallScaledPatch(x, y, 0, W_CachePatchName("NEEDIT", PU_CACHE));
@@ -3573,6 +3574,8 @@ static void M_DrawGenericMenu(void)
case IT_CVAR:
{
consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction;
+ char asterisks[MAXSTRINGLENGTH+1];
+ size_t sl;
switch (currentMenu->menuitems[i].status & IT_CVARTYPE)
{
case IT_CV_SLIDER:
@@ -3580,6 +3583,27 @@ static void M_DrawGenericMenu(void)
case IT_CV_NOPRINT: // color use this
case IT_CV_INVISSLIDER: // monitor toggles use this
break;
+ case IT_CV_PASSWORD:
+ if (i == itemOn)
+ {
+ V_DrawRightAlignedThinString(x + MAXSTRINGLENGTH*8 + 10, y, V_ALLOWLOWERCASE, va(M_GetText("Tab: %s password"), cv->value ? "hide" : "show"));
+ }
+
+ if (!cv->value || i != itemOn)
+ {
+ sl = strlen(cv->string);
+ memset(asterisks, '*', sl);
+ memset(asterisks + sl, 0, MAXSTRINGLENGTH+1-sl);
+
+ M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
+ V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, asterisks);
+ if (skullAnimCounter < 4 && i == itemOn)
+ V_DrawCharacter(x + 8 + V_StringWidth(asterisks, 0), y + 12,
+ '_' | 0x80, false);
+ y += 16;
+ break;
+ }
+ /* fallthru */
case IT_CV_STRING:
M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
@@ -3787,7 +3811,7 @@ static void M_DrawPauseMenu(void)
if (emblem->collected)
V_DrawSmallMappedPatch(40, 44 + (i*8), 0, W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE),
- R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
+ R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_MENUCACHE));
else
V_DrawSmallScaledPatch(40, 44 + (i*8), 0, W_CachePatchName("NEEDIT", PU_CACHE));
@@ -4551,9 +4575,14 @@ static char *M_AddonsHeaderPath(void)
#define CLEARNAME Z_Free(refreshdirname);\
refreshdirname = NULL
+static boolean prevmajormods = false;
+
static void M_AddonsClearName(INT32 choice)
{
- CLEARNAME;
+ if (!majormods || prevmajormods)
+ {
+ CLEARNAME;
+ }
M_StopMessage(choice);
}
@@ -4563,10 +4592,17 @@ static boolean M_AddonsRefresh(void)
if ((refreshdirmenu & REFRESHDIR_NORMAL) && !preparefilemenu(true))
{
UNEXIST;
+ if (refreshdirname)
+ {
+ CLEARNAME;
+ }
return true;
}
- if (refreshdirmenu & REFRESHDIR_ADDFILE)
+ if (!majormods && prevmajormods)
+ prevmajormods = false;
+
+ if ((refreshdirmenu & REFRESHDIR_ADDFILE) || (majormods && !prevmajormods))
{
char *message = NULL;
@@ -4574,7 +4610,7 @@ static boolean M_AddonsRefresh(void)
{
S_StartSound(NULL, sfx_s26d);
if (refreshdirmenu & REFRESHDIR_MAX)
- message = va("%c%s\x80\nMaximum number of add-ons reached.\nA file could not be loaded.\nIf you want to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
+ message = va("%c%s\x80\nMaximum number of add-ons reached.\nA file could not be loaded.\nIf you wish to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
else
message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
}
@@ -4583,6 +4619,12 @@ static boolean M_AddonsRefresh(void)
S_StartSound(NULL, sfx_s224);
message = va("%c%s\x80\nA file was loaded with %s.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings"));
}
+ else if (majormods && !prevmajormods)
+ {
+ S_StartSound(NULL, sfx_s221);
+ message = va("%c%s\x80\nGameplay has now been modified.\nIf you wish to play Record Attack mode, restart the game to clear existing add-ons.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
+ prevmajormods = majormods;
+ }
if (message)
{
@@ -4733,7 +4775,7 @@ static void M_DrawAddons(void)
V_DrawSmallScaledPatch(x, y + 4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+3]);
x = BASEVIDWIDTH - x - 16;
- V_DrawSmallScaledPatch(x, y + 4, ((!modifiedgame || savemoddata) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]);
+ V_DrawSmallScaledPatch(x, y + 4, ((!majormods) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]);
if (modifiedgame)
V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+2]);
@@ -5130,7 +5172,7 @@ static void M_GetAllEmeralds(INT32 choice)
emeralds = ((EMERALD7)*2)-1;
M_StartMessage(M_GetText("You now have all 7 emeralds.\nUse them wisely.\nWith great power comes great ring drain.\n"),NULL,MM_NOTHING);
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
}
static void M_DestroyRobotsResponse(INT32 ch)
@@ -5141,7 +5183,7 @@ static void M_DestroyRobotsResponse(INT32 ch)
// Destroy all robots
P_DestroyRobots();
- G_SetGameModified(multiplayer);
+ G_SetGameModified(multiplayer, true);
}
static void M_DestroyRobots(INT32 choice)
@@ -5317,7 +5359,7 @@ static void M_DrawEmblemHints(void)
{
collected = recommendedflags;
V_DrawMappedPatch(12, 12+(28*j), 0, W_CachePatchName(M_GetEmblemPatch(emblem), PU_CACHE),
- R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_CACHE));
+ R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_MENUCACHE));
}
else
{
@@ -5660,7 +5702,7 @@ static void M_DrawLoadGameData(void)
V_DrawScaledPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE));
else
{
- UINT8 *colormap = R_GetTranslationColormap(savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor, 0);
+ UINT8 *colormap = R_GetTranslationColormap(savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor, GTC_MENUCACHE);
V_DrawMappedPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE), colormap);
}
@@ -6350,7 +6392,7 @@ static void M_DrawStatsMaps(int location)
if (exemblem->collected)
V_DrawSmallMappedPatch(295, y, 0, W_CachePatchName(M_GetExtraEmblemPatch(exemblem), PU_CACHE),
- R_GetTranslationColormap(TC_DEFAULT, M_GetExtraEmblemColor(exemblem), GTC_CACHE));
+ R_GetTranslationColormap(TC_DEFAULT, M_GetExtraEmblemColor(exemblem), GTC_MENUCACHE));
else
V_DrawSmallScaledPatch(295, y, 0, W_CachePatchName("NEEDIT", PU_CACHE));
@@ -6485,7 +6527,7 @@ void M_DrawTimeAttackMenu(void)
// Character face!
if (W_CheckNumForName(skins[cv_chooseskin.value-1].facewant) != LUMPERROR)
{
- UINT8 *colormap = R_GetTranslationColormap(cv_chooseskin.value-1, cv_playercolor.value, 0);
+ UINT8 *colormap = R_GetTranslationColormap(cv_chooseskin.value-1, cv_playercolor.value, GTC_MENUCACHE);
V_DrawMappedPatch(BASEVIDWIDTH-x - SHORT(facewantprefix[cv_chooseskin.value-1]->width), y, 0, facewantprefix[cv_chooseskin.value-1], colormap);
}
@@ -6610,7 +6652,7 @@ void M_DrawTimeAttackMenu(void)
if (em->collected)
V_DrawMappedPatch(BASEVIDWIDTH - 64 - 24, y+48, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE),
- R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
+ R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_MENUCACHE));
else
V_DrawScaledPatch(BASEVIDWIDTH - 64 - 24, y+48, 0, W_CachePatchName("NEEDIT", PU_CACHE));
@@ -6770,7 +6812,7 @@ static boolean M_QuitTimeAttackMenu(void)
if (em->collected)
V_DrawSmallMappedPatch(160+88, yHeight, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE),
- R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
+ R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_MENUCACHE));
else
V_DrawSmallScaledPatch(160+88, yHeight, 0, W_CachePatchName("NEEDIT", PU_CACHE));
@@ -7239,6 +7281,7 @@ static void M_DrawConnectMenu(void)
{
UINT16 i, j;
const char *gt = "Unknown";
+ const char *spd = "";
INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE;
for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++)
@@ -7292,7 +7335,17 @@ static void M_DrawConnectMenu(void)
V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags,
va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer));
- V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, va("Gametype: %s", gt));
+ V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, gt);
+
+ if (serverlist[slindex].info.gametype == GT_RACE)
+ {
+ spd = kartspeed_cons_t[serverlist[slindex].info.kartvars & SV_SPEEDMASK].strvalue;
+
+ V_DrawSmallString(currentMenu->x+132, S_LINEY(i)+8, globalflags, va("(%s Speed)", spd));
+ }
+
+ if (serverlist[slindex].info.kartvars & SV_PASSWORD)
+ V_DrawFixedPatch((currentMenu->x - 9) << FRACBITS, (S_LINEY(i)) << FRACBITS, FRACUNIT, globalflags & (~V_ALLOWLOWERCASE), W_CachePatchName("SERVLOCK", PU_CACHE), NULL);
MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL;
}
@@ -7408,6 +7461,20 @@ static void M_ConnectMenu(INT32 choice)
M_Refresh(0);
}
+static void M_ConnectMenuModChecks(INT32 choice)
+{
+ (void)choice;
+ // okay never mind we want to COMMUNICATE to the player pre-emptively instead of letting them try and then get confused when it doesn't work
+
+ if (modifiedgame)
+ {
+ M_StartMessage(M_GetText("Add-ons are currently loaded.\n\nYou will only be able to join a server if\nit has the same ones loaded in the same order, which may be unlikely.\n\nIf you wish to play on other servers,\nrestart the game to clear existing add-ons.\n\n(Press a key)\n"),M_ConnectMenu,MM_EVENTHANDLER);
+ return;
+ }
+
+ M_ConnectMenu(-1);
+}
+
static UINT32 roomIds[NUM_LIST_ROOMS];
static void M_RoomMenu(INT32 choice)
@@ -7513,6 +7580,11 @@ static void M_StartServer(INT32 choice)
// Still need to reset devmode
cv_debug = 0;
+ if (strlen(cv_dummyjoinpassword.string) > 0)
+ D_SetJoinPassword(cv_dummyjoinpassword.string);
+ else
+ joinpasswordset = false;
+
if (demoplayback)
G_StopDemo();
if (metalrecording)
@@ -7732,7 +7804,7 @@ static void M_StartServerMenu(INT32 choice)
// CONNECT VIA IP
// ==============
-static char setupm_ip[16];
+static char setupm_ip[28];
#endif
static UINT8 setupm_pselect = 1;
@@ -7765,12 +7837,12 @@ Update the maxplayers label...
V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 239);
// draw name string
- V_DrawString(x+8,y+12, V_MONOSPACE, setupm_ip);
+ V_DrawString(x+8,y+12, V_ALLOWLOWERCASE, setupm_ip);
// draw text cursor for name
if (itemOn == 8
&& skullAnimCounter < 4) //blink cursor
- V_DrawCharacter(x+8+V_StringWidth(setupm_ip, V_MONOSPACE),y+12,'_',false);
+ V_DrawCharacter(x+8+V_StringWidth(setupm_ip, V_ALLOWLOWERCASE),y+12,'_',false);
#endif
// character bar, ripped off the color bar :V
@@ -7814,7 +7886,7 @@ Update the maxplayers label...
if (!trans && i > cv_splitplayers.value)
trans = V_TRANSLUCENT;
- colmap = R_GetTranslationColormap(pskin, pcol, 0);
+ colmap = R_GetTranslationColormap(pskin, pcol, GTC_MENUCACHE);
V_DrawFixedPatch(x<= 16-1)
+ if (l >= 28-1)
break;
- if (choice == 46 || (choice >= 48 && choice <= 57)) // Rudimentary number and period enforcing
+ // Rudimentary number and period enforcing - also allows letters so hostnames can be used instead
+ if ((choice >= '-' && choice <= ':') || (choice >= 'A' && choice <= 'Z') || (choice >= 'a' && choice <= 'z'))
{
S_StartSound(NULL,sfx_menu1); // Tails
setupm_ip[l] = (char)choice;
@@ -8001,7 +8074,6 @@ static void M_HandleConnectIP(INT32 choice)
setupm_ip[l] = (char)choice;
setupm_ip[l+1] = 0;
}
-
break;
}
@@ -8125,7 +8197,7 @@ static void M_DrawSetupMultiPlayerMenu(void)
statdot = W_CachePatchName("K_SDOT2", PU_CACHE); // coloured center
if (setupm_fakecolor)
- V_DrawFixedPatch(((BASEVIDWIDTH - mx - 80) + ((speed-1)*8))<num_frames_to_write = apng_frames;
-
- png_save_uint_32(data, apng_frames);
- png_save_uint_32(data + 4, 0);
-
- oldpos = ftell(apng_FILE);
-
- if (M_PNGfind_acTL())
- png_write_chunk(png_ptr, (png_bytep)acTL_cn, data, (png_size_t)8);
-
- fseek(apng_FILE, oldpos, SEEK_SET);
}
static boolean M_SetupaPNG(png_const_charp filename, png_bytep pal)
@@ -1010,6 +970,16 @@ static boolean M_SetupaPNG(png_const_charp filename, png_bytep pal)
return false;
}
+ apng_ainfo_ptr = apng_create_info_struct(apng_ptr);
+ if (!apng_ainfo_ptr)
+ {
+ CONS_Debug(DBG_RENDER, "M_StartMovie: Error on allocate for apng\n");
+ png_destroy_write_struct(&apng_ptr, &apng_info_ptr);
+ fclose(apng_FILE);
+ remove(filename);
+ return false;
+ }
+
png_init_io(apng_ptr, apng_FILE);
#ifdef PNG_SET_USER_LIMITS_SUPPORTED
@@ -1027,12 +997,11 @@ static boolean M_SetupaPNG(png_const_charp filename, png_bytep pal)
M_PNGText(apng_ptr, apng_info_ptr, true);
-#ifndef PNG_STATIC
- if (apng_set_acTL)
-#endif
- apng_set_acTL(apng_ptr, apng_info_ptr, PNG_UINT_31_MAX, 0);
+ apng_set_set_acTL_fn(apng_ptr, apng_ainfo_ptr, aPNG_set_acTL);
- png_write_info(apng_ptr, apng_info_ptr);
+ apng_set_acTL(apng_ptr, apng_info_ptr, apng_ainfo_ptr, PNG_UINT_31_MAX, 0);
+
+ apng_write_info(apng_ptr, apng_info_ptr, apng_ainfo_ptr);
apng_frames = 0;
@@ -1235,8 +1204,8 @@ void M_StopMovie(void)
if (apng_frames)
{
- M_PNGfix_acTL(apng_ptr, apng_info_ptr);
- png_write_end(apng_ptr, apng_info_ptr);
+ M_PNGfix_acTL(apng_ptr, apng_info_ptr, apng_ainfo_ptr);
+ apng_write_end(apng_ptr, apng_info_ptr, apng_ainfo_ptr);
}
png_destroy_write_struct(&apng_ptr, &apng_info_ptr);
diff --git a/src/md5.h b/src/md5.h
index 0fe017f5..eaa85dc1 100644
--- a/src/md5.h
+++ b/src/md5.h
@@ -22,6 +22,8 @@
# include
#endif
+#define MD5_LEN 16
+
/* The following contortions are an attempt to use the C preprocessor
to determine an unsigned integral type that is 32 bits wide. An
alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but
diff --git a/src/p_enemy.c b/src/p_enemy.c
index fc3ae9d9..d62ec7ef 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -8264,8 +8264,8 @@ void A_JawzChase(mobj_t *actor)
if (actor->tracer)
{
- if (G_RaceGametype()) // Stop looking after first target in race
- actor->extravalue1 = 1;
+ /*if (G_RaceGametype()) // Stop looking after first target in race
+ actor->extravalue1 = 1;*/
if (actor->tracer->health)
{
@@ -8364,13 +8364,13 @@ void A_SPBChase(mobj_t *actor)
if (!playeringame[i] || players[i].spectator || players[i].exiting)
continue; // not in-game
- if (!players[i].mo)
+ /*if (!players[i].mo)
continue; // no mobj
if (players[i].mo->health <= 0)
continue; // dead
- /*if (players[i].kartstuff[k_respawn])
+ if (players[i].kartstuff[k_respawn])
continue;*/ // respawning
if (players[i].kartstuff[k_position] < bestrank)
@@ -8386,18 +8386,35 @@ void A_SPBChase(mobj_t *actor)
{
fixed_t defspeed = wspeed;
fixed_t range = (160*actor->tracer->scale);
+ fixed_t cx = 0, cy =0;
+
+ // Play the intimidating gurgle
+ if (!S_SoundPlaying(actor, actor->info->activesound))
+ S_StartSound(actor, actor->info->activesound);
// Maybe we want SPB to target an object later? IDK lol
- if (actor->tracer->player) // 7/8ths max speed for Knuckles, 3/4ths max speed for min accel, exactly max speed for max accel
+ if (actor->tracer->player)
{
+ UINT8 fracmax = 32;
+ UINT8 spark = ((10-actor->tracer->player->kartspeed) + actor->tracer->player->kartweight) / 2;
+ fixed_t easiness = ((actor->tracer->player->kartspeed + (10-spark)) << FRACBITS) / 2;
+
actor->lastlook = actor->tracer->player-players; // Save the player num for death scumming...
if (!P_IsObjectOnGround(actor->tracer) /*&& !actor->tracer->player->kartstuff[k_pogospring]*/)
- defspeed = (7*actor->tracer->player->speed)/8; // In the air you have no control; basically don't hit unless you make a near complete stop
+ {
+ // In the air you have no control; basically don't hit unless you make a near complete stop
+ defspeed = (7 * actor->tracer->player->speed) / 8;
+ }
else
- defspeed = ((33 - actor->tracer->player->kartspeed) * K_GetKartSpeed(actor->tracer->player, false)) / 32;
+ {
+ // 7/8ths max speed for Knuckles, 3/4ths max speed for min accel, exactly max speed for max accel
+ defspeed = FixedMul(((fracmax+1)<tracer->player, false)) / fracmax;
+ }
- defspeed -= (9*R_PointToDist2(0, 0, actor->tracer->player->cmomx, actor->tracer->player->cmomy))/8; // Be fairer on conveyors
+ // Be fairer on conveyors
+ cx = actor->tracer->player->cmomx;
+ cy = actor->tracer->player->cmomy;
// Switch targets if you're no longer 1st for long enough
if (actor->tracer->player->kartstuff[k_position] <= bestrank)
@@ -8408,10 +8425,6 @@ void A_SPBChase(mobj_t *actor)
spbplace = actor->tracer->player->kartstuff[k_position];
}
- // Play the intimidating gurgle
- if (!S_SoundPlaying(actor, actor->info->activesound))
- S_StartSound(actor, actor->info->activesound);
-
dist = P_AproxDistance(P_AproxDistance(actor->x-actor->tracer->x, actor->y-actor->tracer->y), actor->z-actor->tracer->z);
wspeed = FixedMul(defspeed, FRACUNIT + FixedDiv(dist-range, range));
@@ -8419,10 +8432,18 @@ void A_SPBChase(mobj_t *actor)
wspeed = defspeed;
if (wspeed > (3*defspeed)/2)
wspeed = (3*defspeed)/2;
+ if (wspeed < 20*actor->tracer->scale)
+ wspeed = 20*actor->tracer->scale;
hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
vang = R_PointToAngle2(0, actor->z, dist, actor->tracer->z);
+ // Modify stored speed
+ if (wspeed > actor->cvmem)
+ actor->cvmem += (wspeed - actor->cvmem) / TICRATE;
+ else
+ actor->cvmem = wspeed;
+
{
// Smoothly rotate horz angle
angle_t input = hang - actor->angle;
@@ -8431,7 +8452,7 @@ void A_SPBChase(mobj_t *actor)
input = InvAngle(input);
// Slow down when turning; it looks better and makes U-turns not unfair
- xyspeed = FixedMul(wspeed, max(0, (((180<cvmem, max(0, (((180<cvmem, max(0, (((180<movedir += input;
}
- actor->momx = FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT));
- actor->momy = FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT));
+ actor->momx = cx + FixedMul(FixedMul(xyspeed, FINECOSINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT));
+ actor->momy = cy + FixedMul(FixedMul(xyspeed, FINESINE(actor->angle>>ANGLETOFINESHIFT)), FINECOSINE(actor->movedir>>ANGLETOFINESHIFT));
actor->momz = FixedMul(zspeed, FINESINE(actor->movedir>>ANGLETOFINESHIFT));
// Red speed lines for when it's gaining on its target. A tell for when you're starting to lose too much speed!
@@ -8491,15 +8512,18 @@ void A_SPBChase(mobj_t *actor)
{
actor->momx = actor->momy = actor->momz = 0; // Stoooop
- if (actor->lastlook != -1 && playeringame[actor->lastlook] && players[actor->lastlook].mo)
+ if (actor->lastlook != -1
+ && playeringame[actor->lastlook]
+ && !players[actor->lastlook].spectator
+ && !players[actor->lastlook].exiting)
{
spbplace = players[actor->lastlook].kartstuff[k_position];
- if (actor->extravalue2-- <= 0)
+ if (actor->extravalue2-- <= 0 && players[actor->lastlook].mo)
{
P_SetTarget(&actor->tracer, players[actor->lastlook].mo);
- actor->extravalue1 = 1; // TARGETING
+ actor->extravalue1 = 1; // TARGET ACQUIRED
actor->extravalue2 = 7*TICRATE;
- actor->extravalue2 = 0;
+ actor->cvmem = wspeed;
}
}
else
@@ -8513,22 +8537,12 @@ void A_SPBChase(mobj_t *actor)
{
actor->lastlook = -1; // Just make sure this is reset
- // No one there?
- if (player == NULL || !player->mo)
+ if (!player || !player->mo || player->mo->health <= 0 || player->kartstuff[k_respawn])
{
-#if 0
- // SELF-DESTRUCT?
- mobj_t *spbexplode;
-
- S_StopSound(actor); // Don't continue playing the gurgle or the siren
- spbexplode = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPBEXPLOSION);
- P_SetTarget(&spbexplode->target, actor->target);
-
- P_RemoveMobj(actor);
-#else
+ // No one there? Completely STOP.
actor->momx = actor->momy = actor->momz = 0;
-#endif
- spbplace = -1;
+ if (!player)
+ spbplace = -1;
return;
}
@@ -8582,6 +8596,7 @@ void A_SPBChase(mobj_t *actor)
S_StartSound(actor, actor->info->attacksound); // Siren sound; might not need this anymore, but I'm keeping it for now just for debugging.
actor->extravalue1 = 1; // TARGET ACQUIRED
actor->extravalue2 = 7*TICRATE;
+ actor->cvmem = wspeed;
}
}
@@ -8625,6 +8640,7 @@ void A_LightningFollowPlayer(mobj_t *actor)
else // else just teleport to player directly
P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z);
+ K_MatchGenericExtraFlags(actor, actor->target); // copy our target for graviflip
actor->momx = actor->target->momx;
actor->momy = actor->target->momy;
actor->momz = actor->target->momz; // Give momentum since we don't teleport to our player literally every frame.
diff --git a/src/p_inter.c b/src/p_inter.c
index f9cccd69..673df055 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -141,6 +141,9 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
|| (weapon != 3 && player->kartstuff[k_itemamount])
|| player->kartstuff[k_itemheld])
return false;
+
+ if (weapon == 3 && player->kartstuff[k_itemtype] == KITEM_THUNDERSHIELD)
+ return false; // No stacking thunder shields!
}
}
@@ -411,6 +414,12 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
player->kartstuff[k_roulettetype] = 2;
}
+#if 0
+ // Eggbox snipe!
+ if (special->type == MT_EGGMANITEM && special->health > 1)
+ S_StartSound(toucher, sfx_bsnipe);
+#endif
+
{
mobj_t *poof = P_SpawnMobj(special->x, special->y, special->z, MT_EXPLODE);
S_StartSound(poof, special->info->deathsound);
@@ -579,30 +588,31 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (special->health <= 0 || toucher->health <= 0)
return;
- if (!player->mo || player->spectator)
+ if (player->spectator)
return;
- if (special->tracer && toucher == special->tracer)
+ if (special->tracer && !P_MobjWasRemoved(special->tracer) && toucher == special->tracer)
{
mobj_t *spbexplode;
- S_StopSound(special); // Don't continue playing the gurgle or the siren
-
- if (!player->kartstuff[k_invincibilitytimer] && !player->kartstuff[k_growshrinktimer])
+ if (player->kartstuff[k_invincibilitytimer] > 0 || player->kartstuff[k_growshrinktimer] > 0 || player->kartstuff[k_hyudorotimer] > 0)
{
+ //player->powers[pw_flashing] = 0;
K_DropHnextList(player);
K_StripItems(player);
- //player->powers[pw_flashing] = 0;
}
+ S_StopSound(special); // Don't continue playing the gurgle or the siren
+
spbexplode = P_SpawnMobj(toucher->x, toucher->y, toucher->z, MT_SPBEXPLOSION);
spbexplode->extravalue1 = 1; // Tell K_ExplodePlayer to use extra knockback
- P_SetTarget(&spbexplode->target, special->target);
+ if (special->target && !P_MobjWasRemoved(special->target))
+ P_SetTarget(&spbexplode->target, special->target);
P_RemoveMobj(special);
}
else
- K_SpinPlayer(player, NULL, 0, special, false);
+ K_SpinPlayer(player, special->target, 0, special, false);
return;
/*case MT_EERIEFOG:
special->frame &= ~FF_TRANS80;
@@ -1467,6 +1477,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
player->starpostz = special->z>>FRACBITS;
player->starpostangle = special->angle;
player->starpostnum = special->health;
+ player->kartstuff[k_starpostflip] = special->spawnpoint->options & MTF_OBJECTFLIP; // store flipping
//S_StartSound(toucher, special->info->painsound);
return;
@@ -2951,66 +2962,6 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
}
}
-/*
-static inline void P_SuperDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage) // SRB2kart - unused.
-{
- fixed_t fallbackspeed;
- angle_t ang;
-
- P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
-
- if (player->mo->eflags & MFE_VERTICALFLIP)
- player->mo->z--;
- else
- player->mo->z++;
-
- if (player->mo->eflags & MFE_UNDERWATER)
- P_SetObjectMomZ(player->mo, FixedDiv(10511*FRACUNIT,2600*FRACUNIT), false);
- else
- P_SetObjectMomZ(player->mo, FixedDiv(69*FRACUNIT,10*FRACUNIT), false);
-
- ang = R_PointToAngle2(inflictor->x, inflictor->y, player->mo->x, player->mo->y);
-
- // explosion and rail rings send you farther back, making it more difficult
- // to recover
- if (inflictor->flags2 & MF2_SCATTER && source)
- {
- fixed_t dist = P_AproxDistance(P_AproxDistance(source->x-player->mo->x, source->y-player->mo->y), source->z-player->mo->z);
-
- dist = FixedMul(128*FRACUNIT, inflictor->scale) - dist/4;
-
- if (dist < FixedMul(4*FRACUNIT, inflictor->scale))
- dist = FixedMul(4*FRACUNIT, inflictor->scale);
-
- fallbackspeed = dist;
- }
- else if (inflictor->flags2 & MF2_EXPLOSION)
- {
- if (inflictor->flags2 & MF2_RAILRING)
- fallbackspeed = FixedMul(28*FRACUNIT, inflictor->scale); // 7x
- else
- fallbackspeed = FixedMul(20*FRACUNIT, inflictor->scale); // 5x
- }
- else if (inflictor->flags2 & MF2_RAILRING)
- fallbackspeed = FixedMul(16*FRACUNIT, inflictor->scale); // 4x
- else
- fallbackspeed = FixedMul(4*FRACUNIT, inflictor->scale); // the usual amount of force
-
- P_InstaThrust(player->mo, ang, fallbackspeed);
-
- // SRB2kart - This shouldn't be reachable, but this frame is invalid.
- //if (player->charflags & SF_SUPERANIMS)
- // P_SetPlayerMobjState(player->mo, S_PLAY_SUPERHIT);
- //else
- P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
-
- P_ResetPlayer(player);
-
- if (player->timeshit != UINT8_MAX)
- ++player->timeshit;
-}
-*/
-
void P_RemoveShield(player_t *player)
{
if (player->powers[pw_shield] & SH_FORCE)
diff --git a/src/p_local.h b/src/p_local.h
index 0b64307d..1ac613bd 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -128,7 +128,6 @@ extern fixed_t t_cam4_dist, t_cam4_height, t_cam4_rotate;
fixed_t P_GetPlayerHeight(player_t *player);
fixed_t P_GetPlayerSpinHeight(player_t *player);
-INT32 P_GetPlayerControlDirection(player_t *player);
void P_AddPlayerScore(player_t *player, UINT32 amount);
void P_ResetCamera(player_t *player, camera_t *thiscam);
boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam);
@@ -275,8 +274,6 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
void P_Attract(mobj_t *source, mobj_t *enemy, boolean nightsgrab);
mobj_t *P_GetClosestAxis(mobj_t *source);
-boolean P_CanRunOnWater(player_t *player, ffloor_t *rover);
-
void P_FlashPal(player_t *pl, UINT16 type, UINT16 duration);
#define PAL_WHITE 1
#define PAL_MIXUP 2
diff --git a/src/p_map.c b/src/p_map.c
index c307e572..256c9cef 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -747,14 +747,15 @@ static boolean PIT_CheckThing(mobj_t *thing)
&& !(tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ || tmthing->type == MT_JAWZ_DUD))
return true;
+ if (thing->player && thing->player->kartstuff[k_hyudorotimer])
+ return true; // no interaction
+
if (thing->type == MT_PLAYER)
{
// Player Damage
P_DamageMobj(thing, tmthing, tmthing->target, 1);
K_KartBouncing(thing, tmthing, false, false);
-
- if (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ || tmthing->type == MT_JAWZ_DUD)
- S_StartSound(thing, sfx_s3k7b);
+ S_StartSound(thing, sfx_s3k7b);
// This Item Damage
if (tmthing->eflags & MFE_VERTICALFLIP)
@@ -852,7 +853,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (thing->type == MT_PLAYER)
{
- S_StartSound(NULL, sfx_cgot); //let all players hear it.
+ S_StartSound(NULL, sfx_bsnipe); //let all players hear it.
HU_SetCEchoFlags(0);
HU_SetCEchoDuration(5);
HU_DoCEcho(va("%s\\was hit by a kitchen sink.\\\\\\\\", player_names[thing->player-players]));
@@ -914,6 +915,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (thing->type == MT_PLAYER)
{
+ // Banana snipe!
+ if (tmthing->type == MT_BANANA && tmthing->health > 1)
+ S_StartSound(thing, sfx_bsnipe);
+
// Player Damage
K_SpinPlayer(thing->player, tmthing->target, 0, tmthing, (tmthing->type == MT_BANANA || tmthing->type == MT_BANANA_SHIELD));
@@ -982,7 +987,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (thing->type == MT_PLAYER)
{
- P_KillMobj(tmthing, thing, thing);
+ // Bomb punting
+ if ((tmthing->state >= &states[S_SSMINE1] && tmthing->state <= &states[S_SSMINE4])
+ || (tmthing->state >= &states[S_SSMINE_DEPLOY8] && tmthing->state <= &states[S_SSMINE_DEPLOY13]))
+ P_KillMobj(tmthing, thing, thing);
+ else
+ K_PuntMine(tmthing, thing);
}
else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD
|| thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD)
@@ -1023,6 +1033,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
&& !(thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD))
return true;
+ if (tmthing->player && tmthing->player->kartstuff[k_hyudorotimer]) // I thought about doing this for just the objects below but figured it should apply to everything.
+ return true; // no interaction
+
if (thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD
|| thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD)
{
@@ -1035,9 +1048,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
// Player Damage
P_DamageMobj(tmthing, thing, thing->target, 1);
K_KartBouncing(tmthing, thing, false, false);
-
- if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ || thing->type == MT_JAWZ_DUD)
- S_StartSound(tmthing, sfx_s3k7b);
+ S_StartSound(tmthing, sfx_s3k7b);
// Other Item Damage
if (thing->eflags & MFE_VERTICALFLIP)
@@ -1060,6 +1071,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->health <= 0 || thing->health <= 0)
return true;
+ // Banana snipe!
+ if (thing->type == MT_BANANA && thing->health > 1)
+ S_StartSound(tmthing, sfx_bsnipe);
+
// Player Damage
K_SpinPlayer(tmthing->player, thing->target, 0, tmthing, (thing->type == MT_BANANA || thing->type == MT_BANANA_SHIELD));
@@ -1083,7 +1098,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->health <= 0 || thing->health <= 0)
return true;
- P_KillMobj(thing, tmthing, tmthing);
+ // Bomb punting
+ if ((thing->state >= &states[S_SSMINE1] && thing->state <= &states[S_SSMINE4])
+ || (thing->state >= &states[S_SSMINE_DEPLOY8] && thing->state <= &states[S_SSMINE_DEPLOY13]))
+ P_KillMobj(thing, tmthing, tmthing);
+ else
+ K_PuntMine(thing, tmthing);
}
else if (thing->type == MT_MINEEXPLOSION && tmthing->player)
{
@@ -2094,7 +2114,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
continue;
}
- if (thing->player && (P_CheckSolidLava(thing, rover) || P_CanRunOnWater(thing->player, rover)))
+ if (thing->player && P_CheckSolidLava(thing, rover))
;
else if (thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE))
;
@@ -2767,8 +2787,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
thing->eflags |= MFE_JUSTSTEPPEDDOWN;
}
#ifdef ESLOPE
- // HACK TO FIX DSZ2: apply only if slopes are involved
- else if (tmceilingslope && tmceilingz < thingtop && thingtop - tmceilingz <= maxstep)
+ else if (tmceilingz < thingtop && thingtop - tmceilingz <= maxstep)
{
thing->z = (thing->ceilingz = thingtop = tmceilingz) - thing->height;
thing->eflags |= MFE_JUSTSTEPPEDDOWN;
@@ -2781,8 +2800,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
thing->eflags |= MFE_JUSTSTEPPEDDOWN;
}
#ifdef ESLOPE
- // HACK TO FIX DSZ2: apply only if slopes are involved
- else if (tmfloorslope && tmfloorz > thing->z && tmfloorz - thing->z <= maxstep)
+ else if (tmfloorz > thing->z && tmfloorz - thing->z <= maxstep)
{
thing->z = thing->floorz = tmfloorz;
thing->eflags |= MFE_JUSTSTEPPEDDOWN;
@@ -3231,129 +3249,6 @@ isblocking:
return false; // stop
}
-//
-// P_IsClimbingValid
-//
-// Unlike P_DoClimbing, don't use when up against a one-sided linedef.
-//
-static boolean P_IsClimbingValid(player_t *player, angle_t angle)
-{
- fixed_t platx, platy;
- subsector_t *glidesector;
- fixed_t floorz, ceilingz;
-
- platx = P_ReturnThrustX(player->mo, angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
- platy = P_ReturnThrustY(player->mo, angle, player->mo->radius + FixedMul(8*FRACUNIT, player->mo->scale));
-
- glidesector = R_PointInSubsector(player->mo->x + platx, player->mo->y + platy);
-
-#ifdef ESLOPE
- floorz = glidesector->sector->f_slope ? P_GetZAt(glidesector->sector->f_slope, player->mo->x, player->mo->y) : glidesector->sector->floorheight;
- ceilingz = glidesector->sector->c_slope ? P_GetZAt(glidesector->sector->c_slope, player->mo->x, player->mo->y) : glidesector->sector->ceilingheight;
-#else
- floorz = glidesector->sector->floorheight;
- ceilingz = glidesector->sector->ceilingheight;
-#endif
-
- if (glidesector->sector != player->mo->subsector->sector)
- {
- boolean floorclimb = false;
- fixed_t topheight, bottomheight;
-
- if (glidesector->sector->ffloors)
- {
- ffloor_t *rover;
- for (rover = glidesector->sector->ffloors; rover; rover = rover->next)
- {
- if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER))
- continue;
-
- topheight = *rover->topheight;
- bottomheight = *rover->bottomheight;
-
-#ifdef ESLOPE
- if (*rover->t_slope)
- topheight = P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y);
- if (*rover->b_slope)
- bottomheight = P_GetZAt(*rover->b_slope, player->mo->x, player->mo->y);
-#endif
-
- floorclimb = true;
-
- if (player->mo->eflags & MFE_VERTICALFLIP)
- {
- if ((topheight < player->mo->z + player->mo->height) && ((player->mo->z + player->mo->height + player->mo->momz) < topheight))
- {
- floorclimb = true;
- }
- if (topheight < player->mo->z) // Waaaay below the ledge.
- {
- floorclimb = false;
- }
- if (bottomheight > player->mo->z + player->mo->height - FixedMul(16*FRACUNIT,player->mo->scale))
- {
- floorclimb = false;
- }
- }
- else
- {
- if ((bottomheight > player->mo->z) && ((player->mo->z - player->mo->momz) > bottomheight))
- {
- floorclimb = true;
- }
- if (bottomheight > player->mo->z + player->mo->height) // Waaaay below the ledge.
- {
- floorclimb = false;
- }
- if (topheight < player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale))
- {
- floorclimb = false;
- }
- }
-
- if (floorclimb)
- break;
- }
- }
-
- if (player->mo->eflags & MFE_VERTICALFLIP)
- {
- if ((floorz <= player->mo->z + player->mo->height)
- && ((player->mo->z + player->mo->height - player->mo->momz) <= floorz))
- floorclimb = true;
-
- if ((floorz > player->mo->z)
- && glidesector->sector->floorpic == skyflatnum)
- return false;
-
- if ((player->mo->z + player->mo->height - FixedMul(16*FRACUNIT,player->mo->scale) > ceilingz)
- || (player->mo->z + player->mo->height <= floorz))
- floorclimb = true;
- }
- else
- {
- if ((ceilingz >= player->mo->z)
- && ((player->mo->z - player->mo->momz) >= ceilingz))
- floorclimb = true;
-
- if ((ceilingz < player->mo->z+player->mo->height)
- && glidesector->sector->ceilingpic == skyflatnum)
- return false;
-
- if ((player->mo->z + FixedMul(16*FRACUNIT,player->mo->scale) < ceilingz)
- || (player->mo->z >= ceilingz))
- floorclimb = true;
- }
-
- if (!floorclimb)
- return false;
-
- return true;
- }
-
- return false;
-}
-
//
// PTR_SlideTraverse
//
@@ -3407,117 +3302,7 @@ isblocking:
P_ProcessSpecialSector(slidemo->player, slidemo->subsector->sector, li->polyobj->lines[0]->backsector);
}
- if (slidemo->player && (slidemo->player->pflags & PF_GLIDING || slidemo->player->climbing)
- && slidemo->player->charability == CA_GLIDEANDCLIMB)
- {
- line_t *checkline = li;
- sector_t *checksector;
- ffloor_t *rover;
- fixed_t topheight, bottomheight;
- boolean fofline = false;
- INT32 side = P_PointOnLineSide(slidemo->x, slidemo->y, li);
-
- if (!side && li->backsector)
- checksector = li->backsector;
- else
- checksector = li->frontsector;
-
- if (checksector->ffloors)
- {
- for (rover = checksector->ffloors; rover; rover = rover->next)
- {
- if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER) || (rover->flags & FF_BUSTUP))
- continue;
-
- topheight = *rover->topheight;
- bottomheight = *rover->bottomheight;
-
-#ifdef ESLOPE
- if (*rover->t_slope)
- topheight = P_GetZAt(*rover->t_slope, slidemo->x, slidemo->y);
- if (*rover->b_slope)
- bottomheight = P_GetZAt(*rover->b_slope, slidemo->x, slidemo->y);
-#endif
-
- if (topheight < slidemo->z)
- continue;
-
- if (bottomheight > slidemo->z + slidemo->height)
- continue;
-
- // Got this far, so I guess it's climbable. // TODO: Climbing check, also, better method to do this?
- if (rover->master->flags & ML_TFERLINE)
- {
- size_t linenum = li-checksector->lines[0];
- checkline = rover->master->frontsector->lines[0] + linenum;
- fofline = true;
- }
-
- break;
- }
- }
-
- // see about climbing on the wall
- if (!(checkline->flags & ML_NOCLIMB))
- {
- boolean canclimb;
- angle_t climbangle, climbline;
- INT32 whichside = P_PointOnLineSide(slidemo->x, slidemo->y, li);
-
- climbangle = climbline = R_PointToAngle2(li->v1->x, li->v1->y, li->v2->x, li->v2->y);
-
- if (whichside) // on second side?
- climbline += ANGLE_180;
-
- climbangle += (ANGLE_90 * (whichside ? -1 : 1));
-
- canclimb = (li->backsector ? P_IsClimbingValid(slidemo->player, climbangle) : true);
-
- if (((!slidemo->player->climbing && abs((signed)(slidemo->angle - ANGLE_90 - climbline)) < ANGLE_45)
- || (slidemo->player->climbing == 1 && abs((signed)(slidemo->angle - climbline)) < ANGLE_135))
- && canclimb)
- {
- slidemo->angle = climbangle;
- if (!demoplayback || P_AnalogMove(slidemo->player))
- {
- if (slidemo->player == &players[consoleplayer])
- localangle = slidemo->angle;
- else if (slidemo->player == &players[secondarydisplayplayer])
- localangle2 = slidemo->angle;
- else if (slidemo->player == &players[thirddisplayplayer])
- localangle3 = slidemo->angle;
- else if (slidemo->player == &players[fourthdisplayplayer])
- localangle4 = slidemo->angle;
- }
-
- if (!slidemo->player->climbing)
- {
- S_StartSound(slidemo->player->mo, sfx_s3k4a);
- slidemo->player->climbing = 5;
- }
-
- slidemo->player->pflags &= ~(PF_GLIDING|PF_SPINNING|PF_JUMPED|PF_THOKKED);
- slidemo->player->glidetime = 0;
- slidemo->player->secondjump = 0;
-
- if (slidemo->player->climbing > 1)
- slidemo->momz = slidemo->momx = slidemo->momy = 0;
-
- if (fofline)
- whichside = 0;
-
- if (!whichside)
- {
- slidemo->player->lastsidehit = checkline->sidenum[whichside];
- slidemo->player->lastlinehit = (INT16)(checkline - lines);
- }
-
- P_Thrust(slidemo, slidemo->angle, FixedMul(5*FRACUNIT, slidemo->scale));
- }
- }
- }
-
- if (in->frac < bestslidefrac && (!slidemo->player || !slidemo->player->climbing))
+ if (in->frac < bestslidefrac)
{
secondslidefrac = bestslidefrac;
secondslideline = bestslideline;
diff --git a/src/p_maputl.c b/src/p_maputl.c
index 1be57399..c5a593d3 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -649,7 +649,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
if (!(rover->flags & FF_EXISTS))
continue;
- if (mobj->player && (P_CheckSolidLava(mobj, rover) || P_CanRunOnWater(mobj->player, rover)))
+ if (mobj->player && P_CheckSolidLava(mobj, rover))
;
else if (!((rover->flags & FF_BLOCKPLAYER && mobj->player)
|| (rover->flags & FF_BLOCKOTHERS && !mobj->player)))
@@ -693,7 +693,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
if (!(rover->flags & FF_EXISTS))
continue;
- if (mobj->player && (P_CheckSolidLava(mobj, rover) || P_CanRunOnWater(mobj->player, rover)))
+ if (mobj->player && P_CheckSolidLava(mobj, rover))
;
else if (!((rover->flags & FF_BLOCKPLAYER && mobj->player)
|| (rover->flags & FF_BLOCKOTHERS && !mobj->player)))
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 4379b958..e1ac3f2d 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -167,58 +167,8 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
I_Error("P_SetPlayerMobjState used for non-player mobj. Use P_SetMobjState instead!\n(Mobj type: %d, State: %d)", mobj->type, state);
#endif
- // Catch state changes for Super Sonic
- /* // SRB2kart - don't need
- if (player->powers[pw_super] && (player->charflags & SF_SUPERANIMS))
- {
- switch (state)
- {
- case S_PLAY_STND:
- case S_PLAY_TAP1:
- case S_PLAY_TAP2:
- case S_PLAY_GASP:
- P_SetPlayerMobjState(mobj, S_PLAY_SUPERSTAND);
- return true;
- case S_PLAY_FALL1:
- case S_PLAY_SPRING:
- case S_PLAY_RUN1:
- case S_PLAY_RUN2:
- case S_PLAY_RUN3:
- case S_PLAY_RUN4:
- P_SetPlayerMobjState(mobj, S_PLAY_SUPERWALK1);
- return true;
- case S_PLAY_FALL2:
- case S_PLAY_RUN5:
- case S_PLAY_RUN6:
- case S_PLAY_RUN7:
- case S_PLAY_RUN8:
- P_SetPlayerMobjState(mobj, S_PLAY_SUPERWALK2);
- return true;
- case S_PLAY_SPD1:
- case S_PLAY_SPD2:
- P_SetPlayerMobjState(mobj, S_PLAY_SUPERFLY1);
- return true;
- case S_PLAY_SPD3:
- case S_PLAY_SPD4:
- P_SetPlayerMobjState(mobj, S_PLAY_SUPERFLY2);
- return true;
- case S_PLAY_TEETER1:
- case S_PLAY_TEETER2:
- P_SetPlayerMobjState(mobj, S_PLAY_SUPERTEETER);
- return true;
- case S_PLAY_ATK1:
- case S_PLAY_ATK2:
- case S_PLAY_ATK3:
- case S_PLAY_ATK4:
- if (!(player->charflags & SF_SUPERSPIN))
- return true;
- break;
- default:
- break;
- }
- }
// You were in pain state after taking a hit, and you're moving out of pain state now?
- else */if (mobj->state == &states[mobj->info->painstate] && player->powers[pw_flashing] == K_GetKartFlashing(player) && state != mobj->info->painstate)
+ if (mobj->state == &states[mobj->info->painstate] && player->powers[pw_flashing] == K_GetKartFlashing(player) && state != mobj->info->painstate)
{
// Start flashing, since you've landed.
player->powers[pw_flashing] = K_GetKartFlashing(player)-1;
@@ -260,51 +210,6 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
st = &states[state];
mobj->state = st;
mobj->tics = st->tics;
-
- // Adjust the player's animation speed to match their velocity.
- if (!(disableSpeedAdjust || player->charflags & SF_NOSPEEDADJUST))
- {
- fixed_t speed = FixedDiv(player->speed, FixedMul(mobj->scale, player->mo->movefactor)); // fixed_t speed = FixedDiv(player->speed, mobj->scale);
- if (player->panim == PA_ROLL)
- {
- if (speed > 16<tics = 1;
- else
- mobj->tics = 2;
- }
- else if (player->panim == PA_FALL)
- {
- speed = FixedDiv(abs(mobj->momz), mobj->scale);
- if (speed < 10<tics = 4;
- else if (speed < 20<tics = 3;
- else if (speed < 30<tics = 2;
- else
- mobj->tics = 1;
- }
- else if (P_IsObjectOnGround(mobj) || player->powers[pw_super]) // Only if on the ground or superflying.
- {
- if (player->panim == PA_WALK)
- {
- if (speed > 12<tics = 2;
- else if (speed > 6<tics = 3;
- else
- mobj->tics = 4;
- }
- else if (player->panim == PA_RUN)
- {
- if (speed > 52<tics = 1;
- else
- mobj->tics = 2;
- }
- }
- }
-
mobj->sprite = st->sprite;
mobj->frame = st->frame;
mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
@@ -1354,7 +1259,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
if (wasflip == !(mo->eflags & MFE_VERTICALFLIP)) // note!! == ! is not equivalent to != here - turns numeric into bool this way
P_PlayerFlip(mo);
if (mo->player->kartstuff[k_pogospring])
- gravityadd = FixedMul(gravityadd, 5*FRACUNIT/2);
+ gravityadd = (5*gravityadd)/2;
}
else
{
@@ -1405,10 +1310,14 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
case MT_BANANA:
case MT_EGGMANITEM:
case MT_SSMINE:
- gravityadd = FixedMul(gravityadd, 5*FRACUNIT/2);
- break;
case MT_SINK:
- gravityadd = FixedMul(gravityadd, 5*FRACUNIT); // Double gravity
+ if (mo->extravalue2 > 0)
+ gravityadd *= mo->extravalue2;
+ /* FALLTHRU */
+ case MT_ORBINAUT:
+ case MT_JAWZ:
+ case MT_JAWZ_DUD:
+ gravityadd = (5*gravityadd)/2;
break;
case MT_SIGN:
gravityadd /= 8;
@@ -2098,7 +2007,7 @@ static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motyp
topheight = P_GetFOFTopZ(mo, sector, rover, mo->x, mo->y, NULL);
bottomheight = P_GetFOFBottomZ(mo, sector, rover, mo->x, mo->y, NULL);
- if (mo->player && (P_CheckSolidLava(mo, rover) || P_CanRunOnWater(mo->player, rover))) // only the player should be affected
+ if (mo->player && P_CheckSolidLava(mo, rover)) // only the player should be affected
;
else if (motype != 0 && rover->flags & FF_SWIMMABLE) // "scenery" only
continue;
@@ -3221,28 +3130,6 @@ static boolean P_SceneryZMovement(mobj_t *mo)
return true;
}
-// P_CanRunOnWater
-//
-// Returns true if player can waterrun on the 3D floor
-//
-boolean P_CanRunOnWater(player_t *player, ffloor_t *rover)
-{
- fixed_t topheight =
-#ifdef ESLOPE
- *rover->t_slope ? P_GetZAt(*rover->t_slope, player->mo->x, player->mo->y) :
-#endif
- *rover->topheight;
-
- if (!(player->pflags & PF_NIGHTSMODE) && !player->homing
- && (((player->charability == CA_SWIM) || player->powers[pw_super] || player->charflags & SF_RUNONWATER) && player->mo->ceilingz-topheight >= player->mo->height)
- && (rover->flags & FF_SWIMMABLE) && !(player->pflags & PF_SPINNING) && player->speed > FixedMul(player->runspeed, player->mo->scale)
- && !(player->pflags & PF_SLIDING)
- && abs(player->mo->z - topheight) < FixedMul(30*FRACUNIT, player->mo->scale))
- return true;
-
- return false;
-}
-
//
// P_MobjCheckWater
//
@@ -3399,8 +3286,8 @@ void P_MobjCheckWater(mobj_t *mobj)
// skipping stone!
if (p && p->kartstuff[k_waterskip] < 2
- && ((p->speed/2 > abs(mobj->momz)) // Going more forward than horizontal, so you can skip across the water.
- || (p->speed > K_GetKartSpeed(p,false)/4 && p->kartstuff[k_waterskip])) // Already skipped once, so you can skip once more!
+ && ((p->speed/3 > abs(mobj->momz)) // Going more forward than horizontal, so you can skip across the water.
+ || (p->speed > K_GetKartSpeed(p,false)/3 && p->kartstuff[k_waterskip])) // Already skipped once, so you can skip once more!
&& ((!(mobj->eflags & MFE_VERTICALFLIP) && thingtop - mobj->momz > mobj->watertop)
|| ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z - mobj->momz < mobj->waterbottom)))
{
@@ -6637,7 +6524,7 @@ void P_MobjThinker(mobj_t *mobj)
case MT_SINK_SHIELD:
if ((mobj->health > 0
&& (!mobj->target || !mobj->target->player || mobj->target->player->health <= 0 || mobj->target->player->spectator))
- || (mobj->health <= 0 && mobj->z <= mobj->floorz)
+ || (mobj->health <= 0 && P_IsObjectOnGround(mobj))
|| P_CheckDeathPitCollide(mobj)) // When in death state
{
P_RemoveMobj(mobj);
@@ -6651,19 +6538,22 @@ void P_MobjThinker(mobj_t *mobj)
fixed_t y = P_RandomRange(-35, 35)*mobj->scale;
fixed_t z = P_RandomRange(0, 70)*mobj->scale;
mobj_t *smoke = P_SpawnMobj(mobj->x + x, mobj->y + y, mobj->z + z, MT_SMOKE);
+ P_SetMobjState(smoke, S_OPAQUESMOKE1);
+ K_MatchGenericExtraFlags(smoke, mobj);
smoke->scale = mobj->scale * 2;
smoke->destscale = mobj->scale * 6;
- smoke->momz = P_RandomRange(4, 9)*FRACUNIT;
+ smoke->momz = P_RandomRange(4, 9)*FRACUNIT*P_MobjFlip(smoke);
}
break;
case MT_BOOMPARTICLE:
{
fixed_t x = P_RandomRange(-16, 16)*mobj->scale;
fixed_t y = P_RandomRange(-16, 16)*mobj->scale;
- fixed_t z = P_RandomRange(0, 32)*mobj->scale;
+ fixed_t z = P_RandomRange(0, 32)*mobj->scale*P_MobjFlip(mobj);
if (leveltime % 2 == 0)
{
mobj_t *smoke = P_SpawnMobj(mobj->x + x, mobj->y + y, mobj->z + z, MT_BOSSEXPLODE);
+ K_MatchGenericExtraFlags(smoke, mobj);
P_SetMobjState(smoke, S_QUICKBOOM1);
smoke->scale = mobj->scale/2;
smoke->destscale = mobj->scale;
@@ -6672,6 +6562,8 @@ void P_MobjThinker(mobj_t *mobj)
else
{
mobj_t *smoke = P_SpawnMobj(mobj->x + x, mobj->y + y, mobj->z + z, MT_SMOKE);
+ P_SetMobjState(smoke, S_OPAQUESMOKE1);
+ K_MatchGenericExtraFlags(smoke, mobj);
smoke->scale = mobj->scale;
smoke->destscale = mobj->scale*2;
}
@@ -6777,7 +6669,7 @@ void P_MobjThinker(mobj_t *mobj)
}
else if ((mobj->health > 0
&& (!mobj->target || !mobj->target->player || !mobj->target->player->mo || mobj->target->player->health <= 0 || mobj->target->player->spectator))
- || (mobj->health <= 0 && mobj->z <= mobj->floorz)
+ || (mobj->health <= 0 && P_IsObjectOnGround(mobj))
|| P_CheckDeathPitCollide(mobj)) // When in death state
{
P_RemoveMobj(mobj);
@@ -7225,6 +7117,9 @@ void P_MobjThinker(mobj_t *mobj)
y = mobj->target->y;
z = mobj->target->z + (80*mapobjectscale);
}
+ if (mobj->target->eflags & MFE_VERTICALFLIP)
+ z += mobj->target->height - FixedMul(mobj->target->scale, mobj->height);
+
P_TeleportMove(mobj, x, y, z);
}
break;
@@ -7420,7 +7315,7 @@ void P_MobjThinker(mobj_t *mobj)
case MT_BANANA:
case MT_EGGMANITEM:
case MT_SPB:
- if (mobj->z <= mobj->floorz)
+ if (P_IsObjectOnGround(mobj))
{
P_RemoveMobj(mobj);
return;
@@ -7433,7 +7328,7 @@ void P_MobjThinker(mobj_t *mobj)
break;
case MT_JAWZ:
case MT_JAWZ_DUD:
- if (mobj->z <= mobj->floorz)
+ if (P_IsObjectOnGround(mobj))
P_SetMobjState(mobj, mobj->info->xdeathstate);
// fallthru
case MT_JAWZ_SHIELD:
@@ -7441,10 +7336,10 @@ void P_MobjThinker(mobj_t *mobj)
break;
case MT_SSMINE:
case MT_SPBEXPLOSION:
- if (mobj->health > -100)
+ if (mobj->extravalue2 != -100)
{
P_SetMobjState(mobj, mobj->info->deathstate);
- mobj->health = -100;
+ mobj->extravalue2 = -100;
}
else
{
@@ -7467,7 +7362,7 @@ void P_MobjThinker(mobj_t *mobj)
/* FALLTHRU */
case MT_SMK_MOLE:
mobj->flags2 ^= MF2_DONTDRAW;
- if (mobj->z <= mobj->floorz)
+ if (P_IsObjectOnGround(mobj))
{
P_RemoveMobj(mobj);
return;
@@ -7488,7 +7383,7 @@ void P_MobjThinker(mobj_t *mobj)
}
mobj->flags2 ^= MF2_DONTDRAW;
- if (mobj->z <= mobj->floorz)
+ if (P_IsObjectOnGround(mobj))
{
P_RemoveMobj(mobj);
return;
@@ -8157,7 +8052,7 @@ void P_MobjThinker(mobj_t *mobj)
mobj->friction = ORIG_FRICTION/4;
if (mobj->momx || mobj->momy)
P_SpawnGhostMobj(mobj);
- if (mobj->z <= mobj->floorz && mobj->health > 1)
+ if (P_IsObjectOnGround(mobj) && mobj->health > 1)
{
S_StartSound(mobj, mobj->info->activesound);
mobj->momx = mobj->momy = 0;
@@ -8177,7 +8072,7 @@ void P_MobjThinker(mobj_t *mobj)
case MT_SINK:
if (mobj->momx || mobj->momy)
P_SpawnGhostMobj(mobj);
- if (mobj->z <= mobj->floorz)
+ if (P_IsObjectOnGround(mobj))
{
S_StartSound(mobj, mobj->info->deathsound);
P_SetMobjState(mobj, S_NULL);
@@ -8190,28 +8085,26 @@ void P_MobjThinker(mobj_t *mobj)
mobj->color = mobj->target->player->skincolor;
else
mobj->color = SKINCOLOR_KETCHUP;
+
if (mobj->momx || mobj->momy)
P_SpawnGhostMobj(mobj);
- if (P_IsObjectOnGround(mobj))
+
+ if (P_IsObjectOnGround(mobj) && (mobj->state == &states[S_SSMINE_AIR1] || mobj->state == &states[S_SSMINE_AIR2]))
{
- if (mobj->state == &states[S_SSMINE_AIR1] || mobj->state == &states[S_SSMINE_AIR2])
- P_SetMobjState(mobj, S_SSMINE_DEPLOY1);
- if (mobj->reactiontime >= mobj->info->reactiontime)
+ if (mobj->extravalue1 > 0)
+ mobj->extravalue1--;
+ else
{
mobj->momx = mobj->momy = 0;
S_StartSound(mobj, mobj->info->activesound);
- mobj->reactiontime--;
+ P_SetMobjState(mobj, S_SSMINE_DEPLOY1);
}
}
- if (mobj->reactiontime && mobj->reactiontime < mobj->info->reactiontime)
- {
- mobj->reactiontime--;
- if (!mobj->reactiontime)
- P_KillMobj(mobj, NULL, NULL);
- }
+
if ((mobj->state >= &states[S_SSMINE1] && mobj->state <= &states[S_SSMINE4])
|| (mobj->state >= &states[S_SSMINE_DEPLOY8] && mobj->state <= &states[S_SSMINE_DEPLOY13]))
A_GrenadeRing(mobj);
+
if (mobj->threshold > 0)
mobj->threshold--;
break;
@@ -8355,6 +8248,7 @@ void P_MobjThinker(mobj_t *mobj)
break;
case MT_INSTASHIELDB:
mobj->flags2 ^= MF2_DONTDRAW;
+ K_MatchGenericExtraFlags(mobj, mobj->target);
/* FALLTHRU */
case MT_INSTASHIELDA:
if (!mobj->target || !mobj->target->health || (mobj->target->player && !mobj->target->player->kartstuff[k_instashield]))
@@ -8363,6 +8257,7 @@ void P_MobjThinker(mobj_t *mobj)
return;
}
P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z);
+ K_MatchGenericExtraFlags(mobj, mobj->target);
break;
case MT_BATTLEPOINT:
if (!mobj->target || P_MobjWasRemoved(mobj->target))
@@ -8383,7 +8278,7 @@ void P_MobjThinker(mobj_t *mobj)
if (mobj->movefactor < mobj->target->height)
mobj->movefactor = mobj->target->height;
}
-
+ K_MatchGenericExtraFlags(mobj, mobj->target);
P_TeleportMove(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (mobj->target->height/2) + mobj->movefactor);
break;
case MT_THUNDERSHIELD:
@@ -8508,6 +8403,10 @@ void P_MobjThinker(mobj_t *mobj)
mobj->flags2 &= ~MF2_DONTDRAW;
}
+ // Update mobj antigravity status:
+ mobj->eflags = (mobj->eflags & ~MFE_VERTICALFLIP)|(mobj->target->eflags & MFE_VERTICALFLIP);
+ mobj->flags2 = (mobj->flags2 & ~MF2_OBJECTFLIP)|(mobj->target->flags2 & MF2_OBJECTFLIP);
+
// Now for the wheels
{
const fixed_t rad = FixedMul(mobjinfo[MT_PLAYER].radius, mobj->target->scale);
@@ -8529,6 +8428,7 @@ void P_MobjThinker(mobj_t *mobj)
P_SetScale(cur, mobj->target->scale);
cur->color = mobj->target->color;
cur->colorized = true;
+ K_FlipFromObject(cur, mobj->target);
if (mobj->flags2 & MF2_DONTDRAW)
cur->flags2 |= MF2_DONTDRAW;
@@ -11124,7 +11024,7 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
if (mthing->options >> ZSHIFT)
z -= ((mthing->options >> ZSHIFT) << FRACBITS);
if (p->kartstuff[k_respawn])
- z -= 128*FRACUNIT; // Too late for v1, but for later: 128*mapobjectscale;
+ z -= 128*mapobjectscale;
}
else
{
@@ -11132,7 +11032,7 @@ void P_MovePlayerToSpawn(INT32 playernum, mapthing_t *mthing)
if (mthing->options >> ZSHIFT)
z += ((mthing->options >> ZSHIFT) << FRACBITS);
if (p->kartstuff[k_respawn])
- z += 128*FRACUNIT; // Too late for v1, but for later: 128*mapobjectscale;
+ z += 128*mapobjectscale;
}
if (mthing->options & MTF_OBJECTFLIP) // flip the player!
@@ -11193,7 +11093,14 @@ void P_MovePlayerToStarpost(INT32 playernum)
#endif
sector->ceilingheight;
- z = (p->starpostz + 128) << FRACBITS; // Respawn off the ground
+ if (mobj->player->kartstuff[k_starpostflip])
+ z = (p->starpostz<height;
+ else
+ z = (p->starpostz<starpostz + 128) << FRACBITS; // reverse gravity exists, pls
+ mobj->player->kartstuff[k_starpostflip] = 0;
+
if (z < floor)
z = floor;
else if (z > ceiling - mobjinfo[MT_PLAYER].height)
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 555a2614..0061ee02 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -249,6 +249,8 @@ static void P_NetArchivePlayers(void)
WRITEUINT32(save_p, players[i].jointime);
+ WRITEUINT8(save_p, players[i].splitscreenindex);
+
WRITEUINT16(save_p, flags);
if (flags & CAPSULE)
@@ -282,6 +284,12 @@ static void P_NetArchivePlayers(void)
WRITEUINT8(save_p, players[i].accelstart);
WRITEUINT8(save_p, players[i].acceleration);
WRITEFIXED(save_p, players[i].jumpfactor);
+
+ for (j = 0; j < MAXPREDICTTICS; j++)
+ {
+ WRITEINT16(save_p, players[i].lturn_max[j]);
+ WRITEINT16(save_p, players[i].rturn_max[j]);
+ }
}
}
@@ -420,6 +428,8 @@ static void P_NetUnArchivePlayers(void)
players[i].jointime = READUINT32(save_p);
+ players[i].splitscreenindex = READUINT8(save_p);
+
flags = READUINT16(save_p);
if (flags & CAPSULE)
@@ -456,6 +466,12 @@ static void P_NetUnArchivePlayers(void)
players[i].accelstart = READUINT8(save_p);
players[i].acceleration = READUINT8(save_p);
players[i].jumpfactor = READFIXED(save_p);
+
+ for (j = 0; j < MAXPREDICTTICS; j++)
+ {
+ players[i].lturn_max[j] = READINT16(save_p);
+ players[i].rturn_max[j] = READINT16(save_p);
+ }
}
}
@@ -663,8 +679,6 @@ static void P_NetArchiveWorld(void)
WRITEUINT16(put, 0xffff);
- mld = W_CacheLumpNum(lastloadedmaplumpnum+ML_LINEDEFS, PU_CACHE);
- msd = W_CacheLumpNum(lastloadedmaplumpnum+ML_SIDEDEFS, PU_CACHE);
// do lines
for (i = 0; i < numlines; i++, mld++, li++)
{
@@ -683,13 +697,13 @@ static void P_NetArchiveWorld(void)
diff |= LD_S1TEXOFF;
//SoM: 4/1/2000: Some textures are colormaps. Don't worry about invalid textures.
if (R_CheckTextureNumForName(msd[li->sidenum[0]].toptexture) != -1
- && si->toptexture != R_TextureNumForName(msd[li->sidenum[0]].toptexture))
+ && si->toptexture != R_TextureNumForName(msd[li->sidenum[0]].toptexture))
diff |= LD_S1TOPTEX;
if (R_CheckTextureNumForName(msd[li->sidenum[0]].bottomtexture) != -1
- && si->bottomtexture != R_TextureNumForName(msd[li->sidenum[0]].bottomtexture))
+ && si->bottomtexture != R_TextureNumForName(msd[li->sidenum[0]].bottomtexture))
diff |= LD_S1BOTTEX;
if (R_CheckTextureNumForName(msd[li->sidenum[0]].midtexture) != -1
- && si->midtexture != R_TextureNumForName(msd[li->sidenum[0]].midtexture))
+ && si->midtexture != R_TextureNumForName(msd[li->sidenum[0]].midtexture))
diff |= LD_S1MIDTEX;
}
if (li->sidenum[1] != 0xffff)
@@ -698,13 +712,13 @@ static void P_NetArchiveWorld(void)
if (si->textureoffset != SHORT(msd[li->sidenum[1]].textureoffset)<sidenum[1]].toptexture) != -1
- && si->toptexture != R_TextureNumForName(msd[li->sidenum[1]].toptexture))
+ && si->toptexture != R_TextureNumForName(msd[li->sidenum[1]].toptexture))
diff2 |= LD_S2TOPTEX;
if (R_CheckTextureNumForName(msd[li->sidenum[1]].bottomtexture) != -1
- && si->bottomtexture != R_TextureNumForName(msd[li->sidenum[1]].bottomtexture))
+ && si->bottomtexture != R_TextureNumForName(msd[li->sidenum[1]].bottomtexture))
diff2 |= LD_S2BOTTEX;
if (R_CheckTextureNumForName(msd[li->sidenum[1]].midtexture) != -1
- && si->midtexture != R_TextureNumForName(msd[li->sidenum[1]].midtexture))
+ && si->midtexture != R_TextureNumForName(msd[li->sidenum[1]].midtexture))
diff2 |= LD_S2MIDTEX;
if (diff2)
diff |= LD_DIFF2;
@@ -3301,6 +3315,7 @@ static void P_NetArchiveMisc(void)
WRITEUINT32(save_p, wantedcalcdelay);
WRITEUINT32(save_p, indirectitemcooldown);
+ WRITEUINT32(save_p, hyubgone);
WRITEUINT32(save_p, mapreset);
WRITEUINT8(save_p, nospectategrief);
WRITEUINT8(save_p, thwompsactive);
@@ -3409,6 +3424,7 @@ static inline boolean P_NetUnArchiveMisc(void)
wantedcalcdelay = READUINT32(save_p);
indirectitemcooldown = READUINT32(save_p);
+ hyubgone = READUINT32(save_p);
mapreset = READUINT32(save_p);
nospectategrief = READUINT8(save_p);
thwompsactive = (boolean)READUINT8(save_p);
diff --git a/src/p_setup.c b/src/p_setup.c
index 49b22184..ba4554e6 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -234,7 +234,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
DEH_WriteUndoline("LEVELFLAGS", va("%d", mapheaderinfo[num]->levelflags), UNDO_NONE);
mapheaderinfo[num]->levelflags = 0;
DEH_WriteUndoline("MENUFLAGS", va("%d", mapheaderinfo[num]->menuflags), UNDO_NONE);
- mapheaderinfo[num]->menuflags = 0;
+ mapheaderinfo[num]->menuflags = (mainwads ? 0 : LF2_EXISTSHACK); // see p_setup.c - prevents replacing maps in addons with easier versions
// TODO grades support for delfile (pfft yeah right)
P_DeleteGrades(num);
// SRB2Kart
@@ -1120,7 +1120,7 @@ static inline void P_SpawnEmblems(void)
static void P_SpawnSecretItems(boolean loademblems)
{
// Now let's spawn those funky emblem things! Tails 12-08-2002
- if (netgame || multiplayer || (modifiedgame && !savemoddata)) // No cheating!!
+ if (netgame || multiplayer || majormods) // No cheating!!
return;
if (loademblems)
@@ -2857,6 +2857,9 @@ boolean P_SetupLevel(boolean skipprecip)
lastwipetic = nowtime;
if (moviemode) // make sure we save frames for the white hold too
M_SaveFrame();
+
+ // Keep the network alive
+ NetKeepAlive();
}
ranspecialwipe = 1;
@@ -3211,10 +3214,9 @@ boolean P_SetupLevel(boolean skipprecip)
if (!cv_analog4.changed)
CV_SetValue(&cv_analog4, 0);*/
-#ifdef HWRENDER
- if (rendermode != render_soft && rendermode != render_none)
- CV_Set(&cv_grfov, cv_grfov.defaultvalue);
-#endif
+ // Shouldn't be necessary with render parity?
+ /*if (rendermode != render_none)
+ CV_Set(&cv_fov, cv_fov.defaultvalue);*/
displayplayer = consoleplayer; // Start with your OWN view, please!
}
@@ -3241,6 +3243,7 @@ boolean P_SetupLevel(boolean skipprecip)
wantedcalcdelay = wantedfrequency*2;
indirectitemcooldown = 0;
+ hyubgone = 0;
mapreset = 0;
nospectategrief = 0;
thwompsactive = false;
@@ -3272,7 +3275,7 @@ boolean P_SetupLevel(boolean skipprecip)
nextmapoverride = 0;
skipstats = false;
- if (!(netgame || multiplayer) && (!modifiedgame || savemoddata))
+ if (!(netgame || multiplayer) && !majormods)
mapvisited[gamemap-1] |= MV_VISITED;
levelloading = false;
@@ -3435,7 +3438,7 @@ boolean P_AddWadFile(const char *wadfilename)
//
R_AddSkins(wadnum); // faB: wadfile index in wadfiles[]
- //
+ //
// edit music defs
//
S_LoadMusicDefs(wadnum);
@@ -3455,6 +3458,14 @@ boolean P_AddWadFile(const char *wadfilename)
continue;
num = (INT16)M_MapNumber(name[3], name[4]);
+ // we want to record whether this map exists. if it doesn't have a header, we can assume it's not relephant
+ if (num <= NUMMAPS && mapheaderinfo[num-1])
+ {
+ if (mapheaderinfo[num-1]->menuflags & LF2_EXISTSHACK)
+ G_SetGameModified(multiplayer, true); // oops, double-defined - no record attack privileges for you
+ mapheaderinfo[num-1]->menuflags |= LF2_EXISTSHACK;
+ }
+
//If you replaced the map you're on, end the level when done.
if (num == gamemap)
replacedcurrentmap = true;
@@ -3481,6 +3492,8 @@ boolean P_AddWadFile(const char *wadfilename)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
+ refreshdirmenu &= ~REFRESHDIR_GAMEDATA; // Under usual circumstances we'd wait for REFRESHDIR_GAMEDATA to disappear the next frame, but it's a bit too dangerous for that...
+
return true;
}
diff --git a/src/p_spec.c b/src/p_spec.c
index ca4967ce..67bb7472 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1758,12 +1758,12 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
switch (specialtype)
{
- case 305: // continuous
+ /*case 305: // continuous
case 306: // each time
case 307: // once
if (!(actor && actor->player && actor->player->charability == dist/10))
return false;
- break;
+ break;*/
case 309: // continuous
case 310: // each time
// Only red team members can activate this.
@@ -3864,14 +3864,6 @@ DoneSection2:
P_InstaThrust(player->mo, player->mo->angle, linespeed);
- /*if (GETSECSPECIAL(sector->special, 3) == 6 && (player->charability2 == CA2_SPINDASH)) // SRB2kart
- {
- if (!(player->pflags & PF_SPINNING))
- player->pflags |= PF_SPINNING;
-
- //P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
- }*/
-
player->kartstuff[k_dashpadcooldown] = TICRATE/3;
player->kartstuff[k_drift] = 0;
player->kartstuff[k_driftcharge] = 0;
@@ -4255,13 +4247,14 @@ DoneSection2:
player->starpostx = player->mo->x>>FRACBITS;
player->starposty = player->mo->y>>FRACBITS;
player->starpostz = player->mo->floorz>>FRACBITS;
+ player->kartstuff[k_starpostflip] = player->mo->flags2 & MF2_OBJECTFLIP; // store flipping
player->starpostangle = player->mo->angle; //R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy); torn; a momentum-based guess is less likely to be wrong in general, but when it IS wrong, it fucks you over entirely...
}
else
{
// SRB2kart 200117
// Reset starposts (checkpoints) info
- player->starpostangle = player->starpostx = player->starposty = player->starpostz = 0;
+ player->starpostangle = player->starpostx = player->starposty = player->starpostz = player->kartstuff[k_starpostflip] = 0;
}
if (P_IsLocalPlayer(player))
@@ -5781,7 +5774,7 @@ void P_SpawnSpecials(INT32 fromnetsave)
lines[i].special = 0;
continue;
}
- /*else -- commented out because irrelevant to kart
+ /*else -- commented out because irrelevant to kart. keeping here because we can use these flags for something else now
{
if ((players[consoleplayer].charability == CA_THOK && (lines[i].flags & ML_NOSONIC))
|| (players[consoleplayer].charability == CA_FLY && (lines[i].flags & ML_NOTAILS))
@@ -7997,12 +7990,13 @@ static void P_SearchForDisableLinedefs(void)
}
else if ((lines[i].flags & ML_NETONLY) == ML_NETONLY)
continue; // Net-only never triggers in single player
- else if (players[consoleplayer].charability == CA_THOK && (lines[i].flags & ML_NOSONIC))
+ // commented out because irrelevant to kart. keeping here because we can use these flags for something else now
+ /*else if (players[consoleplayer].charability == CA_THOK && (lines[i].flags & ML_NOSONIC))
continue;
else if (players[consoleplayer].charability == CA_FLY && (lines[i].flags & ML_NOTAILS))
continue;
else if (players[consoleplayer].charability == CA_GLIDEANDCLIMB && (lines[i].flags & ML_NOKNUX))
- continue;
+ continue;*/
// Disable any linedef specials with our tag.
for (j = -1; (j = P_FindLineFromLineTag(&lines[i], j)) >= 0;)
diff --git a/src/p_tick.c b/src/p_tick.c
index b46b248b..85eaea9b 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -680,6 +680,8 @@ void P_Ticker(boolean run)
if (indirectitemcooldown)
indirectitemcooldown--;
+ if (hyubgone)
+ hyubgone--;
if (G_BattleGametype())
{
diff --git a/src/p_user.c b/src/p_user.c
index ab6c61dc..ebc525c4 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -172,7 +172,7 @@ boolean P_AutoPause(void)
if (netgame || modeattacking)
return false;
- return (menuactive || window_notinfocus);
+ return (menuactive || ( window_notinfocus && cv_pauseifunfocused.value ));
}
//
@@ -3634,165 +3634,6 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) // SRB2kart - unused.
}
*/
-#if 0
-//
-// P_DoSuperStuff()
-//
-// Handle related superform functionality.
-//
-static void P_DoSuperStuff(player_t *player)
-{
- mobj_t *spark;
- ticcmd_t *cmd = &player->cmd;
- //if (player->mo->state >= &states[S_PLAY_SUPERTRANS1] && player->mo->state <= &states[S_PLAY_SUPERTRANS9])
- // return; // don't do anything right now, we're in the middle of transforming!
-
- if (player->pflags & PF_NIGHTSMODE)
- return; // NiGHTS Super doesn't mix with normal super
-
- // Does player have all emeralds? If so, flag the "Ready For Super!"
- /*if ((ALL7EMERALDS(emeralds) || ALL7EMERALDS(player->powers[pw_emeralds])) && player->health > 50)
- player->pflags |= PF_SUPERREADY;
- else
- player->pflags &= ~PF_SUPERREADY;*/
-
- if (player->powers[pw_super])
- {
- // If you're super and not Sonic, de-superize!
- if (!((ALL7EMERALDS(emeralds)) && (player->charflags & SF_SUPER)) && !(ALL7EMERALDS(player->powers[pw_emeralds])))
- {
- player->powers[pw_super] = 0;
- P_SetPlayerMobjState(player->mo, S_KART_STND1);
- P_RestoreMusic(player);
- P_SpawnShieldOrb(player);
-
- // Restore color
- if (player->powers[pw_shield] & SH_FIREFLOWER)
- {
- player->mo->color = SKINCOLOR_WHITE;
- G_GhostAddColor(GHC_FIREFLOWER);
- }
- else
- {
- player->mo->color = player->skincolor;
- G_GhostAddColor(GHC_NORMAL);
- }
-
- if (gametype != GT_COOP)
- {
- HU_SetCEchoFlags(0);
- HU_SetCEchoDuration(5);
- HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players]));
- }
- return;
- }
-
- // Deplete one ring every second while super
- if ((leveltime % TICRATE == 0) && !(player->exiting))
- {
- player->health--;
- player->mo->health--;
- }
-
- // future todo: a skin option for this, and possibly more colors
- switch (player->skin)
- {
- case 1: /* Tails */ player->mo->color = SKINCOLOR_TSUPER1; break;
- case 2: /* Knux */ player->mo->color = SKINCOLOR_KSUPER1; break;
- default: /* everyone */ player->mo->color = SKINCOLOR_SUPER1; break;
- }
- player->mo->color += abs( ( (signed)( (unsigned)leveltime >> 1 ) % 9) - 4);
-
- if ((cmd->forwardmove != 0 || cmd->sidemove != 0 || player->pflags & (PF_CARRIED|PF_ROPEHANG|PF_ITEMHANG|PF_MACESPIN))
- && !(leveltime % TICRATE) && (player->mo->momx || player->mo->momy))
- {
- spark = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_SUPERSPARK);
- spark->destscale = player->mo->scale;
- P_SetScale(spark, player->mo->scale);
- }
-
- G_GhostAddColor(GHC_SUPER);
-
- // Ran out of rings while super!
- if (player->health <= 1 || player->exiting)
- {
- player->powers[pw_emeralds] = 0; // lost the power stones
- P_SpawnGhostMobj(player->mo);
-
- player->powers[pw_super] = 0;
-
- // Restore color
- if (player->powers[pw_shield] & SH_FIREFLOWER)
- {
- player->mo->color = SKINCOLOR_WHITE;
- G_GhostAddColor(GHC_FIREFLOWER);
- }
- else
- {
- player->mo->color = player->skincolor;
- G_GhostAddColor(GHC_NORMAL);
- }
-
- if (gametype != GT_COOP)
- player->powers[pw_flashing] = K_GetKartFlashing(player)-1;
-
-/*
- if (player->mo->health > 0)
- {
- if ((player->pflags & PF_JUMPED) || (player->pflags & PF_SPINNING))
- P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
- else if (player->panim == PA_RUN)
- P_SetPlayerMobjState(player->mo, S_PLAY_SPD1);
- else if (player->panim == PA_WALK)
- P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
- else
- P_SetPlayerMobjState(player->mo, S_PLAY_STND);
-
- if (!player->exiting)
- {
- player->health = 1;
- player->mo->health = 1;
- }
- }
-*/
-
- // Inform the netgame that the champion has fallen in the heat of battle.
- if (gametype != GT_COOP)
- {
- S_StartSound(NULL, sfx_s3k66); //let all players hear it.
- HU_SetCEchoFlags(0);
- HU_SetCEchoDuration(5);
- HU_DoCEcho(va("%s\\is no longer super.\\\\\\\\", player_names[player-players]));
- }
-
- // Resume normal music if you're the console player
- P_RestoreMusic(player);
-
- // If you had a shield, restore its visual significance.
- P_SpawnShieldOrb(player);
- }
- }
-}
-#endif
-
-//
-// P_SuperReady
-//
-// Returns true if player is ready to turn super, duh
-//
-/*boolean P_SuperReady(player_t *player)
-{
- if ((player->pflags & PF_SUPERREADY) && !player->powers[pw_super] && !player->powers[pw_tailsfly]
- && !(player->powers[pw_shield] & SH_NOSTACK)
- && !player->powers[pw_invulnerability]
- && !(maptol & TOL_NIGHTS) // don't turn 'regular super' in nights levels
- && player->pflags & PF_JUMPED
- && ((player->charflags & SF_SUPER) || ALL7EMERALDS(player->powers[pw_emeralds])))
- return true;
-
- return false;
-}*/
-
//
// P_DoJump
//
@@ -4509,7 +4350,7 @@ boolean P_AnalogMove(player_t *player)
// 1 = pressing in the direction of movement
// 2 = pressing in the opposite direction of movement
//
-INT32 P_GetPlayerControlDirection(player_t *player)
+/*INT32 P_GetPlayerControlDirection(player_t *player)
{
ticcmd_t *cmd = &player->cmd;
angle_t controllerdirection, controlplayerdirection;
@@ -4584,7 +4425,7 @@ INT32 P_GetPlayerControlDirection(player_t *player)
}
// Control scheme for 2d levels.
-/*static void P_2dMovement(player_t *player)
+static void P_2dMovement(player_t *player)
{
ticcmd_t *cmd;
INT32 topspeed, acceleration, thrustfactor;
@@ -4794,24 +4635,11 @@ static void P_3dMovement(player_t *player)
cmd->forwardmove = cmd->sidemove = 0;
if (player->kartstuff[k_sneakertimer])
cmd->forwardmove = 50;
- if (player->pflags & PF_GLIDING)
- {
- if (!player->skidtime)
- player->pflags &= ~PF_GLIDING;
- else if (player->exiting || mapreset)
- {
- player->pflags &= ~PF_GLIDING;
- P_SetPlayerMobjState(player->mo, S_KART_WALK1); // SRB2kart - was S_PLAY_RUN1
- player->skidtime = 0;
- }
- }
- if (player->pflags & PF_SPINNING && !(player->exiting || mapreset))
- {
- player->pflags &= ~PF_SPINNING;
- P_SetPlayerMobjState(player->mo, S_KART_STND1); // SRB2kart - was S_PLAY_STND
- }
}
+ if (!(player->pflags & PF_FORCESTRAFE) && !player->kartstuff[k_pogospring])
+ cmd->sidemove = 0;
+
if (analogmove)
{
movepushangle = (cmd->angleturn<<16 /* not FRACBITS */);
@@ -4819,9 +4647,7 @@ static void P_3dMovement(player_t *player)
else
{
if (player->kartstuff[k_drift] != 0)
- {
movepushangle = player->mo->angle-(ANGLE_45/5)*player->kartstuff[k_drift];
- }
else
movepushangle = player->mo->angle;
}
@@ -5039,10 +4865,10 @@ static void P_SpectatorMovement(player_t *player)
// Quake-style flying spectators :D
player->mo->momz += FixedMul(cmd->forwardmove*mapobjectscale, AIMINGTOSLOPE(player->aiming));
}
- if (cmd->sidemove != 0)
+ /*if (cmd->sidemove != 0) -- was disabled in practice anyways, since sidemove was suppressed
{
P_Thrust(player->mo, player->mo->angle-ANGLE_90, cmd->sidemove*mapobjectscale);
- }
+ }*/
}
//
@@ -6397,99 +6223,6 @@ void P_ElementalFireTrail(player_t *player)
}
}
-/*static void P_SkidStuff(player_t *player)
-{
- fixed_t pmx = player->rmomx + player->cmomx;
- fixed_t pmy = player->rmomy + player->cmomy;
-
- // Knuckles glides into the dirt.
- // SRB2kart - don't need
- if (player->pflags & PF_GLIDING && player->skidtime)
- {
- // Fell off a ledge...
- if (!onground)
- {
- player->skidtime = 0;
- player->pflags &= ~(PF_GLIDING|PF_JUMPED);
- P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
- }
- // Get up and brush yourself off, idiot.
- else if (player->glidetime > 15)
- {
- P_ResetPlayer(player);
- P_SetPlayerMobjState(player->mo, S_PLAY_STND);
- player->mo->momx = player->cmomx;
- player->mo->momy = player->cmomy;
- }
- // Didn't stop yet? Skid FOREVER!
- else if (player->skidtime == 1)
- player->skidtime = 3*TICRATE+1;
- // Spawn a particle every 3 tics.
- else if (!(player->skidtime % 3))
- {
- mobj_t *particle = P_SpawnMobj(player->mo->x + P_RandomRange(-player->mo->radius, player->mo->radius), player->mo->y + P_RandomRange(-player->mo->radius, player->mo->radius),
- player->mo->z + (player->mo->eflags & MFE_VERTICALFLIP ? player->mo->height - mobjinfo[MT_PARTICLE].height : 0),
- MT_PARTICLE);
- particle->tics = 10;
-
- particle->eflags |= player->mo->eflags & MFE_VERTICALFLIP;
- P_SetScale(particle, player->mo->scale >> 2);
- particle->destscale = player->mo->scale << 2;
- particle->scalespeed = FixedMul(particle->scalespeed, player->mo->scale); // scale the scaling speed!
- P_SetObjectMomZ(particle, FRACUNIT, false);
- S_StartSound(player->mo, sfx_s3k7e); // the proper "Knuckles eats dirt" sfx.
- }
- }
- // Skidding!
- elseif (onground && !(player->mo->eflags & MFE_GOOWATER) && !(player->pflags & (PF_JUMPED|PF_SPINNING|PF_SLIDING)) && !(player->charflags & SF_NOSKID))
- {
- if (player->skidtime)
- {
- // Spawn a particle every 3 tics.
- if (!(player->skidtime % 3))
- {
- mobj_t *particle = P_SpawnMobj(player->mo->x, player->mo->y,
- player->mo->z + (player->mo->eflags & MFE_VERTICALFLIP ? player->mo->height - mobjinfo[MT_PARTICLE].height : 0),
- MT_PARTICLE);
- particle->tics = 10;
-
- particle->eflags |= player->mo->eflags & MFE_VERTICALFLIP;
- P_SetScale(particle, player->mo->scale >> 2);
- particle->destscale = player->mo->scale << 2;
- particle->scalespeed = FixedMul(particle->scalespeed, player->mo->scale); // scale the scaling speed!
- P_SetObjectMomZ(particle, FRACUNIT, false);
- }
- }
- else if (P_AproxDistance(pmx, pmy) >= FixedMul(player->runspeed/2, player->mo->scale) // if you were moving faster than half your run speed last frame
- && (player->mo->momx != pmx || player->mo->momy != pmy) // and you are moving differently this frame
- && P_GetPlayerControlDirection(player) == 2) // and your controls are pointing in the opposite direction to your movement
- { // check for skidding
- angle_t mang = R_PointToAngle2(0,0,pmx,pmy); // movement angle
- angle_t pang = R_PointToAngle2(pmx,pmy,player->mo->momx,player->mo->momy); // push angle
- angle_t dang = mang - pang; // delta angle
-
- if (dang > ANGLE_180) // Make delta angle always positive, invert it if it's negative.
- dang = InvAngle(dang);
-
- // If your push angle is more than this close to a full 180 degrees, trigger a skid.
- if (dang > ANGLE_157h)
- {
- player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS; //player->skidtime = TICRATE/2;
- S_StartSound(player->mo, sfx_skid);
- if (player->panim != PA_WALK)
- P_SetPlayerMobjState(player->mo, S_KART_WALK2); // SRB2kart - was S_PLAY_RUN4
- player->mo->tics = player->skidtime;
- }
- }
- }
- else {
- if (player->skidtime) {
- player->skidtime = 0;
- S_StopSound(player->mo);
- }
- }
-}*/
-
//
// P_MovePlayer
static void P_MovePlayer(player_t *player)
@@ -6633,8 +6366,53 @@ static void P_MovePlayer(player_t *player)
P_2dMovement(player);
else*/
{
- if (!player->climbing && (!P_AnalogMove(player)))
- player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
+ INT16 angle_diff, max_left_turn, max_right_turn;
+ boolean add_delta = true;
+
+ // Kart: store the current turn range for later use
+ if (((player->mo && player->speed > 0) // Moving
+ || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn
+ || (player->kartstuff[k_respawn]) // Respawning
+ || (player->spectator || objectplacing)) // Not a physical player
+ && !(player->kartstuff[k_spinouttimer] && player->kartstuff[k_sneakertimer])) // Spinning and boosting cancels out turning
+ {
+ player->lturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, KART_FULLTURN)+1;
+ player->rturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, -KART_FULLTURN)-1;
+ } else {
+ player->lturn_max[leveltime%MAXPREDICTTICS] = player->rturn_max[leveltime%MAXPREDICTTICS] = 0;
+ }
+
+ if (leveltime >= starttime)
+ {
+ // KART: Don't directly apply angleturn! It may have been either A) forged by a malicious client, or B) not be a smooth turn due to a player dropping frames.
+ // Instead, turn the player only up to the amount they're supposed to turn accounting for latency. Allow exactly 1 extra turn unit to try to keep old replays synced.
+ angle_diff = cmd->angleturn - (player->mo->angle>>16);
+ max_left_turn = player->lturn_max[(leveltime + MAXPREDICTTICS - cmd->latency) % MAXPREDICTTICS];
+ max_right_turn = player->rturn_max[(leveltime + MAXPREDICTTICS - cmd->latency) % MAXPREDICTTICS];
+
+ //CONS_Printf("----------------\nangle diff: %d - turning options: %d to %d - ", angle_diff, max_left_turn, max_right_turn);
+
+ if (angle_diff > max_left_turn)
+ angle_diff = max_left_turn;
+ else if (angle_diff < max_right_turn)
+ angle_diff = max_right_turn;
+ else
+ {
+ // Try to keep normal turning as accurate to 1.0.1 as possible to reduce replay desyncs.
+ player->mo->angle = cmd->angleturn<<16;
+ add_delta = false;
+ }
+ //CONS_Printf("applied turn: %d\n", angle_diff);
+
+ if (add_delta) {
+ player->mo->angle += angle_diff<<16;
+ player->mo->angle &= ~0xFFFF; // Try to keep the turning somewhat similar to how it was before?
+ //CONS_Printf("leftover turn (%s): %5d or %4d%%\n",
+ // player_names[player-players],
+ // (INT16) (cmd->angleturn - (player->mo->angle>>16)),
+ // (INT16) (cmd->angleturn - (player->mo->angle>>16)) * 100 / (angle_diff ? angle_diff : 1));
+ }
+ }
ticruned++;
if ((cmd->angleturn & TICCMD_RECEIVED) == 0)
@@ -7032,6 +6810,7 @@ static void P_MovePlayer(player_t *player)
//ANALOG CONTROL//
//////////////////
+#if 0
// This really looks like it should be moved to P_3dMovement. -Red
if (P_AnalogMove(player)
&& (cmd->forwardmove != 0 || cmd->sidemove != 0) && !player->climbing && !twodlevel && !(player->mo->flags2 & MF2_TWOD))
@@ -7088,6 +6867,7 @@ static void P_MovePlayer(player_t *player)
else if (player == &players[fourthdisplayplayer])
localangle4 = player->mo->angle;
}
+#endif
///////////////////////////
//BOMB SHIELD ACTIVATION,//
@@ -7291,74 +7071,6 @@ static void P_MovePlayer(player_t *player)
if (CheckForBustableBlocks)
P_CheckBustableBlocks(player);
- // Special handling for
- // gliding in 2D mode
- if ((twodlevel || player->mo->flags2 & MF2_TWOD) && player->pflags & PF_GLIDING && player->charability == CA_GLIDEANDCLIMB
- && !(player->mo->flags & MF_NOCLIP))
- {
- msecnode_t *node; // only place it's being used in P_MovePlayer now
- fixed_t oldx;
- fixed_t oldy;
- fixed_t floorz, ceilingz;
-
- oldx = player->mo->x;
- oldy = player->mo->y;
-
- P_UnsetThingPosition(player->mo);
- player->mo->x += player->mo->momx;
- player->mo->y += player->mo->momy;
- P_SetThingPosition(player->mo);
-
- for (node = player->mo->touching_sectorlist; node; node = node->m_sectorlist_next)
- {
- if (!node->m_sector)
- break;
-
- if (node->m_sector->ffloors)
- {
- ffloor_t *rover;
- fixed_t topheight, bottomheight;
-
- for (rover = node->m_sector->ffloors; rover; rover = rover->next)
- {
- if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER))
- continue;
-
- topheight = P_GetFOFTopZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
- bottomheight = P_GetFOFBottomZ(player->mo, node->m_sector, rover, player->mo->x, player->mo->y, NULL);
- if (topheight > player->mo->z && bottomheight < player->mo->z)
- {
- P_ResetPlayer(player);
- S_StartSound(player->mo, sfx_s3k4a);
- player->climbing = 5;
- player->mo->momx = player->mo->momy = player->mo->momz = 0;
- break;
- }
- }
- }
-
- floorz = P_GetFloorZ(player->mo, node->m_sector, player->mo->x, player->mo->y, NULL);
- ceilingz = P_GetCeilingZ(player->mo, node->m_sector, player->mo->x, player->mo->y, NULL);
-
- if (player->mo->z+player->mo->height > ceilingz
- && node->m_sector->ceilingpic == skyflatnum)
- continue;
-
- if (floorz > player->mo->z || ceilingz < player->mo->z)
- {
- P_ResetPlayer(player);
- S_StartSound(player->mo, sfx_s3k4a);
- player->climbing = 5;
- player->mo->momx = player->mo->momy = player->mo->momz = 0;
- break;
- }
- }
- P_UnsetThingPosition(player->mo);
- player->mo->x = oldx;
- player->mo->y = oldy;
- P_SetThingPosition(player->mo);
- }
-
// Check for a BOUNCY sector!
if (CheckForBouncySector)
P_CheckBouncySectors(player);
@@ -7690,12 +7402,14 @@ static void P_NukeAllPlayers(player_t *player)
//
void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
{
- const fixed_t ns = 60 << FRACBITS;
+ const fixed_t ns = 60 * mapobjectscale;
mobj_t *mo;
angle_t fa;
thinker_t *think;
INT32 i;
+ radius = FixedMul(radius, mapobjectscale);
+
for (i = 0; i < 16; i++)
{
fa = (i*(FINEANGLES/16));
@@ -8133,6 +7847,8 @@ void P_ResetCamera(player_t *player, camera_t *thiscam)
boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled)
{
+ static UINT8 lookbackdelay[4] = {0,0,0,0};
+ UINT8 num;
angle_t angle = 0, focusangle = 0, focusaiming = 0;
fixed_t x, y, z, dist, height, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight;
fixed_t pan, xpan, ypan;
@@ -8261,6 +7977,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (thiscam == &camera)
{
+ num = 0;
camspeed = cv_cam_speed.value;
camstill = cv_cam_still.value;
camrotate = cv_cam_rotate.value;
@@ -8270,6 +7987,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
else if (thiscam == &camera2) // Camera 2
{
+ num = 1;
camspeed = cv_cam2_speed.value;
camstill = cv_cam2_still.value;
camrotate = cv_cam2_rotate.value;
@@ -8279,6 +7997,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
else if (thiscam == &camera3) // Camera 3
{
+ num = 2;
camspeed = cv_cam3_speed.value;
camstill = cv_cam3_still.value;
camrotate = cv_cam3_rotate.value;
@@ -8288,6 +8007,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
else // Camera 4
{
+ num = 3;
camspeed = cv_cam4_speed.value;
camstill = cv_cam4_still.value;
camrotate = cv_cam4_rotate.value;
@@ -8310,19 +8030,23 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
else if (player->exiting) // SRB2Kart: Leave the camera behind while exiting, for dramatic effect!
camstill = true;
- else if (lookback) // SRB2kart - Camera flipper
+ else if (lookback || lookbackdelay[num]) // SRB2kart - Camera flipper
{
- camrotate += 180;
- camspeed *= 2;
- if (camspeed > FRACUNIT)
- camspeed = FRACUNIT;
+ camspeed = FRACUNIT;
+ if (lookback)
+ {
+ camrotate += 180;
+ lookbackdelay[num] = 2;
+ }
+ else
+ lookbackdelay[num]--;
}
if (mo->eflags & MFE_VERTICALFLIP)
camheight += thiscam->height;
- if (splitscreen == 1)
- camspeed = (3*camspeed)/4;
+ if (camspeed > FRACUNIT)
+ camspeed = FRACUNIT;
if (timeover)
angle = mo->angle + FixedAngle(camrotate*FRACUNIT);
@@ -8332,16 +8056,21 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
angle = thiscam->angle;
else
{
- angle_t input = focusangle + FixedAngle(camrotate<angle;
- boolean invert = (input > ANGLE_180);
- if (invert)
- input = InvAngle(input);
+ if (camspeed == FRACUNIT)
+ angle = focusangle + FixedAngle(camrotate<angle;
+ boolean invert = (input > ANGLE_180);
+ if (invert)
+ input = InvAngle(input);
- input = FixedAngle(FixedMul(AngleFixed(input), camspeed));
- if (invert)
- input = InvAngle(input);
+ input = FixedAngle(FixedMul(AngleFixed(input), camspeed));
+ if (invert)
+ input = InvAngle(input);
- angle = thiscam->angle + input;
+ angle = thiscam->angle + input;
+ }
}
if (!resetcalled && (leveltime > starttime && timeover != 2)
@@ -8369,13 +8098,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
height -= FixedMul(height, player->kartstuff[k_boostcam]);
}
- // in splitscreen modes, mess with the camera distances to make it feel proportional to how it feels normally
- if (splitscreen == 1) // widescreen splits should get x1.5 distance
- {
- dist = FixedMul(dist, 3*FRACUNIT/2);
- height = FixedMul(height, 3*FRACUNIT/2);
- }
-
x = mo->x - FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
y = mo->y - FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
@@ -8641,10 +8363,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
{
thiscam->momx = x - thiscam->x;
thiscam->momy = y - thiscam->y;
- if (splitscreen == 1) // Wide-screen needs to follow faster, due to a smaller vertical:horizontal ratio of screen space
- thiscam->momz = FixedMul(z - thiscam->z, (3*camspeed)/4);
- else
- thiscam->momz = FixedMul(z - thiscam->z, camspeed/2);
+ thiscam->momz = FixedMul(z - thiscam->z, camspeed/2);
}
thiscam->pan = pan;
@@ -8665,8 +8384,25 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (twodlevel || (mo->flags2 & MF2_TWOD) || (!camstill && !timeover)) // Keep the view still...
{
G_ClipAimingPitch((INT32 *)&angle);
- dist = thiscam->aiming - angle;
- thiscam->aiming -= (dist>>3);
+
+ if (camspeed == FRACUNIT)
+ thiscam->aiming = angle;
+ else
+ {
+ angle_t input;
+ boolean invert;
+
+ input = thiscam->aiming - angle;
+ invert = (input > ANGLE_180);
+ if (invert)
+ input = InvAngle(input);
+
+ input = FixedAngle(FixedMul(AngleFixed(input), (5*camspeed)/16));
+ if (invert)
+ input = InvAngle(input);
+
+ thiscam->aiming -= input;
+ }
}
if (!resetcalled && (player->playerstate == PST_DEAD || player->playerstate == PST_REBORN))
@@ -8731,6 +8467,7 @@ boolean P_SpectatorJoinGame(player_t *player)
}
player->spectator = false;
player->pflags &= ~PF_WANTSTOJOIN;
+ player->kartstuff[k_spectatewait] = 0;
player->ctfteam = changeto;
player->playerstate = PST_REBORN;
@@ -8755,6 +8492,7 @@ boolean P_SpectatorJoinGame(player_t *player)
}
player->spectator = false;
player->pflags &= ~PF_WANTSTOJOIN;
+ player->kartstuff[k_spectatewait] = 0;
player->playerstate = PST_REBORN;
//Reset away view
@@ -9844,4 +9582,3 @@ void P_PlayerAfterThink(player_t *player)
K_KartPlayerAfterThink(player);
}
-
diff --git a/src/r_draw.h b/src/r_draw.h
index 9a81a7f5..900802ce 100644
--- a/src/r_draw.h
+++ b/src/r_draw.h
@@ -102,6 +102,8 @@ extern lumpnum_t viewborderlump[8];
// ------------------------------------------------
#define GTC_CACHE 1
+#define GTC_MENUCACHE GTC_CACHE
+//@TODO Add a separate caching mechanism for menu colormaps distinct from in-level GTC_CACHE. For now this is still preferable to memory leaks...
#define TC_DEFAULT -1
#define TC_BOSS -2
diff --git a/src/r_main.c b/src/r_main.c
index 020d9e78..1a72d616 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -56,6 +56,7 @@ INT32 centerx, centery;
fixed_t centerxfrac, centeryfrac;
fixed_t projection;
fixed_t projectiony; // aspect ratio
+fixed_t fovtan; // field of view
// just for profiling purposes
size_t framecount;
@@ -134,11 +135,14 @@ static CV_PossibleValue_t drawdist_precip_cons_t[] = {
{1024, "1024"}, {1536, "1536"}, {2048, "2048"},
{0, "None"}, {0, NULL}};
+static CV_PossibleValue_t fov_cons_t[] = {{0, "MIN"}, {179*FRACUNIT, "MAX"}, {0, NULL}};
+
//static CV_PossibleValue_t precipdensity_cons_t[] = {{0, "None"}, {1, "Light"}, {2, "Moderate"}, {4, "Heavy"}, {6, "Thick"}, {8, "V.Thick"}, {0, NULL}};
static CV_PossibleValue_t translucenthud_cons_t[] = {{0, "MIN"}, {10, "MAX"}, {0, NULL}};
static CV_PossibleValue_t maxportals_cons_t[] = {{0, "MIN"}, {12, "MAX"}, {0, NULL}}; // lmao rendering 32 portals, you're a card
static CV_PossibleValue_t homremoval_cons_t[] = {{0, "No"}, {1, "Yes"}, {2, "Flash"}, {0, NULL}};
+static void Fov_OnChange(void);
static void ChaseCam_OnChange(void);
static void ChaseCam2_OnChange(void);
static void ChaseCam3_OnChange(void);
@@ -175,6 +179,7 @@ consvar_t cv_drawdist = {"drawdist", "Infinite", CV_SAVE, drawdist_cons_t, NULL,
//consvar_t cv_drawdist_nights = {"drawdist_nights", "2048", CV_SAVE, drawdist_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_drawdist_precip = {"drawdist_precip", "1024", CV_SAVE, drawdist_precip_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
//consvar_t cv_precipdensity = {"precipdensity", "Moderate", CV_SAVE, precipdensity_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_fov = {"fov", "90", CV_FLOAT|CV_CALL, fov_cons_t, Fov_OnChange, 0, NULL, NULL, 0, 0, NULL};
// Okay, whoever said homremoval causes a performance hit should be shot.
consvar_t cv_homremoval = {"homremoval", "Yes", CV_SAVE, homremoval_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -227,6 +232,14 @@ void SplitScreen_OnChange(void)
}
}
}
+static void Fov_OnChange(void)
+{
+ // Shouldn't be needed with render parity?
+ //if ((netgame || multiplayer) && !cv_debug && cv_fov.value != 90*FRACUNIT)
+ // CV_Set(&cv_fov, cv_fov.defaultvalue);
+
+ R_SetViewSize();
+}
static void ChaseCam_OnChange(void)
{
@@ -517,7 +530,7 @@ static void R_InitTextureMapping(void)
//
// Calc focallength
// so FIELDOFVIEW angles covers SCREENWIDTH.
- focallength = FixedDiv(centerxfrac,
+ focallength = FixedDiv(projection,
FINETANGENT(FINEANGLES/4+/*cv_fov.value*/ FIELDOFVIEW/2));
#ifdef ESLOPE
@@ -632,6 +645,7 @@ void R_ExecuteSetViewSize(void)
INT32 j;
INT32 level;
INT32 startmapl;
+ angle_t fov;
setsizeneeded = false;
@@ -660,9 +674,12 @@ void R_ExecuteSetViewSize(void)
centerxfrac = centerx<> ANGLETOFINESHIFT);
+ if (splitscreen == 1) // Splitscreen FOV should be adjusted to maintain expected vertical view
+ fovtan = 17*fovtan/10;
+
+ projection = projectiony = FixedDiv(centerxfrac, fovtan);
R_InitViewBuffer(scaledviewwidth, viewheight);
@@ -688,7 +705,7 @@ void R_ExecuteSetViewSize(void)
for (i = 0; i < j; i++)
{
dy = ((i - viewheight*8)<>ANGLETOFINESHIFT)) & FINEMASK)*160)>>FRACBITS)
+#define AIMINGTODY(a) FixedDiv((FINETANGENT((2048+(((INT32)a)>>ANGLETOFINESHIFT)) & FINEMASK)*160)>>FRACBITS, fovtan)
// recalc necessary stuff for mouseaiming
// slopes are already calculated for the full possible view (which is 4*viewheight).
@@ -1490,6 +1507,7 @@ void R_RegisterEngineStuff(void)
CV_RegisterVar(&cv_drawdist);
//CV_RegisterVar(&cv_drawdist_nights);
CV_RegisterVar(&cv_drawdist_precip);
+ CV_RegisterVar(&cv_fov);
CV_RegisterVar(&cv_chasecam);
CV_RegisterVar(&cv_chasecam2);
diff --git a/src/r_main.h b/src/r_main.h
index 7d3e26a8..38a58968 100644
--- a/src/r_main.h
+++ b/src/r_main.h
@@ -79,6 +79,7 @@ extern consvar_t cv_flipcam, cv_flipcam2, cv_flipcam3, cv_flipcam4;
extern consvar_t cv_shadow, cv_shadowoffs;
extern consvar_t cv_translucency;
extern consvar_t /*cv_precipdensity,*/ cv_drawdist, /*cv_drawdist_nights,*/ cv_drawdist_precip;
+extern consvar_t cv_fov;
extern consvar_t cv_skybox;
extern consvar_t cv_tailspickup;
diff --git a/src/r_plane.c b/src/r_plane.c
index 3c1fc30e..0ff97fcc 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -1051,7 +1051,7 @@ void R_DrawSinglePlane(visplane_t *pl)
temp = P_GetZAt(pl->slope, pl->viewx, pl->viewy);
zeroheight = FIXED_TO_FLOAT(temp);
-#define ANG2RAD(angle) ((float)((angle)*M_PI)/ANGLE_180)
+#define ANG2RAD(angle) ((float)((angle)*M_PIl)/ANGLE_180)
// p is the texture origin in view space
// Don't add in the offsets at this stage, because doing so can result in
diff --git a/src/r_segs.c b/src/r_segs.c
index 59abea3a..62c0523d 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -3093,8 +3093,9 @@ void R_StoreWallRange(INT32 start, INT32 stop)
else
markceiling = false;
- // Don't render the ceiling again when rendering polyobjects
- if (curline->polyseg)
+ // Don't mark ceiling flat lines for polys unless this line has an upper texture, otherwise we get flat leakage pulling downward
+ // (If it DOES have an upper texture and we do this, the ceiling won't render at all)
+ if (curline->polyseg && !curline->sidedef->toptexture)
markceiling = false;
}
@@ -3106,8 +3107,9 @@ void R_StoreWallRange(INT32 start, INT32 stop)
else
markfloor = false;
- // Don't render the floor again when rendering polyobjects
- if (curline->polyseg)
+ // Don't mark floor flat lines for polys unless this line has a lower texture, otherwise we get flat leakage pulling upward
+ // (If it DOES have a lower texture and we do this, the floor won't render at all)
+ if (curline->polyseg && !curline->sidedef->bottomtexture)
markfloor = false;
}
diff --git a/src/r_things.c b/src/r_things.c
index 1825d2d9..a40830ac 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1315,6 +1315,7 @@ static void R_ProjectSprite(mobj_t *thing)
return;
scalestep = (yscale2 - yscale)/(x2 - x1);
+ scalestep = scalestep ? scalestep : 1;
// The following two are alternate sorting methods which might be more applicable in some circumstances. TODO - maybe enable via MF2?
// sortscale = max(yscale, yscale2);
@@ -2500,7 +2501,7 @@ void R_DrawMasked(void)
// ==========================================================================
INT32 numskins = 0;
-skin_t skins[MAXSKINS+1];
+skin_t skins[MAXSKINS];
// FIXTHIS: don't work because it must be inistilised before the config load
//#define SKINVALUES
#ifdef SKINVALUES
@@ -2536,23 +2537,6 @@ static void Sk_SetDefaultValue(skin_t *skin)
skin->kartweight = 5;
//
- skin->normalspeed = 36<runspeed = 28<thrustfactor = 5;
- skin->accelstart = 96;
- skin->acceleration = 40;
-
- skin->ability = CA_NONE;
- skin->ability2 = CA2_SPINDASH;
- skin->jumpfactor = FRACUNIT;
- skin->actionspd = 30<mindash = 15<maxdash = 90<thokitem = -1;
- skin->spinitem = -1;
- skin->revitem = -1;
-
skin->highresscale = FRACUNIT>>1;
for (i = 0; i < sfx_skinsoundslot0; i++)
@@ -2586,7 +2570,7 @@ void R_InitSkins(void)
#ifdef SKINVALUES
skin_cons_t[0].strvalue = skins[0].name;
#endif
- skin->flags = SF_SUPER|SF_SUPERANIMS|SF_SUPERSPIN;
+ skin->flags = 0;
strcpy(skin->realname, "Sonic");
strcpy(skin->hudname, "SONIC");
@@ -2595,20 +2579,11 @@ void R_InitSkins(void)
strncpy(skin->facemmap, "PLAYMMAP", 9);
skin->prefcolor = SKINCOLOR_BLUE;
- skin->ability = CA_THOK;
- skin->actionspd = 60<kartspeed = 8;
skin->kartweight = 2;
//
- skin->normalspeed = 36<runspeed = 28<thrustfactor = 5;
- skin->accelstart = 96;
- skin->acceleration = 40;
-
skin->spritedef.numframes = sprites[SPR_PLAY].numframes;
skin->spritedef.spriteframes = sprites[SPR_PLAY].spriteframes;
ST_LoadFaceGraphics(skin->facerank, skin->facewant, skin->facemmap, 0);
@@ -2635,7 +2610,7 @@ INT32 R_SkinAvailable(const char *name)
}
// network code calls this when a 'skin change' is received
-void SetPlayerSkin(INT32 playernum, const char *skinname)
+boolean SetPlayerSkin(INT32 playernum, const char *skinname)
{
INT32 i;
player_t *player = &players[playernum];
@@ -2646,7 +2621,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname)
if (stricmp(skins[i].name, skinname) == 0)
{
SetPlayerSkinByNum(playernum, i);
- return;
+ return true;
}
}
@@ -2656,6 +2631,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname)
CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname);
SetPlayerSkinByNum(playernum, 0);
+ return false;
}
// Same as SetPlayerSkin, but uses the skin #.
@@ -2671,31 +2647,12 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
if (player->mo)
player->mo->skin = skin;
- player->charability = (UINT8)skin->ability;
- player->charability2 = (UINT8)skin->ability2;
-
player->charflags = (UINT32)skin->flags;
- player->thokitem = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem;
- player->spinitem = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem;
- player->revitem = skin->revitem < 0 ? (mobjtype_t)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem;
-
- player->actionspd = skin->actionspd;
- player->mindash = skin->mindash;
- player->maxdash = skin->maxdash;
-
// SRB2kart
player->kartspeed = skin->kartspeed;
player->kartweight = skin->kartweight;
- player->normalspeed = skin->normalspeed;
- player->runspeed = skin->runspeed;
- player->thrustfactor = skin->thrustfactor;
- player->accelstart = skin->accelstart;
- player->acceleration = skin->acceleration;
-
- player->jumpfactor = skin->jumpfactor;
-
/*if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback || modeattacking))
{
if (playernum == consoleplayer)
@@ -2771,7 +2728,7 @@ void R_AddSkins(UINT16 wadnum)
// advance by default
lastlump = lump + 1;
- if (numskins > MAXSKINS)
+ if (numskins >= MAXSKINS)
{
CONS_Debug(DBG_RENDER, "ignored skin (%d skins maximum)\n", MAXSKINS);
continue; // so we know how many skins couldn't be added
@@ -2894,28 +2851,8 @@ void R_AddSkins(UINT16 wadnum)
#define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value);
// character type identification
FULLPROCESS(flags)
- FULLPROCESS(ability)
- FULLPROCESS(ability2)
-
- FULLPROCESS(thokitem)
- FULLPROCESS(spinitem)
- FULLPROCESS(revitem)
#undef FULLPROCESS
-#define GETSPEED(field) else if (!stricmp(stoken, #field)) skin->field = atoi(value)<field = atoi(value);
- GETINT(thrustfactor)
- GETINT(accelstart)
- GETINT(acceleration)
-#undef GETINT
-
#define GETKARTSTAT(field) \
else if (!stricmp(stoken, #field)) \
{ \
@@ -2933,8 +2870,6 @@ void R_AddSkins(UINT16 wadnum)
else if (!stricmp(stoken, "prefcolor"))
skin->prefcolor = K_GetKartColorByName(value);
- else if (!stricmp(stoken, "jumpfactor"))
- skin->jumpfactor = FLOAT_TO_FIXED(atof(value));
else if (!stricmp(stoken, "highresscale"))
skin->highresscale = FLOAT_TO_FIXED(atof(value));
else
diff --git a/src/r_things.h b/src/r_things.h
index a8635034..6f48cc5b 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -83,29 +83,11 @@ typedef struct
char hudname[SKINNAMESIZE+1]; // HUD name to display (officially exactly 5 characters long)
char facerank[9], facewant[9], facemmap[9]; // Arbitrarily named patch lumps
- UINT8 ability; // ability definition
- UINT8 ability2; // secondary ability definition
- INT32 thokitem;
- INT32 spinitem;
- INT32 revitem;
- fixed_t actionspd;
- fixed_t mindash;
- fixed_t maxdash;
-
// SRB2kart
UINT8 kartspeed;
UINT8 kartweight;
//
- fixed_t normalspeed; // Normal ground
- fixed_t runspeed; // Speed that you break into your run animation
-
- UINT8 thrustfactor; // Thrust = thrustfactor * acceleration
- UINT8 accelstart; // Acceleration if speed = 0
- UINT8 acceleration; // Acceleration
-
- fixed_t jumpfactor; // multiple of standard jump height
-
// Definable color translation table
UINT8 starttranscolor;
UINT8 prefcolor;
@@ -192,9 +174,9 @@ typedef struct drawnode_s
} drawnode_t;
extern INT32 numskins;
-extern skin_t skins[MAXSKINS + 1];
+extern skin_t skins[MAXSKINS];
-void SetPlayerSkin(INT32 playernum,const char *skinname);
+boolean SetPlayerSkin(INT32 playernum,const char *skinname);
void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002
INT32 R_SkinAvailable(const char *name);
void R_AddSkins(UINT16 wadnum);
diff --git a/src/s_sound.c b/src/s_sound.c
index 856aa045..2ddffa3f 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -57,6 +57,9 @@ static void GameMIDIMusic_OnChange(void);
static void GameSounds_OnChange(void);
static void GameDigiMusic_OnChange(void);
+static void PlayMusicIfUnfocused_OnChange(void);
+static void PlaySoundIfUnfocused_OnChange(void);
+
// commands for music and sound servers
#ifdef MUSSERV
consvar_t musserver_cmd = {"musserver_cmd", "musserver", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -110,6 +113,9 @@ consvar_t cv_gamemidimusic = {"midimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_O
#endif
consvar_t cv_gamesounds = {"sounds", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameSounds_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_playmusicifunfocused = {"playmusicifunfocused", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, PlayMusicIfUnfocused_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_playsoundifunfocused = {"playsoundsifunfocused", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, PlaySoundIfUnfocused_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
#define S_MAX_VOLUME 127
// when to clip out sounds
@@ -270,6 +276,9 @@ void S_RegisterSoundStuff(void)
CV_RegisterVar(&cv_gamemidimusic);
#endif
+ CV_RegisterVar(&cv_playmusicifunfocused);
+ CV_RegisterVar(&cv_playsoundifunfocused);
+
COM_AddCommand("tunes", Command_Tunes_f);
COM_AddCommand("restartaudio", Command_RestartAudio_f);
@@ -1897,6 +1906,10 @@ static boolean S_PlayMusic(boolean looping)
}
S_InitMusicVolume(); // switch between digi and sequence volume
+
+ if (window_notinfocus && !cv_playmusicifunfocused.value)
+ I_PauseSong();
+
return true;
}
@@ -1978,6 +1991,24 @@ void S_ResumeAudio(void)
I_ResumeCD();
}
+void S_DisableSound(void)
+{
+ if (sound_started && !sound_disabled)
+ {
+ sound_disabled = true;
+ S_StopSounds();
+ }
+}
+
+void S_EnableSound(void)
+{
+ if (sound_started && sound_disabled)
+ {
+ sound_disabled = false;
+ S_InitSfxChannels(cv_soundvolume.value);
+ }
+}
+
void S_SetMusicVolume(INT32 digvolume, INT32 seqvolume)
{
if (digvolume < 0)
@@ -2156,15 +2187,11 @@ void GameSounds_OnChange(void)
if (sound_disabled)
{
- sound_disabled = false;
- S_InitSfxChannels(cv_soundvolume.value);
- S_StartSound(NULL, sfx_strpst);
+ if (!( cv_playsoundifunfocused.value && window_notinfocus ))
+ S_EnableSound();
}
else
- {
- sound_disabled = true;
- S_StopSounds();
- }
+ S_DisableSound();
}
void GameDigiMusic_OnChange(void)
@@ -2251,3 +2278,28 @@ void GameMIDIMusic_OnChange(void)
}
}
#endif
+
+static void PlayMusicIfUnfocused_OnChange(void)
+{
+ if (window_notinfocus)
+ {
+ if (cv_playmusicifunfocused.value)
+ I_PauseSong();
+ else
+ I_ResumeSong();
+ }
+}
+
+static void PlaySoundIfUnfocused_OnChange(void)
+{
+ if (!cv_gamesounds.value)
+ return;
+
+ if (window_notinfocus)
+ {
+ if (cv_playsoundifunfocused.value)
+ S_DisableSound();
+ else
+ S_EnableSound();
+ }
+}
diff --git a/src/s_sound.h b/src/s_sound.h
index 1ad519c2..1c938681 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -33,6 +33,8 @@ extern consvar_t cv_gamedigimusic;
extern consvar_t cv_gamemidimusic;
#endif
extern consvar_t cv_gamesounds;
+extern consvar_t cv_playmusicifunfocused;
+extern consvar_t cv_playsoundifunfocused;
#ifdef SNDSERV
extern consvar_t sndserver_cmd, sndserver_arg;
@@ -169,6 +171,10 @@ void S_StopMusic(void);
void S_PauseAudio(void);
void S_ResumeAudio(void);
+// Enable and disable sound effects
+void S_EnableSound(void);
+void S_DisableSound(void);
+
//
// Updates music & sounds
//
diff --git a/src/screen.c b/src/screen.c
index af6aed03..4de2abd0 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -403,7 +403,7 @@ void SCR_DisplayTicRate(void)
tic_t i;
tic_t ontic = I_GetTime();
tic_t totaltics = 0;
- INT32 ticcntcolor = 0;
+ const UINT8 *ticcntcolor = NULL;
for (i = lasttic + 1; i < TICRATE+lasttic && i < ontic; ++i)
fpsgraph[i % TICRATE] = false;
@@ -414,13 +414,36 @@ void SCR_DisplayTicRate(void)
if (fpsgraph[i])
++totaltics;
- if (totaltics <= TICRATE/2) ticcntcolor = V_REDMAP;
- else if (totaltics == TICRATE) ticcntcolor = V_GREENMAP;
+ if (totaltics <= TICRATE/2) ticcntcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SALMON, GTC_CACHE);
+ else if (totaltics == TICRATE) ticcntcolor = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_MINT, GTC_CACHE);
- V_DrawString(vid.width-(24*vid.dupx), vid.height-(16*vid.dupy),
+ /*V_DrawString(vid.width-(24*vid.dupx), vid.height-(16*vid.dupy),
V_YELLOWMAP|V_NOSCALESTART, "FPS");
V_DrawString(vid.width-(40*vid.dupx), vid.height-( 8*vid.dupy),
- ticcntcolor|V_NOSCALESTART, va("%02d/%02u", totaltics, TICRATE));
+ ticcntcolor|V_NOSCALESTART, va("%02d/%02u", totaltics, TICRATE));*/
+
+ // draw "FPS"
+ V_DrawFixedPatch(306< servermaxping)) // only show 2 (warning) if our ping is at a bad level
+ {
+ INT32 dispy = cv_ticrate.value ? 160 : 181;
+ HU_drawPing(307, dispy, ping, V_SNAPTORIGHT | V_SNAPTOBOTTOM);
+ }
+}
diff --git a/src/screen.h b/src/screen.h
index 9ad254d3..5b4a8e58 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -180,5 +180,6 @@ FUNCMATH boolean SCR_IsAspectCorrect(INT32 width, INT32 height);
// move out to main code for consistency
void SCR_DisplayTicRate(void);
+void SCR_DisplayLocalPing(void);
#undef DNWH
#endif //__SCREEN_H__
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index ec5d63ac..de215705 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -70,6 +70,8 @@ if(${SDL2_FOUND})
set(SRB2_SDL2_TOTAL_SOURCES
${SRB2_CORE_SOURCES}
${SRB2_CORE_HEADERS}
+ ${SRB2_PNG_SOURCES}
+ ${SRB2_PNG_HEADERS}
${SRB2_CORE_RENDER_SOURCES}
${SRB2_CORE_GAME_SOURCES}
${SRB2_LUA_SOURCES}
@@ -80,7 +82,8 @@ if(${SDL2_FOUND})
${SRB2_SDL2_HEADERS}
)
- source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS})
+ source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS}
+ ${SRB2_PNG_SOURCES} ${SRB2_PNG_HEADERS})
source_group("Renderer" FILES ${SRB2_CORE_RENDER_SOURCES})
source_group("Game" FILES ${SRB2_CORE_GAME_SOURCES})
source_group("Assembly" FILES ${SRB2_ASM_SOURCES} ${SRB2_NASM_SOURCES})
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 1b26c3d3..45b1faab 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -164,6 +164,7 @@
+
@@ -321,6 +322,7 @@
+
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index d789a61f..8556627b 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -306,6 +306,9 @@
LUA
+
+ M_Misc
+
M_Misc
diff --git a/src/sdl/Srb2SDL-vc9.vcproj b/src/sdl/Srb2SDL-vc9.vcproj
index d2a268f8..3898aeba 100644
--- a/src/sdl/Srb2SDL-vc9.vcproj
+++ b/src/sdl/Srb2SDL-vc9.vcproj
@@ -2834,6 +2834,50 @@
RelativePath="..\m_argv.h"
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index f8517618..f92f1f14 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2166,6 +2166,12 @@ void I_InitJoystick(void)
if (M_CheckParm("-nojoy"))
return;
+ if (M_CheckParm("-noxinput"))
+ SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
+
+ if (M_CheckParm("-nohidapi"))
+ SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
+
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
{
CONS_Printf("I_InitJoystick()...\n");
@@ -2968,8 +2974,8 @@ static void I_ShutdownTimer(void)
//
tic_t I_GetTime (void)
{
- static Uint32 basetime = 0;
- Uint32 ticks = SDL_GetTicks();
+ static Uint64 basetime = 0;
+ Uint64 ticks = SDL_GetTicks();
if (!basetime)
basetime = ticks;
@@ -3012,7 +3018,7 @@ void I_StartupTimer(void)
void I_Sleep(void)
{
- if (cv_sleep.value != -1)
+ if (cv_sleep.value > 0)
SDL_Delay(cv_sleep.value);
}
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 880d5808..56b6c9aa 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -64,7 +64,7 @@
#include "../m_menu.h"
#include "../d_main.h"
#include "../s_sound.h"
-#include "../i_sound.h" // midi pause/unpause
+#include "../i_sound.h" // midi pause/unpause
#include "../i_joy.h"
#include "../st_stuff.h"
#include "../g_game.h"
@@ -615,9 +615,13 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
{
// Tell game we got focus back, resume music if necessary
window_notinfocus = false;
+
if (!paused)
I_ResumeSong(); //resume it
+ if (cv_gamesounds.value)
+ S_EnableSound();
+
if (!firsttimeonmouse)
{
if (cv_usemouse.value) I_StartupMouse();
@@ -630,7 +634,10 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
{
// Tell game we lost focus, pause music
window_notinfocus = true;
- I_PauseSong();
+ if (!cv_playmusicifunfocused.value)
+ I_PauseSong();
+ if (!cv_playsoundifunfocused.value)
+ S_DisableSound();
if (!disable_mouse)
{
@@ -1229,7 +1236,7 @@ void I_GetEvent(void)
// update the menu
if (currentMenu == &OP_JoystickSetDef)
M_SetupJoystickMenu(0);
- break;
+ break;
case SDL_QUIT:
I_Quit();
M_QuitResponse('y');
@@ -1327,6 +1334,7 @@ void I_UpdateNoBlit(void)
// from PrBoom's src/SDL/i_video.c
static inline boolean I_SkipFrame(void)
{
+#if 0
static boolean skip = false;
if (rendermode != render_soft)
@@ -1345,6 +1353,8 @@ static inline boolean I_SkipFrame(void)
default:
return false;
}
+#endif
+ return false;
}
//
@@ -1361,6 +1371,9 @@ void I_FinishUpdate(void)
if (cv_ticrate.value)
SCR_DisplayTicRate();
+ if (cv_showping.value && netgame && consoleplayer != serverplayer)
+ SCR_DisplayLocalPing();
+
if (rendermode == render_soft && screens[0])
{
SDL_Rect rect;
diff --git a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
index 94a3fbb6..a8ecbf7f 100644
--- a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
+++ b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
@@ -43,6 +43,7 @@
1E44AF0D0B67CDE900BAD059 /* m_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AEFE0B67CDE900BAD059 /* m_fixed.c */; };
1E44AF0F0B67CDE900BAD059 /* m_menu.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF000B67CDE900BAD059 /* m_menu.c */; };
1E44AF110B67CDE900BAD059 /* m_misc.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* m_misc.c */; };
+ 1E44AF110B67CDE900BAD059 /* apng.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* apng.c */; };
1E44AF130B67CDE900BAD059 /* m_random.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF040B67CDE900BAD059 /* m_random.c */; };
1E44AF1A0B67CE2A00BAD059 /* info.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF180B67CE2A00BAD059 /* info.c */; };
1E44AF1E0B67CE3600BAD059 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF1C0B67CE3600BAD059 /* md5.c */; };
@@ -253,7 +254,9 @@
1E44AF000B67CDE900BAD059 /* m_menu.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_menu.c; path = ../../m_menu.c; sourceTree = SOURCE_ROOT; };
1E44AF010B67CDE900BAD059 /* m_menu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_menu.h; path = ../../m_menu.h; sourceTree = SOURCE_ROOT; };
1E44AF020B67CDE900BAD059 /* m_misc.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_misc.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; };
+ 1E44AF020B67CDE900BAD059 /* apng.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = apng.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; };
1E44AF030B67CDE900BAD059 /* m_misc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_misc.h; path = ../../m_misc.h; sourceTree = SOURCE_ROOT; };
+ 1E44AF020B67CDE900BAD059 /* apng.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = apng.h; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; };
1E44AF040B67CDE900BAD059 /* m_random.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_random.c; path = ../../m_random.c; sourceTree = SOURCE_ROOT; };
1E44AF050B67CDE900BAD059 /* m_random.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_random.h; path = ../../m_random.h; sourceTree = SOURCE_ROOT; };
1E44AF060B67CDE900BAD059 /* m_swap.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_swap.h; path = ../../m_swap.h; sourceTree = SOURCE_ROOT; };
@@ -679,6 +682,8 @@
1E44AEFF0B67CDE900BAD059 /* m_fixed.h */,
1E44AF020B67CDE900BAD059 /* m_misc.c */,
1E44AF030B67CDE900BAD059 /* m_misc.h */,
+ 1E44AF020B67CDE900BAD059 /* apng.c */,
+ 1E44AF030B67CDE900BAD059 /* apng.h */,
676BB51C0E0DE06100C95963 /* m_queue.c */,
676BB51D0E0DE06100C95963 /* m_queue.h */,
1E44AF040B67CDE900BAD059 /* m_random.c */,
diff --git a/src/sdl/srb2icon.png b/src/sdl/srb2icon.png
new file mode 100644
index 00000000..2aca4b20
Binary files /dev/null and b/src/sdl/srb2icon.png differ
diff --git a/src/sdl12/Srb2SDL-vc10.vcxproj b/src/sdl12/Srb2SDL-vc10.vcxproj
index 9c550a09..0ac7e9e5 100644
--- a/src/sdl12/Srb2SDL-vc10.vcxproj
+++ b/src/sdl12/Srb2SDL-vc10.vcxproj
@@ -875,6 +875,16 @@
%(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+
+ %(AdditionalIncludeDirectories)
+ %(PreprocessorDefinitions)
+ %(AdditionalIncludeDirectories)
+ %(PreprocessorDefinitions)
+ %(AdditionalIncludeDirectories)
+ %(PreprocessorDefinitions)
+ %(AdditionalIncludeDirectories)
+ %(PreprocessorDefinitions)
+
%(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
@@ -1333,6 +1343,7 @@
+
diff --git a/src/sdl12/Srb2SDL-vc9.vcproj b/src/sdl12/Srb2SDL-vc9.vcproj
index d2a268f8..fa386e38 100644
--- a/src/sdl12/Srb2SDL-vc9.vcproj
+++ b/src/sdl12/Srb2SDL-vc9.vcproj
@@ -2790,6 +2790,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c
index 0e5adbb4..d055a4ca 100644
--- a/src/sdl12/i_system.c
+++ b/src/sdl12/i_system.c
@@ -2914,7 +2914,7 @@ void I_StartupTimer(void)
void I_Sleep(void)
{
#if !(defined (_arch_dreamcast) || defined (_XBOX))
- if (cv_sleep.value != -1)
+ if (cv_sleep.value > 0)
SDL_Delay(cv_sleep.value);
#endif
}
diff --git a/src/sdl12/i_video.c b/src/sdl12/i_video.c
index 69cf5ca9..a2a20c61 100644
--- a/src/sdl12/i_video.c
+++ b/src/sdl12/i_video.c
@@ -1343,6 +1343,9 @@ void I_FinishUpdate(void)
if (cv_ticrate.value)
SCR_DisplayTicRate();
+ if (cv_showping.value && netgame && consoleplayer != serverplayer)
+ SCR_DisplayLocalPing();
+
if (render_soft == rendermode && screens[0])
{
SDL_Rect *dstrect = NULL;
diff --git a/src/sdl12/macosx/Srb2mac.pbproj/project.pbxproj b/src/sdl12/macosx/Srb2mac.pbproj/project.pbxproj
index de12201f..1f8e3276 100644
--- a/src/sdl12/macosx/Srb2mac.pbproj/project.pbxproj
+++ b/src/sdl12/macosx/Srb2mac.pbproj/project.pbxproj
@@ -1365,6 +1365,20 @@
path = ../../m_misc.h;
refType = 2;
};
+ 84177764085A10EB000C01D8 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ name = apng.c;
+ path = ../../apng.c;
+ refType = 2;
+ };
+ 84177765085A10EB000C01D8 = {
+ fileEncoding = 30;
+ isa = PBXFileReference;
+ name = m_misc.h;
+ path = ../../apng.h;
+ refType = 2;
+ };
84177766085A10EB000C01D8 = {
fileEncoding = 30;
isa = PBXFileReference;
diff --git a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj
index 0ab40c45..69c544c5 100644
--- a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj
+++ b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj
@@ -43,6 +43,7 @@
1E44AF0D0B67CDE900BAD059 /* m_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AEFE0B67CDE900BAD059 /* m_fixed.c */; };
1E44AF0F0B67CDE900BAD059 /* m_menu.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF000B67CDE900BAD059 /* m_menu.c */; };
1E44AF110B67CDE900BAD059 /* m_misc.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* m_misc.c */; };
+ 1E44AF110B67CDE900BAD059 /* apng.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* apng.c */; };
1E44AF130B67CDE900BAD059 /* m_random.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF040B67CDE900BAD059 /* m_random.c */; };
1E44AF1A0B67CE2A00BAD059 /* info.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF180B67CE2A00BAD059 /* info.c */; };
1E44AF1E0B67CE3600BAD059 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF1C0B67CE3600BAD059 /* md5.c */; };
@@ -254,6 +255,8 @@
1E44AF010B67CDE900BAD059 /* m_menu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_menu.h; path = ../../m_menu.h; sourceTree = SOURCE_ROOT; };
1E44AF020B67CDE900BAD059 /* m_misc.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_misc.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; };
1E44AF030B67CDE900BAD059 /* m_misc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_misc.h; path = ../../m_misc.h; sourceTree = SOURCE_ROOT; };
+ 1E44AF020B67CDE900BAD059 /* apng.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = apng.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; };
+ 1E44AF030B67CDE900BAD059 /* apng.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = apng.h; path = ../../m_misc.h; sourceTree = SOURCE_ROOT; };
1E44AF040B67CDE900BAD059 /* m_random.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_random.c; path = ../../m_random.c; sourceTree = SOURCE_ROOT; };
1E44AF050B67CDE900BAD059 /* m_random.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_random.h; path = ../../m_random.h; sourceTree = SOURCE_ROOT; };
1E44AF060B67CDE900BAD059 /* m_swap.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_swap.h; path = ../../m_swap.h; sourceTree = SOURCE_ROOT; };
@@ -679,6 +682,8 @@
1E44AEFF0B67CDE900BAD059 /* m_fixed.h */,
1E44AF020B67CDE900BAD059 /* m_misc.c */,
1E44AF030B67CDE900BAD059 /* m_misc.h */,
+ 1E44AF020B67CDE900BAD059 /* apng.c */,
+ 1E44AF030B67CDE900BAD059 /* apng.h */,
676BB51C0E0DE06100C95963 /* m_queue.c */,
676BB51D0E0DE06100C95963 /* m_queue.h */,
1E44AF040B67CDE900BAD059 /* m_random.c */,
diff --git a/src/sounds.c b/src/sounds.c
index a3bc8bf4..61fddb76 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -815,6 +815,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"chain", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Mementos Reaper
{"mkuma", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Trigger Happy Havoc Monokuma
{"toada", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Arid Sands Toad scream
+ {"bsnipe", false, 96, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Banana sniping
{"itfree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // :shitsfree:
{"dbgsal", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR}, // Debug notification
diff --git a/src/sounds.h b/src/sounds.h
index 4c341d49..bb46ea9d 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -890,6 +890,7 @@ typedef enum
sfx_chain,
sfx_mkuma,
sfx_toada,
+ sfx_bsnipe,
sfx_itfree,
sfx_dbgsal,
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 8ebd2132..36a658ae 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -1952,31 +1952,38 @@ static void ST_overlayDrawer(void)
#endif
)
{
+ const char *itemtxt = M_GetText("Item - Join Game");
+
+ if (stplyr->powers[pw_flashing])
+ itemtxt = M_GetText("Item - . . .");
+ else if (stplyr->pflags & PF_WANTSTOJOIN)
+ itemtxt = M_GetText("Item - Cancel Join");
+ else if (G_GametypeHasTeams())
+ itemtxt = M_GetText("Item - Join Team");
+
+ if (cv_ingamecap.value)
+ {
+ UINT8 numingame = 0;
+ UINT8 i;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i] && !players[i].spectator)
+ numingame++;
+
+ itemtxt = va("%s (%s: %d)", itemtxt, M_GetText("Slots left"), max(0, cv_ingamecap.value - numingame));
+ }
+
// SRB2kart: changed positions & text
if (splitscreen)
{
INT32 splitflags = K_calcSplitFlags(0);
V_DrawThinString(2, (BASEVIDHEIGHT/2)-20, V_YELLOWMAP|V_HUDTRANSHALF|splitflags, M_GetText("- SPECTATING -"));
- if (stplyr->powers[pw_flashing])
- V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - . . ."));
- else if (stplyr->pflags & PF_WANTSTOJOIN)
- V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - Cancel Join"));
- /*else if (G_GametypeHasTeams())
- V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - Join Team"));*/
- else
- V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, M_GetText("Item - Join Game"));
+ V_DrawThinString(2, (BASEVIDHEIGHT/2)-10, V_HUDTRANSHALF|splitflags, itemtxt);
}
else
{
V_DrawString(2, BASEVIDHEIGHT-40, V_HUDTRANSHALF|V_YELLOWMAP, M_GetText("- SPECTATING -"));
- if (stplyr->powers[pw_flashing])
- V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - . . ."));
- else if (stplyr->pflags & PF_WANTSTOJOIN)
- V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - Cancel Join"));
- /*else if (G_GametypeHasTeams())
- V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - Join Team"));*/
- else
- V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, M_GetText("Item - Join Game"));
+ V_DrawString(2, BASEVIDHEIGHT-30, V_HUDTRANSHALF, itemtxt);
V_DrawString(2, BASEVIDHEIGHT-20, V_HUDTRANSHALF, M_GetText("Accelerate - Float"));
V_DrawString(2, BASEVIDHEIGHT-10, V_HUDTRANSHALF, M_GetText("Brake - Sink"));
}
diff --git a/src/v_video.c b/src/v_video.c
index 4685c46c..16d39e1d 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -1341,8 +1341,8 @@ void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed)
V_DrawScaledPatch(x, y, flags, hu_font[c]);
}
-// Writes a single character for the chat. (draw WHITE if bit 7 set)
-// Essentially the same as the above but it's small or big depending on what resolution you've chosen to huge..
+// Writes a single character for the chat (half scaled). (draw WHITE if bit 7 set)
+// 16/02/19: Scratch the scaling thing, chat doesn't work anymore under 2x res -Lat'
//
void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UINT8 *colormap)
{
@@ -1358,13 +1358,11 @@ void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UI
if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
return;
- w = (vid.width < 640 ) ? (SHORT(hu_font[c]->width)/2) : (SHORT(hu_font[c]->width)); // use normal sized characters if we're using a terribly low resolution.
+ w = SHORT(hu_font[c]->width)/2;
if (x + w > vid.width)
return;
- V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, (vid.width < 640) ? (FRACUNIT) : (FRACUNIT/2), flags, hu_font[c], colormap);
-
-
+ V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/2, flags, hu_font[c], colormap);
}
// Precompile a wordwrapped string to any given width.
@@ -2014,6 +2012,28 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits)
} while (--digits);
}
+// Draws a number using the PING font thingy.
+// TODO: Merge number drawing functions into one with "font name" selection.
+
+void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap)
+{
+ INT32 w = SHORT(pingnum[0]->width); // this SHOULD always be 5 but I guess custom graphics exist.
+
+ if (flags & V_NOSCALESTART)
+ w *= vid.dupx;
+
+ if (num < 0)
+ num = -num;
+
+ // draw the number
+ do
+ {
+ x -= (w-1); // Oni wanted their outline to intersect.
+ V_DrawFixedPatch(x<filename);
DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0);
break;
+#ifdef HAVE_BLUA
case RET_LUA:
LUA_LoadLump(numwadfiles - 1, 0);
break;
+#endif
default:
break;
}
+ if (refreshdirmenu & REFRESHDIR_GAMEDATA)
+ G_LoadGameData();
+ DEH_UpdateMaxFreeslots();
+
W_InvalidateLumpnumCache();
return wadfile->numlumps;
}
@@ -846,16 +855,16 @@ void W_UnloadWadFile(UINT16 num)
* \return 1 if all files were loaded, 0 if at least one was missing or
* invalid.
*/
-INT32 W_InitMultipleFiles(char **filenames)
+INT32 W_InitMultipleFiles(char **filenames, boolean addons)
{
INT32 rc = 1;
- // open all the files, load headers, and count lumps
- numwadfiles = 0;
-
// will be realloced as lumps are added
for (; *filenames; filenames++)
{
+ if (addons && !W_VerifyNMUSlumps(*filenames))
+ G_SetGameModified(true, false);
+
//CONS_Debug(DBG_SETUP, "Loading %s\n", *filenames);
rc &= (W_InitFile(*filenames) != INT16_MAX) ? 1 : 0;
}
@@ -1547,7 +1556,6 @@ void *W_CachePatchName(const char *name, INT32 tag)
return W_CachePatchNum(num, tag);
}
#ifndef NOMD5
-#define MD5_LEN 16
/**
* Prints an MD5 string into a human-readable textual format.
diff --git a/src/w_wad.h b/src/w_wad.h
index e2e17740..762d3708 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -133,7 +133,7 @@ void W_UnloadWadFile(UINT16 num);
// W_InitMultipleFiles returns 1 if all is okay, 0 otherwise,
// so that it stops with a message if a file was not found, but not if all is okay.
-INT32 W_InitMultipleFiles(char **filenames);
+INT32 W_InitMultipleFiles(char **filenames, boolean addons);
const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump);
const char *W_CheckNameForNum(lumpnum_t lumpnum);
diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj
index d2cf8cde..ced3d128 100644
--- a/src/win32/Srb2win-vc10.vcxproj
+++ b/src/win32/Srb2win-vc10.vcxproj
@@ -182,6 +182,7 @@
+
@@ -334,6 +335,7 @@
+
diff --git a/src/win32/Srb2win-vc9.vcproj b/src/win32/Srb2win-vc9.vcproj
index 24235bc7..a64b8638 100644
--- a/src/win32/Srb2win-vc9.vcproj
+++ b/src/win32/Srb2win-vc9.vcproj
@@ -2851,6 +2851,50 @@
RelativePath="..\m_misc.h"
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c
index 75aca68d..47250334 100644
--- a/src/win32/win_sys.c
+++ b/src/win32/win_sys.c
@@ -261,7 +261,7 @@ tic_t I_GetTime(void)
void I_Sleep(void)
{
- if (cv_sleep.value != -1)
+ if (cv_sleep.value > 0)
Sleep(cv_sleep.value);
}
@@ -639,9 +639,6 @@ void I_Error(const char *error, ...)
if (!errorcount)
{
M_SaveConfig(NULL); // save game config, cvars..
-#ifndef NONET
- D_SaveBan(); // save the ban list
-#endif
G_SaveGameData();
}
diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c
index 9f3d13f8..45f96e9d 100644
--- a/src/win32/win_vid.c
+++ b/src/win32/win_vid.c
@@ -366,6 +366,9 @@ void I_FinishUpdate(void)
if (cv_ticrate.value)
SCR_DisplayTicRate();
+ if (cv_showping.value && netgame && consoleplayer != serverplayer)
+ SCR_DisplayLocalPing();
+
//
if (bDIBMode)
{
diff --git a/src/win32ce/win_sys.c b/src/win32ce/win_sys.c
index 3b6a4725..091171b5 100644
--- a/src/win32ce/win_sys.c
+++ b/src/win32ce/win_sys.c
@@ -265,7 +265,7 @@ tic_t I_GetTime(void)
void I_Sleep(void)
{
- if (cv_sleep.value != -1)
+ if (cv_sleep.value > 0)
Sleep(cv_sleep.value);
}
diff --git a/src/win32ce/win_vid.c b/src/win32ce/win_vid.c
index 5e8e7e1f..244dfaf3 100644
--- a/src/win32ce/win_vid.c
+++ b/src/win32ce/win_vid.c
@@ -198,6 +198,9 @@ void I_FinishUpdate(void)
if (cv_ticrate.value)
SCR_DisplayTicRate();
+ if (cv_showping.value && netgame && consoleplayer != serverplayer)
+ SCR_DisplayLocalPing();
+
//
if (bDIBMode)
{
diff --git a/src/y_inter.c b/src/y_inter.c
index 379d5cd3..095b4ad3 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -432,7 +432,7 @@ void Y_IntermissionDrawer(void)
if (data.match.encore)
V_DrawCenteredString(-4 + x + BASEVIDWIDTH/2, 12-8, hilicol, "ENCORE MODE");
- if (!gutter)
+ if (data.match.numplayers > NUMFORNEWCOLUMN)
{
V_DrawFill(x+156, 24, 1, 158, 0);
V_DrawFill((x-3) - duptweak, 182, dupadjust-2, 1, 0);
@@ -474,16 +474,12 @@ void Y_IntermissionDrawer(void)
V_DrawScaledPatch(x+16, y-4, 0, W_CachePatchName(va("K_CHILI%d", cursorframe+1), PU_CACHE));
}
- if (!gutter)
- strlcpy(strtime, data.match.name[i], 6);
- else
- STRBUFCPY(strtime, data.match.name[i]);
+ STRBUFCPY(strtime, data.match.name[i]);
- V_DrawString(x+36, y,
- ((data.match.num[i] == whiteplayer)
- ? hilicol|V_ALLOWLOWERCASE
- : V_ALLOWLOWERCASE),
- strtime);
+ if (data.match.numplayers > NUMFORNEWCOLUMN)
+ V_DrawThinString(x+36, y-1, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime);
+ else
+ V_DrawString(x+36, y, ((data.match.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime);
if (data.match.rankingsmode)
{
@@ -494,17 +490,23 @@ void Y_IntermissionDrawer(void)
else
snprintf(strtime, sizeof strtime, "(+ %d)", data.match.increase[data.match.num[i]]);
- V_DrawRightAlignedString(x+120+gutter, y, 0, strtime);
+ if (data.match.numplayers > NUMFORNEWCOLUMN)
+ V_DrawRightAlignedThinString(x+135+gutter, y-1, V_6WIDTHSPACE, strtime);
+ else
+ V_DrawRightAlignedString(x+120+gutter, y, 0, strtime);
}
snprintf(strtime, sizeof strtime, "%d", data.match.val[i]);
- V_DrawRightAlignedString(x+152+gutter, y, 0, strtime);
+ if (data.match.numplayers > NUMFORNEWCOLUMN)
+ V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, strtime);
+ else
+ V_DrawRightAlignedString(x+152+gutter, y, 0, strtime);
}
else
{
if (data.match.val[i] == (UINT32_MAX-1))
- V_DrawRightAlignedThinString(x+152+gutter, y-1, 0, "NO CONTEST.");
+ V_DrawRightAlignedThinString(x+152+gutter, y-1, (data.match.numplayers > NUMFORNEWCOLUMN ? V_6WIDTHSPACE : 0), "NO CONTEST.");
else
{
if (intertype == int_race)
@@ -513,10 +515,18 @@ void Y_IntermissionDrawer(void)
G_TicsToSeconds(data.match.val[i]), G_TicsToCentiseconds(data.match.val[i]));
strtime[sizeof strtime - 1] = '\0';
- V_DrawRightAlignedString(x+152+gutter, y, 0, strtime);
+ if (data.match.numplayers > NUMFORNEWCOLUMN)
+ V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, strtime);
+ else
+ V_DrawRightAlignedString(x+152+gutter, y, 0, strtime);
}
else
- V_DrawRightAlignedString(x+152+gutter, y, 0, va("%i", data.match.val[i]));
+ {
+ if (data.match.numplayers > NUMFORNEWCOLUMN)
+ V_DrawRightAlignedThinString(x+152+gutter, y-1, V_6WIDTHSPACE, va("%i", data.match.val[i]));
+ else
+ V_DrawRightAlignedString(x+152+gutter, y, 0, va("%i", data.match.val[i]));
+ }
}
}
@@ -786,7 +796,7 @@ void Y_StartIntermission(void)
}
case int_race: // (time-only race)
{
- if ((!modifiedgame || savemoddata) && !multiplayer && !demoplayback) // remove this once we have a proper time attack screen
+ if (!majormods && !multiplayer && !demoplayback) // remove this once we have a proper time attack screen
{
// Update visitation flags
mapvisited[gamemap-1] |= MV_BEATEN;
@@ -1037,7 +1047,7 @@ void Y_VoteDrawer(void)
V_DrawFixedPatch((BASEVIDWIDTH-60)<