diff --git a/.circleci/config.yml b/.circleci/config.yml index ca9105685..1784ba1ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ jobs: build: working_directory: /root/SRB2 docker: - - image: debian:jessie + - image: debian:stretch environment: CC: ccache gcc -m32 PKG_CONFIG_LIBDIR: /usr/lib/i386-linux-gnu/pkgconfig @@ -36,14 +36,20 @@ 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 libpng-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: Clean build + name: Compile without network support and BLUA + command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1 + - run: + name: wipe build + command: make -C src LINUX=1 cleandep + - run: + name: rebuild depend command: make -C src LINUX=1 clean - restore_cache: keys: diff --git a/.travis.yml b/.travis.yml index e9c8d0d71..b6f8a7aa7 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 @@ -249,17 +254,21 @@ matrix: # osx_image: xcode7.2 # if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/ # #Apple LLVM version 7.0.2 (clang-700.1.81) +# - os: osx +# osx_image: xcode7.3 +# #Apple LLVM version 7.3.0 (clang-703.0.31) +# - os: osx +# osx_image: xcode7.3 +# #Apple LLVM version 7.3.0 (clang-703.0.31) - os: osx - osx_image: xcode7.3 if: env(DPL_ENABLED) != "1" OR env(DPL_TERMINATE_TESTS) != "1" OR NOT branch =~ /^.*deployer.*$/ - #Apple LLVM version 7.3.0 (clang-703.0.31) + #Default: macOS 10.13 and Xcode 9.4.1 ################################ # Deployer Buildbots - OSX ################################ - os: osx - osx_image: xcode7.3 if: env(DPL_ENABLED) = "1" AND (env(_DPL_JOB_ENABLED) = "1" OR env(DPL_JOB_ENABLE_ALL) = "1") AND (branch =~ /^.*deployer.*$/ OR (tag IS present AND env(DPL_TAG_ENABLED) = "1")) AND env(DPL_TERMINATE_MAIN) != "1" @@ -559,6 +568,16 @@ addons: - libgme-dev - zlib1g-dev - p7zip-full + homebrew: + taps: + - mazmazz/srb2 + packages: + - sdl2_mixer + - game-music-emu + - p7zip + - cmake + update: true + before_install: @@ -591,18 +610,10 @@ install: # * `sdl2_mixer` requires options from the formula tap https://github.com/mazmazz/homebrew-srb2 # * `brew postinstall` runs post-install scripts after building a bottle - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - brew update; - brew tap mazmazz/srb2; - fi; - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then - if [[ "$__DPL_ACTIVE" != "1" ]]; then - brew install sdl2 sdl2_mixer game-music-emu p7zip; - brew install cmake||true; - else - brew install --build-bottle sdl2 game-music-emu p7zip; + if [[ "$__DPL_ACTIVE" == "1" ]]; then + brew install --build-bottle sdl2 game-music-emu; brew install --build-bottle mazmazz/srb2/sdl2_mixer --with-flac --with-mpg123; - brew postinstall sdl2 game-music-emu p7zip mazmazz/srb2/sdl2_mixer; - brew install cmake||true; + brew postinstall sdl2 game-music-emu mazmazz/srb2/sdl2_mixer; fi; fi - mkdir -p $HOME/srb2_cache @@ -667,7 +678,6 @@ before_script: -DCPACK_PACKAGE_VENDOR="${PROGRAM_VENDOR}" -DSRB2_SDL2_EXE_NAME="${PROGRAM_FILENAME}" - script: # Build our Makefile from Cmake! - if [[ "$__DPL_ACTIVE" == "1" ]]; then diff --git a/CMakeLists.txt b/CMakeLists.txt index 43142386d..7995034d3 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.1.0 LANGUAGES C) if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR}) @@ -56,13 +56,19 @@ macro(copy_files_to_build_dir target dlllist_var) endif() endmacro() -# 64-bit check -if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) +# bitness check +set(SRB2_SYSTEM_BITS 0) +if(CMAKE_SIZEOF_VOID_P EQUAL 8) message(STATUS "Target is 64-bit") set(SRB2_SYSTEM_BITS 64) -else() +endif() +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + message(STATUS "Target is 32-bit") set(SRB2_SYSTEM_BITS 32) endif() +if(${SRB2_SYSTEM_BITS} EQUAL 0) + message(STATUS "Target bitness is unknown") +endif() # OS macros if (UNIX) diff --git a/appveyor.yml b/appveyor.yml index e7ce1b2f7..3d46cf6de 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 1.0.2.{branch}-{build} +version: 1.1.0.{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-v110 # 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/CMakeLists.txt b/assets/CMakeLists.txt index 1eab62cc9..89be796ad 100644 --- a/assets/CMakeLists.txt +++ b/assets/CMakeLists.txt @@ -13,12 +13,12 @@ set(SRB2_ASSET_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/installer" CACHE STRING "Path to directory that contains all asset files for the installer.") set(SRB2_ASSET_HASHED -"srb2.srb;\ -patch.kart;\ -gfx.kart;\ -textures.kart;\ -chars.kart;\ -maps.kart" +"main.kart;\ +gfx.pk3;\ +textures.pk3;\ +chars.pk3;\ +maps.wad;\ +patch.pk3" CACHE STRING "Asset filenames to apply MD5 checks. No spaces between entries!" ) diff --git a/libs/dll-binaries/i686/libgme.dll b/libs/dll-binaries/i686/libgme.dll index ddf8b0d82..9a31bc4d2 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 2ba99450f..598c2d71c 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 8beee872f..392b01856 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 62391ebb5..034ba4821 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 551782518..741574afe 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 53fab4186..5222fe271 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 d9a2452c7..5a7d2f560 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 3c6464fc7..534be8a85 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 5bbfbf551..f18928f4b 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 acf571f67..6c22b678e 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 30b25dcfc..942e86e27 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 b96f4b611..d98f7ce7e 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 824a1a240..55ac4688f 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 90f60ed29..19aae1135 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 4742e0990..10c245090 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 b011777ad..b65afd30b 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 c05f25eb4..47709840a 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 1f2a2d150..cb07061b4 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 4f420d9ed..49fd5b1df 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 1f2a2d150..cb07061b4 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 82a501dbd..4cfe5e7a8 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 d56d87396..2c5e95853 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 38079dc2a..8348f12de 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 e93777cbd..2f97c173c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -401,7 +401,11 @@ if(${SRB2_CONFIG_HWRENDER}) ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_light.c ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_main.c ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2.c + ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2load.c + ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md3load.c + ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_model.c ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_trick.c + ${CMAKE_CURRENT_SOURCE_DIR}/hardware/u_list.c ) set (SRB2_HWRENDER_HEADERS @@ -415,6 +419,10 @@ if(${SRB2_CONFIG_HWRENDER}) ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_light.h ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_main.h ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2.h + ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2load.h + ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md3load.h + ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_model.h + ${CMAKE_CURRENT_SOURCE_DIR}/hardware/u_list.h ) set(SRB2_R_OPENGL_SOURCES diff --git a/src/Makefile b/src/Makefile index 7cfde5630..105f1f4c4 100644 --- a/src/Makefile +++ b/src/Makefile @@ -83,16 +83,14 @@ # SRB2 data files D_DIR?=../bin/Resources -D_FILES=$(D_DIR)/srb2.srb \ - $(D_DIR)/patch.dta \ - #$(D_DIR)/music.dta \ - $(D_DIR)/gfx.kart \ - $(D_DIR)/textures.kart \ - $(D_DIR)/chars.kart \ - $(D_DIR)/maps.kart \ - $(D_DIR)/sounds.kart \ - $(D_DIR)/patch.kart \ - $(D_DIR)/music.kart \ +D_FILES=$(D_DIR)/main.kart \ + $(D_DIR)/gfx.pk3 \ + $(D_DIR)/textures.pk3 \ + $(D_DIR)/chars.pk3 \ + $(D_DIR)/maps.wad \ + $(D_DIR)/patch.pk3 \ + $(D_DIR)/sounds.wad \ + $(D_DIR)/music.wad \ PKG_CONFIG?=pkg-config @@ -282,7 +280,8 @@ ifndef DC endif OPTS+=-DHWRENDER OBJS+=$(OBJDIR)/hw_bsp.o $(OBJDIR)/hw_draw.o $(OBJDIR)/hw_light.o \ - $(OBJDIR)/hw_main.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o $(OBJDIR)/hw_trick.o + $(OBJDIR)/hw_main.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o $(OBJDIR)/hw_trick.o \ + $(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o endif ifdef NOHS @@ -742,16 +741,18 @@ ifdef MINGW $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \ doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \ command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \ - d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ + hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ + hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ + am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ else $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \ doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \ command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \ - d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ + hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ + hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ + am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h $(CC) $(CFLAGS) $(WFLAGS) -I/usr/X11R6/include -c $< -o $@ endif @@ -903,24 +904,27 @@ ifndef NOHW $(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \ doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \ command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \ - d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ + hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ + hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ + am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h $(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@ $(OBJDIR)/ogl_win.o: hardware/r_opengl/ogl_win.c hardware/r_opengl/r_opengl.h \ doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \ command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \ - d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ + hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ + hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ + am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h $(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@ $(OBJDIR)/r_minigl.o: hardware/r_minigl/r_minigl.c hardware/r_opengl/r_opengl.h \ doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \ command.h hardware/hw_data.h hardware/hw_glide.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h am_map.h \ - d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ + hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ + hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ + am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h $(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@ endif diff --git a/src/Makefile.cfg b/src/Makefile.cfg index 1238050b3..a0398154a 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/android/i_sound.c b/src/android/i_sound.c index 2bb304424..b5a1c3646 100644 --- a/src/android/i_sound.c +++ b/src/android/i_sound.c @@ -96,6 +96,37 @@ boolean I_SetSongSpeed(float speed) return false; } +/// ------------------------ +// MUSIC SEEKING +/// ------------------------ + +UINT32 I_GetSongLength(void) +{ + return 0; +} + +boolean I_SetSongLoopPoint(UINT32 looppoint) +{ + (void)looppoint; + return false; +} + +UINT32 I_GetSongLoopPoint(void) +{ + return 0; +} + +boolean I_SetSongPosition(UINT32 position) +{ + (void)position; + return false; +} + +UINT32 I_GetSongPosition(void) +{ + return 0; +} + /// ------------------------ // MUSIC PLAYBACK /// ------------------------ @@ -140,3 +171,44 @@ void I_SetMusicVolume(INT32 volume) { (void)volume; } + +/// ------------------------ +// MUSIC FADING +/// ------------------------ + +void I_SetInternalMusicVolume(UINT8 volume) +{ + (void)volume; +} + +void I_StopFadingSong(void) +{ +} + +boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void)); +{ + (void)target_volume; + (void)source_volume; + (void)ms; + return false; +} + +boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void)); +{ + (void)target_volume; + (void)ms; + return false; +} + +boolean I_FadeOutStopSong(UINT32 ms) +{ + (void)ms; + return false; +} + +boolean I_FadeInPlaySong(UINT32 ms, boolean looping) +{ + (void)ms; + (void)looping; + return false; +} diff --git a/src/b_bot.c b/src/b_bot.c index b84db288c..c16976b07 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -271,13 +271,7 @@ void B_RespawnBot(INT32 playernum) player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol]; P_TeleportMove(tails, x, y, z); - if (player->charability == CA_FLY) - { - P_SetPlayerMobjState(tails, S_KART_STND1); // SRB2kart - was S_PLAY_ABL1 - tails->player->powers[pw_tailsfly] = (UINT16)-1; - } - else - P_SetPlayerMobjState(tails, S_KART_STND1); // SRB2kart - was S_PLAY_FALL1 + P_SetPlayerMobjState(tails, S_KART_STND1); // SRB2kart - was S_PLAY_FALL1 P_SetScale(tails, sonic->scale); tails->destscale = sonic->destscale; } diff --git a/src/command.c b/src/command.c index bb2ea86e6..3eebe32d1 100644 --- a/src/command.c +++ b/src/command.c @@ -50,6 +50,7 @@ static void COM_Exec_f(void); static void COM_Wait_f(void); static void COM_Help_f(void); static void COM_Toggle_f(void); +static void COM_Add_f(void); static void CV_EnforceExecVersion(void); static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr); @@ -156,6 +157,20 @@ void COM_BufInsertText(const char *ptext) } } +/** Progress the wait timer and flush waiting console commands when ready. + */ +void +COM_BufTicker(void) +{ + if (com_wait) + { + com_wait--; + return; + } + + COM_BufExecute(); +} + /** Flushes (executes) console commands in the buffer. */ void COM_BufExecute(void) @@ -165,12 +180,6 @@ void COM_BufExecute(void) char line[1024] = ""; INT32 quotes; - if (com_wait) - { - com_wait--; - return; - } - while (com_text.cursize) { // find a '\n' or; line break @@ -291,6 +300,7 @@ void COM_Init(void) COM_AddCommand("wait", COM_Wait_f); COM_AddCommand("help", COM_Help_f); COM_AddCommand("toggle", COM_Toggle_f); + COM_AddCommand("add", COM_Add_f); RegisterNetXCmd(XD_NETVAR, Got_NetVar); } @@ -537,10 +547,41 @@ static void COM_ExecuteString(char *ptext) { CONS_Alert(CONS_WARNING, M_GetText("Alias recursion cycle detected!\n")); recursion = 0; - return; } - recursion++; - COM_BufInsertText(a->value); + else + { + char buf[1024]; + char *write = buf, *read = a->value, *seek = read; + + while ((seek = strchr(seek, '$')) != NULL) + { + memcpy(write, read, seek-read); + write += seek-read; + + seek++; + + if (*seek >= '1' && *seek <= '9') + { + if (com_argc > (size_t)(*seek - '0')) + { + memcpy(write, com_argv[*seek - '0'], strlen(com_argv[*seek - '0'])); + write += strlen(com_argv[*seek - '0']); + } + seek++; + } + else + { + *write = '$'; + write++; + } + + read = seek; + } + WRITESTRING(write, read); + + recursion++; + COM_BufInsertText(buf); + } return; } } @@ -563,8 +604,6 @@ static void COM_ExecuteString(char *ptext) static void COM_Alias_f(void) { cmdalias_t *a; - char cmd[1024]; - size_t i, c; if (COM_Argc() < 3) { @@ -577,19 +616,9 @@ static void COM_Alias_f(void) com_alias = a; a->name = Z_StrDup(COM_Argv(1)); - - // copy the rest of the command line - cmd[0] = 0; // start out with a null string - c = COM_Argc(); - for (i = 2; i < c; i++) - { - strcat(cmd, COM_Argv(i)); - if (i != c) - strcat(cmd, " "); - } - strcat(cmd, "\n"); - - a->value = Z_StrDup(cmd); + // Just use arg 2 if it's the only other argument, in case the alias is wrapped in quotes (backward compat, or multiple commands in one string). + // Otherwise pull the whole string and seek to the end of the alias name. The strctr is in case the alias is quoted. + a->value = Z_StrDup(COM_Argc() == 3 ? COM_Argv(2) : (strchr(COM_Args() + strlen(a->name), ' ') + 1)); } /** Prints a line of text to the console. @@ -855,6 +884,27 @@ static void COM_Toggle_f(void) CV_AddValue(cvar, +1); } +/** Command variant of CV_AddValue + */ +static void COM_Add_f(void) +{ + consvar_t *cvar; + + if (COM_Argc() != 3) + { + CONS_Printf(M_GetText("Add : Add to the value of a cvar. Negative values work too!\n")); + return; + } + cvar = CV_FindVar(COM_Argv(1)); + if (!cvar) + { + CONS_Alert(CONS_NOTICE, M_GetText("%s is not a cvar\n"), COM_Argv(1)); + return; + } + + CV_AddValue(cvar, atoi(COM_Argv(2))); +} + // ========================================================================= // VARIABLE SIZE BUFFERS // ========================================================================= @@ -1254,7 +1304,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) { @@ -1355,7 +1406,7 @@ static void Got_NetVar(UINT8 **p, INT32 playernum) Setvalue(cvar, svalue, stealth); } -void CV_SaveNetVars(UINT8 **p) +void CV_SaveNetVars(UINT8 **p, boolean isdemorecording) { consvar_t *cvar; UINT8 *count_p = *p; @@ -1365,10 +1416,32 @@ void CV_SaveNetVars(UINT8 **p) // the client will reset all netvars to default before loading WRITEUINT16(*p, 0x0000); for (cvar = consvar_vars; cvar; cvar = cvar->next) - if ((cvar->flags & CV_NETVAR) && !CV_IsSetToDefault(cvar)) + if (((cvar->flags & CV_NETVAR) && !CV_IsSetToDefault(cvar)) || (isdemorecording && cvar->netid == cv_numlaps.netid)) { WRITEUINT16(*p, cvar->netid); - WRITESTRING(*p, cvar->string); + + // UGLY HACK: Save proper lap count in net replays + if (isdemorecording && cvar->netid == cv_numlaps.netid) + { + if (cv_basenumlaps.value && + (!(mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) + || (mapheaderinfo[gamemap - 1]->numlaps > cv_basenumlaps.value)) + ) + { + WRITESTRING(*p, cv_basenumlaps.string); + } + else + { + char buf[9]; + sprintf(buf, "%d", mapheaderinfo[gamemap - 1]->numlaps); + WRITESTRING(*p, buf); + } + } + else + { + WRITESTRING(*p, cvar->string); + } + WRITEUINT8(*p, false); ++count; } diff --git a/src/command.h b/src/command.h index 82dfaf8aa..6b5d513ef 100644 --- a/src/command.h +++ b/src/command.h @@ -45,6 +45,9 @@ void COM_ImmedExecute(const char *ptext); // Execute commands in buffer, flush them void COM_BufExecute(void); +// As above; and progress the wait timer. +void COM_BufTicker(void); + // setup command buffer, at game tartup void COM_Init(void); @@ -95,7 +98,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 @@ -160,7 +164,7 @@ void CV_AddValue(consvar_t *var, INT32 increment); void CV_SaveVariables(FILE *f); // load/save gamesate (load and save option and for network join in game) -void CV_SaveNetVars(UINT8 **p); +void CV_SaveNetVars(UINT8 **p, boolean isdemorecording); void CV_LoadNetVars(UINT8 **p); // reset cheat netvars after cheats is deactivated diff --git a/src/config.h.in b/src/config.h.in index a1f5d0a6e..92708a46c 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -11,19 +11,13 @@ #ifdef CMAKECONFIG -// Base SRB2 hashes -#define ASSET_HASH_SRB2_SRB "${SRB2_ASSET_srb2.srb_HASH}" -#ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_DTA "${SRB2_ASSET_patch.dta_HASH}" -#endif - -// SRB2Kart-specific hashes -#define ASSET_HASH_GFX_KART "${SRB2_ASSET_gfx.kart_HASH}" -#define ASSET_HASH_TEXTURES_KART "${SRB2_ASSET_textures.kart_HASH}" -#define ASSET_HASH_CHARS_KART "${SRB2_ASSET_chars.kart_HASH}" -#define ASSET_HASH_MAPS_KART "${SRB2_ASSET_maps.kart_HASH}" -#ifdef USE_PATCH_KART -#define ASSET_HASH_PATCH_KART "${SRB2_ASSET_patch.kart_HASH}" +#define ASSET_HASH_MAIN_KART "${SRB2_ASSET_main.kart_HASH}" +#define ASSET_HASH_GFX_PK3 "${SRB2_ASSET_gfx.pk3_HASH}" +#define ASSET_HASH_TEXTURES_PK3 "${SRB2_ASSET_textures.pk3_HASH}" +#define ASSET_HASH_CHARS_PK3 "${SRB2_ASSET_chars.pk3_HASH}" +#define ASSET_HASH_MAPS_WAD "${SRB2_ASSET_maps.wad_HASH}" +#ifdef USE_PATCH_FILE +#define ASSET_HASH_PATCH_PK3 "${SRB2_ASSET_patch.pk3_HASH}" #endif #define SRB2_COMP_REVISION "${SRB2_COMP_REVISION}" @@ -34,25 +28,17 @@ #else /* 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 2019 / 01 / 18 - Kart v1.0.2 - Main assets - * Last updated 2019 / 02 / 04 - Kart v1.0.3 - patch.kart + * Last updated 2019 / 05 / 06 - Kart v1.1.0 - patch.kart */ -// Base SRB2 hashes -#define ASSET_HASH_SRB2_SRB "c1b9577687f8a795104aef4600720ea7" -#ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_DTA "b04fd9624bfd94dc96dcf4f400f7deb4" -#endif - -// SRB2Kart-specific hashes -#define ASSET_HASH_GFX_KART "99c39f223d84ebc78e67ab68f3bead95" -#define ASSET_HASH_TEXTURES_KART "ec8e9b7535cf585afe72ef277b08f490" -#define ASSET_HASH_CHARS_KART "e2c428347dde52858a3dacd29fc5b964" -#define ASSET_HASH_MAPS_KART "1335cd064656aedca359cfbb5233ac4a" -#ifdef USE_PATCH_KART -#define ASSET_HASH_PATCH_KART "e06c1c90e5645c886026311964f8e1f5" +#define ASSET_HASH_MAIN_KART "00000000000000000000000000000000" +#define ASSET_HASH_GFX_PK3 "99c39f223d84ebc78e67ab68f3bead95" +#define ASSET_HASH_TEXTURES_PK3 "ec8e9b7535cf585afe72ef277b08f490" +#define ASSET_HASH_CHARS_PK3 "e2c428347dde52858a3dacd29fc5b964" +#define ASSET_HASH_MAPS_WAD "1335cd064656aedca359cfbb5233ac4a" +#ifdef USE_PATCH_FILE +#define ASSET_HASH_PATCH_PK3 "6461b30bb20754a16a1b582120f55842" #endif #endif diff --git a/src/console.c b/src/console.c index a10d73e7f..1defa7e82 100644 --- a/src/console.c +++ b/src/console.c @@ -96,6 +96,7 @@ static size_t input_len; // length of current line, used to bound cursor and suc // protos. static void CON_InputInit(void); static void CON_RecalcSize(void); +static void CON_ChangeHeight(void); static void CONS_hudlines_Change(void); static void CONS_backcolor_Change(void); @@ -130,12 +131,12 @@ static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"} static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Black"}, {2, "Sepia"}, - {3, "Brown"}, {4, "Pink"}, {5, "Raspberry"}, - {6, "Red"}, {7, "Creamsicle"}, {8, "Orange"}, - {9, "Gold"}, {10,"Yellow"}, {11,"Emerald"}, - {12,"Green"}, {13,"Cyan"}, {14,"Steel"}, - {15,"Periwinkle"}, {16,"Blue"}, {17,"Purple"}, - {18,"Lavender"}, + {3, "Brown"}, {4, "Pink"}, {5, "Red"}, + {6, "Orange"}, {7, "Gold"}, {8, "Yellow"}, + {9, "Peridot"}, {10,"Green"}, {11,"Aquamarine"}, + {12,"Cyan"}, {13,"Steel"}, {14,"Blue"}, + {15,"Purple"}, {16,"Magenta"}, {17,"Lavender"}, + {18,"Rose"}, {0, NULL}}; consvar_t cons_backcolor = {"con_backcolor", "Black", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL}; @@ -152,11 +153,11 @@ static CV_PossibleValue_t menuhighlight_cons_t[] = {V_SKYMAP, "Always sky-blue"}, {V_GOLDMAP, "Always gold"}, {V_LAVENDERMAP, "Always lavender"}, - {V_TEAMAP, "Always tea-green"}, - {V_STEELMAP, "Always steel-blue"}, + {V_AQUAMAP, "Always aqua-green"}, + {V_MAGENTAMAP, "Always magenta"}, {V_PINKMAP, "Always pink"}, {V_BROWNMAP, "Always brown"}, - {V_PEACHMAP, "Always peach"}, + {V_TANMAP, "Always tan"}, {0, NULL} }; consvar_t cons_menuhighlight = {"menuhighlight", "Game type", CV_SAVE, menuhighlight_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -261,27 +262,27 @@ void CON_SetupBackColormap(void) switch (cons_backcolor.value) { - case 0: palindex = 15; break; // White - case 1: palindex = 31; break; // Gray - case 2: palindex = 47; break; // Sepia - case 3: palindex = 63; break; // Brown - case 4: palindex = 150; shift = 7; break; // Pink - case 5: palindex = 127; shift = 7; break; // Raspberry - case 6: palindex = 143; break; // Red - case 7: palindex = 86; shift = 7; break; // Creamsicle - case 8: palindex = 95; break; // Orange - case 9: palindex = 119; shift = 7; break; // Gold - case 10: palindex = 111; break; // Yellow - case 11: palindex = 191; shift = 7; break; // Emerald - case 12: palindex = 175; break; // Green - case 13: palindex = 219; break; // Cyan - case 14: palindex = 207; shift = 7; break; // Steel - case 15: palindex = 230; shift = 7; break; // Periwinkle - case 16: palindex = 239; break; // Blue - case 17: palindex = 199; shift = 7; break; // Purple - case 18: palindex = 255; shift = 7; break; // Lavender - // Default green - default: palindex = 175; break; + case 0: palindex = 15; break; // White + case 1: palindex = 31; break; // Black + case 2: palindex = 251; break; // Sepia + case 3: palindex = 239; break; // Brown + case 4: palindex = 214; shift = 7; break; // Pink + case 5: palindex = 47; break; // Red + case 6: palindex = 63; break; // Orange + case 7: palindex = 71; shift = 7; break; // Gold + case 8: palindex = 79; shift = 7; break; // Yellow + case 9: palindex = 191; shift = 8; break; // Peridot + case 10: palindex = 111; break; // Green + case 11: palindex = 127; shift = 7; break; // Aquamarine + case 12: palindex = 139; break; // Cyan + case 13: palindex = 175; shift = 7; break; // Steel + case 14: palindex = 159; break; // Blue + case 15: palindex = 169; break; // Purple + case 16: palindex = 187; break; // Magenta + case 17: palindex = 199; shift = 7; break; // Lavender + case 18: palindex = 207; shift = 7; break; // Rose + // Default black + default: palindex = 31; break; } @@ -302,7 +303,7 @@ static void CONS_backcolor_Change(void) // TODO: This could probably be improved somehow... // These colormaps are 99% identical, with just a few changed bytes UINT8 *yellowmap, *purplemap, *greenmap, *bluemap, *graymap, *redmap, *orangemap,\ - *skymap, *goldmap, *lavendermap, *teamap, *steelmap, *pinkmap, *brownmap, *peachmap; + *skymap, *goldmap, *lavendermap, *aquamap, *magentamap, *pinkmap, *brownmap, *tanmap; static void CON_SetupColormaps(void) { @@ -319,11 +320,11 @@ static void CON_SetupColormaps(void) skymap = (orangemap+256); lavendermap = (skymap+256); goldmap = (lavendermap+256); - teamap = (goldmap+256); - steelmap = (teamap+256); - pinkmap = (steelmap+256); + aquamap = (goldmap+256); + magentamap = (aquamap+256); + pinkmap = (magentamap+256); brownmap = (pinkmap+256); - peachmap = (brownmap+256); + tanmap = (brownmap+256); // setup the other colormaps, for console text @@ -334,21 +335,21 @@ static void CON_SetupColormaps(void) *memorysrc = (UINT8)(i & 0xFF); // remap each color to itself... // SRB2Kart: Different console font, new colors - purplemap[120] = (UINT8)194; - yellowmap[120] = (UINT8)103; - greenmap[120] = (UINT8)162; - bluemap[120] = (UINT8)228; - redmap[120] = (UINT8)126; // battle - graymap[120] = (UINT8)10; - orangemap[120] = (UINT8)85; // record attack - skymap[120] = (UINT8)214; // race - lavendermap[120] = (UINT8)248; - goldmap[120] = (UINT8)114; - teamap[120] = (UINT8)177; - steelmap[120] = (UINT8)201; - pinkmap[120] = (UINT8)145; - brownmap[120] = (UINT8)48; - peachmap[120] = (UINT8)69; // nice + purplemap[0] = (UINT8)163; + yellowmap[0] = (UINT8)73; + greenmap[0] = (UINT8)98; + bluemap[0] = (UINT8)148; + redmap[0] = (UINT8)34; // battle + graymap[0] = (UINT8)10; + orangemap[0] = (UINT8)52; // record attack + skymap[0] = (UINT8)132; // race + lavendermap[0] = (UINT8)192; + goldmap[0] = (UINT8)65; + aquamap[0] = (UINT8)121; + magentamap[0] = (UINT8)182; + pinkmap[0] = (UINT8)210; + brownmap[0] = (UINT8)224; + tanmap[0] = (UINT8)217; // no longer nice :( // Init back colormap CON_SetupBackColormap(); @@ -467,6 +468,12 @@ static void CON_RecalcSize(void) con_destlines = vid.height; } + if (con_destlines > 0) // Resize console if already open + { + CON_ChangeHeight(); + con_curlines = con_destlines; + } + // check for change of video width if (conw == con_width) return; // didn't change @@ -516,6 +523,20 @@ static void CON_RecalcSize(void) Z_Free(tmp_buffer); } +static void CON_ChangeHeight(void) +{ + INT32 minheight = 20 * con_scalefactor; // 20 = 8+8+4 + + // toggle console in + con_destlines = (cons_height.value*vid.height)/100; + if (con_destlines < minheight) + con_destlines = minheight; + else if (con_destlines > vid.height) + con_destlines = vid.height; + + con_destlines &= ~0x3; // multiple of text row height +} + // Handles Console moves in/out of screen (per frame) // static void CON_MoveConsole(void) @@ -544,6 +565,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) @@ -604,16 +641,7 @@ void CON_Ticker(void) CON_ClearHUD(); } else - { - // toggle console in - con_destlines = (cons_height.value*vid.height)/100; - if (con_destlines < minheight) - con_destlines = minheight; - else if (con_destlines > vid.height) - con_destlines = vid.height; - - con_destlines &= ~0x3; // multiple of text row height - } + CON_ChangeHeight(); } // console movement @@ -1084,16 +1112,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')) { @@ -1143,6 +1161,7 @@ static void CON_Print(char *msg) { size_t l; INT32 controlchars = 0; // for color changing + char color = '\x80'; // keep color across lines if (msg == NULL) return; @@ -1168,7 +1187,7 @@ static void CON_Print(char *msg) { if (*msg & 0x80) { - con_line[con_cx++] = *(msg++); + color = con_line[con_cx++] = *(msg++); controlchars++; continue; } @@ -1176,12 +1195,14 @@ static void CON_Print(char *msg) { con_cy--; CON_Linefeed(); + color = '\x80'; controlchars = 0; } else if (*msg == '\n') // linefeed { CON_Linefeed(); - controlchars = 0; + con_line[con_cx++] = color; + controlchars = 1; } else if (*msg == ' ') // space { @@ -1189,7 +1210,8 @@ static void CON_Print(char *msg) if (con_cx - controlchars >= con_width-11) { CON_Linefeed(); - controlchars = 0; + con_line[con_cx++] = color; + controlchars = 1; } } else if (*msg == '\t') @@ -1204,7 +1226,8 @@ static void CON_Print(char *msg) if (con_cx - controlchars >= con_width-11) { CON_Linefeed(); - controlchars = 0; + con_line[con_cx++] = color; + controlchars = 1; } } msg++; @@ -1221,7 +1244,8 @@ static void CON_Print(char *msg) if ((con_cx - controlchars) + l > con_width-11) { CON_Linefeed(); - controlchars = 0; + con_line[con_cx++] = color; + controlchars = 1; } // a word at a time @@ -1448,7 +1472,7 @@ static void CON_DrawInput(void) { x -= charwidth*3; if (input_sel < c) - V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART); + V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART); for (i = 0; i < 3; ++i, x += charwidth) V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value); } @@ -1459,7 +1483,7 @@ static void CON_DrawInput(void) { if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c)) { - V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 107 | V_NOSCALESTART); + V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 77 | V_NOSCALESTART); V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, !cv_allcaps.value); } else @@ -1473,7 +1497,7 @@ static void CON_DrawInput(void) if (rellip) { if (input_sel > cend) - V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 107 | V_NOSCALESTART); + V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART); for (i = 0; i < 3; ++i, x += charwidth) V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value); } @@ -1576,8 +1600,7 @@ static void CON_DrawConsole(void) i = con_cy - con_scrollup; // skip the last empty line due to the cursor being at the start of a new line - if (!con_scrollup && !con_cx) - i--; + i--; i -= (con_curlines - minheight) / charheight; diff --git a/src/console.h b/src/console.h index 98df6ee2b..7ed585177 100644 --- a/src/console.h +++ b/src/console.h @@ -39,11 +39,13 @@ extern UINT32 con_scalefactor; // console text scale factor extern consvar_t cons_backcolor, cons_menuhighlight; extern UINT8 *yellowmap, *purplemap, *greenmap, *bluemap, *graymap, *redmap, *orangemap,\ - *skymap, *goldmap, *lavendermap, *teamap, *steelmap, *pinkmap, *brownmap, *peachmap; + *skymap, *goldmap, *lavendermap, *aquamap, *magentamap, *pinkmap, *brownmap, *tanmap; // 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 7516df2e0..fa228df06 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -21,7 +21,9 @@ #include "i_system.h" #include "i_video.h" #include "d_net.h" +#include "d_netfil.h" // fileneedednum #include "d_main.h" +#include "d_event.h" #include "g_game.h" #include "hu_stuff.h" #include "keys.h" @@ -72,6 +74,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) @@ -89,11 +92,10 @@ tic_t jointimeout = (3*TICRATE); static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame? static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout? -#ifdef NEWPING 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. -#endif +tic_t servermaxping = 800; // server's max ping. Defaults to 800 SINT8 nodetoplayer[MAXNETNODES]; SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) SINT8 nodetoplayer3[MAXNETNODES]; // say the numplayer for this node if any (splitscreen == 2) @@ -137,6 +139,7 @@ static UINT8 localtextcmd3[MAXTEXTCMD]; // splitscreen == 2 static UINT8 localtextcmd4[MAXTEXTCMD]; // splitscreen == 3 static tic_t neededtic; SINT8 servernode = 0; // the number of the server node +char connectedservername[MAXSERVERNAME]; /// \brief do we accept new players? /// \todo WORK! boolean acceptnewnode = true; @@ -164,7 +167,7 @@ ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; static textcmdtic_t *textcmds[TEXTCMD_HASH_SIZE] = {NULL}; -consvar_t cv_showjoinaddress = {"showjoinaddress", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_showjoinaddress = {"showjoinaddress", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}}; consvar_t cv_playbackspeed = {"playbackspeed", "1", 0, playbackspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -582,21 +585,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->kartspeed = (UINT8)players[i].kartspeed; rsp->kartweight = (UINT8)players[i].kartweight; // - rsp->normalspeed = (fixed_t)LONG(players[i].normalspeed); - rsp->runspeed = (fixed_t)LONG(players[i].runspeed); - rsp->thrustfactor = players[i].thrustfactor; - rsp->accelstart = players[i].accelstart; - rsp->acceleration = players[i].acceleration; - rsp->charability = players[i].charability; - rsp->charability2 = players[i].charability2; rsp->charflags = (UINT32)LONG(players[i].charflags); - rsp->thokitem = (UINT32)LONG(players[i].thokitem); //mobjtype_t - rsp->spinitem = (UINT32)LONG(players[i].spinitem); //mobjtype_t - rsp->revitem = (UINT32)LONG(players[i].revitem); //mobjtype_t - rsp->actionspd = (fixed_t)LONG(players[i].actionspd); - rsp->mindash = (fixed_t)LONG(players[i].mindash); - rsp->maxdash = (fixed_t)LONG(players[i].maxdash); - rsp->jumpfactor = (fixed_t)LONG(players[i].jumpfactor); rsp->speed = (fixed_t)LONG(players[i].speed); rsp->jumping = players[i].jumping; @@ -648,6 +637,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. @@ -717,21 +708,7 @@ static void resynch_read_player(resynch_pak *rsp) players[i].kartspeed = (UINT8)rsp->kartspeed; players[i].kartweight = (UINT8)rsp->kartweight; - players[i].normalspeed = (fixed_t)LONG(rsp->normalspeed); - players[i].runspeed = (fixed_t)LONG(rsp->runspeed); - players[i].thrustfactor = rsp->thrustfactor; - players[i].accelstart = rsp->accelstart; - players[i].acceleration = rsp->acceleration; - players[i].charability = rsp->charability; - players[i].charability2 = rsp->charability2; players[i].charflags = (UINT32)LONG(rsp->charflags); - players[i].thokitem = (UINT32)LONG(rsp->thokitem); //mobjtype_t - players[i].spinitem = (UINT32)LONG(rsp->spinitem); //mobjtype_t - players[i].revitem = (UINT32)LONG(rsp->revitem); //mobjtype_t - players[i].actionspd = (fixed_t)LONG(rsp->actionspd); - players[i].mindash = (fixed_t)LONG(rsp->mindash); - players[i].maxdash = (fixed_t)LONG(rsp->maxdash); - players[i].jumpfactor = (fixed_t)LONG(rsp->jumpfactor); players[i].speed = (fixed_t)LONG(rsp->speed); players[i].jumping = rsp->jumping; @@ -783,6 +760,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 +1100,24 @@ typedef enum CL_DOWNLOADSAVEGAME, #endif CL_CONNECTED, - CL_ABORTED + CL_ABORTED, + CL_ASKFULLFILELIST, + 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 UINT16 cl_lastcheckedfilecount = 0; +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 +1154,8 @@ static void CV_LoadPlayerNames(UINT8 **p) } #ifdef CLIENT_LOADINGSCREEN +static UINT32 SL_SearchServer(INT32 node); + // // CL_DrawConnectionStatus // @@ -1182,15 +1175,46 @@ static inline void CL_DrawConnectionStatus(void) if (cl_mode != CL_DOWNLOADFILES) { INT32 i, animtime = ((ccstime / 4) & 15) + 16; - UINT8 palstart = (cl_mode == CL_SEARCHING) ? 128 : 160; + UINT8 palstart = (cl_mode == CL_SEARCHING) ? 32 : 96; // 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) @@ -1206,10 +1230,16 @@ static inline void CL_DrawConnectionStatus(void) cltext = M_GetText("Waiting to download game state..."); break; #endif + case CL_ASKFULLFILELIST: + cltext = M_GetText("This server has a LOT of files!"); + break; case CL_ASKJOIN: 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; @@ -1229,8 +1259,8 @@ static inline void CL_DrawConnectionStatus(void) dldlength = (INT32)((file->currentsize/(double)file->totalsize) * 256); if (dldlength > 256) dldlength = 256; - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 175); - V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 160); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111); + V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 96); memset(tempname, 0, sizeof(tempname)); // offset filename to just the name only part @@ -1262,6 +1292,14 @@ static inline void CL_DrawConnectionStatus(void) } #endif +static boolean CL_AskFileList(INT32 firstfile) +{ + netbuffer->packettype = PT_TELLFILESNEEDED; + netbuffer->u.filesneedednum = firstfile; + + return HSendPacket(servernode, true, 0, sizeof (INT32)); +} + /** Sends a special packet to declare how many players in local * Used only in arbitratrenetstart() * Sends a PT_CLIENTJOIN packet to the server @@ -1287,6 +1325,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 +1348,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); @@ -1371,7 +1418,7 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) netbuffer->u.serverinfo.actnum = 0; //mapheaderinfo[gamemap-1]->actnum - p = PutFileNeeded(); + p = PutFileNeeded(0); HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); } @@ -1389,33 +1436,13 @@ static void SV_SendPlayerInfo(INT32 node) continue; } - netbuffer->u.playerinfo[i].node = (UINT8)playernode[i]; + netbuffer->u.playerinfo[i].node = i; strncpy(netbuffer->u.playerinfo[i].name, (const char *)&player_names[i], MAXPLAYERNAME+1); netbuffer->u.playerinfo[i].name[MAXPLAYERNAME] = '\0'; //fetch IP address - { - const char *claddress; - UINT32 numericaddress[4]; - - memset(netbuffer->u.playerinfo[i].address, 0, 4); - if (playernode[i] == 0) - { - //127.0.0.1 - netbuffer->u.playerinfo[i].address[0] = 127; - netbuffer->u.playerinfo[i].address[3] = 1; - } - else if (playernode[i] > 0 && I_GetNodeAddress && (claddress = I_GetNodeAddress(playernode[i])) != NULL) - { - if (sscanf(claddress, "%d.%d.%d.%d", &numericaddress[0], &numericaddress[1], &numericaddress[2], &numericaddress[3]) < 4) - goto badaddress; - netbuffer->u.playerinfo[i].address[0] = (UINT8)numericaddress[0]; - netbuffer->u.playerinfo[i].address[1] = (UINT8)numericaddress[1]; - netbuffer->u.playerinfo[i].address[2] = (UINT8)numericaddress[2]; - netbuffer->u.playerinfo[i].address[3] = (UINT8)numericaddress[3]; - } - } - badaddress: + //No, don't do that, you fuckface. + memset(netbuffer->u.playerinfo[i].address, 0, 4); if (G_GametypeHasTeams()) { @@ -1500,7 +1527,7 @@ static boolean SV_SendServerConfig(INT32 node) op = p = netbuffer->u.servercfg.varlengthinputs; CV_SavePlayerNames(&p); - CV_SaveNetVars(&p); + CV_SaveNetVars(&p, false); { const size_t len = sizeof (serverconfig_pak) + (size_t)(p - op); @@ -1679,8 +1706,8 @@ static void CL_LoadReceivedSavegame(void) } paused = false; - demoplayback = false; - titledemo = false; + demo.playback = false; + demo.title = false; automapactive = false; // load a base level @@ -1740,8 +1767,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; @@ -1864,10 +1889,70 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room) #endif // ifndef NONET +static boolean CL_FinishedFileList(void) +{ + INT32 i; + CONS_Printf(M_GetText("Checking files...\n")); + i = CL_CheckFiles(); + if (i == 3) // too many files + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You have too many WAD files loaded\n" + "to add ones the server is using.\n" + "Please restart SRB2Kart before connecting.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + else if (i == 2) // cannot join for some reason + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + M_StartMessage(M_GetText( + "You have WAD files loaded or have\n" + "modified the game in some way, and\n" + "your file list does not match\n" + "the server's file list.\n" + "Please restart SRB2Kart before connecting.\n\n" + "Press ESC\n" + ), NULL, MM_NOTHING); + return false; + } + else if (i == 1) + cl_mode = CL_ASKJOIN; + else + { + // must download something + // can we, though? + if (!CL_CheckDownloadable()) // nope! + { + 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); + return false; + } + + cl_mode = CL_ASKDOWNLOADFILES; + } + return true; +} + /** 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 @@ -1907,63 +1992,16 @@ static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent) if (client) { - D_ParseFileneeded(serverlist[i].info.fileneedednum, - serverlist[i].info.fileneeded); - CONS_Printf(M_GetText("Checking files...\n")); - i = CL_CheckFiles(); - if (i == 3) // too many files + D_ParseFileneeded(serverlist[i].info.fileneedednum, serverlist[i].info.fileneeded, 0); + if (serverlist[i].info.kartvars & SV_LOTSOFADDONS) { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You have too many WAD files loaded\n" - "to add ones the server is using.\n" - "Please restart SRB2Kart before connecting.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); + cl_mode = CL_ASKFULLFILELIST; + cl_lastcheckedfilecount = 0; + return true; + } + + if (!CL_FinishedFileList()) return false; - } - else if (i == 2) // cannot join for some reason - { - D_QuitNetGame(); - CL_Reset(); - D_StartTitle(); - M_StartMessage(M_GetText( - "You have WAD files loaded or have\n" - "modified the game in some way, and\n" - "your file list does not match\n" - "the server's file list.\n" - "Please restart SRB2Kart before connecting.\n\n" - "Press ESC\n" - ), NULL, MM_NOTHING); - return false; - } - else if (i == 1) - cl_mode = CL_ASKJOIN; - else - { - // must download something - // can we, though? - if (!CL_CheckDownloadable()) // nope! - { - 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); - return false; - } - // no problem if can't send packet, we will retry later - if (CL_SendRequestFile()) - cl_mode = CL_DOWNLOADFILES; - } } else cl_mode = CL_ASKJOIN; // files need not be checked for the server. @@ -1992,7 +2030,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 @@ -2014,6 +2052,22 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic return false; break; + case CL_ASKFULLFILELIST: + if (cl_lastcheckedfilecount == UINT16_MAX) // All files retrieved + { + if (!CL_FinishedFileList()) + return false; + } + else if (fileneedednum != cl_lastcheckedfilecount || *asksent + NEWTICRATE < I_GetTime()) + { + if (CL_AskFileList(fileneedednum)) + { + cl_lastcheckedfilecount = fileneedednum; + *asksent = I_GetTime(); + } + } + break; + case CL_DOWNLOADFILES: waitmore = false; for (i = 0; i < fileneedednum; i++) @@ -2030,6 +2084,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 +2093,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 +2123,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,20 +2153,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(); - // Only ESC and non-keyboard keys abort connection - if (key == KEY_ESCAPE || key >= 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(); + D_ProcessEvents(); + if (gamestate != GS_WAITINGPLAYERS) return false; - } // why are these here? this is for servers, we're a client //if (key == 's' && server) @@ -2114,6 +2185,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 ??? @@ -2134,6 +2270,7 @@ static void CL_ConnectToServer(boolean viams) #endif cl_mode = CL_SEARCHING; + cl_challengenum = 0; #ifdef CLIENT_LOADINGSCREEN lastfilenum = -1; @@ -2172,17 +2309,14 @@ static void CL_ConnectToServer(boolean viams) if (i != -1) { - INT32 j; + UINT8 num = serverlist[i].info.gametype; const char *gametypestr = NULL; + + strncpy(connectedservername, serverlist[i].info.servername, MAXSERVERNAME); + CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); - for (j = 0; gametype_cons_t[j].strvalue; j++) - { - if (gametype_cons_t[j].value == serverlist[i].info.gametype) - { - gametypestr = gametype_cons_t[j].strvalue; - break; - } - } + if (num < NUMGAMETYPES) + gametypestr = Gametype_Names[num]; if (gametypestr) CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, @@ -2191,6 +2325,8 @@ static void CL_ConnectToServer(boolean viams) SL_ClearServerList(servernode); #endif + cl_challengeattempted = 0; + do { // If the connection was aborted for some reason, leave @@ -2217,7 +2353,7 @@ static void CL_ConnectToServer(boolean viams) #endif DEBFILE(va("Synchronisation Finished\n")); - displayplayer = consoleplayer; + displayplayers[0] = consoleplayer; } #ifndef NONET @@ -2389,7 +2525,7 @@ static void Command_connect(void) return; } - if (Playing() || titledemo) + if (Playing() || demo.title) { CONS_Printf(M_GetText("You cannot connect while in a game. End this game first.\n")); return; @@ -2481,14 +2617,16 @@ void CL_ClearPlayer(INT32 playernum) // // Removes a player from the current game // -static void CL_RemovePlayer(INT32 playernum, INT32 reason) +void CL_RemovePlayer(INT32 playernum, INT32 reason) { // Sanity check: exceptional cases (i.e. c-fails) can cause multiple // kick commands to be issued for the same player. if (!playeringame[playernum]) return; - if (server && !demoplayback) + demo_extradata[playernum] |= DXD_PLAYSTATE; + + if (server && !demo.playback) { INT32 node = playernode[playernum]; //playerpernode[node] = 0; // It'd be better to remove them all at once, but ghosting happened, so continue to let CL_RemovePlayer do it one-by-one @@ -2542,6 +2680,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 @@ -2561,8 +2701,8 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason) RemoveAdminPlayer(playernum); // don't stay admin after you're gone } - if (playernum == displayplayer) - displayplayer = consoleplayer; // don't look through someone's view who isn't there + if (playernum == displayplayers[0] && !demo.playback) + displayplayers[0] = consoleplayer; // don't look through someone's view who isn't there #ifdef HAVE_BLUA LUA_InvalidatePlayer(&players[playernum]); @@ -2582,7 +2722,7 @@ void CL_Reset(void) G_StopMetalRecording(); if (metalplayback) G_StopMetalDemo(); - if (demorecording) + if (demo.recording) G_CheckDemoStatus(); // reset client/server code @@ -2725,7 +2865,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) { @@ -2756,6 +2899,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) @@ -2905,12 +3084,10 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false); kickreason = KR_KICK; break; -#ifdef NEWPING case KICK_MSG_PING_HIGH: HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false); kickreason = KR_PINGLIMIT; break; -#endif case KICK_MSG_CON_FAIL: HU_AddChatText(va("\x82*%s left the game (Synch Failure)", player_names[pnum]), false); kickreason = KR_SYNCH; @@ -2983,10 +3160,8 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) D_StartTitle(); if (msg == KICK_MSG_CON_FAIL) M_StartMessage(M_GetText("Server closed connection\n(Synch failure)\nPress ESC\n"), NULL, MM_NOTHING); -#ifdef NEWPING else if (msg == KICK_MSG_PING_HIGH) M_StartMessage(M_GetText("Server closed connection\n(Broke ping limit)\nPress ESC\n"), NULL, MM_NOTHING); -#endif else if (msg == KICK_MSG_BANNED) M_StartMessage(M_GetText("You have been banned by the server\n\nPress ESC\n"), NULL, MM_NOTHING); else if (msg == KICK_MSG_CUSTOM_KICK) @@ -3030,15 +3205,15 @@ 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 }; +consvar_t cv_allownewplayer = {"allowjoin", "On", CV_SAVE|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 +consvar_t cv_joinnextround = {"joinnextround", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done #endif static CV_PossibleValue_t maxplayers_cons_t[] = {{2, "MIN"}, {MAXPLAYERS, "MAX"}, {0, NULL}}; consvar_t cv_maxplayers = {"maxplayers", "8", CV_SAVE, maxplayers_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t resynchattempts_cons_t[] = {{0, "MIN"}, {20, "MAX"}, {0, NULL}}; consvar_t cv_resynchattempts = {"resynchattempts", "5", CV_SAVE, resynchattempts_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL }; -consvar_t cv_blamecfail = {"blamecfail", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; +consvar_t cv_blamecfail = {"blamecfail", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; // max file size to send to a player (in kilobytes) static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}}; @@ -3062,6 +3237,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); @@ -3080,12 +3256,6 @@ void D_ClientServerInit(void) RegisterNetXCmd(XD_ADDPLAYER, Got_AddPlayer); RegisterNetXCmd(XD_REMOVEPLAYER, Got_RemovePlayer); #ifndef NONET - CV_RegisterVar(&cv_allownewplayer); -#ifdef VANILLAJOINNEXTROUND - CV_RegisterVar(&cv_joinnextround); -#endif - CV_RegisterVar(&cv_showjoinaddress); - CV_RegisterVar(&cv_blamecfail); #ifdef DUMPCONSISTENCY CV_RegisterVar(&cv_dumpconsistency); #endif @@ -3095,6 +3265,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(); @@ -3249,6 +3422,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) { INT16 node, newplayernum; UINT8 splitscreenplayer = 0; + UINT8 i; if (playernum != serverplayer && !IsPlayerAdmin(playernum)) { @@ -3282,40 +3456,27 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) if (node == mynode) { playernode[newplayernum] = 0; // for information only + if (splitscreenplayer) { - if (splitscreenplayer == 1) - { - secondarydisplayplayer = newplayernum; - DEBFILE("spawning my brother\n"); - if (botingame) - players[newplayernum].bot = 1; - // Same goes for player 2 when relevant - } - else if (splitscreenplayer == 2) - { - thirddisplayplayer = newplayernum; - DEBFILE("spawning my sister\n"); - } - else if (splitscreenplayer == 3) - { - fourthdisplayplayer = newplayernum; - DEBFILE("spawning my trusty pet dog\n"); - } + displayplayers[splitscreenplayer] = newplayernum; + DEBFILE(va("spawning one of my sister number %d\n", splitscreenplayer)); + if (splitscreenplayer == 1 && botingame) + players[newplayernum].bot = 1; } else { consoleplayer = newplayernum; - displayplayer = newplayernum; - secondarydisplayplayer = newplayernum; - thirddisplayplayer = newplayernum; - fourthdisplayplayer = newplayernum; + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + displayplayers[i] = newplayernum; DEBFILE("spawning me\n"); } D_SendPlayerConfig(); addedtogame = true; } + players[newplayernum].splitscreenindex = splitscreenplayer; + if (netgame) { if (server && cv_showjoinaddress.value) @@ -3456,7 +3617,7 @@ boolean Playing(void) boolean SV_SpawnServer(void) { - if (demoplayback) + if (demo.playback) G_StopDemo(); // reset engine parameter if (metalplayback) G_StopMetalDemo(); @@ -3582,6 +3743,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]) @@ -3590,6 +3778,7 @@ static void HandleConnect(SINT8 node) #ifndef NONET newnode = true; #endif + SV_AddNode(node); /// \note Wait what??? @@ -3736,6 +3925,39 @@ static void HandlePacketFromAwayNode(SINT8 node) #endif break; + case PT_TELLFILESNEEDED: + if (server && serverrunning) + { + UINT8 *p; + INT32 firstfile = netbuffer->u.filesneedednum; + + netbuffer->packettype = PT_MOREFILESNEEDED; + netbuffer->u.filesneededcfg.first = firstfile; + netbuffer->u.filesneededcfg.more = 0; + + p = PutFileNeeded(firstfile); + + HSendPacket(node, false, 0, p - ((UINT8 *)&netbuffer->u)); + } + else // Shouldn't get this if you aren't the server...? + Net_CloseConnection(node); + break; + + case PT_MOREFILESNEEDED: + if (server && serverrunning) + { // But wait I thought I'm the server? + Net_CloseConnection(node); + break; + } + SERVERONLY + if (cl_mode == CL_ASKFULLFILELIST && netbuffer->u.filesneededcfg.first == fileneedednum) + { + D_ParseFileneeded(netbuffer->u.filesneededcfg.num, netbuffer->u.filesneededcfg.files, netbuffer->u.filesneededcfg.first); + if (!netbuffer->u.filesneededcfg.more) + cl_lastcheckedfilecount = UINT16_MAX; // Got the whole file list + } + break; + case PT_ASKINFO: if (server && serverrunning) { @@ -3745,6 +3967,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? @@ -3773,6 +4032,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; @@ -3788,6 +4082,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); @@ -4332,7 +4629,6 @@ FILESTAMP resynch_local_inprogress = true; CL_AcknowledgeResynch(&netbuffer->u.resynchpak); break; -#ifdef NEWPING case PT_PING: // Only accept PT_PING from the server. if (node != servernode) @@ -4353,14 +4649,15 @@ 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; -#endif case PT_SERVERCFG: break; case PT_FILEFRAGMENT: @@ -4932,16 +5229,16 @@ void TryRunTics(tic_t realtics) if (realtics >= 1) { - COM_BufExecute(); + COM_BufTicker(); if (mapchangepending) D_MapChange(-1, 0, encoremode, false, 2, false, fromlevelselect); // finish the map change } NetUpdate(); - if (demoplayback) + if (demo.playback) { - neededtic = gametic + (realtics * cv_playbackspeed.value); + neededtic = gametic + realtics * (gamestate == GS_LEVEL ? cv_playbackspeed.value : 1); // start a game after a demo maketic += realtics; firstticstosend = maketic; @@ -4998,7 +5295,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; @@ -5019,6 +5327,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. @@ -5029,12 +5340,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); } } } @@ -5049,24 +5368,25 @@ 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 % 255)) + 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++) @@ -5075,7 +5395,6 @@ static void UpdatePingTable(void) pingmeasurecount++; } } -#endif // Handle timeouts to prevent definitive freezes from happenning static void HandleNodeTimeouts(void) @@ -5100,9 +5419,7 @@ void NetKeepAlive(void) if (realtics <= 0) // nothing new to update return; -#ifdef NEWPING UpdatePingTable(); -#endif if (server) CL_SendClientKeepAlive(); @@ -5149,9 +5466,7 @@ void NetUpdate(void) gametime = nowtime; -#ifdef NEWPING UpdatePingTable(); -#endif if (client) maketic = neededtic; @@ -5176,7 +5491,7 @@ FILESTAMP } else { - if (!demoplayback) + if (!demo.playback) { INT32 counts; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 62bd8bc17..e7879d582 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 @@ -73,6 +76,9 @@ typedef enum 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. // In addition, this packet can't occupy all the available slots. @@ -87,9 +93,11 @@ typedef enum PT_NODETIMEOUT, // Packet sent to self if the connection times out. PT_RESYNCHING, // Packet sent to resync players. // Blocks game advance until synched. -#ifdef NEWPING + + PT_TELLFILESNEEDED, // Client, to server: "what other files do I need starting from this number?" + PT_MOREFILESNEEDED, // Server, to client: "you need these (+ more on top of those)" + PT_PING, // Packet sent to tell clients the other client's latency to server. -#endif NUMPACKETTYPE } packettype_t; @@ -218,21 +226,7 @@ typedef struct UINT8 kartspeed; UINT8 kartweight; // - fixed_t normalspeed; - fixed_t runspeed; - UINT8 thrustfactor; - UINT8 accelstart; - UINT8 acceleration; - UINT8 charability; - UINT8 charability2; UINT32 charflags; - UINT32 thokitem; // mobjtype_t - UINT32 spinitem; // mobjtype_t - UINT32 revitem; // mobjtype_t - fixed_t actionspd; - fixed_t mindash; - fixed_t maxdash; - fixed_t jumpfactor; fixed_t speed; UINT8 jumping; @@ -283,6 +277,8 @@ typedef struct tic_t jointime; + UINT8 splitscreenindex; + //player->mo stuff UINT8 hasmo; // Boolean @@ -351,9 +347,22 @@ 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_LOTSOFADDONS 0x20 +#define SV_DEDICATED 0x40 +#define SV_PASSWORD 0x80 + #define MAXSERVERNAME 32 #define MAXFILENEEDED 915 // This packet is too large @@ -366,7 +375,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; @@ -421,6 +430,14 @@ typedef struct UINT8 ctfteam; } ATTRPACK plrconfig; +typedef struct +{ + INT32 first; + UINT8 num; + UINT8 more; + UINT8 files[MAXFILENEEDED]; // is filled with writexxx (byteptr.h) +} ATTRPACK filesneededconfig_pak; + // // Network packet data // @@ -445,16 +462,17 @@ 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) -#ifdef NEWPING - UINT32 pingtable[MAXPLAYERS]; // 128 bytes -#endif + plrinfo playerinfo[MAXPLAYERS]; // 576 bytes(?) + plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?) + INT32 filesneedednum; // 4 bytes + filesneededconfig_pak filesneededcfg; // ??? bytes + UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes } u; // This is needed to pack diff packet types data together } ATTRPACK doomdata_t; @@ -488,9 +506,7 @@ extern consvar_t cv_playbackspeed; #define KICK_MSG_PLAYER_QUIT 3 #define KICK_MSG_TIMEOUT 4 #define KICK_MSG_BANNED 5 -#ifdef NEWPING #define KICK_MSG_PING_HIGH 6 -#endif #define KICK_MSG_CUSTOM_KICK 7 #define KICK_MSG_CUSTOM_BAN 8 @@ -511,15 +527,15 @@ extern boolean dedicated; // For dedicated server extern UINT16 software_MAXPACKETLENGTH; extern boolean acceptnewnode; extern SINT8 servernode; +extern char connectedservername[MAXSERVERNAME]; void Command_Ping_f(void); extern tic_t connectiontimeout; extern tic_t jointimeout; -#ifdef NEWPING extern UINT16 pingmeasurecount; extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS]; -#endif +extern tic_t servermaxping; extern consvar_t #ifdef VANILLAJOINNEXTROUND @@ -551,7 +567,9 @@ void CL_AddSplitscreenPlayer(void); void CL_RemoveSplitscreenPlayer(UINT8 p); void CL_Reset(void); void CL_ClearPlayer(INT32 playernum); +void CL_RemovePlayer(INT32 playernum, INT32 reason); 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 28f89f4f0..1404a5e61 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -74,7 +74,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "m_cond.h" // condition initialization #include "fastcmp.h" #include "keys.h" -#include "filesrch.h" // refreshdirmenu, mainwadstally +#include "filesrch.h" // refreshdirmenu #ifdef CMAKECONFIG #include "config.h" @@ -111,20 +111,15 @@ 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 boolean singletics = false; // timedemo boolean lastdraw = false; -postimg_t postimgtype = postimg_none; -INT32 postimgparam; -postimg_t postimgtype2 = postimg_none; -INT32 postimgparam2; -postimg_t postimgtype3 = postimg_none; -INT32 postimgparam3; -postimg_t postimgtype4 = postimg_none; -INT32 postimgparam4; +postimg_t postimgtype[MAXSPLITSCREENPLAYERS]; +INT32 postimgparam[MAXSPLITSCREENPLAYERS]; // These variables are only true if // whether the respective sound system is disabled @@ -203,6 +198,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,12 +233,21 @@ 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)) continue; } + if (demo.savemode == DSM_TITLEENTRY) + { + if (G_DemoTitleResponder(ev)) + continue; + } + // Menu input if (M_Responder(ev)) continue; // menu ate the event @@ -268,6 +274,7 @@ static void D_Display(void) boolean forcerefresh = false; static boolean wipe = false; INT32 wipedefindex = 0; + UINT8 i; if (dedicated) return; @@ -416,109 +423,77 @@ static void D_Display(void) // draw the view directly if (cv_renderview.value && !automapactive) { - if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD) + for (i = 0; i <= splitscreen; i++) { - viewwindowy = 0; - viewwindowx = 0; - - topleft = screens[0] + viewwindowy*vid.width + viewwindowx; - objectsdrawn = 0; -#ifdef HWRENDER - if (rendermode != render_soft) - HWR_RenderPlayerView(0, &players[displayplayer]); - else -#endif - if (rendermode != render_none) - R_RenderPlayerView(&players[displayplayer]); - } - - // render the second screen - if (splitscreen && players[secondarydisplayplayer].mo) - { -#ifdef HWRENDER - if (rendermode != render_soft) - HWR_RenderPlayerView(1, &players[secondarydisplayplayer]); - else -#endif - if (rendermode != render_none) + if (players[displayplayers[i]].mo || players[displayplayers[i]].playerstate == PST_DEAD) { - if (splitscreen > 1) + if (i == 0) // Initialize for P1 { - viewwindowx = viewwidth; viewwindowy = 0; - } - else - { viewwindowx = 0; - viewwindowy = viewheight; + + topleft = screens[0] + viewwindowy*vid.width + viewwindowx; + objectsdrawn = 0; } - M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0])); + viewssnum = i; - topleft = screens[0] + viewwindowy*vid.width + viewwindowx; - - R_RenderPlayerView(&players[secondarydisplayplayer]); - - viewwindowy = 0; - M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0])); - } - } - - // render the third screen - if (splitscreen > 1 && players[thirddisplayplayer].mo) - { #ifdef HWRENDER - if (rendermode != render_soft) - HWR_RenderPlayerView(2, &players[thirddisplayplayer]); - else + if (rendermode != render_soft) + HWR_RenderPlayerView(i, &players[displayplayers[i]]); + else #endif - if (rendermode != render_none) - { - viewwindowx = 0; - viewwindowy = viewheight; - M_Memcpy(ylookup, ylookup3, viewheight*sizeof (ylookup[0])); + if (rendermode != render_none) + { + if (i > 0) // Splitscreen-specific + { + switch (i) + { + case 1: + if (splitscreen > 1) + { + viewwindowx = viewwidth; + viewwindowy = 0; + } + else + { + viewwindowx = 0; + viewwindowy = viewheight; + } + M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0])); + break; + case 2: + viewwindowx = 0; + viewwindowy = viewheight; + M_Memcpy(ylookup, ylookup3, viewheight*sizeof (ylookup[0])); + break; + case 3: + viewwindowx = viewwidth; + viewwindowy = viewheight; + M_Memcpy(ylookup, ylookup4, viewheight*sizeof (ylookup[0])); + default: + break; + } - topleft = screens[0] + viewwindowy*vid.width + viewwindowx; + + topleft = screens[0] + viewwindowy*vid.width + viewwindowx; + } - R_RenderPlayerView(&players[thirddisplayplayer]); + R_RenderPlayerView(&players[displayplayers[i]]); - viewwindowy = 0; - M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0])); - } - } - - if (splitscreen > 2 && players[fourthdisplayplayer].mo) // render the fourth screen - { -#ifdef HWRENDER - if (rendermode != render_soft) - HWR_RenderPlayerView(3, &players[fourthdisplayplayer]); - else -#endif - if (rendermode != render_none) - { - viewwindowx = viewwidth; - viewwindowy = viewheight; - M_Memcpy(ylookup, ylookup4, viewheight*sizeof (ylookup[0])); - - topleft = screens[0] + viewwindowy*vid.width + viewwindowx; - - R_RenderPlayerView(&players[fourthdisplayplayer]); - - viewwindowy = 0; - M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0])); + if (i > 0) + M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0])); + } } } if (rendermode == render_soft) { - if (postimgtype) - V_DoPostProcessor(0, postimgtype, postimgparam); - if (postimgtype2) - V_DoPostProcessor(1, postimgtype2, postimgparam2); - if (postimgtype3) - V_DoPostProcessor(2, postimgtype3, postimgparam3); - if (postimgtype4) - V_DoPostProcessor(3, postimgtype4, postimgparam4); + for (i = 0; i <= splitscreen; i++) + { + if (postimgtype[i]) + V_DoPostProcessor(i, postimgtype[i], postimgparam[i]); + } } } @@ -544,7 +519,7 @@ static void D_Display(void) wipegamestate = gamestate; // draw pause pic - if (paused && cv_showhud.value) + if (paused && cv_showhud.value && !demo.playback) { INT32 py; patch_t *patch; @@ -556,6 +531,9 @@ static void D_Display(void) V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - SHORT(patch->width))/2, py, 0, patch); } + if (demo.rewinding) + V_DrawFadeScreen(TC_RAINBOW, (leveltime & 0x20) ? SKINCOLOR_PASTEL : SKINCOLOR_MOONSLAM); + // vid size change is now finished if it was on... vid.recalc = 0; @@ -613,6 +591,9 @@ static void D_Display(void) V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s); } + if (cv_shittyscreen.value) + V_DrawVhsEffect(cv_shittyscreen.value == 2); + I_FinishUpdate(); // page flip or blit buffer } } @@ -630,9 +611,6 @@ void D_SRB2Loop(void) if (dedicated) server = true; - if (M_CheckParm("-voodoo")) // 256x256 Texture Limiter - COM_BufAddText("gr_voodoocompatibility on\n"); - // Pushing of + parameters is now done back in D_SRB2Main, not here. CONS_Printf("I_StartupKeyboard()...\n"); @@ -729,7 +707,7 @@ void D_SRB2Loop(void) M_DoScreenShot(); } - // consoleplayer -> displayplayer (hear sounds from viewpoint) + // consoleplayer -> displayplayers (hear sounds from viewpoint) S_UpdateSounds(); // move positional sounds // check for media change, loop music.. @@ -808,13 +786,13 @@ void D_StartTitle(void) maptol = 0; gameaction = ga_nothing; - displayplayer = consoleplayer = 0; + memset(displayplayers, 0, sizeof(displayplayers)); + consoleplayer = 0; //demosequence = -1; gametype = GT_RACE; // SRB2kart paused = false; advancedemo = false; F_StartTitleScreen(); - CON_ToggleOff(); // Reset the palette -- SRB2Kart: actually never mind let's do this in the middle of every fade /*if (rendermode != render_none) @@ -824,12 +802,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); @@ -839,16 +817,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; } } @@ -858,11 +836,11 @@ static inline void D_CleanFile(void) static void IdentifyVersion(void) { - char *srb2wad1, *srb2wad2; + char *mainresource; const char *srb2waddir = NULL; #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) - // change to the directory where 'srb2.srb' is found + // change to the directory where 'main.kart' is found srb2waddir = I_LocateWad(); #endif @@ -893,46 +871,34 @@ static void IdentifyVersion(void) srb2waddir = I_GetWadDir(); #endif // Commercial. - srb2wad1 = malloc(strlen(srb2waddir)+1+8+1); - srb2wad2 = malloc(strlen(srb2waddir)+1+8+1); - if (srb2wad1 == NULL && srb2wad2 == NULL) + mainresource = malloc(strlen(srb2waddir)+1+9+1); + if (mainresource == NULL) I_Error("No more free memory to look in %s", srb2waddir); - if (srb2wad1 != NULL) - sprintf(srb2wad1, pandf, srb2waddir, "srb2.srb"); - if (srb2wad2 != NULL) - sprintf(srb2wad2, pandf, srb2waddir, "srb2.wad"); + if (mainresource != NULL) + sprintf(mainresource, pandf, srb2waddir, "main.kart"); // will be overwritten in case of -cdrom or unix/win home snprintf(configfile, sizeof configfile, "%s" PATHSEP CONFIGFILENAME, srb2waddir); configfile[sizeof configfile - 1] = '\0'; // Load the IWAD - if (srb2wad2 != NULL && FIL_ReadFileOK(srb2wad2)) - D_AddFile(srb2wad2); - else if (srb2wad1 != NULL && FIL_ReadFileOK(srb2wad1)) - D_AddFile(srb2wad1); + if (mainresource != NULL && FIL_ReadFileOK(mainresource)) + D_AddFile(mainresource, startupwadfiles); else - I_Error("SRB2.SRB/SRB2.WAD not found! Expected in %s, ss files: %s or %s\n", srb2waddir, srb2wad1, srb2wad2); + I_Error("MAIN.KART not found! Expected in %s, ss file: %s \n", srb2waddir, mainresource); - if (srb2wad1) - free(srb2wad1); - if (srb2wad2) - free(srb2wad2); + if (mainresource) + free(mainresource); // if you change the ordering of this or add/remove a file, be sure to update the md5 // checking in D_SRB2Main -#ifdef USE_PATCH_DTA - // Add our crappy patches to fix our bugs - 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")); -#ifdef USE_PATCH_KART - D_AddFile(va(pandf,srb2waddir,"patch.kart")); + D_AddFile(va(pandf,srb2waddir,"gfx.pk3"), startupwadfiles); + D_AddFile(va(pandf,srb2waddir,"textures.pk3"), startupwadfiles); + D_AddFile(va(pandf,srb2waddir,"chars.pk3"), startupwadfiles); + D_AddFile(va(pandf,srb2waddir,"maps.wad"), startupwadfiles); // TODO: make this a pk3 too! +#ifdef USE_PATCH_FILE + D_AddFile(va(pandf,srb2waddir,"patch.pk3"), startupwadfiles); #endif #if !defined (HAVE_SDL) || defined (HAVE_MIXER) @@ -941,12 +907,12 @@ 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"); \ } - MUSICTEST("sounds.kart") - MUSICTEST("music.kart") + MUSICTEST("sounds.wad") + MUSICTEST("music.wad") #undef MUSICTEST #endif } @@ -1006,9 +972,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; @@ -1160,11 +1129,7 @@ void D_SRB2Main(void) const char *s = M_GetNextParm(); if (s) // Check for NULL? - { - if (!W_VerifyNMUSlumps(s)) - G_SetGameModified(true, false); - D_AddFile(s); - } + D_AddFile(s, startuppwads); } } } @@ -1212,47 +1177,103 @@ void D_SRB2Main(void) // Setup default unlockable conditions M_SetupDefaultConditionSets(); + // Setup character tables + // Have to be done here before files are loaded + M_InitCharacterTables(); + // 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; #ifndef DEVELOP // Check MD5s of autoloaded files // Note: Do not add any files that ignore MD5! - W_VerifyFileMD5(mainwads, ASSET_HASH_SRB2_SRB); // srb2.srb/srb2.wad -#ifdef USE_PATCH_DTA - mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_PATCH_DTA); // patch.dta -#endif - 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 -#ifdef USE_PATCH_KART - mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_PATCH_KART); // patch.kart + W_VerifyFileMD5(mainwads, ASSET_HASH_MAIN_KART); // main.kart + mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_GFX_PK3); // gfx.pk3 + mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_TEXTURES_PK3); // textures.pk3 + mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_CHARS_PK3); // chars.pk3 + mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_MAPS_WAD); // maps.wad -- 4 - If you touch this, make sure to touch up the majormods stuff below. +#ifdef USE_PATCH_FILE + mainwads++; W_VerifyFileMD5(mainwads, ASSET_HASH_PATCH_PK3); // patch.pk3 #endif #else -#ifdef USE_PATCH_DTA - mainwads++; // patch.dta -#endif - mainwads++; // gfx.kart - mainwads++; // textures.kart - mainwads++; // chars.kart - mainwads++; // maps.kart -#ifdef USE_PATCH_KART - mainwads++; // patch.kart + mainwads++; // gfx.pk3 + mainwads++; // textures.pk3 + mainwads++; // chars.pk3 + mainwads++; // maps.wad +#ifdef USE_PATCH_FILE + mainwads++; // patch.pk3 #endif #endif //ifndef DEVELOP - 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(); @@ -1325,10 +1346,9 @@ void D_SRB2Main(void) midi_disabled = true; #endif } - if (M_CheckParm("-nosound")) - sound_disabled = true; - if (M_CheckParm("-nomusic")) // combines -nomidimusic and -nodigmusic + if (M_CheckParm("-noaudio")) // combines -nosound and -nomusic { + sound_disabled = true; digital_disabled = true; #ifndef NO_MIDI midi_disabled = true; @@ -1336,12 +1356,24 @@ void D_SRB2Main(void) } else { + if (M_CheckParm("-nosound")) + sound_disabled = true; + if (M_CheckParm("-nomusic")) // combines -nomidimusic and -nodigmusic + { + digital_disabled = true; #ifndef NO_MIDI - if (M_CheckParm("-nomidimusic")) - midi_disabled = true; // WARNING: DOS version initmusic in I_StartupSound + midi_disabled = true; #endif - if (M_CheckParm("-nodigmusic")) - digital_disabled = true; // WARNING: DOS version initmusic in I_StartupSound + } + else + { +#ifndef NO_MIDI + if (M_CheckParm("-nomidimusic")) + midi_disabled = true; // WARNING: DOS version initmusic in I_StartupSound +#endif + if (M_CheckParm("-nodigmusic")) + digital_disabled = true; // WARNING: DOS version initmusic in I_StartupSound + } } if (!( sound_disabled && digital_disabled #ifndef NO_MIDI @@ -1437,7 +1469,7 @@ void D_SRB2Main(void) if (M_CheckParm("-playdemo")) { - singledemo = true; // quit after one demo + demo.quitafterplaying = true; // quit after one demo G_DeferedPlayDemo(tmp); } else @@ -1464,6 +1496,8 @@ void D_SRB2Main(void) // as having been modified for the first game. M_PushSpecialParameters(); // push all "+" parameter at the command buffer + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); + if (M_CheckParm("-gametype") && M_IsNextParm()) { // from Command_Map_f @@ -1471,13 +1505,9 @@ void D_SRB2Main(void) INT16 newgametype = -1; const char *sgametype = M_GetNextParm(); - for (j = 0; gametype_cons_t[j].strvalue; j++) - if (!strcasecmp(gametype_cons_t[j].strvalue, sgametype)) - { - newgametype = (INT16)gametype_cons_t[j].value; - break; - } - if (!gametype_cons_t[j].strvalue) // reached end of the list with no match + newgametype = G_GetGametypeByName(sgametype); + + if (newgametype == -1) // reached end of the list with no match { j = atoi(sgametype); // assume they gave us a gametype number, which is okay too if (j >= 0 && j < NUMGAMETYPES) @@ -1532,13 +1562,13 @@ void D_SRB2Main(void) } else if (M_CheckParm("-skipintro")) { - CON_ToggleOff(); - CON_ClearHUD(); F_StartTitleScreen(); } else F_StartIntro(); // Tails 03-03-2002 + CON_ToggleOff(); + if (dedicated && server) { pagename = "TITLESKY"; diff --git a/src/d_net.c b/src/d_net.c index 6702a60a4..94b11d518 100644 --- a/src/d_net.c +++ b/src/d_net.c @@ -185,22 +185,10 @@ typedef struct UINT8 nextacknum; UINT8 flags; -#ifndef NEWPING - // jacobson tcp timeout evaluation algorithm (Karn variation) - fixed_t ping; - fixed_t varping; - INT32 timeout; // computed with ping and varping -#endif } node_t; static node_t nodes[MAXNETNODES]; -#ifndef NEWPING -#define PINGDEFAULT ((200*TICRATE*FRACUNIT)/1000) -#define VARPINGDEFAULT ((50*TICRATE*FRACUNIT)/1000) -#define TIMEOUT(p,v) (p+4*v+FRACUNIT/2)>>FRACBITS; -#else -#define NODETIMEOUT 14 //What the above boiled down to... -#endif +#define NODETIMEOUT 14 #ifndef NONET // return <0 if a < b (mod 256) @@ -320,19 +308,7 @@ static UINT8 GetAcktosend(INT32 node) static void RemoveAck(INT32 i) { INT32 node = ackpak[i].destinationnode; -#ifndef NEWPING - fixed_t trueping = (I_GetTime() - ackpak[i].senttime)<>FRACBITS,(double)FIXED_TO_FLOAT(nodes[node].ping),(double)FIXED_TO_FLOAT(nodes[node].varping),nodes[node].timeout)); -#else DEBFILE(va("Remove ack %d\n",ackpak[i].acknum)); -#endif ackpak[i].acknum = 0; if (nodes[node].flags & NF_CLOSE) Net_CloseConnection(node); @@ -519,11 +495,7 @@ void Net_AckTicker(void) { const INT32 nodei = ackpak[i].destinationnode; node_t *node = &nodes[nodei]; -#ifdef NEWPING if (ackpak[i].acknum && ackpak[i].senttime + NODETIMEOUT < I_GetTime()) -#else - if (ackpak[i].acknum && ackpak[i].senttime + node->timeout < I_GetTime()) -#endif { if (ackpak[i].resentnum > 10 && (node->flags & NF_CLOSE)) { @@ -534,13 +506,8 @@ void Net_AckTicker(void) ackpak[i].acknum = 0; continue; } -#ifdef NEWPING DEBFILE(va("Resend ack %d, %u<%d at %u\n", ackpak[i].acknum, ackpak[i].senttime, NODETIMEOUT, I_GetTime())); -#else - DEBFILE(va("Resend ack %d, %u<%d at %u\n", ackpak[i].acknum, ackpak[i].senttime, - node->timeout, I_GetTime())); -#endif M_Memcpy(netbuffer, ackpak[i].pak.raw, ackpak[i].length); ackpak[i].senttime = I_GetTime(); ackpak[i].resentnum++; @@ -658,11 +625,6 @@ void Net_WaitAllAckReceived(UINT32 timeout) static void InitNode(node_t *node) { node->acktosend_head = node->acktosend_tail = 0; -#ifndef NEWPING - node->ping = PINGDEFAULT; - node->varping = VARPINGDEFAULT; - node->timeout = TIMEOUT(node->ping, node->varping); -#endif node->firstacktosend = 0; node->nextacknum = 1; node->remotefirstack = 0; @@ -821,10 +783,6 @@ static const char *packettypename[NUMPACKETTYPE] = "CLIENTMIS", "CLIENT2CMD", "CLIENT2MIS", - "CLIENT3CMD", - "CLIENT3MIS", - "CLIENT4CMD", - "CLIENT4MIS", "NODEKEEPALIVE", "NODEKEEPALIVEMIS", "SERVERTICS", @@ -841,6 +799,15 @@ static const char *packettypename[NUMPACKETTYPE] = "RESYNCHEND", "RESYNCHGET", + "CLIENT3CMD", + "CLIENT3MIS", + "CLIENT4CMD", + "CLIENT4MIS", + "BASICKEEPALIVE", + + "JOINCHALLENGE", + "DOWNLOADFILESOKAY", + "FILEFRAGMENT", "TEXTCMD", "TEXTCMD2", @@ -849,9 +816,7 @@ static const char *packettypename[NUMPACKETTYPE] = "CLIENTJOIN", "NODETIMEOUT", "RESYNCHING", -#ifdef NEWPING "PING" -#endif }; static void DebugPrintpacket(const char *header) @@ -868,7 +833,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: { @@ -1405,30 +1370,73 @@ boolean D_CheckNetGame(void) return ret; } +struct pingcell +{ + INT32 num; + INT32 ms; +}; + +static int pingcellcmp(const void *va, const void *vb) +{ + const struct pingcell *a, *b; + a = va; + b = vb; + return ( a->ms - b->ms ); +} + +/* +New ping command formatted nicely to present ping in +ascending order. And with equally spaced columns. +The caller's ping is presented at the bottom too, for +convenience. +*/ + void Command_Ping_f(void) { -#ifndef NEWPING - if(server) + struct pingcell pingv[MAXPLAYERS]; + INT32 pingc; + + int name_width = 0; + int ms_width = 0; + + int n; + INT32 i; + + pingc = 0; + for (i = 1; i < MAXPLAYERS; ++i) + if (playeringame[i]) { -#endif - INT32 i; - for (i = 0; i < MAXPLAYERS;i++) - { -#ifndef NEWPING - const INT32 node = playernode[i]; - if (playeringame[i] && node != 0) - CONS_Printf(M_GetText("%.2d : %s\n %d tics, %d ms.\n"), i, player_names[i], - GetLag(node), G_TicsToMilliseconds(GetLag(node))); -#else - if (playeringame[i] && i != 0) - CONS_Printf(M_GetText("%.2d : %s\n %d ms\n"), i, player_names[i], playerpingtable[i]); -#endif - } -#ifndef NEWPING + n = strlen(player_names[i]); + if (n > name_width) + name_width = n; + + n = playerpingtable[i]; + if (n > ms_width) + ms_width = n; + + pingv[pingc].num = i; + pingv[pingc].ms = playerpingtable[i]; + pingc++; + } + + if (ms_width < 10) ms_width = 1; + else if (ms_width < 100) ms_width = 2; + else ms_width = 3; + + qsort(pingv, pingc, sizeof (struct pingcell), &pingcellcmp); + + for (i = 0; i < pingc; ++i) + { + CONS_Printf("%02d : %-*s %*d ms\n", + pingv[i].num, + name_width, player_names[pingv[i].num], + ms_width, pingv[i].ms); + } + + if (!server && playeringame[consoleplayer]) + { + CONS_Printf("\nYour ping is %d ms\n", playerpingtable[consoleplayer]); } - else - CONS_Printf(M_GetText("Only the server can use this.\n")); -#endif } void D_CloseConnection(void) diff --git a/src/d_net.h b/src/d_net.h index 9cc1bbd2a..eb657eec1 100644 --- a/src/d_net.h +++ b/src/d_net.h @@ -19,9 +19,8 @@ #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 #define STATLENGTH (TICRATE*2) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index cea468cc6..43ce0a837 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -129,6 +129,9 @@ static void Command_StopMovie_f(void); static void Command_Map_f(void); static void Command_ResetCamera_f(void); +static void Command_View_f (void); +static void Command_SetViews_f(void); + static void Command_Addfile(void); static void Command_ListWADS_f(void); #ifdef DELFILE @@ -169,6 +172,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); @@ -380,6 +384,7 @@ consvar_t cv_kartdebugwaypoints = {"kartdebugwaypoints", "Off", CV_NETVAR|CV_CHE 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}; @@ -423,7 +428,7 @@ consvar_t cv_numlaps = {"numlaps", "3", CV_NETVAR|CV_CALL|CV_NOINIT, numlaps_con static CV_PossibleValue_t basenumlaps_cons_t[] = {{1, "MIN"}, {50, "MAX"}, {0, "Map default"}, {0, NULL}}; consvar_t cv_basenumlaps = {"basenumlaps", "Map default", CV_NETVAR|CV_CALL|CV_CHEAT, basenumlaps_cons_t, BaseNumLaps_OnChange, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_forceskin = {"forceskin", "-1", CV_NETVAR|CV_CALL|CV_CHEAT, NULL, ForceSkin_OnChange, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_forceskin = {"forceskin", "Off", CV_NETVAR|CV_CALL|CV_CHEAT, Forceskin_cons_t, ForceSkin_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_downloading = {"downloading", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_allowexitlevel = {"allowexitlevel", "No", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -434,10 +439,16 @@ static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE consvar_t cv_nettimeout = {"nettimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL}; //static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; consvar_t cv_jointimeout = {"jointimeout", "105", CV_CALL|CV_SAVE, nettimeout_cons_t, JoinTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL}; -#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}; -#endif + +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}; + // Intermission time Tails 04-19-2002 static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}}; consvar_t cv_inttime = {"inttime", "20", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -506,6 +517,24 @@ const char *netxcmdnames[MAXNETXCMD - 1] = */ void D_RegisterServerCommands(void) { + INT32 i; + Forceskin_cons_t[0].value = -1; + Forceskin_cons_t[0].strvalue = "Off"; + + for (i = 0; i < NUMGAMETYPES; i++) + { + gametype_cons_t[i].value = i; + gametype_cons_t[i].strvalue = Gametype_Names[i]; + } + gametype_cons_t[NUMGAMETYPES].value = 0; + gametype_cons_t[NUMGAMETYPES].strvalue = NULL; + + // Set the values to 0/NULL, it will be overwritten later when a skin is assigned to the slot. + for (i = 1; i < MAXSKINS; i++) + { + Forceskin_cons_t[i].value = 0; + Forceskin_cons_t[i].strvalue = NULL; + } RegisterNetXCmd(XD_NAMEANDCOLOR, Got_NameAndColor); RegisterNetXCmd(XD_WEAPONPREF, Got_WeaponPref); RegisterNetXCmd(XD_MAP, Got_Mapcmd); @@ -527,6 +556,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 @@ -657,6 +688,14 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_maxsend); CV_RegisterVar(&cv_noticedownload); CV_RegisterVar(&cv_downloadspeed); +#ifndef NONET + CV_RegisterVar(&cv_allownewplayer); +#ifdef VANILLAJOINNEXTROUND + CV_RegisterVar(&cv_joinnextround); +#endif + CV_RegisterVar(&cv_showjoinaddress); + CV_RegisterVar(&cv_blamecfail); +#endif COM_AddCommand("ping", Command_Ping_f); CV_RegisterVar(&cv_nettimeout); @@ -664,9 +703,9 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_skipmapcheck); CV_RegisterVar(&cv_sleep); -#ifdef NEWPING CV_RegisterVar(&cv_maxping); -#endif + CV_RegisterVar(&cv_pingtimeout); + CV_RegisterVar(&cv_showping); #ifdef SEENAMES CV_RegisterVar(&cv_allowseenames); @@ -714,6 +753,13 @@ void D_RegisterClientCommands(void) COM_AddCommand("resetcamera", Command_ResetCamera_f); + COM_AddCommand("view", Command_View_f); + COM_AddCommand("view2", Command_View_f); + COM_AddCommand("view3", Command_View_f); + COM_AddCommand("view4", Command_View_f); + + COM_AddCommand("setviews", Command_SetViews_f); + COM_AddCommand("setcontrol", Command_Setcontrol_f); COM_AddCommand("setcontrol2", Command_Setcontrol2_f); COM_AddCommand("setcontrol3", Command_Setcontrol3_f); @@ -788,6 +834,9 @@ void D_RegisterClientCommands(void) COM_AddCommand("displayplayer", Command_Displayplayer_f); + CV_RegisterVar(&cv_recordmultiplayerdemos); + CV_RegisterVar(&cv_netdemosyncquality); + // FIXME: not to be here.. but needs be done for config loading CV_RegisterVar(&cv_usegamma); @@ -809,6 +858,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,6 +890,10 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_driftaxis2); CV_RegisterVar(&cv_driftaxis3); CV_RegisterVar(&cv_driftaxis4); + CV_RegisterVar(&cv_deadzone); + CV_RegisterVar(&cv_deadzone2); + CV_RegisterVar(&cv_deadzone3); + CV_RegisterVar(&cv_deadzone4); // filesrch.c CV_RegisterVar(&cv_addons_option); @@ -905,6 +960,8 @@ void D_RegisterClientCommands(void) // screen.c CV_RegisterVar(&cv_fullscreen); CV_RegisterVar(&cv_renderview); + CV_RegisterVar(&cv_vhseffect); + CV_RegisterVar(&cv_shittyscreen); CV_RegisterVar(&cv_scr_depth); CV_RegisterVar(&cv_scr_width); CV_RegisterVar(&cv_scr_height); @@ -1116,11 +1173,11 @@ static void CleanupPlayerName(INT32 playernum, const char *newname) // spaces may have been removed if (playernum == consoleplayer) CV_StealthSet(&cv_playername, tmpname); - else if (playernum == secondarydisplayplayer || (!netgame && playernum == 1)) + else if (playernum == displayplayers[1] || (!netgame && playernum == 1)) CV_StealthSet(&cv_playername2, tmpname); - else if (playernum == thirddisplayplayer || (!netgame && playernum == 2)) + else if (playernum == displayplayers[2] || (!netgame && playernum == 2)) CV_StealthSet(&cv_playername3, tmpname); - else if (playernum == fourthdisplayplayer || (!netgame && playernum == 3)) + else if (playernum == displayplayers[3] || (!netgame && playernum == 3)) CV_StealthSet(&cv_playername4, tmpname); else I_Assert(((void)"CleanupPlayerName used on non-local player", 0)); @@ -1152,6 +1209,7 @@ static void SetPlayerName(INT32 playernum, char *newname) HU_AddChatText(va("\x82*%s renamed to %s", player_names[playernum], newname), false); strcpy(player_names[playernum], newname); + demo_extradata[playernum] |= DXD_NAME; } } else @@ -1227,11 +1285,11 @@ static void ForceAllSkins(INT32 forcedskin) { if (i == consoleplayer) CV_StealthSet(&cv_skin, skins[forcedskin].name); - else if (i == secondarydisplayplayer) + else if (i == displayplayers[1]) CV_StealthSet(&cv_skin2, skins[forcedskin].name); - else if (i == thirddisplayplayer) + else if (i == displayplayers[2]) CV_StealthSet(&cv_skin3, skins[forcedskin].name); - else if (i == fourthdisplayplayer) + else if (i == displayplayers[3]) CV_StealthSet(&cv_skin4, skins[forcedskin].name); } } @@ -1366,8 +1424,8 @@ static void SendNameAndColor2(void) if (splitscreen < 1 && !botingame) return; // can happen if skin2/color2/name2 changed - if (secondarydisplayplayer != consoleplayer) - secondplaya = secondarydisplayplayer; + if (displayplayers[1] != consoleplayer) + secondplaya = displayplayers[1]; else if (!netgame) // HACK secondplaya = 1; @@ -1416,7 +1474,7 @@ static void SendNameAndColor2(void) CleanupPlayerName(secondplaya, cv_playername2.zstring); strcpy(player_names[secondplaya], cv_playername2.zstring); - // don't use secondarydisplayplayer: the second player must be 1 + // don't use displayplayers[1]: the second player must be 1 players[secondplaya].skincolor = cv_playercolor2.value; if (players[secondplaya].mo) players[secondplaya].mo->color = players[secondplaya].skincolor; @@ -1455,14 +1513,14 @@ static void SendNameAndColor2(void) snac2pending++; // Don't change name if muted - if (cv_mute.value && !(server || IsPlayerAdmin(secondarydisplayplayer))) - CV_StealthSet(&cv_playername2, player_names[secondarydisplayplayer]); + if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[1]))) + CV_StealthSet(&cv_playername2, player_names[displayplayers[1]]); else // Cleanup name if changing it - CleanupPlayerName(secondarydisplayplayer, cv_playername2.zstring); + CleanupPlayerName(displayplayers[1], cv_playername2.zstring); // Don't change skin if the server doesn't want you to. - if (!CanChangeSkin(secondarydisplayplayer)) - CV_StealthSet(&cv_skin2, skins[players[secondarydisplayplayer].skin].name); + if (!CanChangeSkin(displayplayers[1])) + CV_StealthSet(&cv_skin2, skins[players[displayplayers[1]].skin].name); // check if player has the skin loaded (cv_skin2 may have // the name of a skin that was available in the previous game) @@ -1489,8 +1547,8 @@ static void SendNameAndColor3(void) if (splitscreen < 2) return; // can happen if skin3/color3/name3 changed - if (thirddisplayplayer != consoleplayer) - thirdplaya = thirddisplayplayer; + if (displayplayers[2] != consoleplayer) + thirdplaya = displayplayers[2]; else if (!netgame) // HACK thirdplaya = 2; @@ -1531,7 +1589,7 @@ static void SendNameAndColor3(void) CleanupPlayerName(thirdplaya, cv_playername3.zstring); strcpy(player_names[thirdplaya], cv_playername3.zstring); - // don't use thirddisplayplayer: the third player must be 2 + // don't use displayplayers[2]: the third player must be 2 players[thirdplaya].skincolor = cv_playercolor3.value; if (players[thirdplaya].mo) players[thirdplaya].mo->color = players[thirdplaya].skincolor; @@ -1570,14 +1628,14 @@ static void SendNameAndColor3(void) snac3pending++; // Don't change name if muted - if (cv_mute.value && !(server || IsPlayerAdmin(thirddisplayplayer))) - CV_StealthSet(&cv_playername3, player_names[thirddisplayplayer]); + if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[2]))) + CV_StealthSet(&cv_playername3, player_names[displayplayers[2]]); else // Cleanup name if changing it - CleanupPlayerName(thirddisplayplayer, cv_playername3.zstring); + CleanupPlayerName(displayplayers[2], cv_playername3.zstring); // Don't change skin if the server doesn't want you to. - if (!CanChangeSkin(thirddisplayplayer)) - CV_StealthSet(&cv_skin3, skins[players[thirddisplayplayer].skin].name); + if (!CanChangeSkin(displayplayers[2])) + CV_StealthSet(&cv_skin3, skins[players[displayplayers[2]].skin].name); // check if player has the skin loaded (cv_skin3 may have // the name of a skin that was available in the previous game) @@ -1604,8 +1662,8 @@ static void SendNameAndColor4(void) if (splitscreen < 3) return; // can happen if skin4/color4/name4 changed - if (fourthdisplayplayer != consoleplayer) - fourthplaya = fourthdisplayplayer; + if (displayplayers[3] != consoleplayer) + fourthplaya = displayplayers[3]; else if (!netgame) // HACK fourthplaya = 3; @@ -1654,7 +1712,7 @@ static void SendNameAndColor4(void) CleanupPlayerName(fourthplaya, cv_playername4.zstring); strcpy(player_names[fourthplaya], cv_playername4.zstring); - // don't use fourthdisplayplayer: the second player must be 4 + // don't use displayplayers[3]: the second player must be 4 players[fourthplaya].skincolor = cv_playercolor4.value; if (players[fourthplaya].mo) players[fourthplaya].mo->color = players[fourthplaya].skincolor; @@ -1693,14 +1751,14 @@ static void SendNameAndColor4(void) snac4pending++; // Don't change name if muted - if (cv_mute.value && !(server || IsPlayerAdmin(fourthdisplayplayer))) - CV_StealthSet(&cv_playername4, player_names[fourthdisplayplayer]); + if (cv_mute.value && !(server || IsPlayerAdmin(displayplayers[3]))) + CV_StealthSet(&cv_playername4, player_names[displayplayers[3]]); else // Cleanup name if changing it - CleanupPlayerName(fourthdisplayplayer, cv_playername4.zstring); + CleanupPlayerName(displayplayers[3], cv_playername4.zstring); // Don't change skin if the server doesn't want you to. - if (!CanChangeSkin(fourthdisplayplayer)) - CV_StealthSet(&cv_skin4, skins[players[fourthdisplayplayer].skin].name); + if (!CanChangeSkin(displayplayers[3])) + CV_StealthSet(&cv_skin4, skins[players[displayplayers[3]].skin].name); // check if player has the skin loaded (cv_skin4 may have // the name of a skin that was available in the previous game) @@ -1730,12 +1788,12 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) #endif if (playernum == consoleplayer) - snacpending--; - else if (playernum == secondarydisplayplayer) + snacpending--; // TODO: make snacpending an array instead of 4 separate vars? + else if (playernum == displayplayers[1]) snac2pending--; - else if (playernum == thirddisplayplayer) + else if (playernum == displayplayers[2]) snac3pending--; - else if (playernum == fourthdisplayplayer) + else if (playernum == displayplayers[3]) snac4pending--; #ifdef PARANOIA @@ -1755,10 +1813,11 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) p->skincolor = color % MAXSKINCOLORS; if (p->mo) p->mo->color = (UINT8)p->skincolor; + demo_extradata[playernum] |= DXD_COLOR; // normal player colors - if (server && (p != &players[consoleplayer] && p != &players[secondarydisplayplayer] - && p != &players[thirddisplayplayer] && p != &players[fourthdisplayplayer])) + if (server && (p != &players[consoleplayer] && p != &players[displayplayers[1]] + && p != &players[displayplayers[2]] && p != &players[displayplayers[3]])) { boolean kick = false; @@ -1795,11 +1854,11 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (playernum == consoleplayer) CV_StealthSet(&cv_skin, skins[forcedskin].name); - else if (playernum == secondarydisplayplayer) + else if (playernum == displayplayers[1]) CV_StealthSet(&cv_skin2, skins[forcedskin].name); - else if (playernum == thirddisplayplayer) + else if (playernum == displayplayers[2]) CV_StealthSet(&cv_skin3, skins[forcedskin].name); - else if (playernum == fourthdisplayplayer) + else if (playernum == displayplayers[3]) CV_StealthSet(&cv_skin4, skins[forcedskin].name); } else @@ -1886,7 +1945,198 @@ void D_SendPlayerConfig(void) // Only works for displayplayer, sorry! static void Command_ResetCamera_f(void) { - P_ResetCamera(&players[displayplayer], &camera); + P_ResetCamera(&players[displayplayers[0]], &camera[0]); +} + +/* Consider replacing nametonum with this */ +static INT32 LookupPlayer(const char *s) +{ + INT32 playernum; + + if (*s == '0')/* clever way to bypass atoi */ + return 0; + + if (( playernum = atoi(s) )) + { + playernum = max(min(playernum, MAXPLAYERS-1), 0);/* not out of range */ + return playernum; + } + + for (playernum = 0; playernum < MAXPLAYERS; ++playernum) + { + /* Match name case-insensitively: fully, or partially the start. */ + if (playeringame[playernum]) + if (strnicmp(player_names[playernum], s, strlen(s)) == 0) + { + return playernum; + } + } + return -1; +} + +static INT32 FindPlayerByPlace(INT32 place) +{ + INT32 playernum; + for (playernum = 0; playernum < MAXPLAYERS; ++playernum) + if (playeringame[playernum]) + { + if (players[playernum].kartstuff[k_position] == place) + { + return playernum; + } + } + return -1; +} + +// +// GetViewablePlayerPlaceRange +// Return in first and last, that player available to view, sorted by placement +// in the race. +// +static void GetViewablePlayerPlaceRange(INT32 *first, INT32 *last) +{ + INT32 i; + INT32 place; + + (*first) = MAXPLAYERS; + (*last) = 0; + + for (i = 0; i < MAXPLAYERS; ++i) + if (G_CouldView(i)) + { + place = players[i].kartstuff[k_position]; + if (place < (*first)) + (*first) = place; + if (place > (*last)) + (*last) = place; + } +} + +#define PRINTVIEWPOINT( pre,suf ) \ + CONS_Printf(pre"viewing \x84(%d) \x83%s\x80"suf".\n",\ + (*displayplayerp), player_names[(*displayplayerp)]); +static void Command_View_f(void) +{ + INT32 *displayplayerp; + INT32 olddisplayplayer; + int viewnum; + const char *playerparam; + INT32 placenum; + INT32 playernum; + INT32 firstplace, lastplace; + char c; + /* easy peasy */ + c = COM_Argv(0)[strlen(COM_Argv(0))-1];/* may be digit */ + switch (c) + { + case '2': viewnum = 2; break; + case '3': viewnum = 3; break; + case '4': viewnum = 4; break; + default: viewnum = 1; + } + + if (viewnum > 1 && !( multiplayer && demo.playback )) + { + CONS_Alert(CONS_NOTICE, + "You must be viewing a multiplayer replay to use this.\n"); + return; + } + + displayplayerp = &displayplayers[viewnum]; + + if (COM_Argc() > 1)/* switch to player */ + { + playerparam = COM_Argv(1); + if (playerparam[0] == '#')/* search by placement */ + { + placenum = atoi(&playerparam[1]); + playernum = FindPlayerByPlace(placenum); + if (playernum == -1 || !G_CouldView(playernum)) + { + GetViewablePlayerPlaceRange(&firstplace, &lastplace); + if (playernum == -1) + { + CONS_Alert(CONS_WARNING, "There is no player in that place! "); + } + else + { + CONS_Alert(CONS_WARNING, + "That player cannot be viewed currently! " + "The first player that you can view is \x82#%d\x80; ", + firstplace); + } + CONS_Printf("Last place is \x82#%d\x80.\n", lastplace); + return; + } + } + else + { + if (( playernum = LookupPlayer(COM_Argv(1)) ) == -1) + { + CONS_Alert(CONS_WARNING, "There is no player by that name!\n"); + return; + } + if (!playeringame[playernum]) + { + CONS_Alert(CONS_WARNING, "There is no player using that slot!\n"); + return; + } + } + + olddisplayplayer = (*displayplayerp); + G_ResetView(viewnum, playernum, false); + + /* The player we wanted was corrected to who it already was. */ + if ((*displayplayerp) == olddisplayplayer) + return; + + if ((*displayplayerp) != playernum)/* differ parameter */ + { + /* skipped some */ + CONS_Alert(CONS_NOTICE, "That player cannot be viewed currently.\n"); + PRINTVIEWPOINT ("Now "," instead") + } + else + PRINTVIEWPOINT ("Now ",) + } + else/* print current view */ + { + if (splitscreen < viewnum-1)/* We can't see those guys! */ + return; + PRINTVIEWPOINT ("Currently ",) + } +} +#undef PRINTVIEWPOINT + +static void Command_SetViews_f(void) +{ + UINT8 splits; + UINT8 newsplits; + + if (!( demo.playback && multiplayer )) + { + CONS_Alert(CONS_NOTICE, + "You must be viewing a multiplayer replay to use this.\n"); + return; + } + + if (COM_Argc() != 2) + { + CONS_Printf("setviews : set the number of split screens\n"); + return; + } + + splits = splitscreen+1; + + newsplits = atoi(COM_Argv(1)); + newsplits = min(max(newsplits, 1), 4); + if (newsplits > splits) + G_AdjustView(newsplits, 0, true); + else + { + splitscreen = newsplits-1; + R_ExecuteSetViewSize(); + } } // ======================================================================== @@ -1899,9 +2149,14 @@ static void Command_Playdemo_f(void) { char name[256]; - if (COM_Argc() != 2) + if (COM_Argc() < 2) { - CONS_Printf(M_GetText("playdemo : playback a demo\n")); + CONS_Printf("playdemo [-addfiles / -force]:\n"); + CONS_Printf(M_GetText( + "Play back a demo file. The full path from your Kart directory must be given.\n\n" + + "* With \"-addfiles\", any required files are added from a list contained within the demo file.\n" + "* With \"-force\", the demo is played even if the necessary files have not been added.\n")); return; } @@ -1912,7 +2167,7 @@ static void Command_Playdemo_f(void) } // disconnect from server here? - if (demoplayback) + if (demo.playback) G_StopDemo(); if (metalplayback) G_StopMetalDemo(); @@ -1923,6 +2178,9 @@ static void Command_Playdemo_f(void) CONS_Printf(M_GetText("Playing back demo '%s'.\n"), name); + demo.loadfiles = strcmp(COM_Argv(2), "-addfiles") == 0; + demo.ignorefiles = strcmp(COM_Argv(2), "-force") == 0; + // Internal if no extension, external if one exists // If external, convert the file name to a path in SRB2's home directory if (FIL_CheckExtension(name)) @@ -1948,7 +2206,7 @@ static void Command_Timedemo_f(void) } // disconnect from server here? - if (demoplayback) + if (demo.playback) G_StopDemo(); if (metalplayback) G_StopMetalDemo(); @@ -2072,7 +2330,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r { //CL_AddSplitscreenPlayer(); botingame = true; - secondarydisplayplayer = 1; + displayplayers[1] = 1; playeringame[1] = true; players[1].bot = 1; SendNameAndColor2(); @@ -2124,12 +2382,8 @@ void D_ModifyClientVote(SINT8 voted, UINT8 splitplayer) char *p = buf; UINT8 player = consoleplayer; - if (splitplayer == 1) - player = secondarydisplayplayer; - else if (splitplayer == 2) - player = thirddisplayplayer; - else if (splitplayer == 3) - player = fourthdisplayplayer; + if (splitplayer > 0) + player = displayplayers[splitplayer]; WRITESINT8(p, voted); WRITEUINT8(p, player); @@ -2189,13 +2443,15 @@ static void Command_Map_f(void) { const char *mapname; size_t i; - INT32 j, newmapnum; - boolean newresetplayers; + INT32 newmapnum; + boolean newresetplayers, newencoremode; INT32 newgametype = gametype; - // max length of command: map map03 -gametype coop -noresetplayers -force - // 1 2 3 4 5 6 + // max length of command: map map03 -gametype race -noresetplayers -force -encore + // 1 2 3 4 5 6 7 // = 8 arg max + // i don't know whether this is intrinsic to the system or just someone being weird but + // "noresetplayers" is pretty useless for kart if it turns out this is too close to the limit if (COM_Argc() < 2 || COM_Argc() > 8) { CONS_Printf(M_GetText("map [-gametype [-force]: warp to map\n")); @@ -2255,29 +2511,30 @@ static void Command_Map_f(void) return; } - for (j = 0; gametype_cons_t[j].strvalue; j++) - if (!strcasecmp(gametype_cons_t[j].strvalue, COM_Argv(i+1))) - { - // Don't do any variable setting here. Wait until you get your - // map packet first to avoid sending the same info twice! - newgametype = gametype_cons_t[j].value; - break; - } - - if (!gametype_cons_t[j].strvalue) // reached end of the list with no match + newgametype = G_GetGametypeByName(COM_Argv(i+1)); + if (newgametype == -1) // reached end of the list with no match { - // assume they gave us a gametype number, which is okay too - for (j = 0; gametype_cons_t[j].strvalue != NULL; j++) - { - if (atoi(COM_Argv(i+1)) == gametype_cons_t[j].value) - { - newgametype = gametype_cons_t[j].value; - break; - } - } + INT32 j = atoi(COM_Argv(i+1)); // assume they gave us a gametype number, which is okay too + if (j >= 0 && j < NUMGAMETYPES) + newgametype = (INT16)j; } } + // new encoremode value + // use cvar by default + + newencoremode = (boolean)cv_kartencore.value; + + if (COM_CheckParm("-encore")) + { + if (!M_SecretUnlocked(SECRET_ENCORE) && !newencoremode) + { + CONS_Alert(CONS_NOTICE, M_GetText("You haven't unlocked Encore Mode yet!\n")); + return; + } + newencoremode = !newencoremode; + } + if (!(i = COM_CheckParm("-force")) && newgametype == gametype) // SRB2Kart newresetplayers = false; // if not forcing and gametypes is the same @@ -2286,15 +2543,20 @@ static void Command_Map_f(void) ; // The player wants us to trek on anyway. Do so. // G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer // Alternatively, bail if the map header is completely missing anyway. - else + else if (!mapheaderinfo[newmapnum-1] + || !(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype))) { - if (!mapheaderinfo[newmapnum-1] - || !(mapheaderinfo[newmapnum-1]->typeoflevel & G_TOLFlag(newgametype))) + char gametypestring[32] = "Single Player"; + + if (multiplayer) { - CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, - (multiplayer ? gametype_cons_t[newgametype].strvalue : "Single Player")); - return; + if (newgametype >= 0 && newgametype < NUMGAMETYPES + && Gametype_Names[newgametype]) + strcpy(gametypestring, Gametype_Names[newgametype]); } + + CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring); + return; } // Prevent warping to locked levels @@ -2308,7 +2570,7 @@ static void Command_Map_f(void) } fromlevelselect = false; - D_MapChange(newmapnum, newgametype, (boolean)cv_kartencore.value, newresetplayers, 0, false, false); + D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false); } /** Receives a map command and changes the map. @@ -2379,10 +2641,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) CON_LogMessage(M_GetText("Speeding off to level...\n")); } - CON_ToggleOff(); - CON_ClearHUD(); - - if (demoplayback && !timingdemo) + if (demo.playback && !demo.timing) precache = false; if (resetplayer) @@ -2399,17 +2658,19 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) LUAh_MapChange(mapnumber); #endif*/ + demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING; + demo.savebutton = 0; G_InitNew(pencoremode, mapname, resetplayer, skipprecutscene); - if (demoplayback && !timingdemo) + if (demo.playback && !demo.timing) precache = true; - if (timingdemo) + if (demo.timing) G_DoneLevelLoad(); if (metalrecording) G_BeginMetal(); - if (demorecording) // Okay, level loaded, character spawned and skinned, + if (demo.recording) // Okay, level loaded, character spawned and skinned, G_BeginRecording(); // I AM NOW READY TO RECORD. - demo_start = true; + demo.deferstart = true; } static void Command_Pause(void) @@ -2459,13 +2720,13 @@ static void Got_Pause(UINT8 **cp, INT32 playernum) return; } - if (modeattacking) + if (modeattacking && !demo.playback) return; paused = READUINT8(*cp); dedicatedpause = READUINT8(*cp); - if (!demoplayback) + if (!demo.playback) { if (netgame) { @@ -2552,6 +2813,7 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum) if (players[respawnplayer].mo) P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 10000); + demo_extradata[playernum] |= DXD_RESPAWN; } /** Deals with an ::XD_RANDOMSEED message in a netgame. @@ -2773,11 +3035,11 @@ static void Command_Teamchange2_f(void) return; } - if (players[secondarydisplayplayer].spectator) - error = !(NetPacket.packet.newteam || (players[secondarydisplayplayer].pflags & PF_WANTSTOJOIN)); + if (players[displayplayers[1]].spectator) + error = !(NetPacket.packet.newteam || (players[displayplayers[1]].pflags & PF_WANTSTOJOIN)); else if (G_GametypeHasTeams()) - error = (NetPacket.packet.newteam == (unsigned)players[secondarydisplayplayer].ctfteam); - else if (G_GametypeHasSpectators() && !players[secondarydisplayplayer].spectator) + error = (NetPacket.packet.newteam == (unsigned)players[displayplayers[1]].ctfteam); + else if (G_GametypeHasSpectators() && !players[displayplayers[1]].spectator) error = (NetPacket.packet.newteam == 3); #ifdef PARANOIA else @@ -2864,11 +3126,11 @@ static void Command_Teamchange3_f(void) return; } - if (players[thirddisplayplayer].spectator) - error = !(NetPacket.packet.newteam || (players[thirddisplayplayer].pflags & PF_WANTSTOJOIN)); + if (players[displayplayers[2]].spectator) + error = !(NetPacket.packet.newteam || (players[displayplayers[2]].pflags & PF_WANTSTOJOIN)); else if (G_GametypeHasTeams()) - error = (NetPacket.packet.newteam == (unsigned)players[thirddisplayplayer].ctfteam); - else if (G_GametypeHasSpectators() && !players[thirddisplayplayer].spectator) + error = (NetPacket.packet.newteam == (unsigned)players[displayplayers[2]].ctfteam); + else if (G_GametypeHasSpectators() && !players[displayplayers[2]].spectator) error = (NetPacket.packet.newteam == 3); #ifdef PARANOIA else @@ -2955,11 +3217,11 @@ static void Command_Teamchange4_f(void) return; } - if (players[fourthdisplayplayer].spectator) - error = !(NetPacket.packet.newteam || (players[fourthdisplayplayer].pflags & PF_WANTSTOJOIN)); + if (players[displayplayers[3]].spectator) + error = !(NetPacket.packet.newteam || (players[displayplayers[3]].pflags & PF_WANTSTOJOIN)); else if (G_GametypeHasTeams()) - error = (NetPacket.packet.newteam == (unsigned)players[fourthdisplayplayer].ctfteam); - else if (G_GametypeHasSpectators() && !players[fourthdisplayplayer].spectator) + error = (NetPacket.packet.newteam == (unsigned)players[displayplayers[3]].ctfteam); + else if (G_GametypeHasSpectators() && !players[displayplayers[3]].spectator) error = (NetPacket.packet.newteam == 3); #ifdef PARANOIA else @@ -3356,8 +3618,8 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) HU_AddChatText(va("\x82*%s became a spectator.", player_names[playernum]), false); // "entered the game" text was moved to P_SpectatorJoinGame //reset view if you are changed, or viewing someone who was changed. - if (playernum == consoleplayer || displayplayer == playernum) - displayplayer = consoleplayer; + if (playernum == consoleplayer || displayplayers[0] == playernum) + displayplayers[0] = consoleplayer; if (G_GametypeHasTeams()) { @@ -3365,11 +3627,11 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) { if (playernum == consoleplayer) //CTF and Team Match colors. CV_SetValue(&cv_playercolor, NetPacket.packet.newteam + 5); - else if (playernum == secondarydisplayplayer) + else if (playernum == displayplayers[1]) CV_SetValue(&cv_playercolor2, NetPacket.packet.newteam + 5); - else if (playernum == thirddisplayplayer) + else if (playernum == displayplayers[2]) CV_SetValue(&cv_playercolor3, NetPacket.packet.newteam + 5); - else if (playernum == fourthdisplayplayer) + else if (playernum == displayplayers[3]) CV_SetValue(&cv_playercolor4, NetPacket.packet.newteam + 5); } } @@ -3377,6 +3639,8 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) if (gamestate != GS_LEVEL) return; + demo_extradata[playernum] |= DXD_PLAYSTATE; + // Clear player score and rings if a spectator. if (players[playernum].spectator) { @@ -3418,6 +3682,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); @@ -3431,7 +3696,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) @@ -3470,7 +3735,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) @@ -3493,11 +3758,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 } @@ -3508,9 +3773,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; @@ -3522,9 +3787,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 @@ -3693,6 +3958,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; @@ -3905,14 +4295,6 @@ static void Command_Addfile(void) if (*p == '\\' || *p == '/' || *p == ':') break; ++p; - // check total packet size and no of files currently loaded - // See W_LoadWadFile in w_wad.c - if ((numwadfiles >= MAX_WADFILES) - || ((packetsizetally + nameonlylength(fn) + 22) > MAXFILENEEDED*sizeof(UINT8))) - { - CONS_Alert(CONS_ERROR, M_GetText("Too many files loaded to add %s\n"), fn); - return; - } WRITESTRINGN(buf_p,p,240); @@ -4027,8 +4409,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum) } // See W_LoadWadFile in w_wad.c - if ((numwadfiles >= MAX_WADFILES) - || ((packetsizetally + nameonlylength(filename) + 22) > MAXFILENEEDED*sizeof(UINT8))) + if (numwadfiles >= MAX_WADFILES) toomany = true; else ncs = findfile(filename,md5sum,true); @@ -4228,12 +4609,22 @@ static void Command_ModDetails_f(void) // static void Command_ShowGametype_f(void) { + const char *gametypestr = NULL; + if (!(netgame || multiplayer)) // print "Single player" instead of "Race" { CONS_Printf(M_GetText("Current gametype is %s\n"), "Single Player"); return; } - CONS_Printf(M_GetText("Current gametype is %s\n"), gametype_cons_t[gametype].strvalue); + + // get name string for current gametype + if (gametype >= 0 && gametype < NUMGAMETYPES) + gametypestr = Gametype_Names[gametype]; + + if (gametypestr) + CONS_Printf(M_GetText("Current gametype is %s\n"), gametypestr); + else // string for current gametype was not found above (should never happen) + CONS_Printf(M_GetText("Unknown gametype set (%d)\n"), gametype); } /** Plays the intro. @@ -4309,7 +4700,7 @@ static void PointLimit_OnChange(void) static void NumLaps_OnChange(void) { - if (!G_RaceGametype() || (modeattacking || demoplayback)) + if (!G_RaceGametype() || (modeattacking || demo.playback)) return; if (server && Playing() @@ -4377,9 +4768,18 @@ static void TimeLimit_OnChange(void) */ void D_GameTypeChanged(INT32 lastgametype) { - if (multiplayer) - CONS_Printf(M_GetText("Gametype was changed from %s to %s\n"), gametype_cons_t[lastgametype].strvalue, gametype_cons_t[gametype].strvalue); + if (netgame) + { + const char *oldgt = NULL, *newgt = NULL; + if (lastgametype >= 0 && lastgametype < NUMGAMETYPES) + oldgt = Gametype_Names[lastgametype]; + if (gametype >= 0 && lastgametype < NUMGAMETYPES) + newgt = Gametype_Names[gametype]; + + if (oldgt && newgt) + CONS_Printf(M_GetText("Gametype was changed from %s to %s\n"), oldgt, newgt); + } // Only do the following as the server, not as remote admin. // There will always be a server, and this only needs to be done once. if (server && (multiplayer || netgame)) @@ -4749,7 +5149,7 @@ static void Command_ExitLevel_f(void) CONS_Printf(M_GetText("This only works in a netgame.\n")); else if (!(server || (IsPlayerAdmin(consoleplayer)))) CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n")); - else if (gamestate != GS_LEVEL || demoplayback) + else if (gamestate != GS_LEVEL || demo.playback) CONS_Printf(M_GetText("You must be in a level to use this.\n")); else SendNetXCmd(XD_EXITLEVEL, NULL, 0); @@ -4847,13 +5247,13 @@ static void Got_PickVotecmd(UINT8 **cp, INT32 playernum) Y_SetupVoteFinish(pick, level); } -/** Prints the number of the displayplayer. +/** Prints the number of displayplayers[0]. * * \todo Possibly remove this; it was useful for debugging at one point. */ static void Command_Displayplayer_f(void) { - CONS_Printf(M_GetText("Displayplayer is %d\n"), displayplayer); + CONS_Printf(M_GetText("Displayplayer is %d\n"), displayplayers[0]); } /** Quits a game and returns to the title screen. @@ -5012,27 +5412,11 @@ static void Command_Archivetest_f(void) /** Makes a change to ::cv_forceskin take effect immediately. * - * \todo Move the enforcement code out of SendNameAndColor() so this hack - * isn't needed. * \sa Command_SetForcedSkin_f, cv_forceskin, forcedskin * \author Graue */ static void ForceSkin_OnChange(void) { - if ((server || IsPlayerAdmin(consoleplayer)) && (cv_forceskin.value < -1 || cv_forceskin.value >= numskins)) - { - if (cv_forceskin.value == -2) - CV_SetValue(&cv_forceskin, numskins-1); - else - { - // hack because I can't restrict this and still allow added skins to be used with forceskin. - if (!menuactive) - CONS_Printf(M_GetText("Valid skin numbers are 0 to %d (-1 disables)\n"), numskins - 1); - CV_SetValue(&cv_forceskin, -1); - } - return; - } - // NOT in SP, silly! if (!(netgame || multiplayer)) return; @@ -5041,7 +5425,7 @@ static void ForceSkin_OnChange(void) CONS_Printf("The server has lifted the forced skin restrictions.\n"); else { - CONS_Printf("The server is restricting all players to skin \"%s\".\n",skins[cv_forceskin.value].name); + CONS_Printf("The server is restricting all players to skin \"%s\".\n",cv_forceskin.string); ForceAllSkins(cv_forceskin.value); } } @@ -5064,7 +5448,7 @@ static void Name2_OnChange(void) if (cv_mute.value) //Secondary player can't be admin. { CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n")); - CV_StealthSet(&cv_playername2, player_names[secondarydisplayplayer]); + CV_StealthSet(&cv_playername2, player_names[displayplayers[1]]); } else SendNameAndColor2(); @@ -5075,7 +5459,7 @@ static void Name3_OnChange(void) if (cv_mute.value) //Third player can't be admin. { CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n")); - CV_StealthSet(&cv_playername3, player_names[thirddisplayplayer]); + CV_StealthSet(&cv_playername3, player_names[displayplayers[2]]); } else SendNameAndColor3(); @@ -5086,7 +5470,7 @@ static void Name4_OnChange(void) if (cv_mute.value) //Secondary player can't be admin. { CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n")); - CV_StealthSet(&cv_playername4, player_names[fourthdisplayplayer]); + CV_StealthSet(&cv_playername4, player_names[displayplayers[3]]); } else SendNameAndColor4(); @@ -5127,12 +5511,12 @@ static void Skin2_OnChange(void) if (!Playing() || !splitscreen) return; // do whatever you want - if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer)) + if (CanChangeSkin(displayplayers[1]) && !P_PlayerMoving(displayplayers[1])) SendNameAndColor2(); else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); - CV_StealthSet(&cv_skin2, skins[players[secondarydisplayplayer].skin].name); + CV_StealthSet(&cv_skin2, skins[players[displayplayers[1]].skin].name); } } @@ -5141,12 +5525,12 @@ static void Skin3_OnChange(void) if (!Playing() || splitscreen < 2) return; // do whatever you want - if (CanChangeSkin(thirddisplayplayer) && !P_PlayerMoving(thirddisplayplayer)) + if (CanChangeSkin(displayplayers[2]) && !P_PlayerMoving(displayplayers[2])) SendNameAndColor3(); else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); - CV_StealthSet(&cv_skin3, skins[players[thirddisplayplayer].skin].name); + CV_StealthSet(&cv_skin3, skins[players[displayplayers[2]].skin].name); } } @@ -5155,12 +5539,12 @@ static void Skin4_OnChange(void) if (!Playing() || splitscreen < 3) return; // do whatever you want - if (CanChangeSkin(fourthdisplayplayer) && !P_PlayerMoving(fourthdisplayplayer)) + if (CanChangeSkin(displayplayers[3]) && !P_PlayerMoving(displayplayers[3])) SendNameAndColor4(); else { CONS_Alert(CONS_NOTICE, M_GetText("You can't change your skin at the moment.\n")); - CV_StealthSet(&cv_skin4, skins[players[fourthdisplayplayer].skin].name); + CV_StealthSet(&cv_skin4, skins[players[displayplayers[3]].skin].name); } } @@ -5201,7 +5585,7 @@ static void Color2_OnChange(void) if (!Playing() || !splitscreen) return; // do whatever you want - if (!P_PlayerMoving(secondarydisplayplayer)) + if (!P_PlayerMoving(displayplayers[1])) { // Color change menu scrolling fix is no longer necessary SendNameAndColor2(); @@ -5209,7 +5593,7 @@ static void Color2_OnChange(void) else { CV_StealthSetValue(&cv_playercolor2, - players[secondarydisplayplayer].skincolor); + players[displayplayers[1]].skincolor); } } @@ -5218,7 +5602,7 @@ static void Color3_OnChange(void) if (!Playing() || splitscreen < 2) return; // do whatever you want - if (!P_PlayerMoving(thirddisplayplayer)) + if (!P_PlayerMoving(displayplayers[2])) { // Color change menu scrolling fix is no longer necessary SendNameAndColor3(); @@ -5226,7 +5610,7 @@ static void Color3_OnChange(void) else { CV_StealthSetValue(&cv_playercolor3, - players[thirddisplayplayer].skincolor); + players[displayplayers[2]].skincolor); } } @@ -5235,7 +5619,7 @@ static void Color4_OnChange(void) if (!Playing() || splitscreen < 3) return; // do whatever you want - if (!P_PlayerMoving(fourthdisplayplayer)) + if (!P_PlayerMoving(displayplayers[3])) { // Color change menu scrolling fix is no longer necessary SendNameAndColor4(); @@ -5243,7 +5627,7 @@ static void Color4_OnChange(void) else { CV_StealthSetValue(&cv_playercolor4, - players[fourthdisplayplayer].skincolor); + players[displayplayers[3]].skincolor); } } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 05be43a68..437d0420e 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -126,8 +126,8 @@ 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, cv_kartdebugcolorize; extern consvar_t cv_kartdebugwaypoints; -extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes; extern consvar_t cv_itemfinder; @@ -144,9 +144,9 @@ extern consvar_t cv_ringslinger, cv_soundtest; extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionboxes; -#ifdef NEWPING extern consvar_t cv_maxping; -#endif +extern consvar_t cv_pingtimeout; +extern consvar_t cv_showping; extern consvar_t cv_skipmapcheck; @@ -247,6 +247,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 99a058403..0b9dc9126 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -107,19 +107,39 @@ INT32 lastfilenum = -1; * Used to have size limiting built in - now handed via W_LoadWadFile in w_wad.c * */ -UINT8 *PutFileNeeded(void) +UINT8 *PutFileNeeded(UINT16 firstfile) { - size_t i, count = 0; - UINT8 *p = netbuffer->u.serverinfo.fileneeded; + size_t i; + UINT8 count = 0; + UINT8 *p_start = netbuffer->packettype == PT_MOREFILESNEEDED ? netbuffer->u.filesneededcfg.files : netbuffer->u.serverinfo.fileneeded; + UINT8 *p = p_start; char wadfilename[MAX_WADPATH] = ""; UINT8 filestatus; - for (i = 0; i < numwadfiles; i++) + for (i = mainwads; i < numwadfiles; i++) { // If it has only music/sound lumps, don't put it in the list if (!wadfiles[i]->important) continue; + if (firstfile) + { // Skip files until we reach the first file. + firstfile--; + continue; + } + + nameonly(strcpy(wadfilename, wadfiles[i]->filename)); + + if (p + 1 + 4 + strlen(wadfilename) + 16 > p_start + MAXFILENEEDED) + { + // Too many files to send all at once + if (netbuffer->packettype == PT_MOREFILESNEEDED) + netbuffer->u.filesneededcfg.more = 1; + else + netbuffer->u.serverinfo.kartvars |= SV_LOTSOFADDONS; + break; + } + filestatus = 1; // Importance - not really used any more, holds 1 by default for backwards compat with MS // Store in the upper four bits @@ -134,30 +154,32 @@ UINT8 *PutFileNeeded(void) count++; WRITEUINT32(p, wadfiles[i]->filesize); - nameonly(strcpy(wadfilename, wadfiles[i]->filename)); WRITESTRINGN(p, wadfilename, MAX_WADPATH); WRITEMEM(p, wadfiles[i]->md5sum, 16); } - netbuffer->u.serverinfo.fileneedednum = (UINT8)count; + if (netbuffer->packettype == PT_MOREFILESNEEDED) + netbuffer->u.filesneededcfg.num = count; + else + netbuffer->u.serverinfo.fileneedednum = count; return p; } /** Parses the serverinfo packet and fills the fileneeded table on client * - * \param fileneedednum_parm The number of files needed to join the server + * \param fileneedednum_parm The number of files (sent in this page) needed to join the server * \param fileneededstr The memory block containing the list of needed files - * + * \param firstfile The first file index to read from */ -void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr) +void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 firstfile) { INT32 i; UINT8 *p; UINT8 filestatus; - fileneedednum = fileneedednum_parm; + fileneedednum = firstfile + fileneedednum_parm; p = (UINT8 *)fileneededstr; - for (i = 0; i < fileneedednum; i++) + for (i = firstfile; i < fileneedednum; i++) { fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet filestatus = READUINT8(p); // The first byte is the file status @@ -336,9 +358,9 @@ INT32 CL_CheckFiles(void) // return 1; // the first is the iwad (the main wad file) - // we don't care if it's called srb2.srb or srb2.wad. // Never download the IWAD, just assume it's there and identical - fileneeded[0].status = FS_OPEN; + // ...No! Why were we sending the base wads to begin with?? + //fileneeded[0].status = FS_OPEN; // Modified game handling -- check for an identical file list // must be identical in files loaded AND in order @@ -346,7 +368,7 @@ INT32 CL_CheckFiles(void) if (modifiedgame) { CONS_Debug(DBG_NETPLAY, "game is modified; only doing basic checks\n"); - for (i = 1, j = 1; i < fileneedednum || j < numwadfiles;) + for (i = 0, j = mainwads; i < fileneedednum || j < numwadfiles;) { if (j < numwadfiles && !wadfiles[j]->important) { @@ -373,15 +395,12 @@ INT32 CL_CheckFiles(void) return 1; } - // See W_LoadWadFile in w_wad.c - packetsize = packetsizetally; - - for (i = 1; i < fileneedednum; i++) + for (i = 0; i < fileneedednum; i++) { CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename); // Check in already loaded files - for (j = 1; wadfiles[j]; j++) + for (j = mainwads; wadfiles[j]; j++) { nameonly(strcpy(wadfilename, wadfiles[j]->filename)); if (!stricmp(wadfilename, fileneeded[i].filename) && @@ -397,8 +416,7 @@ INT32 CL_CheckFiles(void) packetsize += nameonlylength(fileneeded[i].filename) + 22; - if ((numwadfiles+filestoget >= MAX_WADFILES) - || (packetsize > MAXFILENEEDED*sizeof(UINT8))) + if (mainwads+filestoget >= MAX_WADFILES) return 3; filestoget++; @@ -755,17 +773,14 @@ void Got_Filetxpak(void) char *filename = file->filename; static INT32 filetime = 0; - if (!(strcmp(filename, "srb2.srb") - && strcmp(filename, "srb2.wad") - && strcmp(filename, "patch.dta") - //&& strcmp(filename, "music.dta") - && strcmp(filename, "gfx.kart") - && strcmp(filename, "textures.kart") - && strcmp(filename, "chars.kart") - && strcmp(filename, "maps.kart") - && strcmp(filename, "sounds.kart") - && strcmp(filename, "music.kart") - && strcmp(filename, "patch.kart") + if (!(strcmp(filename, "main.kart") + && strcmp(filename, "gfx.pk3") + && strcmp(filename, "textures.pk3") + && strcmp(filename, "chars.pk3") + && strcmp(filename, "maps.wad") + && strcmp(filename, "patch.pk3") + && strcmp(filename, "sounds.wad") + && strcmp(filename, "music.wad") )) I_Error("Tried to download \"%s\"", filename); diff --git a/src/d_netfil.h b/src/d_netfil.h index 3d7c2ed59..2f0333311 100644 --- a/src/d_netfil.h +++ b/src/d_netfil.h @@ -53,8 +53,8 @@ extern char downloaddir[512]; extern INT32 lastfilenum; #endif -UINT8 *PutFileNeeded(void); -void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr); +UINT8 *PutFileNeeded(UINT16 firstfile); +void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr, UINT16 firstfile); void CL_PrepareDownloadSaveGame(const char *tmpsave); INT32 CL_CheckFiles(void); diff --git a/src/d_player.h b/src/d_player.h index b430f20a4..c6a7f0f34 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -32,42 +32,9 @@ // 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 -typedef enum -{ - CA_NONE=0, - CA_THOK, - CA_FLY, - CA_GLIDEANDCLIMB, - CA_HOMINGTHOK, - CA_SWIM, - CA_DOUBLEJUMP, - CA_FLOAT, - CA_SLOWFALL, - CA_TELEKINESIS, - CA_FALLSWITCH, - CA_JUMPBOOST, - CA_AIRDRILL, - CA_JUMPTHOK -} charability_t; - -//Secondary skin abilities -typedef enum -{ - CA2_NONE=0, - CA2_SPINDASH, - CA2_MULTIABILITY -} charability2_t; - // // Player states. // @@ -275,6 +242,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 @@ -354,6 +322,7 @@ typedef enum 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; @@ -446,29 +415,8 @@ typedef struct player_s UINT8 kartweight; // Kart weight stat between 1 and 9 // - fixed_t normalspeed; // Normal ground - fixed_t runspeed; // Speed you break into the run animation - UINT8 thrustfactor; // Thrust = thrustfactor * acceleration - UINT8 accelstart; // Starting acceleration if speed = 0. - UINT8 acceleration; // Acceleration - - // See charability_t and charability2_t for more information. - UINT8 charability; // Ability definition - UINT8 charability2; // Secondary ability definition - UINT32 charflags; // Extra abilities/settings for skins (combinable stuff) // See SF_ flags - - mobjtype_t thokitem; // Object # to spawn for the thok - mobjtype_t spinitem; // Object # to spawn for spindash/spinning - mobjtype_t revitem; // Object # to spawn for spindash/spinning - - fixed_t actionspd; // Speed of thok/glide/fly - fixed_t mindash; // Minimum spindash speed - fixed_t maxdash; // Maximum spindash speed - - fixed_t jumpfactor; // How high can the player jump? - SINT8 lives; SINT8 continues; // continues that player has acquired @@ -571,6 +519,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/dehacked.c b/src/dehacked.c index 11aed24d0..7e81a529f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1216,6 +1216,13 @@ static void readlevelheader(MYFILE *f, INT32 num) #endif else if (fastcmp(word, "MUSICTRACK")) mapheaderinfo[num-1]->mustrack = ((UINT16)i - 1); + else if (fastcmp(word, "MUSICPOS")) + mapheaderinfo[num-1]->muspos = (UINT32)get_number(word2); + else if (fastcmp(word, "MUSICINTERFADEOUT")) + mapheaderinfo[num-1]->musinterfadeout = (UINT32)get_number(word2); + else if (fastcmp(word, "MUSICINTER")) + deh_strlcpy(mapheaderinfo[num-1]->musintername, word2, + sizeof(mapheaderinfo[num-1]->musintername), va("Level header %d: intermission music", num)); else if (fastcmp(word, "FORCECHARACTER")) { strlcpy(mapheaderinfo[num-1]->forcecharacter, word2, SKINNAMESIZE+1); @@ -1539,6 +1546,11 @@ static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum) DEH_WriteUndoline(word, va("%u", cutscenes[num]->scene[scenenum].musswitchflags), UNDO_NONE); cutscenes[num]->scene[scenenum].musswitchflags = ((UINT16)i) & MUSIC_TRACKMASK; } + else if (fastcmp(word, "MUSICPOS")) + { + DEH_WriteUndoline(word, va("%u", cutscenes[num]->scene[scenenum].musswitchposition), UNDO_NONE); + cutscenes[num]->scene[scenenum].musswitchposition = (UINT32)get_number(word2); + } else if (fastcmp(word, "MUSICLOOP")) { DEH_WriteUndoline(word, va("%u", cutscenes[num]->scene[scenenum].musicloop), UNDO_NONE); @@ -2142,11 +2154,12 @@ static boolean GoodDataFileName(const char *s) p = s + strlen(s) - strlen(tail); if (p <= s) return false; // too short if (!fasticmp(p, tail)) return false; // doesn't end in .dat -#ifdef DELFILE - if (fasticmp(s, "gamedata.dat") && !disableundo) return false; -#else - if (fasticmp(s, "gamedata.dat")) return false; -#endif + + if (fasticmp(s, "gamedata.dat")) return false; // Vanilla SRB2 gamedata + if (fasticmp(s, "main.dat")) return false; // Vanilla SRB2 time attack replay folder + if (fasticmp(s, "kartdata.dat")) return false; // SRB2Kart gamedata + if (fasticmp(s, "kart.dat")) return false; // SRB2Kart time attack replay folder + if (fasticmp(s, "online.dat")) return false; // SRB2Kart online replay folder return true; } @@ -3103,11 +3116,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); @@ -3741,8 +3749,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) } else if (fastcmp(word, "SRB2")) { - if (mainwads) // srb2.srb triggers this warning otherwise - deh_warning("Patch is only compatible with base SRB2."); + 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, @@ -7150,6 +7157,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 @@ -8133,90 +8147,172 @@ 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 +static const char *COLOR_ENUMS[] = { // Rejigged for Kart. + "NONE", // SKINCOLOR_NONE + "WHITE", // SKINCOLOR_WHITE + "SILVER", // SKINCOLOR_SILVER + "GREY", // SKINCOLOR_GREY + "NICKEL", // SKINCOLOR_NICKEL + "BLACK", // SKINCOLOR_BLACK + "FAIRY", // SKINCOLOR_FAIRY + "POPCORN", // SKINCOLOR_POPCORN + "ARTICHOKE", // SKINCOLOR_ARTICHOKE + "PIGEON", // SKINCOLOR_PIGEON + "SEPIA", // SKINCOLOR_SEPIA + "BEIGE", // SKINCOLOR_BEIGE + "CARAMEL", // SKINCOLOR_CARAMEL + "PEACH", // SKINCOLOR_PEACH + "BROWN", // SKINCOLOR_BROWN + "LEATHER", // SKINCOLOR_LEATHER + "SALMON", // SKINCOLOR_SALMON + "PINK", // SKINCOLOR_PINK + "ROSE", // SKINCOLOR_ROSE + "CINNAMON", // SKINCOLOR_CINNAMON + "RUBY", // SKINCOLOR_RUBY + "RASPBERRY", // SKINCOLOR_RASPBERRY + "RED", // SKINCOLOR_RED + "CRIMSON", // SKINCOLOR_CRIMSON + "MAROON", // SKINCOLOR_MAROON + "LEMONADE", // SKINCOLOR_LEMONADE + "SCARLET", // SKINCOLOR_SCARLET + "KETCHUP", // SKINCOLOR_KETCHUP + "DAWN", // SKINCOLOR_DAWN + "SUNSET", // SKINCOLOR_SUNSET + "CREAMSICLE", // SKINCOLOR_CREAMSICLE + "ORANGE", // SKINCOLOR_ORANGE + "ROSEWOOD", // SKINCOLOR_ROSEWOOD + "TANGERINE", // SKINCOLOR_TANGERINE + "TAN", // SKINCOLOR_TAN + "CREAM", // SKINCOLOR_CREAM + "GOLD", // SKINCOLOR_GOLD + "ROYAL", // SKINCOLOR_ROYAL + "BRONZE", // SKINCOLOR_BRONZE + "COPPER", // SKINCOLOR_COPPER + "YELLOW", // SKINCOLOR_YELLOW + "MUSTARD", // SKINCOLOR_MUSTARD + "BANANA", // SKINCOLOR_BANANA + "OLIVE", // SKINCOLOR_OLIVE + "CROCODILE", // SKINCOLOR_CROCODILE + "PERIDOT", // SKINCOLOR_PERIDOT + "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 + "TURTLE", // SKINCOLOR_TURTLE + "SWAMP", // SKINCOLOR_SWAMP + "DREAM", // SKINCOLOR_DREAM + "PLAGUE", // SKINCOLOR_PLAGUE + "EMERALD", // SKINCOLOR_EMERALD + "ALGAE", // SKINCOLOR_ALGAE + "CARIBBEAN", // SKINCOLOR_CARIBBEAN + "AZURE", // SKINCOLOR_AZURE + "AQUAMARINE", // SKINCOLOR_AQUAMARINE + "TURQUOISE", // SKINCOLOR_TURQUOISE + "TEAL", // SKINCOLOR_TEAL + "CYAN", // SKINCOLOR_CYAN + "JAWZ", // SKINCOLOR_JAWZ + "CERULEAN", // SKINCOLOR_CERULEAN + "NAVY", // SKINCOLOR_NAVY + "PLATINUM", // SKINCOLOR_PLATINUM + "SLATE", // SKINCOLOR_SLATE + "STEEL", // SKINCOLOR_STEEL + "THUNDER", // SKINCOLOR_THUNDER + "NOVA", // SKINCOLOR_NOVA + "RUST", // SKINCOLOR_RUST + "WRISTWATCH", // SKINCOLOR_WRISTWATCH + "JET", // SKINCOLOR_JET + "SAPPHIRE", // SKINCOLOR_SAPPHIRE + "ULTRAMARINE", // SKINCOLOR_ULTRAMARINE + "PERIWINKLE", // SKINCOLOR_PERIWINKLE + "BLUE", // SKINCOLOR_BLUE + "BLUEBERRY", // SKINCOLOR_BLUEBERRY + "THISTLE", // SKINCOLOR_THISTLE + "PURPLE", // SKINCOLOR_PURPLE + "PASTEL", // SKINCOLOR_PASTEL + "MOONSLAM", // SKINCOLOR_MOONSLAM + "DUSK", // SKINCOLOR_DUSK + "BUBBLEGUM", // SKINCOLOR_BUBBLEGUM + "MAGENTA", // SKINCOLOR_MAGENTA + "FUCHSIA", // SKINCOLOR_FUCHSIA + "TOXIC", // SKINCOLOR_TOXIC + "MAUVE", // SKINCOLOR_MAUVE + "LAVENDER", // SKINCOLOR_LAVENDER + "BYZANTIUM", // SKINCOLOR_BYZANTIUM + "POMEGRANATE", // SKINCOLOR_POMEGRANATE + "LILAC", // SKINCOLOR_LILAC + "TAFFY", // SKINCOLOR_TAFFY - // 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[] = { @@ -8254,6 +8350,7 @@ static const char *const POWERS_LIST[] = { "INGOOP" // In goop }; +#ifdef HAVE_BLUA static const char *const KARTSTUFF_LIST[] = { "POSITION", "OLDPOSITION", @@ -8262,6 +8359,7 @@ static const char *const KARTSTUFF_LIST[] = { "NEXTCHECK", "WAYPOINT", "STARPOSTWP", + "STARPOSTFLIP", "RESPAWN", "DROPDASH", @@ -8335,8 +8433,10 @@ static const char *const KARTSTUFF_LIST[] = { "ITEMBLINKMODE", "GETSPARKS", "JAWZTARGETDELAY", - "SPECTATEWAIT" + "SPECTATEWAIT", + "GROWCANCEL" }; +#endif static const char *const HUDITEMS_LIST[] = { "LIVESNAME", @@ -8409,6 +8509,7 @@ struct { // doomdef.h constants {"TICRATE",TICRATE}, + {"MUSICRATE",MUSICRATE}, {"RING_DIST",RING_DIST}, {"PUSHACCEL",PUSHACCEL}, {"MODID",MODID}, // I don't know, I just thought it would be cool for a wad to potentially know what mod it was loaded into. @@ -8559,34 +8660,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 - {"CA_NONE",CA_NONE}, // now slot 0! - {"CA_THOK",CA_THOK}, - {"CA_FLY",CA_FLY}, - {"CA_GLIDEANDCLIMB",CA_GLIDEANDCLIMB}, - {"CA_HOMINGTHOK",CA_HOMINGTHOK}, - {"CA_DOUBLEJUMP",CA_DOUBLEJUMP}, - {"CA_FLOAT",CA_FLOAT}, - {"CA_SLOWFALL",CA_SLOWFALL}, - {"CA_SWIM",CA_SWIM}, - {"CA_TELEKINESIS",CA_TELEKINESIS}, - {"CA_FALLSWITCH",CA_FALLSWITCH}, - {"CA_JUMPBOOST",CA_JUMPBOOST}, - {"CA_AIRDRILL",CA_AIRDRILL}, - {"CA_JUMPTHOK",CA_JUMPTHOK}, - // Secondary - {"CA2_NONE",CA2_NONE}, // now slot 0! - {"CA2_SPINDASH",CA2_SPINDASH}, - {"CA2_MULTIABILITY",CA2_MULTIABILITY}, // Sound flags {"SF_TOTALLYSINGLE",SF_TOTALLYSINGLE}, @@ -8813,11 +8887,11 @@ struct { {"V_SKYMAP",V_SKYMAP}, {"V_LAVENDERMAP",V_LAVENDERMAP}, {"V_GOLDMAP",V_GOLDMAP}, - {"V_TEAMAP",V_TEAMAP}, - {"V_STEELMAP",V_STEELMAP}, + {"V_AQUAMAP",V_AQUAMAP}, + {"V_MAGENTAMAP",V_MAGENTAMAP}, {"V_PINKMAP",V_PINKMAP}, {"V_BROWNMAP",V_BROWNMAP}, - {"V_PEACHMAP",V_PEACHMAP}, + {"V_TANMAP",V_TANMAP}, {"V_TRANSLUCENT",V_TRANSLUCENT}, {"V_10TRANS",V_10TRANS}, {"V_20TRANS",V_20TRANS}, @@ -9042,20 +9116,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; } @@ -9738,7 +9798,7 @@ static inline int lib_getenum(lua_State *L) // DYNAMIC variables too!! // Try not to add anything that would break netgames or timeattack replays here. - // You know, like consoleplayer, displayplayer, secondarydisplayplayer, or gametime. + // You know, like consoleplayer, displayplayers, or gametime. if (fastcmp(word,"gamemap")) { lua_pushinteger(L, gamemap); return 1; @@ -9811,8 +9871,11 @@ static inline int lib_getenum(lua_State *L) } else if (fastcmp(word,"mapmusflags")) { lua_pushinteger(L, mapmusflags); return 1; + } else if (fastcmp(word,"mapmusposition")) { + lua_pushinteger(L, mapmusposition); + return 1; } else if (fastcmp(word,"server")) { - if ((!multiplayer || !netgame) && !playeringame[serverplayer]) + if ((!multiplayer || !(netgame || demo.playback)) && !playeringame[serverplayer]) return 0; LUA_PushUserdata(L, &players[serverplayer], META_PLAYER); return 1; @@ -9852,6 +9915,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; @@ -9861,6 +9927,9 @@ static inline int lib_getenum(lua_State *L) } else if (fastcmp(word,"mapobjectscale")) { lua_pushinteger(L, mapobjectscale); return 1; + } else if (fastcmp(word,"numlaps")) { + lua_pushinteger(L, cv_numlaps.value); + return 1; } return 0; } diff --git a/src/djgppdos/i_sound.c b/src/djgppdos/i_sound.c index 52c90aac2..88b598622 100644 --- a/src/djgppdos/i_sound.c +++ b/src/djgppdos/i_sound.c @@ -438,6 +438,37 @@ boolean I_SetSongSpeed(float speed) return false; } +/// ------------------------ +// MUSIC SEEKING +/// ------------------------ + +UINT32 I_GetSongLength(void) +{ + return 0; +} + +boolean I_SetSongLoopPoint(UINT32 looppoint) +{ + (void)looppoint; + return false; +} + +UINT32 I_GetSongLoopPoint(void) +{ + return 0; +} + +boolean I_SetSongPosition(UINT32 position) +{ + (void)position; + return false; +} + +UINT32 I_GetSongPosition(void) +{ + return 0; +} + /// ------------------------ // MUSIC PLAYBACK /// ------------------------ @@ -545,3 +576,44 @@ int I_QrySongPlaying(int handle) return (midi_pos==-1); } #endif + +/// ------------------------ +// MUSIC FADING +/// ------------------------ + +void I_SetInternalMusicVolume(UINT8 volume) +{ + (void)volume; +} + +void I_StopFadingSong(void) +{ +} + +boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void)); +{ + (void)target_volume; + (void)source_volume; + (void)ms; + return false; +} + +boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void)); +{ + (void)target_volume; + (void)ms; + return false; +} + +boolean I_FadeOutStopSong(UINT32 ms) +{ + (void)ms; + return false; +} + +boolean I_FadeInPlaySong(UINT32 ms, boolean looping) +{ + (void)ms; + (void)looping; + return false; +} diff --git a/src/djgppdos/i_video.c b/src/djgppdos/i_video.c index 612c72215..7829acbb9 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 ab863c6f6..4d3934773 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -149,20 +149,21 @@ extern FILE *logstream; // most interface strings are ignored in development mode. // we use comprevision and compbranch instead. #else -#define VERSION 100 // Game version -#define SUBVERSION 3 // more precise version number -#define VERSIONSTRING "v1.0.3" -#define VERSIONSTRINGW L"v1.0.3" -// Hey! If you change this, add 1 to the MODVERSION below! -// Otherwise we can't force updates! +#define VERSION 110 // Game version +#define SUBVERSION 0 // more precise version number +#define VERSIONSTRING "v1.1" +#define VERSIONSTRINGW L"v1.1" +// Hey! If you change this, add 1 to the MODVERSION below! Otherwise we can't force updates! +// And change CMakeLists.txt, for CMake users! +// AND appveyor.yml, for the build bots! #endif +// Maintain compatibility with 1.0.x record attack replays? +#define DEMO_COMPAT_100 + // Does this version require an added patch file? // Comment or uncomment this as necessary. -//#define USE_PATCH_DTA - -// Kart has it's own, as well. -#define USE_PATCH_KART +//#define USE_PATCH_FILE // Use .kart extension addons #define USE_KART @@ -221,7 +222,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 3 +#define MODVERSION 5 // Filter consvars by version // To version config.cfg, MAJOREXECVERSION is set equal to MODVERSION automatically. @@ -244,7 +245,7 @@ extern FILE *logstream; // NOTE: it needs more than this to increase the number of players... #define MAXPLAYERS 16 -#define MAXSKINS 64 +#define MAXSKINS 128 #define PLAYERSMASK (MAXPLAYERS-1) #define MAXPLAYERNAME 21 @@ -256,89 +257,170 @@ typedef enum SKINCOLOR_GREY, SKINCOLOR_NICKEL, SKINCOLOR_BLACK, + SKINCOLOR_FAIRY, + SKINCOLOR_POPCORN, + SKINCOLOR_ARTICHOKE, + SKINCOLOR_PIGEON, SKINCOLOR_SEPIA, SKINCOLOR_BEIGE, + SKINCOLOR_CARAMEL, + SKINCOLOR_PEACH, SKINCOLOR_BROWN, SKINCOLOR_LEATHER, SKINCOLOR_SALMON, SKINCOLOR_PINK, SKINCOLOR_ROSE, + SKINCOLOR_CINNAMON, SKINCOLOR_RUBY, SKINCOLOR_RASPBERRY, SKINCOLOR_RED, SKINCOLOR_CRIMSON, + SKINCOLOR_MAROON, + SKINCOLOR_LEMONADE, + SKINCOLOR_SCARLET, SKINCOLOR_KETCHUP, SKINCOLOR_DAWN, + SKINCOLOR_SUNSET, SKINCOLOR_CREAMSICLE, SKINCOLOR_ORANGE, - SKINCOLOR_PUMPKIN, SKINCOLOR_ROSEWOOD, - SKINCOLOR_BURGUNDY, SKINCOLOR_TANGERINE, - SKINCOLOR_PEACH, - SKINCOLOR_CARAMEL, + SKINCOLOR_TAN, + SKINCOLOR_CREAM, SKINCOLOR_GOLD, + SKINCOLOR_ROYAL, SKINCOLOR_BRONZE, + SKINCOLOR_COPPER, SKINCOLOR_YELLOW, SKINCOLOR_MUSTARD, + SKINCOLOR_BANANA, SKINCOLOR_OLIVE, + SKINCOLOR_CROCODILE, + SKINCOLOR_PERIDOT, 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_TURTLE, SKINCOLOR_SWAMP, SKINCOLOR_DREAM, - SKINCOLOR_AQUA, + SKINCOLOR_PLAGUE, + SKINCOLOR_EMERALD, + SKINCOLOR_ALGAE, + SKINCOLOR_CARIBBEAN, + SKINCOLOR_AZURE, + SKINCOLOR_AQUAMARINE, + SKINCOLOR_TURQUOISE, SKINCOLOR_TEAL, SKINCOLOR_CYAN, SKINCOLOR_JAWZ, // Oni's torment SKINCOLOR_CERULEAN, SKINCOLOR_NAVY, + SKINCOLOR_PLATINUM, SKINCOLOR_SLATE, SKINCOLOR_STEEL, + SKINCOLOR_THUNDER, + SKINCOLOR_NOVA, + SKINCOLOR_RUST, + SKINCOLOR_WRISTWATCH, SKINCOLOR_JET, SKINCOLOR_SAPPHIRE, // sweet mother, i cannot weave - slender aphrodite has overcome me with longing for a girl + SKINCOLOR_ULTRAMARINE, SKINCOLOR_PERIWINKLE, SKINCOLOR_BLUE, SKINCOLOR_BLUEBERRY, - SKINCOLOR_DUSK, + SKINCOLOR_THISTLE, SKINCOLOR_PURPLE, + SKINCOLOR_PASTEL, + SKINCOLOR_MOONSLAM, + SKINCOLOR_DUSK, + SKINCOLOR_BUBBLEGUM, + SKINCOLOR_MAGENTA, + SKINCOLOR_FUCHSIA, + SKINCOLOR_TOXIC, + SKINCOLOR_MAUVE, SKINCOLOR_LAVENDER, SKINCOLOR_BYZANTIUM, SKINCOLOR_POMEGRANATE, SKINCOLOR_LILAC, + SKINCOLOR_TAFFY, - // 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; @@ -348,6 +430,8 @@ typedef enum #define NEWTICRATERATIO 1 // try 4 for 140 fps :) #define NEWTICRATE (TICRATE*NEWTICRATERATIO) +#define MUSICRATE 1000 // sound timing is calculated by milliseconds + #define RING_DIST 1280*FRACUNIT // how close you need to be to a ring to attract it #define PUSHACCEL (2*FRACUNIT) // Acceleration for MF2_SLIDEPUSH items. @@ -471,13 +555,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. @@ -527,9 +615,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Polyobject fake flat code #define POLYOBJECTS_PLANES -/// Improved way of dealing with ping values and a ping limit. -#define NEWPING - /// See name of player in your crosshair #define SEENAMES diff --git a/src/doomstat.h b/src/doomstat.h index 9ae2726d7..1f855da27 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -33,8 +33,10 @@ extern INT16 gamemap; extern char mapmusname[7]; extern UINT16 mapmusflags; +extern UINT32 mapmusposition; #define MUSIC_TRACKMASK 0x0FFF // ----************ #define MUSIC_RELOADRESET 0x8000 // *--------------- +#define MUSIC_FORCERESET 0x4000 // -*-------------- // Use other bits if necessary. extern INT16 maptol; @@ -57,7 +59,6 @@ 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; @@ -78,7 +79,10 @@ extern boolean addedtogame; // true after the server has added you extern boolean multiplayer; extern INT16 gametype; + +#define MAXSPLITSCREENPLAYERS 4 // Max number of players on a single computer extern UINT8 splitscreen; + extern boolean circuitmap; // Does this level have 'circuit mode'? extern boolean fromlevelselect; extern boolean forceresetplayers, deferencoremode; @@ -107,14 +111,8 @@ extern UINT8 window_notinfocus; // are we in focus? (backend independant -- hand extern boolean nodrawers; extern boolean noblit; extern boolean lastdraw; -extern postimg_t postimgtype; -extern INT32 postimgparam; -extern postimg_t postimgtype2; -extern INT32 postimgparam2; -extern postimg_t postimgtype3; -extern INT32 postimgparam3; -extern postimg_t postimgtype4; -extern INT32 postimgparam4; +extern postimg_t postimgtype[MAXSPLITSCREENPLAYERS]; +extern INT32 postimgparam[MAXSPLITSCREENPLAYERS]; extern INT32 viewwindowx, viewwindowy; extern INT32 viewwidth, scaledviewwidth; @@ -123,10 +121,7 @@ extern boolean gamedataloaded; // Player taking events, and displaying. extern INT32 consoleplayer; -extern INT32 displayplayer; -extern INT32 secondarydisplayplayer; // for splitscreen -extern INT32 thirddisplayplayer; -extern INT32 fourthdisplayplayer; +extern INT32 displayplayers[MAXSPLITSCREENPLAYERS]; // Maps of special importance extern INT16 spstage_start; @@ -157,6 +152,7 @@ typedef struct char musswitch[7]; UINT16 musswitchflags; + UINT32 musswitchposition; UINT8 fadecolor; // Color number for fade, 0 means don't do the first fade UINT8 fadeinid; // ID of the first fade, to a color -- ignored if fadecolor is 0 @@ -228,6 +224,7 @@ typedef struct INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end. char musname[7]; ///< Music track to play. "" for no music. UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore. + UINT32 muspos; ///< Music position to jump to. char forcecharacter[17]; ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable. UINT8 weather; ///< 0 = sunny day, 1 = storm, 2 = snow, 3 = rain, 4 = blank, 5 = thunder w/o rain, 6 = rain w/o lightning, 7 = heat wave. INT16 skynum; ///< Sky number to use. @@ -261,6 +258,10 @@ typedef struct //boolean automap; ///< Displays a level's white map outline in modified games fixed_t mobj_scale; ///< Replacement for TOL_ERZ3 + // Music stuff. + UINT32 musinterfadeout; ///< Fade out level music on intermission screen in milliseconds + char musintername[7]; ///< Intermission screen music. + // Lua stuff. // (This is not ifdeffed so the map header structure can stay identical, just in case.) UINT8 numCustomOptions; ///< Internal. For Lua custom value support. @@ -331,7 +332,10 @@ enum GameType // SRB2Kart GT_HIDEANDSEEK, GT_CTF }; -// If you alter this list, update gametype_cons_t in m_menu.c +// If you alter this list, update dehacked.c, and Gametype_Names in g_game.c + +// String names for gametypes +extern const char *Gametype_Names[NUMGAMETYPES]; extern tic_t totalplaytime; extern UINT32 matchesplayed; @@ -468,6 +472,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; @@ -543,9 +548,7 @@ extern consvar_t cv_forceskin; // force clients to use the server's skin extern consvar_t cv_downloading; // allow clients to downloading WADs. extern consvar_t cv_nettimeout; // SRB2Kart: Advanced server options menu extern consvar_t cv_jointimeout; -#ifdef NEWPING extern consvar_t cv_maxping; -#endif extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS]; extern INT32 serverplayer; extern INT32 adminplayers[MAXPLAYERS]; diff --git a/src/doomtype.h b/src/doomtype.h index 5d6434845..67491abb3 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -366,16 +366,18 @@ size_t strlcpy(char *dst, const char *src, size_t siz); /* Miscellaneous types that don't fit anywhere else (Can this be changed?) */ +typedef struct +{ + UINT8 red; + UINT8 green; + UINT8 blue; + UINT8 alpha; +} byteColor_t; + union FColorRGBA { UINT32 rgba; - struct - { - UINT8 red; - UINT8 green; - UINT8 blue; - UINT8 alpha; - } s; + byteColor_t s; } ATTRPACK; typedef union FColorRGBA RGBA_t; diff --git a/src/dummy/i_sound.c b/src/dummy/i_sound.c index 7275bb1ae..f09158e01 100644 --- a/src/dummy/i_sound.c +++ b/src/dummy/i_sound.c @@ -95,6 +95,37 @@ boolean I_SetSongSpeed(float speed) return false; } +/// ------------------------ +// MUSIC SEEKING +/// ------------------------ + +UINT32 I_GetSongLength(void) +{ + return 0; +} + +boolean I_SetSongLoopPoint(UINT32 looppoint) +{ + (void)looppoint; + return false; +} + +UINT32 I_GetSongLoopPoint(void) +{ + return 0; +} + +boolean I_SetSongPosition(UINT32 position) +{ + (void)position; + return false; +} + +UINT32 I_GetSongPosition(void) +{ + return 0; +} + /// ------------------------ // MUSIC PLAYBACK /// ------------------------ @@ -142,4 +173,45 @@ boolean I_SetSongTrack(int track) { (void)track; return false; -} \ No newline at end of file +} + +/// ------------------------ +// MUSIC FADING +/// ------------------------ + +void I_SetInternalMusicVolume(UINT8 volume) +{ + (void)volume; +} + +void I_StopFadingSong(void) +{ +} + +boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void)); +{ + (void)target_volume; + (void)source_volume; + (void)ms; + return false; +} + +boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void)); +{ + (void)target_volume; + (void)ms; + return false; +} + +boolean I_FadeOutStopSong(UINT32 ms) +{ + (void)ms; + return false; +} + +boolean I_FadeInPlaySong(UINT32 ms, boolean looping) +{ + (void)ms; + (void)looping; + return false; +} diff --git a/src/f_finale.c b/src/f_finale.c index b863ea74b..0932550fd 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -173,7 +173,7 @@ static void F_SkyScroll(INT32 scrollspeed) // SRB2Kart: F_DrawPatchCol is over-engineered; recoded to be less shitty and error-prone if (rendermode != render_none) { - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 120); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0); x = -((INT32)animtimer); y = 0; @@ -238,7 +238,6 @@ void F_StartIntro(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); - CON_ClearHUD(); F_NewCutscene(introtext[0]); intro_scenenum = 0; @@ -275,7 +274,7 @@ static void F_IntroDrawScene(void) highres = true; } - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 120); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0); if (background) { @@ -460,9 +459,13 @@ static const char *credits[] = { "\"ZarroTsu\"", "", "\1External Artists", + "\"1-Up Mason\"", + "\"Chengi\"", "\"Chrispy\"", "\"DirkTheHusky\"", + "\"LJSTAR\"", "\"MotorRoach\"", + "\"Mr. McScrewup\"", "\"Nev3r\"", "\"Ritz\"", "\"Rob\"", @@ -471,6 +474,7 @@ static const char *credits[] = { "\"Spherallic\"", "\"VAdaPEGA\"", "\"Virt\"", + "\"Voltrix\"", "\"zxyspku\"", "", "\1Sound Design", @@ -580,10 +584,10 @@ void F_StartCredits(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); - CON_ClearHUD(); S_StopMusic(); S_ChangeMusicInternal("credit", false); + S_ShowMusicCredit(); finalecount = 0; animtimer = 0; @@ -611,7 +615,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 } @@ -643,16 +647,34 @@ void F_CreditDrawer(void) if (((y>>FRACBITS) * vid.dupy) > vid.height) break; } +} +void F_CreditTicker(void) +{ + // "Simulate" the drawing of the credits so that dedicated mode doesn't get stuck + UINT16 i; + fixed_t y = (80< vid.height) + break; + } + + // Do this here rather than in the drawer you doofus! (this is why dedicated mode broke at credits) if (!credits[i] && y <= 120<scene[scenenum].ycoord[picnum]; if (cutscenes[cutnum]->scene[scenenum].musswitch[0]) - S_ChangeMusic(cutscenes[cutnum]->scene[scenenum].musswitch, + S_ChangeMusicEx(cutscenes[cutnum]->scene[scenenum].musswitch, cutscenes[cutnum]->scene[scenenum].musswitchflags, - cutscenes[cutnum]->scene[scenenum].musicloop); + cutscenes[cutnum]->scene[scenenum].musicloop, + cutscenes[cutnum]->scene[scenenum].musswitchposition, 0, 0); // Fade to the next dofadenow = true; @@ -1326,8 +1371,6 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset F_NewCutscene(cutscenes[cutscenenum]->scene[0].text); - CON_ClearHUD(); - cutsceneover = false; runningprecutscene = precutscene; precutresetplayer = resetplayer; @@ -1348,9 +1391,10 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset stoptimer = 0; if (cutscenes[cutnum]->scene[0].musswitch[0]) - S_ChangeMusic(cutscenes[cutnum]->scene[0].musswitch, + S_ChangeMusicEx(cutscenes[cutnum]->scene[0].musswitch, cutscenes[cutnum]->scene[0].musswitchflags, - cutscenes[cutnum]->scene[0].musicloop); + cutscenes[cutnum]->scene[0].musicloop, + cutscenes[cutnum]->scene[scenenum].musswitchposition, 0, 0); else S_StopMusic(); } diff --git a/src/filesrch.c b/src/filesrch.c index 0276e1c90..d132e9fb4 100644 --- a/src/filesrch.c +++ b/src/filesrch.c @@ -341,8 +341,6 @@ size_t dir_on[menudepth]; UINT8 refreshdirmenu = 0; char *refreshdirname = NULL; -size_t packetsizetally = 0; -size_t mainwadstally = 0; #if defined (_XBOX) && defined (_MSC_VER) filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *wantedmd5sum, @@ -368,9 +366,10 @@ void searchfilemenu(char *tempname) return; } -boolean preparefilemenu(boolean samedepth) +boolean preparefilemenu(boolean samedepth, boolean replayhut) { (void)samedepth; + (void)replayhut; return false; } @@ -437,9 +436,10 @@ void searchfilemenu(char *tempname) return; } -boolean preparefilemenu(boolean samedepth) +boolean preparefilemenu(boolean samedepth, boolean replayhut) { (void)samedepth; + (void)replayhut; return false; } @@ -710,7 +710,7 @@ void searchfilemenu(char *tempname) } } -boolean preparefilemenu(boolean samedepth) +boolean preparefilemenu(boolean samedepth, boolean replayhut) { DIR *dirhandle; struct dirent *dent; @@ -759,9 +759,13 @@ boolean preparefilemenu(boolean samedepth) { if (!S_ISDIR(fsstat.st_mode)) // file { - if (!cv_addons_showall.value) + size_t len = strlen(dent->d_name)+1; + if (replayhut) + { + if (strcasecmp(".lmp", dent->d_name+len-5)) continue; // Not a replay + } + else if (!cv_addons_showall.value) { - size_t len = strlen(dent->d_name)+1; UINT8 ext; for (ext = 0; ext < NUM_EXT_TABLE; ext++) if (!strcasecmp(exttable[ext]+1, dent->d_name+len-(exttable[ext][0]))) break; // extension comparison @@ -829,40 +833,49 @@ boolean preparefilemenu(boolean samedepth) if (!S_ISDIR(fsstat.st_mode)) // file { if (!((numfolders+pos) < sizecoredirmenu)) continue; // crash prevention - for (; ext < NUM_EXT_TABLE; ext++) - if (!strcasecmp(exttable[ext]+1, dent->d_name+len-(exttable[ext][0]))) break; // extension comparison - if (ext == NUM_EXT_TABLE && !cv_addons_showall.value) continue; // not an addfile-able (or exec-able) file - ext += EXT_START; // moving to be appropriate position - if (ext >= EXT_LOADSTART) + if (replayhut) { - size_t i; - for (i = 0; i < numwadfiles; i++) + if (strcasecmp(".lmp", dent->d_name+len-5)) continue; // Not a replay + ext = EXT_TXT; // This isn't used anywhere but better safe than sorry for messing with this... + } + else + { + for (; ext < NUM_EXT_TABLE; ext++) + if (!strcasecmp(exttable[ext]+1, dent->d_name+len-(exttable[ext][0]))) break; // extension comparison + if (ext == NUM_EXT_TABLE && !cv_addons_showall.value) continue; // not an addfile-able (or exec-able) file + ext += EXT_START; // moving to be appropriate position + + if (ext >= EXT_LOADSTART) { - if (!filenamebuf[i][0]) + size_t i; + for (i = 0; i < numwadfiles; i++) { - strncpy(filenamebuf[i], wadfiles[i]->filename, MAX_WADPATH); - filenamebuf[i][MAX_WADPATH - 1] = '\0'; - nameonly(filenamebuf[i]); + if (!filenamebuf[i][0]) + { + strncpy(filenamebuf[i], wadfiles[i]->filename, MAX_WADPATH); + filenamebuf[i][MAX_WADPATH - 1] = '\0'; + nameonly(filenamebuf[i]); + } + + if (strcmp(dent->d_name, filenamebuf[i])) + continue; + if (cv_addons_md5.value && !checkfilemd5(menupath, wadfiles[i]->md5sum)) + continue; + + ext |= EXT_LOADED; } - - if (strcmp(dent->d_name, filenamebuf[i])) - continue; - if (cv_addons_md5.value && !checkfilemd5(menupath, wadfiles[i]->md5sum)) - continue; - - ext |= EXT_LOADED; } - } - else if (ext == EXT_TXT) - { - if (!strcmp(dent->d_name, "log.txt") || !strcmp(dent->d_name, "errorlog.txt")) + else if (ext == EXT_TXT) + { + if (!strcmp(dent->d_name, "log.txt") || !strcmp(dent->d_name, "errorlog.txt")) + ext |= EXT_LOADED; + } + + if (!strcmp(dent->d_name, configfile)) ext |= EXT_LOADED; } - if (!strcmp(dent->d_name, configfile)) - ext |= EXT_LOADED; - folder = 0; } else // directory @@ -881,6 +894,8 @@ boolean preparefilemenu(boolean samedepth) strcpy(temp+len, PATHSEP); coredirmenu[folderpos++] = temp; } + else if (replayhut) // Reverse-alphabetical on just the files; acts as a fake "most recent first" with the current filename format + coredirmenu[sizecoredirmenu - 1 - pos++] = temp; else coredirmenu[numfolders + pos++] = temp; } diff --git a/src/filesrch.h b/src/filesrch.h index 01a528482..4cb92b238 100644 --- a/src/filesrch.h +++ b/src/filesrch.h @@ -42,9 +42,6 @@ extern size_t dir_on[menudepth]; extern UINT8 refreshdirmenu; extern char *refreshdirname; -extern size_t packetsizetally; -extern size_t mainwadstally; - typedef enum { EXT_FOLDER = 0, @@ -94,6 +91,6 @@ typedef enum void closefilemenu(boolean validsize); void searchfilemenu(char *tempname); -boolean preparefilemenu(boolean samedepth); +boolean preparefilemenu(boolean samedepth, boolean replayhut); #endif // __FILESRCH_H__ diff --git a/src/g_game.c b/src/g_game.c index 3944c02aa..d9dd3e3cb 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -14,6 +14,7 @@ #include "doomdef.h" #include "console.h" #include "d_main.h" +#include "d_clisrv.h" #include "d_player.h" #include "f_finale.h" #include "filesrch.h" // for refreshdirmenu @@ -77,6 +78,7 @@ static void G_DoStartVote(void); char mapmusname[7]; // Music name UINT16 mapmusflags; // Track and reset bit +UINT32 mapmusposition; // Position to jump to INT16 gamemap = 1; INT16 maptol; @@ -92,7 +94,6 @@ boolean majormods = false; // Set if Lua/Gameplay SOC/replacement map has been a boolean savemoddata = false; UINT8 paused; UINT8 modeattacking = ATTACKING_NONE; -boolean disableSpeedAdjust = true; boolean imcontinuing = false; boolean runemeraldmanager = false; @@ -101,7 +102,6 @@ UINT8 numDemos = 0; //3; -- i'm FED UP of losing my skincolour to a broken UINT32 demoDelayTime = 15*TICRATE; UINT32 demoIdleTime = 3*TICRATE; -boolean timingdemo; // if true, exit with report on completion boolean nodrawers; // for comparative timing purposes boolean noblit; // for comparative timing purposes static tic_t demostarttime; // for comparative timing purposes @@ -113,10 +113,7 @@ boolean addedtogame; player_t players[MAXPLAYERS]; INT32 consoleplayer; // player taking events and displaying -INT32 displayplayer; // view being displayed -INT32 secondarydisplayplayer; // for splitscreen -INT32 thirddisplayplayer; -INT32 fourthdisplayplayer; +INT32 displayplayers[MAXSPLITSCREENPLAYERS]; // view being displayed tic_t gametic; tic_t levelstarttic; // gametic at level start @@ -267,6 +264,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 @@ -286,20 +284,16 @@ UINT32 timesBeaten; UINT32 timesBeatenWithEmeralds; //UINT32 timesBeatenUltimate; -static char demoname[64]; -boolean demorecording; -boolean demoplayback; -boolean titledemo; // Title Screen demo can be cancelled by any key -boolean fromtitledemo; // SRB2Kart: Don't stop the music +//@TODO put these all in a struct for namespacing purposes? +static char demoname[128]; static UINT8 *demobuffer = NULL; -static UINT8 *demo_p, *demotime_p; +static UINT8 *demo_p, *demotime_p, *demoinfo_p; static UINT8 *demoend; static UINT8 demoflags; -static UINT16 demoversion; -boolean singledemo; // quit after playing a demo from cmdline -boolean demo_start; // don't start playing demo right away static boolean demosynced = true; // console warning message +struct demovars_s demo; + boolean metalrecording; // recording as metal sonic mobj_t *metalplayback; static UINT8 *metalbuffer = NULL; @@ -316,10 +310,15 @@ static struct { // EZT_SCALE fixed_t scale, lastscale; + // EZT_KART + INT32 kartitem, kartamount, kartbumpers; + + UINT8 desyncframes; // Don't try to resync unless we've been off for two frames, to monkeypatch a few trouble spots + // EZT_HIT UINT16 hits; mobj_t **hitlist; -} ghostext; +} ghostext[MAXPLAYERS]; // Your naming conventions are stupid and useless. // There is no conflict here. @@ -329,6 +328,12 @@ boolean precache = true; // if true, load all graphics at start INT16 prevmap, nextmap; +static CV_PossibleValue_t recordmultiplayerdemos_cons_t[] = {{0, "Disabled"}, {1, "Manual Save"}, {2, "Auto Save"}, {0, NULL}}; +consvar_t cv_recordmultiplayerdemos = {"netdemo_record", "Manual Save", CV_SAVE, recordmultiplayerdemos_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + +static CV_PossibleValue_t netdemosyncquality_cons_t[] = {{1, "MIN"}, {35, "MAX"}, {0, NULL}}; +consvar_t cv_netdemosyncquality = {"netdemo_syncquality", "1", CV_SAVE, netdemosyncquality_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + static UINT8 *savebuffer; // Analog Control @@ -406,6 +411,8 @@ static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, #endif #endif +static CV_PossibleValue_t deadzone_cons_t[] = {{0, "MIN"}, {FRACUNIT, "MAX"}, {0, NULL}}; + // don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler. // it automatically becomes compact with 20+ players, but if you like it, I guess you can turn that on! @@ -437,6 +444,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}; @@ -473,6 +483,7 @@ consvar_t cv_aimaxis = {"joyaxis_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, consvar_t cv_lookaxis = {"joyaxis_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_fireaxis = {"joyaxis_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_driftaxis = {"joyaxis_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_deadzone = {"joy_deadzone", "0.5", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_turnaxis2 = {"joyaxis2_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_moveaxis2 = {"joyaxis2_move", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -481,6 +492,7 @@ consvar_t cv_aimaxis2 = {"joyaxis2_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL consvar_t cv_lookaxis2 = {"joyaxis2_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_fireaxis2 = {"joyaxis2_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_driftaxis2 = {"joyaxis2_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_deadzone2 = {"joy2_deadzone", "0.5", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_turnaxis3 = {"joyaxis3_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_moveaxis3 = {"joyaxis3_move", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -489,6 +501,7 @@ consvar_t cv_aimaxis3 = {"joyaxis3_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL consvar_t cv_lookaxis3 = {"joyaxis3_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_fireaxis3 = {"joyaxis3_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_driftaxis3 = {"joyaxis3_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_deadzone3 = {"joy3_deadzone", "0.5", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_turnaxis4 = {"joyaxis4_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_moveaxis4 = {"joyaxis4_move", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -497,6 +510,7 @@ consvar_t cv_aimaxis4 = {"joyaxis4_aim", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL consvar_t cv_lookaxis4 = {"joyaxis4_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_fireaxis4 = {"joyaxis4_fire", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_driftaxis4 = {"joyaxis4_drift", "Z-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_deadzone4 = {"joy4_deadzone", "0.5", CV_FLOAT|CV_SAVE, deadzone_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; #if MAXPLAYERS > 16 @@ -922,8 +936,8 @@ static INT32 Joy1Axis(axis_input_e axissel) retaxis = +JOYAXISRANGE; if (!Joystick.bGamepadStyle && axissel < AXISDEAD) { - const INT32 jdeadzone = JOYAXISRANGE/4; - if (-jdeadzone < retaxis && retaxis < jdeadzone) + const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone.value) >> FRACBITS; + if (abs(retaxis) <= jdeadzone) return 0; } if (flp) retaxis = -retaxis; //flip it around @@ -1003,7 +1017,7 @@ static INT32 Joy2Axis(axis_input_e axissel) retaxis = +JOYAXISRANGE; if (!Joystick2.bGamepadStyle && axissel < AXISDEAD) { - const INT32 jdeadzone = JOYAXISRANGE/4; + const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone2.value) >> FRACBITS; if (-jdeadzone < retaxis && retaxis < jdeadzone) return 0; } @@ -1084,7 +1098,7 @@ static INT32 Joy3Axis(axis_input_e axissel) retaxis = +JOYAXISRANGE; if (!Joystick3.bGamepadStyle && axissel < AXISDEAD) { - const INT32 jdeadzone = JOYAXISRANGE/4; + const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone3.value) >> FRACBITS; if (-jdeadzone < retaxis && retaxis < jdeadzone) return 0; } @@ -1164,7 +1178,7 @@ static INT32 Joy4Axis(axis_input_e axissel) retaxis = +JOYAXISRANGE; if (!Joystick4.bGamepadStyle && axissel < AXISDEAD) { - const INT32 jdeadzone = JOYAXISRANGE/4; + const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone4.value) >> FRACBITS; if (-jdeadzone < retaxis && retaxis < jdeadzone) return 0; } @@ -1210,9 +1224,9 @@ INT32 JoyAxis(axis_input_e axissel, UINT8 p) // // set secondaryplayer true to build player 2's ticcmd in splitscreen mode // -INT32 localaiming, localaiming2, localaiming3, localaiming4; -angle_t localangle, localangle2, localangle3, localangle4; -boolean camspin, camspin2, camspin3, camspin4; +INT32 localaiming[MAXSPLITSCREENPLAYERS]; +angle_t localangle[MAXSPLITSCREENPLAYERS]; +boolean camspin[MAXSPLITSCREENPLAYERS]; static fixed_t forwardmove[2] = {25<>16, 50<>16}; static fixed_t sidemove[2] = {2<>16, 4<>16}; @@ -1229,51 +1243,40 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) camera_t *thiscam; angle_t lang; - static INT32 turnheld, turnheld2, turnheld3, turnheld4; // for accelerative turning - static boolean keyboard_look, keyboard_look2, keyboard_look3, keyboard_look4; // true if lookup/down using keyboard - static boolean resetdown, resetdown2, resetdown3, resetdown4; // don't cam reset every frame + static INT32 turnheld[MAXSPLITSCREENPLAYERS]; // for accelerative turning + static boolean keyboard_look[MAXSPLITSCREENPLAYERS]; // true if lookup/down using keyboard + static boolean resetdown[MAXSPLITSCREENPLAYERS]; // don't cam reset every frame + + if (demo.playback) return; + + if (ssplayer == 1) + player = &players[consoleplayer]; + else + player = &players[displayplayers[ssplayer-1]]; + + if (ssplayer == 2) + thiscam = (player->bot == 2 ? &camera[0] : &camera[ssplayer-1]); + else + thiscam = &camera[ssplayer-1]; + lang = localangle[ssplayer-1]; + laim = localaiming[ssplayer-1]; + th = turnheld[ssplayer-1]; + kbl = keyboard_look[ssplayer-1]; + rd = resetdown[ssplayer-1]; switch (ssplayer) { case 2: - player = &players[secondarydisplayplayer]; - thiscam = (player->bot == 2 ? &camera : &camera2); - lang = localangle2; - laim = localaiming2; - th = turnheld2; - kbl = keyboard_look2; - rd = resetdown2; G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); break; case 3: - player = &players[thirddisplayplayer]; - thiscam = &camera3; - lang = localangle3; - laim = localaiming3; - th = turnheld3; - kbl = keyboard_look3; - rd = resetdown3; G_CopyTiccmd(cmd, I_BaseTiccmd3(), 1); break; case 4: - player = &players[fourthdisplayplayer]; - thiscam = &camera4; - lang = localangle4; - laim = localaiming4; - th = turnheld4; - kbl = keyboard_look4; - rd = resetdown4; G_CopyTiccmd(cmd, I_BaseTiccmd4(), 1); break; case 1: default: - player = &players[consoleplayer]; - thiscam = &camera; - lang = localangle; - laim = localaiming; - th = turnheld; - kbl = keyboard_look; - rd = resetdown; G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver break; } @@ -1564,42 +1567,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) if (!hu_stopped) { - switch (ssplayer) - { - case 2: - localangle2 = lang; - localaiming2 = laim; - keyboard_look2 = kbl; - turnheld2 = th; - resetdown2 = rd; - camspin2 = InputDown(gc_lookback, ssplayer); - break; - case 3: - localangle3 = lang; - localaiming3 = laim; - keyboard_look3 = kbl; - turnheld3 = th; - resetdown3 = rd; - camspin3 = InputDown(gc_lookback, ssplayer); - break; - case 4: - localangle4 = lang; - localaiming4 = laim; - keyboard_look4 = kbl; - turnheld4 = th; - resetdown4 = rd; - camspin4 = InputDown(gc_lookback, ssplayer); - break; - case 1: - default: - localangle = lang; - localaiming = laim; - keyboard_look = kbl; - turnheld = th; - resetdown = rd; - camspin = InputDown(gc_lookback, ssplayer); - break; - } + localangle[ssplayer-1] = lang; + localaiming[ssplayer-1] = laim; + keyboard_look[ssplayer-1] = kbl; + turnheld[ssplayer-1] = th; + resetdown[ssplayer-1] = rd; + camspin[ssplayer-1] = InputDown(gc_lookback, ssplayer); } /* Lua: Allow this hook to overwrite ticcmd. @@ -1619,8 +1592,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) //Reset away view if a command is given. if ((cmd->forwardmove || cmd->sidemove || cmd->buttons) - && displayplayer != consoleplayer && ssplayer == 1) - displayplayer = consoleplayer; + && displayplayers[0] != consoleplayer && ssplayer == 1) + displayplayers[0] = consoleplayer; } @@ -1772,27 +1745,24 @@ void G_DoLoadLevel(boolean resetplayer) if (!resetplayer) P_FindEmerald(); - displayplayer = consoleplayer; // view the guy you are playing - if (!splitscreen && !botingame) - secondarydisplayplayer = consoleplayer; - if (splitscreen < 2) - thirddisplayplayer = consoleplayer; - if (splitscreen < 3) - fourthdisplayplayer = consoleplayer; + displayplayers[0] = consoleplayer; // view the guy you are playing + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (i > 0 && !(i == 1 && botingame) && splitscreen < i) + displayplayers[i] = consoleplayer; + } gameaction = ga_nothing; #ifdef PARANOIA Z_CheckHeap(-2); #endif - if (camera.chase) - P_ResetCamera(&players[displayplayer], &camera); - if (camera2.chase && splitscreen) - P_ResetCamera(&players[secondarydisplayplayer], &camera2); - if (camera3.chase && splitscreen > 1) - P_ResetCamera(&players[thirddisplayplayer], &camera3); - if (camera4.chase && splitscreen > 2) - P_ResetCamera(&players[fourthdisplayplayer], &camera4); + for (i = 0; i <= splitscreen; i++) + { + if (camera[i].chase) + P_ResetCamera(&players[displayplayers[i]], &camera[i]); + } // clear cmd building stuff memset(gamekeydown, 0, sizeof (gamekeydown)); @@ -1820,83 +1790,9 @@ 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)) + if (gameaction == ga_nothing && !demo.quitafterplaying && + ((demo.playback && !modeattacking && !demo.title && !multiplayer) || gamestate == GS_TITLESCREEN)) { if (ev->type == ev_keydown && ev->data1 != 301) { @@ -1905,7 +1801,7 @@ boolean G_Responder(event_t *ev) } return false; } - else if (demoplayback && titledemo) + else if (demo.playback && demo.title) { // Title demo uses intro responder if (F_IntroResponder(ev)) @@ -1971,6 +1867,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 (!demo.playback && (splitscreen || !netgame)) + displayplayers[0] = consoleplayer; + else + { + G_AdjustView(1, 1, true); + + // change statusbar also if playing back demo + if (demo.quitafterplaying) + ST_changeDemoView(); + + return true; + } + } + + if (gamestate == GS_LEVEL && ev->type == ev_keydown && multiplayer && demo.playback) + { + if (ev->data1 == gamecontrolbis[gc_viewpoint][0] || ev->data1 == gamecontrolbis[gc_viewpoint][1]) + { + G_AdjustView(2, 1, true); + + return true; + } + else if (ev->data1 == gamecontrol3[gc_viewpoint][0] || ev->data1 == gamecontrol3[gc_viewpoint][1]) + { + G_AdjustView(3, 1, true); + + return true; + } + else if (ev->data1 == gamecontrol4[gc_viewpoint][0] || ev->data1 == gamecontrol4[gc_viewpoint][1]) + { + G_AdjustView(4, 1, true); + + return true; + } + + // Allow pausing + if ( + ev->data1 == gamecontrol[gc_pause][0] + || ev->data1 == gamecontrol[gc_pause][1] + || ev->data1 == KEY_PAUSE + ) + { + paused = !paused; + + if (demo.rewinding) + { + G_ConfirmRewind(leveltime); + paused = true; + S_PauseAudio(); + } + else if (paused) + S_PauseAudio(); + else + S_ResumeAudio(); + + return true; + } + + // Anything else opens the menu if not already open, except for a few keys... + if (!( + // Rankings + ev->data1 == gamecontrol[gc_scores][0] || ev->data1 == gamecontrol[gc_scores][1] + )) + { + M_StartControlPanel(); + + return true; + } + } + // update keys current state G_MapEventsToControls(ev); @@ -2093,6 +2063,255 @@ boolean G_Responder(event_t *ev) return false; } +// +// G_CouldView +// Return whether a player could be viewed by any means. +// +boolean G_CouldView(INT32 playernum) +{ + player_t *player; + + if (playernum < 0 || playernum > MAXPLAYERS-1) + return false; + + if (!playeringame[playernum]) + return false; + + player = &players[playernum]; + + if (player->spectator) + return false; + + // SRB2Kart: Only go through players who are actually playing + if (player->exiting) + return false; + if (( player->pflags & PF_TIMEOVER )) + return false; + + // I don't know if we want this actually, but I'll humor the suggestion anyway + if (G_BattleGametype() && !demo.playback) + { + if (player->kartstuff[k_bumper] <= 0) + return false; + } + + // SRB2Kart: we have no team-based modes, YET... + /*if (G_GametypeHasTeams()) + { + if (players[consoleplayer].ctfteam + && player->ctfteam != players[consoleplayer].ctfteam) + return false; + } + else if (gametype == GT_HIDEANDSEEK) + { + if (players[consoleplayer].pflags & PF_TAGIT) + return false; + } + // Other Tag-based gametypes? + else if (G_TagGametype()) + { + if (!players[consoleplayer].spectator + && (players[consoleplayer].pflags & PF_TAGIT) != (player->pflags & PF_TAGIT)) + return false; + } + else if (G_GametypeHasSpectators() && G_BattleGametype()) + { + if (!players[consoleplayer].spectator) + return false; + }*/ + + return true; +} + +// +// G_CanView +// Return whether a player can be viewed on a particular view (splitscreen). +// +boolean G_CanView(INT32 playernum, UINT8 viewnum, boolean onlyactive) +{ + UINT8 splits; + UINT8 viewd; + INT32 *displayplayerp; + + if (!(onlyactive ? G_CouldView(playernum) : (playeringame[playernum] && !players[playernum].spectator))) + return false; + + splits = splitscreen+1; + if (viewnum > splits) + viewnum = splits; + + for (viewd = 1; viewd < viewnum; ++viewd) + { + displayplayerp = (&displayplayers[viewd-1]); + if ((*displayplayerp) == playernum) + return false; + } + for (viewd = viewnum + 1; viewd <= splits; ++viewd) + { + displayplayerp = (&displayplayers[viewd-1]); + if ((*displayplayerp) == playernum) + return false; + } + + return true; +} + +// +// G_FindView +// Return the next player that can be viewed on a view, wraps forward. +// An out of range startview is corrected. +// +INT32 G_FindView(INT32 startview, UINT8 viewnum, boolean onlyactive, boolean reverse) +{ + INT32 i, dir = reverse ? -1 : 1; + startview = min(max(startview, 0), MAXPLAYERS); + for (i = startview; i < MAXPLAYERS && i >= 0; i += dir) + { + if (G_CanView(i, viewnum, onlyactive)) + return i; + } + for (i = (reverse ? MAXPLAYERS-1 : 0); i != startview; i += dir) + { + if (G_CanView(i, viewnum, onlyactive)) + return i; + } + return -1; +} + +INT32 G_CountPlayersPotentiallyViewable(boolean active) +{ + INT32 total = 0; + INT32 i; + for (i = 0; i < MAXPLAYERS; ++i) + { + if (active ? G_CouldView(i) : (playeringame[i] && !players[i].spectator)) + total++; + } + return total; +} + +// +// G_ResetView +// Correct a viewpoint to playernum or the next available, wraps forward. +// Also promotes splitscreen up to available viewable players. +// An out of range playernum is corrected. +// +void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive) +{ + UINT8 splits; + UINT8 viewd; + + INT32 *displayplayerp; + camera_t *camerap; + + INT32 olddisplayplayer; + INT32 playersviewable; + + splits = splitscreen+1; + + /* Promote splits */ + if (viewnum > splits) + { + playersviewable = G_CountPlayersPotentiallyViewable(onlyactive); + if (playersviewable < splits)/* do not demote */ + return; + + if (viewnum > playersviewable) + viewnum = playersviewable; + splitscreen = viewnum-1; + + /* Prepare extra views for G_FindView to pass. */ + for (viewd = splits+1; viewd < viewnum; ++viewd) + { + displayplayerp = (&displayplayers[viewd-1]); + (*displayplayerp) = INT32_MAX; + } + + R_ExecuteSetViewSize(); + } + + displayplayerp = (&displayplayers[viewnum-1]); + olddisplayplayer = (*displayplayerp); + + /* Check if anyone is available to view. */ + if (( playernum = G_FindView(playernum, viewnum, onlyactive, playernum < olddisplayplayer) ) == -1) + return; + + /* Focus our target view first so that we don't take its player. */ + (*displayplayerp) = playernum; + if ((*displayplayerp) != olddisplayplayer) + { + camerap = &camera[viewnum-1]; + P_ResetCamera(&players[(*displayplayerp)], camerap); + } + + if (viewnum > splits) + { + for (viewd = splits+1; viewd < viewnum; ++viewd) + { + displayplayerp = (&displayplayers[viewd-1]); + camerap = &camera[viewd]; + + (*displayplayerp) = G_FindView(0, viewd, onlyactive, false); + + P_ResetCamera(&players[(*displayplayerp)], camerap); + } + } + + if (viewnum == 1 && demo.playback) + consoleplayer = displayplayers[0]; +} + +// +// G_AdjustView +// Increment a viewpoint by offset from the current player. A negative value +// decrements. +// +void G_AdjustView(UINT8 viewnum, INT32 offset, boolean onlyactive) +{ + INT32 *displayplayerp, oldview; + displayplayerp = &displayplayers[viewnum-1]; + oldview = (*displayplayerp); + G_ResetView(viewnum, ( (*displayplayerp) + offset ), onlyactive); + + // If no other view could be found, go back to what we had. + if ((*displayplayerp) == -1) + (*displayplayerp) = oldview; +} + +// +// G_ResetViews +// Ensures all viewpoints are valid +// Also demotes splitscreen down to one player. +// +void G_ResetViews(void) +{ + UINT8 splits; + UINT8 viewd; + + INT32 playersviewable; + + splits = splitscreen+1; + + playersviewable = G_CountPlayersPotentiallyViewable(false); + /* Demote splits */ + if (playersviewable < splits) + { + splits = playersviewable; + splitscreen = max(splits-1, 0); + R_ExecuteSetViewSize(); + } + + /* + Consider installing a method to focus the last + view elsewhere if all players spectate? + */ + for (viewd = 1; viewd <= splits; ++viewd) + { + G_AdjustView(viewd, 0, false); + } +} + // // G_Ticker // Make ticcmd_ts for the players. @@ -2142,6 +2361,7 @@ void G_Ticker(boolean run) buf = gametic % BACKUPTICS; + if (!demo.playback) // read/write demo and check turbo cheat for (i = 0; i < MAXPLAYERS; i++) { @@ -2149,15 +2369,18 @@ void G_Ticker(boolean run) if (playeringame[i]) { + //@TODO all this throwdir stuff shouldn't be here! But it stays for now to maintain 1.0.4 compat... + // Remove for 1.1! + // SRB2kart // Save the dir the player is holding // to allow items to be thrown forward or backward. if (cmd->buttons & BT_FORWARD) - players[i].kartstuff[k_throwdir] = 1; + players[i].kartstuff[k_throwdir] = 1; else if (cmd->buttons & BT_BACKWARD) - players[i].kartstuff[k_throwdir] = -1; + players[i].kartstuff[k_throwdir] = -1; else - players[i].kartstuff[k_throwdir] = 0; + players[i].kartstuff[k_throwdir] = 0; G_CopyTiccmd(cmd, &netcmds[buf][i], 1); @@ -2170,7 +2393,7 @@ void G_Ticker(boolean run) switch (gamestate) { case GS_LEVEL: - if (titledemo) + if (demo.title) F_TitleDemoTicker(); P_Ticker(run); // tic the game ST_Ticker(); @@ -2300,7 +2523,7 @@ static inline void G_PlayerFinishLevel(INT32 player) // SRB2kart: Increment the "matches played" counter. if (player == consoleplayer) { - if (legitimateexit && !demoplayback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified) + if (legitimateexit && !demo.playback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified) { matchesplayed++; if (M_UpdateUnlockablesAndExtraEmblems(true)) @@ -2322,25 +2545,12 @@ void G_PlayerReborn(INT32 player) INT32 score, marescore; INT32 lives; INT32 continues; - UINT8 charability; - UINT8 charability2; // SRB2kart UINT8 kartspeed; UINT8 kartweight; // - fixed_t normalspeed; - fixed_t runspeed; - UINT8 thrustfactor; - UINT8 accelstart; - UINT8 acceleration; INT32 charflags; INT32 pflags; - UINT32 thokitem; - UINT32 spinitem; - UINT32 revitem; - fixed_t actionspd; - fixed_t mindash; - fixed_t maxdash; INT32 ctfteam; INT32 starposttime; INT16 starpostx; @@ -2348,7 +2558,6 @@ void G_PlayerReborn(INT32 player) INT16 starpostz; INT32 starpostnum; INT32 starpostangle; - fixed_t jumpfactor; INT32 exiting; INT16 numboxes; INT16 totalring; @@ -2357,6 +2566,7 @@ void G_PlayerReborn(INT32 player) UINT8 skincolor; INT32 skin; tic_t jointime; + UINT8 splitscreenindex; boolean spectator; INT16 bot; SINT8 pity; @@ -2371,6 +2581,7 @@ void G_PlayerReborn(INT32 player) INT32 bumper; INT32 comebackpoints; INT32 wanted; + INT32 respawnflip; boolean songcredit = false; score = players[player].score; @@ -2380,6 +2591,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)); @@ -2393,17 +2605,10 @@ void G_PlayerReborn(INT32 player) skincolor = players[player].skincolor; skin = players[player].skin; - charability = players[player].charability; - charability2 = players[player].charability2; // SRB2kart kartspeed = players[player].kartspeed; kartweight = players[player].kartweight; // - normalspeed = players[player].normalspeed; - runspeed = players[player].runspeed; - thrustfactor = players[player].thrustfactor; - accelstart = players[player].accelstart; - acceleration = players[player].acceleration; charflags = players[player].charflags; starposttime = players[player].starposttime; @@ -2411,14 +2616,8 @@ 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; - spinitem = players[player].spinitem; - revitem = players[player].revitem; - actionspd = players[player].actionspd; - mindash = players[player].mindash; - maxdash = players[player].maxdash; mare = players[player].mare; bot = players[player].bot; @@ -2476,29 +2675,17 @@ 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 p->skincolor = skincolor; p->skin = skin; - p->charability = charability; - p->charability2 = charability2; // SRB2kart p->kartspeed = kartspeed; p->kartweight = kartweight; // - p->normalspeed = normalspeed; - p->runspeed = runspeed; - p->thrustfactor = thrustfactor; - p->accelstart = accelstart; - p->acceleration = acceleration; p->charflags = charflags; - p->thokitem = thokitem; - p->spinitem = spinitem; - p->revitem = revitem; - p->actionspd = actionspd; - p->mindash = mindash; - p->maxdash = maxdash; p->starposttime = starposttime; p->starpostx = starpostx; @@ -2506,7 +2693,6 @@ void G_PlayerReborn(INT32 player) p->starpostz = starpostz; p->starpostnum = starpostnum; p->starpostangle = starpostangle; - p->jumpfactor = jumpfactor; p->exiting = exiting; p->numboxes = numboxes; @@ -2530,6 +2716,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; @@ -2549,7 +2736,8 @@ void G_PlayerReborn(INT32 player) { strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname, 7); mapmusname[6] = 0; - mapmusflags = mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK; + mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK); + mapmusposition = mapheaderinfo[gamemap-1]->muspos; songcredit = true; } } @@ -2582,22 +2770,22 @@ void G_PlayerReborn(INT32 player) { if (p == &players[consoleplayer]) CV_SetValue(&cv_playercolor, skincolor_redteam); - else if (p == &players[secondarydisplayplayer]) + else if (p == &players[displayplayers[1]]) CV_SetValue(&cv_playercolor2, skincolor_redteam); - else if (p == &players[thirddisplayplayer]) + else if (p == &players[displayplayers[2]]) CV_SetValue(&cv_playercolor3, skincolor_redteam); - else if (p == &players[fourthdisplayplayer]) + else if (p == &players[displayplayers[3]]) CV_SetValue(&cv_playercolor4, skincolor_redteam); } else if (p->ctfteam == 2 && p->skincolor != skincolor_blueteam) { if (p == &players[consoleplayer]) CV_SetValue(&cv_playercolor, skincolor_blueteam); - else if (p == &players[secondarydisplayplayer]) + else if (p == &players[displayplayers[1]]) CV_SetValue(&cv_playercolor2, skincolor_blueteam); - else if (p == &players[thirddisplayplayer]) + else if (p == &players[displayplayers[2]]) CV_SetValue(&cv_playercolor3, skincolor_blueteam); - else if (p == &players[fourthdisplayplayer]) + else if (p == &players[displayplayers[3]]) CV_SetValue(&cv_playercolor4, skincolor_blueteam); } }*/ @@ -2702,18 +2890,18 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost) if (nummapthings) { if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the first mapthing!\n")); spawnpoint = &mapthings[0]; } else { if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_ERROR, M_GetText("No player spawns found, spawning at the origin!\n")); //P_MovePlayerToSpawn handles this fine if the spawnpoint is NULL. } @@ -2733,9 +2921,9 @@ mapthing_t *G_FindCTFStart(INT32 playernum) if (!numredctfstarts && !numbluectfstarts) //why even bother, eh? { if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_WARNING, M_GetText("No CTF starts in this map!\n")); return NULL; } @@ -2745,9 +2933,9 @@ mapthing_t *G_FindCTFStart(INT32 playernum) if (!numredctfstarts) { if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_WARNING, M_GetText("No Red Team starts in this map!\n")); return NULL; } @@ -2760,9 +2948,9 @@ mapthing_t *G_FindCTFStart(INT32 playernum) } if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Red Team starts!\n")); return NULL; } @@ -2771,9 +2959,9 @@ mapthing_t *G_FindCTFStart(INT32 playernum) if (!numbluectfstarts) { if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_WARNING, M_GetText("No Blue Team starts in this map!\n")); return NULL; } @@ -2785,9 +2973,9 @@ mapthing_t *G_FindCTFStart(INT32 playernum) return bluectfstarts[i]; } if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Blue Team starts!\n")); return NULL; } @@ -2808,17 +2996,17 @@ mapthing_t *G_FindMatchStart(INT32 playernum) return deathmatchstarts[i]; } if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Deathmatch starts!\n")); return NULL; } if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_WARNING, M_GetText("No Deathmatch starts in this map!\n")); return NULL; } @@ -2884,17 +3072,17 @@ mapthing_t *G_FindRaceStart(INT32 playernum) //return playerstarts[0]; if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any Race starts!\n")); return NULL; } if (playernum == consoleplayer - || (splitscreen && playernum == secondarydisplayplayer) - || (splitscreen > 1 && playernum == thirddisplayplayer) - || (splitscreen > 2 && playernum == fourthdisplayplayer)) + || (splitscreen && playernum == displayplayers[1]) + || (splitscreen > 1 && playernum == displayplayers[2]) + || (splitscreen > 2 && playernum == displayplayers[3])) CONS_Alert(CONS_WARNING, M_GetText("No Race starts in this map!\n")); return NULL; } @@ -2986,14 +3174,11 @@ void G_DoReborn(INT32 playernum) if (player->starpostnum) // SRB2kart starpost = true; - if (camera.chase) - P_ResetCamera(&players[displayplayer], &camera); - if (camera2.chase && splitscreen > 0) - P_ResetCamera(&players[secondarydisplayplayer], &camera2); - if (camera3.chase && splitscreen > 1) - P_ResetCamera(&players[thirddisplayplayer], &camera3); - if (camera4.chase && splitscreen > 2) - P_ResetCamera(&players[fourthdisplayplayer], &camera4); + for (i = 0; i <= splitscreen; i++) + { + if (camera[i].chase) + P_ResetCamera(&players[displayplayers[i]], &camera[i]); + } // clear cmd building stuff memset(gamekeydown, 0, sizeof (gamekeydown)); @@ -3015,8 +3200,8 @@ void G_DoReborn(INT32 playernum) if (botingame) { // Bots respawn next to their master. - players[secondarydisplayplayer].playerstate = PST_REBORN; - G_SpawnPlayer(secondarydisplayplayer, false); + players[displayplayers[1]].playerstate = PST_REBORN; + G_SpawnPlayer(displayplayers[1], false); } } else @@ -3055,6 +3240,8 @@ void G_AddPlayer(INT32 playernum) p->jointime = 0; p->playerstate = PST_REBORN; + + demo_extradata[playernum] |= DXD_PLAYSTATE|DXD_COLOR|DXD_NAME|DXD_SKIN; // Set everything } void G_ExitLevel(void) @@ -3077,9 +3264,41 @@ void G_ExitLevel(void) // Remove CEcho text on round end. HU_ClearCEcho(); + + // Don't save demos immediately here! Let standings write first } } +// See also the enum GameType in doomstat.h +const char *Gametype_Names[NUMGAMETYPES] = +{ + "Race", // GT_RACE + "Battle" // GT_MATCH + + /*"Co-op", // GT_COOP + "Competition", // GT_COMPETITION + "Team Match", // GT_TEAMMATCH + "Tag", // GT_TAG + "Hide and Seek", // GT_HIDEANDSEEK + "CTF" // GT_CTF*/ +}; + +// +// G_GetGametypeByName +// +// Returns the number for the given gametype name string, or -1 if not valid. +// +INT32 G_GetGametypeByName(const char *gametypestr) +{ + INT32 i; + + for (i = 0; i < NUMGAMETYPES; i++) + if (!stricmp(gametypestr, Gametype_Names[i])) + return i; + + return -1; // unknown gametype +} + // // G_IsSpecialStage // @@ -3141,7 +3360,7 @@ boolean G_GametypeHasSpectators(void) #if 0 return (gametype != GT_COOP && gametype != GT_COMPETITION && gametype != GT_RACE); #else - return (netgame); //true + return (netgame || (multiplayer && demo.playback)); //true #endif } @@ -3221,12 +3440,12 @@ UINT8 G_GetGametypeColor(INT16 gt) { if (modeattacking // == ATTACKING_RECORD || gamestate == GS_TIMEATTACK) - return orangemap[120]; + return orangemap[0]; if (gt == GT_MATCH) - return redmap[120]; + return redmap[0]; if (gt == GT_RACE) - return skymap[120]; - return 247; // FALLBACK + return skymap[0]; + return 255; // FALLBACK } // @@ -3467,7 +3686,7 @@ static void G_DoCompleted(void) } // play some generic music if there's no win/cool/lose music going on (for exitlevel commands) - if (G_RaceGametype() && j == splitscreen+1 && (cv_inttime.value > 0)) + if (G_RaceGametype() && ((multiplayer && demo.playback) || j == splitscreen+1) && (cv_inttime.value > 0)) S_ChangeMusicInternal("racent", true); if (automapactive) @@ -3477,6 +3696,8 @@ static void G_DoCompleted(void) prevmap = (INT16)(gamemap-1); + if (demo.playback) goto demointermission; + // go to next level // nextmap is 0-based, unlike gamemap if (nextmapoverride != 0) @@ -3579,12 +3800,15 @@ static void G_DoCompleted(void) nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, false, 0, false, NULL); } + // We are committed to this map now. // We may as well allocate its header if it doesn't exist // (That is, if it's a real map) if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) P_AllocMapHeader(nextmap); +demointermission: + if (skipstats && !modeattacking) // Don't skip stats if we're in record attack G_AfterIntermission(); else @@ -3599,6 +3823,20 @@ void G_AfterIntermission(void) HU_ClearCEcho(); //G_NextLevel(); + if (demo.playback) + { + G_StopDemo(); + + if (demo.inreplayhut) + M_ReplayHut(0); + else + D_StartTitle(); + + return; + } + else if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) + G_SaveDemo(); + if (modeattacking) // End the run. { M_EndModeAttackRun(); @@ -3743,6 +3981,9 @@ static void G_DoContinued(void) // when something new is added. void G_EndGame(void) { + if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) + G_SaveDemo(); + // Only do evaluation and credits in coop games. if (gametype == GT_COOP) { @@ -4110,7 +4351,7 @@ static void M_ForceLoadGameResponse(INT32 ch) //set cursaveslot to -1 so nothing gets saved. cursaveslot = -1; - displayplayer = consoleplayer; + displayplayers[0] = consoleplayer; multiplayer = false; splitscreen = 0; SplitScreen_OnChange(); // not needed? @@ -4174,7 +4415,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride) } save_p += VERSIONSIZE; - if (demoplayback) // reset game engine + if (demo.playback) // reset game engine G_StopDemo(); // paused = false; @@ -4200,7 +4441,7 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride) // gameaction = ga_nothing; // G_SetGamestate(GS_LEVEL); - displayplayer = consoleplayer; + displayplayers[0] = consoleplayer; multiplayer = false; splitscreen = 0; SplitScreen_OnChange(); // not needed? @@ -4265,7 +4506,7 @@ void G_SaveGame(UINT32 savegameslot) // // G_DeferedInitNew // Can be called by the startup code or the menu task, -// consoleplayer, displayplayer, playeringame[] should be set. +// consoleplayer, displayplayers[], playeringame[] should be set. // void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, UINT8 ssplayers, boolean FLS) { @@ -4273,7 +4514,7 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar UINT8 color = 0; paused = false; - if (demoplayback) + if (demo.playback) COM_BufAddText("stopdemo\n"); while (ghosts) @@ -4335,7 +4576,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool legitimateexit = false; // SRB2Kart comebackshowninfo = false; - if (!demoplayback && !netgame) // Netgame sets random seed elsewhere, demo playback sets seed just before us! + if (!demo.playback && !netgame) // Netgame sets random seed elsewhere, demo playback sets seed just before us! P_SetRandSeed(M_RandomizedSeed()); // Use a more "Random" random seed //SRB2Kart - Score is literally the only thing you SHOULDN'T reset at all times @@ -4381,7 +4622,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool players[i].marescore = 0; - if (resetplayer) // SRB2Kart + if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart { players[i].score = 0; } @@ -4490,7 +4731,7 @@ char *G_BuildMapTitle(INT32 mapnum) // DEMO RECORDING // -#define DEMOVERSION 0x0001 +#define DEMOVERSION 0x0002 #define DEMOHEADER "\xF0" "KartReplay" "\x0F" #define DF_GHOST 0x01 // This demo contains ghost data too! @@ -4498,6 +4739,16 @@ char *G_BuildMapTitle(INT32 mapnum) #define DF_NIGHTSATTACK 0x04 // This demo is from NiGHTS attack and contains its time left, score, and mares! #define DF_ATTACKMASK 0x06 // This demo is from ??? attack and contains ??? #define DF_ATTACKSHIFT 1 +#define DF_ENCORE 0x40 +#define DF_MULTIPLAYER 0x80 // This demo was recorded in multiplayer mode! + +#ifdef DEMO_COMPAT_100 +#define DF_FILELIST 0x08 // This demo contains an extra files list +#define DF_GAMETYPEMASK 0x30 +#define DF_GAMESHIFT 4 +#endif + +#define DEMO_SPECTATOR 0x40 // For demos #define ZT_FWD 0x01 @@ -4506,9 +4757,20 @@ char *G_BuildMapTitle(INT32 mapnum) #define ZT_BUTTONS 0x08 #define ZT_AIMING 0x10 #define ZT_DRIFT 0x20 +#define ZT_LATENCY 0x40 #define DEMOMARKER 0x80 // demoend -static ticcmd_t oldcmd; +UINT8 demo_extradata[MAXPLAYERS]; +UINT8 demo_writerng; // 0=no, 1=yes, 2=yes but on a timeout +static ticcmd_t oldcmd[MAXPLAYERS]; + +#define DW_END 0xFF // End of extradata block +#define DW_RNG 0xFE // Check RNG seed! + +#define DW_EXTRASTUFF 0xFE // Numbers below this are reserved for writing player slot data + +// Below consts are only used for demo extrainfo sections +#define DW_STANDING 0x00 // For Metal Sonic and time attack ghosts #define GZT_XYZ 0x01 @@ -4530,8 +4792,9 @@ static ticcmd_t oldcmd; #define EZT_SCALE 0x10 // Changed size #define EZT_HIT 0x20 // Damaged a mobj #define EZT_SPRITE 0x40 // Changed sprite set completely out of PLAY (NiGHTS, SOCs, whatever) +#define EZT_KART 0x80 // SRB2Kart: Changed current held item/quantity and bumpers for battle -static mobj_t oldmetal, oldghost; +static mobj_t oldmetal, oldghost[MAXPLAYERS]; void G_SaveMetal(UINT8 **buffer) { @@ -4569,37 +4832,280 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) return dest; } +// Finds a skin with the closest stats if the expected skin doesn't exist. +static INT32 GetSkinNumClosestToStats(UINT8 kartspeed, UINT8 kartweight) +{ + INT32 i, closest_skin = 0; + UINT8 closest_stats = UINT8_MAX, stat_diff; + + for (i = 0; i < numskins; i++) + { + stat_diff = abs(skins[i].kartspeed - kartspeed) + abs(skins[i].kartweight - kartweight); + if (stat_diff < closest_stats) + { + closest_stats = stat_diff; + closest_skin = i; + } + } + + return closest_skin; +} + +static void FindClosestSkinForStats(UINT32 p, UINT8 kartspeed, UINT8 kartweight) +{ + INT32 closest_skin = GetSkinNumClosestToStats(kartspeed, kartweight); + + //CONS_Printf("Using %s instead...\n", skins[closest_skin].name); + SetPlayerSkinByNum(p, closest_skin); +} + +void G_ReadDemoExtraData(void) +{ + INT32 p, extradata, i; + char name[17]; + + memset(name, '\0', 17); + + p = READUINT8(demo_p); + + while (p < DW_EXTRASTUFF) + { + extradata = READUINT8(demo_p); + + if (extradata & DXD_RESPAWN) + { + if (players[p].mo) + P_DamageMobj(players[p].mo, NULL, NULL, 10000); // Is this how this should work..? + } + if (extradata & DXD_SKIN) + { + UINT8 kartspeed, kartweight; + + // Skin + M_Memcpy(name, demo_p, 16); + demo_p += 16; + SetPlayerSkin(p, name); + + kartspeed = READUINT8(demo_p); + kartweight = READUINT8(demo_p); + + + if (stricmp(skins[players[p].skin].name, name) != 0) + FindClosestSkinForStats(p, kartspeed, kartweight); + + players[p].kartspeed = kartspeed; + players[p].kartweight = kartweight; + } + if (extradata & DXD_COLOR) + { + // Color + M_Memcpy(name, demo_p, 16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i], name)) // SRB2kart + { + players[p].skincolor = i; + if (players[p].mo) + players[p].mo->color = i; + break; + } + } + if (extradata & DXD_NAME) + { + // Name + M_Memcpy(player_names[p],demo_p,16); + demo_p += 16; + } + if (extradata & DXD_PLAYSTATE) + { + extradata = READUINT8(demo_p); + + switch (extradata) { + case DXD_PST_PLAYING: + players[p].pflags |= PF_WANTSTOJOIN; // fuck you + break; + + case DXD_PST_SPECTATING: + players[p].pflags &= ~PF_WANTSTOJOIN; // double-fuck you + if (!playeringame[p]) + { + CL_ClearPlayer(p); + playeringame[p] = true; + G_AddPlayer(p); + players[p].spectator = true; + + // There's likely an off-by-one error in timing recording or playback of joins. This hacks around it so I don't have to find out where that is. \o/ + if (oldcmd[p].forwardmove) + P_RandomByte(); + } + else + { + players[p].spectator = true; + if (players[p].mo) + P_DamageMobj(players[p].mo, NULL, NULL, 10000); + else + players[p].playerstate = PST_REBORN; + } + break; + + case DXD_PST_LEFT: + CL_RemovePlayer(p, 0); + break; + } + + G_ResetViews(); + + // maybe these are necessary? + if (G_BattleGametype()) + K_CheckBumpers(); // SRB2Kart + else if (G_RaceGametype()) + P_CheckRacers(); // also SRB2Kart + } + + + p = READUINT8(demo_p); + } + + while (p != DW_END) + { + UINT32 rng; + + switch (p) + { + case DW_RNG: + rng = READUINT32(demo_p); + if (P_GetRandSeed() != rng) + { + P_SetRandSeed(rng); + + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + } + } + + p = READUINT8(demo_p); + } + + if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) + { + // end of demo data stream + G_CheckDemoStatus(); + return; + } +} + +void G_WriteDemoExtraData(void) +{ + INT32 i; + char name[16]; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (demo_extradata[i]) + { + WRITEUINT8(demo_p, i); + WRITEUINT8(demo_p, demo_extradata[i]); + + //if (demo_extradata[i] & DXD_RESPAWN) has no extra data + if (demo_extradata[i] & DXD_SKIN) + { + // Skin + memset(name, 0, 16); + strncpy(name, skins[players[i].skin].name, 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + + WRITEUINT8(demo_p, skins[players[i].skin].kartspeed); + WRITEUINT8(demo_p, skins[players[i].skin].kartweight); + } + if (demo_extradata[i] & DXD_COLOR) + { + // Color + memset(name, 0, 16); + strncpy(name, KartColor_Names[players[i].skincolor], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + } + if (demo_extradata[i] & DXD_NAME) + { + // Name + memset(name, 0, 16); + strncpy(name, player_names[i], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + } + if (demo_extradata[i] & DXD_PLAYSTATE) + { + demo_writerng = 1; + if (!playeringame[i]) + WRITEUINT8(demo_p, DXD_PST_LEFT); + else if ( + players[i].spectator && + !(players[i].pflags & PF_WANTSTOJOIN) // <= fuck you specifically + ) + WRITEUINT8(demo_p, DXD_PST_SPECTATING); + else + WRITEUINT8(demo_p, DXD_PST_PLAYING); + } + } + + demo_extradata[i] = 0; + } + + // May not be necessary, but might as well play it safe... + if ((leveltime & 255) == 128) + demo_writerng = 1; + + { + static UINT8 timeout = 0; + + if (timeout) timeout--; + + if (demo_writerng == 1 || (demo_writerng == 2 && timeout == 0)) + { + demo_writerng = 0; + timeout = 16; + WRITEUINT8(demo_p, DW_RNG); + WRITEUINT32(demo_p, P_GetRandSeed()); + } + } + + WRITEUINT8(demo_p, DW_END); +} + void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) { UINT8 ziptic; - (void)playernum; - if (!demo_p || !demo_start) + if (!demo_p || !demo.deferstart) return; ziptic = READUINT8(demo_p); if (ziptic & ZT_FWD) - oldcmd.forwardmove = READSINT8(demo_p); + oldcmd[playernum].forwardmove = READSINT8(demo_p); if (ziptic & ZT_SIDE) - oldcmd.sidemove = READSINT8(demo_p); + oldcmd[playernum].sidemove = READSINT8(demo_p); if (ziptic & ZT_ANGLE) - oldcmd.angleturn = READINT16(demo_p); + oldcmd[playernum].angleturn = READINT16(demo_p); if (ziptic & ZT_BUTTONS) - oldcmd.buttons = (oldcmd.buttons & (BT_FORWARD|BT_BACKWARD)) | (READUINT16(demo_p) & ~(BT_FORWARD|BT_BACKWARD)); + oldcmd[playernum].buttons = READUINT16(demo_p); if (ziptic & ZT_AIMING) - oldcmd.aiming = READINT16(demo_p); + oldcmd[playernum].aiming = READINT16(demo_p); if (ziptic & ZT_DRIFT) - oldcmd.driftturn = READINT16(demo_p); + oldcmd[playernum].driftturn = READINT16(demo_p); + if (ziptic & ZT_LATENCY) + oldcmd[playernum].latency = READUINT8(demo_p); - G_CopyTiccmd(cmd, &oldcmd, 1); + G_CopyTiccmd(cmd, &oldcmd[playernum], 1); // SRB2kart: Copy-pasted from ticcmd building, removes that crappy demo cam - if (((players[displayplayer].mo && players[displayplayer].speed > 0) // Moving + if (((players[displayplayers[0]].mo && players[displayplayers[0]].speed > 0) // Moving || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn - || (players[displayplayer].kartstuff[k_respawn]) // Respawning - || (players[displayplayer].spectator || objectplacing)) // Not a physical player - && !(players[displayplayer].kartstuff[k_spinouttimer] && players[displayplayer].kartstuff[k_sneakertimer])) // Spinning and boosting cancels out spinout - localangle += (cmd->angleturn<<16); + || (players[displayplayers[0]].kartstuff[k_respawn]) // Respawning + || (players[displayplayers[0]].spectator || objectplacing)) // Not a physical player + && !(players[displayplayers[0]].kartstuff[k_spinouttimer] && players[displayplayers[0]].kartstuff[k_sneakertimer])) // Spinning and boosting cancels out spinout + localangle[0] += (cmd->angleturn<<16); if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) { @@ -4613,54 +5119,60 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) { char ziptic = 0; UINT8 *ziptic_p; - (void)playernum; if (!demo_p) return; ziptic_p = demo_p++; // the ziptic, written at the end of this function - if (cmd->forwardmove != oldcmd.forwardmove) + if (cmd->forwardmove != oldcmd[playernum].forwardmove) { WRITEUINT8(demo_p,cmd->forwardmove); - oldcmd.forwardmove = cmd->forwardmove; + oldcmd[playernum].forwardmove = cmd->forwardmove; ziptic |= ZT_FWD; } - if (cmd->sidemove != oldcmd.sidemove) + if (cmd->sidemove != oldcmd[playernum].sidemove) { WRITEUINT8(demo_p,cmd->sidemove); - oldcmd.sidemove = cmd->sidemove; + oldcmd[playernum].sidemove = cmd->sidemove; ziptic |= ZT_SIDE; } - if (cmd->angleturn != oldcmd.angleturn) + if (cmd->angleturn != oldcmd[playernum].angleturn) { WRITEINT16(demo_p,cmd->angleturn); - oldcmd.angleturn = cmd->angleturn; + oldcmd[playernum].angleturn = cmd->angleturn; ziptic |= ZT_ANGLE; } - if (cmd->buttons != oldcmd.buttons) + if (cmd->buttons != oldcmd[playernum].buttons) { WRITEUINT16(demo_p,cmd->buttons); - oldcmd.buttons = cmd->buttons; + oldcmd[playernum].buttons = cmd->buttons; ziptic |= ZT_BUTTONS; } - if (cmd->aiming != oldcmd.aiming) + if (cmd->aiming != oldcmd[playernum].aiming) { WRITEINT16(demo_p,cmd->aiming); - oldcmd.aiming = cmd->aiming; + oldcmd[playernum].aiming = cmd->aiming; ziptic |= ZT_AIMING; } - if (cmd->driftturn != oldcmd.driftturn) + if (cmd->driftturn != oldcmd[playernum].driftturn) { WRITEINT16(demo_p,cmd->driftturn); - oldcmd.driftturn = cmd->driftturn; + oldcmd[playernum].driftturn = cmd->driftturn; ziptic |= ZT_DRIFT; } + if (cmd->latency != oldcmd[playernum].latency) + { + WRITEUINT8(demo_p,cmd->latency); + oldcmd[playernum].latency = cmd->latency; + ziptic |= ZT_LATENCY; + } + *ziptic_p = ziptic; // attention here for the ticcmd size! @@ -4672,71 +5184,93 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) } } -void G_GhostAddThok(void) +void G_GhostAddThok(INT32 playernum) { - if (!demorecording || !(demoflags & DF_GHOST)) + if (!demo.recording || !(demoflags & DF_GHOST)) return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_THOK; + ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_THOK; } -void G_GhostAddSpin(void) +void G_GhostAddSpin(INT32 playernum) { - if (!demorecording || !(demoflags & DF_GHOST)) + if (!demo.recording || !(demoflags & DF_GHOST)) return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_SPIN; + ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_SPIN; } -void G_GhostAddRev(void) +void G_GhostAddRev(INT32 playernum) { - if (!demorecording || !(demoflags & DF_GHOST)) + if (!demo.recording || !(demoflags & DF_GHOST)) return; - ghostext.flags = (ghostext.flags & ~EZT_THOKMASK) | EZT_REV; + ghostext[playernum].flags = (ghostext[playernum].flags & ~EZT_THOKMASK) | EZT_REV; } -void G_GhostAddFlip(void) +void G_GhostAddFlip(INT32 playernum) { - if (!demorecording || !(demoflags & DF_GHOST)) + if (!demo.recording || !(demoflags & DF_GHOST)) return; - ghostext.flags |= EZT_FLIP; + ghostext[playernum].flags |= EZT_FLIP; } -void G_GhostAddColor(ghostcolor_t color) +void G_GhostAddColor(INT32 playernum, ghostcolor_t color) { - if (!demorecording || !(demoflags & DF_GHOST)) + if (!demo.recording || !(demoflags & DF_GHOST)) return; - if (ghostext.lastcolor == (UINT8)color) + if (ghostext[playernum].lastcolor == (UINT8)color) { - ghostext.flags &= ~EZT_COLOR; + ghostext[playernum].flags &= ~EZT_COLOR; return; } - ghostext.flags |= EZT_COLOR; - ghostext.color = (UINT8)color; + ghostext[playernum].flags |= EZT_COLOR; + ghostext[playernum].color = (UINT8)color; } -void G_GhostAddScale(fixed_t scale) +void G_GhostAddScale(INT32 playernum, fixed_t scale) { - if (!demorecording || !(demoflags & DF_GHOST)) + if (!demo.recording || !(demoflags & DF_GHOST)) return; - if (ghostext.lastscale == scale) + if (ghostext[playernum].lastscale == scale) { - ghostext.flags &= ~EZT_SCALE; + ghostext[playernum].flags &= ~EZT_SCALE; return; } - ghostext.flags |= EZT_SCALE; - ghostext.scale = scale; + ghostext[playernum].flags |= EZT_SCALE; + ghostext[playernum].scale = scale; } -void G_GhostAddHit(mobj_t *victim) +void G_GhostAddHit(INT32 playernum, mobj_t *victim) { - if (!demorecording || !(demoflags & DF_GHOST)) + if (!demo.recording || !(demoflags & DF_GHOST)) return; - ghostext.flags |= EZT_HIT; - ghostext.hits++; - ghostext.hitlist = Z_Realloc(ghostext.hitlist, ghostext.hits * sizeof(mobj_t *), PU_LEVEL, NULL); - ghostext.hitlist[ghostext.hits-1] = victim; + ghostext[playernum].flags |= EZT_HIT; + ghostext[playernum].hits++; + ghostext[playernum].hitlist = Z_Realloc(ghostext[playernum].hitlist, ghostext[playernum].hits * sizeof(mobj_t *), PU_LEVEL, NULL); + ghostext[playernum].hitlist[ghostext[playernum].hits-1] = victim; } -void G_WriteGhostTic(mobj_t *ghost) +void G_WriteAllGhostTics(void) +{ + INT32 i, counter = leveltime; + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!players[i].mo) + continue; + + counter++; + + if (counter % cv_netdemosyncquality.value != 0) // Only write 1 in this many ghost datas per tic to cut down on multiplayer replay size. + continue; + + WRITEUINT8(demo_p, i); + G_WriteGhostTic(players[i].mo, i); + } + WRITEUINT8(demo_p, 0xFF); +} + +void G_WriteGhostTic(mobj_t *ghost, INT32 playernum) { char ziptic = 0; UINT8 *ziptic_p; @@ -4758,40 +5292,41 @@ void G_WriteGhostTic(mobj_t *ghost) ziptic_p = demo_p++; // the ziptic, written at the end of this function - #define MAXMOM (0xFFFF<<8) + #define MAXMOM (0x7FFF<<8) // 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) + if (abs(ghost->x-oldghost[playernum].x) > MAXMOM + || abs(ghost->y-oldghost[playernum].y) > MAXMOM + || abs(ghost->z-oldghost[playernum].z) > MAXMOM + || ((UINT8)(leveltime & 255) > 0 && (UINT8)(leveltime & 255) <= (UINT8)cv_netdemosyncquality.value)) // Hack to enable slightly nicer resyncing { - oldghost.x = ghost->x; - oldghost.y = ghost->y; - oldghost.z = ghost->z; + oldghost[playernum].x = ghost->x; + oldghost[playernum].y = ghost->y; + oldghost[playernum].z = ghost->z; ziptic |= GZT_XYZ; - WRITEFIXED(demo_p,oldghost.x); - WRITEFIXED(demo_p,oldghost.y); - WRITEFIXED(demo_p,oldghost.z); + WRITEFIXED(demo_p,oldghost[playernum].x); + WRITEFIXED(demo_p,oldghost[playernum].y); + WRITEFIXED(demo_p,oldghost[playernum].z); } else { // 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); - if (momx != oldghost.momx - || momy != oldghost.momy) + INT16 momx = (INT16)((ghost->x-oldghost[playernum].x + (1<<4))>>8); + INT16 momy = (INT16)((ghost->y-oldghost[playernum].y + (1<<4))>>8); + if (momx != oldghost[playernum].momx + || momy != oldghost[playernum].momy) { - oldghost.momx = momx; - oldghost.momy = momy; + oldghost[playernum].momx = momx; + oldghost[playernum].momy = momy; ziptic |= GZT_MOMXY; WRITEINT16(demo_p,momx); WRITEINT16(demo_p,momy); } - momx = (INT16)((ghost->z-oldghost.z)>>8); - if (momx != oldghost.momz) + momx = (INT16)((ghost->z-oldghost[playernum].z + (1<<4))>>8); + if (momx != oldghost[playernum].momz) { - oldghost.momz = momx; + oldghost[playernum].momz = momx; ziptic |= GZT_MOMZ; WRITEINT16(demo_p,momx); } @@ -4799,9 +5334,9 @@ void G_WriteGhostTic(mobj_t *ghost) // This SHOULD set oldghost.x/y/z to match ghost->x/y/z // but it keeps the fractional loss of one byte, // so it will hopefully be made up for in future tics. - oldghost.x += oldghost.momx<<8; - oldghost.y += oldghost.momy<<8; - oldghost.z += oldghost.momz<<8; + oldghost[playernum].x += oldghost[playernum].momx<<8; + oldghost[playernum].y += oldghost[playernum].momy<<8; + oldghost[playernum].z += oldghost[playernum].momz<<8; } #undef MAXMOM @@ -4809,56 +5344,72 @@ void G_WriteGhostTic(mobj_t *ghost) // Only store the 8 most relevant bits of angle // because exact values aren't too easy to discern to begin with when only 8 angles have different sprites // and it does not affect this mode of movement at all anyway. - if (ghost->angle>>24 != oldghost.angle) + if (ghost->angle>>24 != oldghost[playernum].angle) { - oldghost.angle = ghost->angle>>24; + oldghost[playernum].angle = ghost->angle>>24; ziptic |= GZT_ANGLE; - WRITEUINT8(demo_p,oldghost.angle); + WRITEUINT8(demo_p,oldghost[playernum].angle); } // Store the sprite frame. frame = ghost->frame & 0xFF; - if (frame != oldghost.frame) + if (frame != oldghost[playernum].frame) { - oldghost.frame = frame; + oldghost[playernum].frame = frame; ziptic |= GZT_SPRITE; - WRITEUINT8(demo_p,oldghost.frame); + WRITEUINT8(demo_p,oldghost[playernum].frame); } // Check for sprite set changes sprite = ghost->sprite; - if (sprite != oldghost.sprite) + if (sprite != oldghost[playernum].sprite) { - oldghost.sprite = sprite; - ghostext.flags |= EZT_SPRITE; + oldghost[playernum].sprite = sprite; + ghostext[playernum].flags |= EZT_SPRITE; } - if (ghostext.flags) + if (ghost->player) + { + if ( + ghostext[playernum].kartitem != ghost->player->kartstuff[k_itemtype] || + ghostext[playernum].kartamount != ghost->player->kartstuff[k_itemamount] || + ghostext[playernum].kartbumpers != ghost->player->kartstuff[k_bumper] + ) + { + ghostext[playernum].flags |= EZT_KART; + ghostext[playernum].kartitem = ghost->player->kartstuff[k_itemtype]; + ghostext[playernum].kartamount = ghost->player->kartstuff[k_itemamount]; + ghostext[playernum].kartbumpers = ghost->player->kartstuff[k_bumper]; + + } + } + + if (ghostext[playernum].color == ghostext[playernum].lastcolor) + ghostext[playernum].flags &= ~EZT_COLOR; + if (ghostext[playernum].scale == ghostext[playernum].lastscale) + ghostext[playernum].flags &= ~EZT_SCALE; + + if (ghostext[playernum].flags) { ziptic |= GZT_EXTRA; + WRITEUINT8(demo_p,ghostext[playernum].flags); - if (ghostext.color == ghostext.lastcolor) - ghostext.flags &= ~EZT_COLOR; - if (ghostext.scale == ghostext.lastscale) - ghostext.flags &= ~EZT_SCALE; - - WRITEUINT8(demo_p,ghostext.flags); - if (ghostext.flags & EZT_COLOR) + if (ghostext[playernum].flags & EZT_COLOR) { - WRITEUINT8(demo_p,ghostext.color); - ghostext.lastcolor = ghostext.color; + WRITEUINT8(demo_p,ghostext[playernum].color); + ghostext[playernum].lastcolor = ghostext[playernum].color; } - if (ghostext.flags & EZT_SCALE) + if (ghostext[playernum].flags & EZT_SCALE) { - WRITEFIXED(demo_p,ghostext.scale); - ghostext.lastscale = ghostext.scale; + WRITEFIXED(demo_p,ghostext[playernum].scale); + ghostext[playernum].lastscale = ghostext[playernum].scale; } - if (ghostext.flags & EZT_HIT) + if (ghostext[playernum].flags & EZT_HIT) { - WRITEUINT16(demo_p,ghostext.hits); - for (i = 0; i < ghostext.hits; i++) + WRITEUINT16(demo_p,ghostext[playernum].hits); + for (i = 0; i < ghostext[playernum].hits; i++) { - mobj_t *mo = ghostext.hitlist[i]; + mobj_t *mo = ghostext[playernum].hitlist[i]; WRITEUINT32(demo_p,UINT32_MAX); // reserved for some method of determining exactly which mobj this is. (mobjnum doesn't work here.) WRITEUINT32(demo_p,mo->type); WRITEUINT16(demo_p,(UINT16)mo->health); @@ -4867,13 +5418,19 @@ void G_WriteGhostTic(mobj_t *ghost) WRITEFIXED(demo_p,mo->z); WRITEANGLE(demo_p,mo->angle); } - Z_Free(ghostext.hitlist); - ghostext.hits = 0; - ghostext.hitlist = NULL; + Z_Free(ghostext[playernum].hitlist); + ghostext[playernum].hits = 0; + ghostext[playernum].hitlist = NULL; } - if (ghostext.flags & EZT_SPRITE) + if (ghostext[playernum].flags & EZT_SPRITE) WRITEUINT8(demo_p,sprite); - ghostext.flags = 0; + if (ghostext[playernum].flags & EZT_KART) + { + WRITEINT32(demo_p, ghostext[playernum].kartitem); + WRITEINT32(demo_p, ghostext[playernum].kartamount); + WRITEINT32(demo_p, ghostext[playernum].kartbumpers); + } + ghostext[playernum].flags = 0; } *ziptic_p = ziptic; @@ -4887,49 +5444,70 @@ void G_WriteGhostTic(mobj_t *ghost) } } +void G_ConsAllGhostTics(void) +{ + UINT8 p = READUINT8(demo_p); + + while (p != 0xFF) + { + G_ConsGhostTic(p); + p = READUINT8(demo_p); + } + + if (*demo_p == DEMOMARKER) + { + // end of demo data stream + G_CheckDemoStatus(); + return; + } +} + // Uses ghost data to do consistency checks on your position. // This fixes desynchronising demos when fighting eggman. -void G_ConsGhostTic(void) +void G_ConsGhostTic(INT32 playernum) { 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) + if (!demo_p || !demo.deferstart) return; if (!(demoflags & DF_GHOST)) return; // No ghost data to use. - testmo = players[0].mo; + testmo = players[playernum].mo; // Grab ghost data. ziptic = READUINT8(demo_p); if (ziptic & GZT_XYZ) { - oldghost.x = READFIXED(demo_p); - oldghost.y = READFIXED(demo_p); - oldghost.z = READFIXED(demo_p); + oldghost[playernum].x = READFIXED(demo_p); + oldghost[playernum].y = READFIXED(demo_p); + oldghost[playernum].z = READFIXED(demo_p); + syncleeway = 0; } else { if (ziptic & GZT_MOMXY) { - oldghost.momx = READINT16(demo_p)<<8; - oldghost.momy = READINT16(demo_p)<<8; + oldghost[playernum].momx = READINT16(demo_p)<<8; + oldghost[playernum].momy = READINT16(demo_p)<<8; } if (ziptic & GZT_MOMZ) - oldghost.momz = READINT16(demo_p)<<8; - oldghost.x += oldghost.momx; - oldghost.y += oldghost.momy; - oldghost.z += oldghost.momz; + oldghost[playernum].momz = READINT16(demo_p)<<8; + oldghost[playernum].x += oldghost[playernum].momx; + oldghost[playernum].y += oldghost[playernum].momy; + oldghost[playernum].z += oldghost[playernum].momz; + syncleeway = FRACUNIT; } if (ziptic & GZT_ANGLE) demo_p++; if (ziptic & GZT_SPRITE) demo_p++; if(ziptic & GZT_NIGHTS) { - if (!testmo->player || !(testmo->player->pflags & PF_NIGHTSMODE) || !testmo->tracer) + if (!testmo || !testmo->player || !(testmo->player->pflags & PF_NIGHTSMODE) || !testmo->tracer) nightsfail = true; else testmo = testmo->tracer; @@ -4985,27 +5563,68 @@ void G_ConsGhostTic(void) } if (ziptic & EZT_SPRITE) demo_p++; + if (ziptic & EZT_KART) + { + ghostext[playernum].kartitem = READINT32(demo_p); + ghostext[playernum].kartamount = READINT32(demo_p); + ghostext[playernum].kartbumpers = READINT32(demo_p); + } } - // 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; - - if (nightsfail || px != gx || py != gy || pz != gz) + if (testmo) { - if (demosynced) - CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); - demosynced = false; + // Re-synchronise + px = testmo->x; + py = testmo->y; + pz = testmo->z; + gx = oldghost[playernum].x; + gy = oldghost[playernum].y; + gz = oldghost[playernum].z; - P_UnsetThingPosition(testmo); - testmo->x = oldghost.x; - testmo->y = oldghost.y; - P_SetThingPosition(testmo); - testmo->z = oldghost.z; + if (nightsfail || abs(px-gx) > syncleeway || abs(py-gy) > syncleeway || abs(pz-gz) > syncleeway) + { + ghostext[playernum].desyncframes++; + + if (ghostext[playernum].desyncframes >= 2) + { + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + + P_UnsetThingPosition(testmo); + testmo->x = oldghost[playernum].x; + testmo->y = oldghost[playernum].y; + P_SetThingPosition(testmo); + testmo->z = oldghost[playernum].z; + + if (abs(testmo->z - testmo->floorz) < 4*FRACUNIT) + testmo->z = testmo->floorz; // Sync players to the ground when they're likely supposed to be there... + + ghostext[playernum].desyncframes = 2; + } + } + else + ghostext[playernum].desyncframes = 0; + + if ( +#ifdef DEMO_COMPAT_100 + demo.version != 0x0001 && +#endif + ( + players[playernum].kartstuff[k_itemtype] != ghostext[playernum].kartitem || + players[playernum].kartstuff[k_itemamount] != ghostext[playernum].kartamount || + players[playernum].kartstuff[k_bumper] != ghostext[playernum].kartbumpers + ) + ) + { + if (demosynced) + CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced!\n")); + demosynced = false; + + players[playernum].kartstuff[k_itemtype] = ghostext[playernum].kartitem; + players[playernum].kartstuff[k_itemamount] = ghostext[playernum].kartamount; + players[playernum].kartstuff[k_bumper] = ghostext[playernum].kartbumpers; + } } if (*demo_p == DEMOMARKER) @@ -5023,6 +5642,38 @@ void G_GhostTicker(void) { // Skip normal demo data. UINT8 ziptic = READUINT8(g->p); + +#ifdef DEMO_COMPAT_100 + if (g->version != 0x0001) + { +#endif + while (ziptic != DW_END) // Get rid of extradata stuff + { + if (ziptic == 0) // Only support player 0 info for now + { + ziptic = READUINT8(g->p); + if (ziptic & DXD_SKIN) + g->p += 18; // We _could_ read this info, but it shouldn't change anything in record attack... + if (ziptic & DXD_COLOR) + g->p += 16; // Same tbh + if (ziptic & DXD_NAME) + g->p += 16; // yea + if (ziptic & DXD_PLAYSTATE && READUINT8(g->p) != DXD_PST_PLAYING) + I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + } + else if (ziptic == DW_RNG) + g->p += 4; // RNG seed + else + I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + + ziptic = READUINT8(g->p); + } + + ziptic = READUINT8(g->p); // Back to actual ziptic stuff +#ifdef DEMO_COMPAT_100 + } +#endif + if (ziptic & ZT_FWD) g->p++; if (ziptic & ZT_SIDE) @@ -5035,9 +5686,24 @@ void G_GhostTicker(void) g->p += 2; if (ziptic & ZT_DRIFT) g->p += 2; + if (ziptic & ZT_LATENCY) + g->p += 1; // Grab ghost data. ziptic = READUINT8(g->p); + +#ifdef DEMO_COMPAT_100 + if (g->version != 0x0001) + { +#endif + if (ziptic == 0xFF) + goto skippedghosttic; // Didn't write ghost info this frame + else if (ziptic != 0) + I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this + ziptic = READUINT8(g->p); +#ifdef DEMO_COMPAT_100 + } +#endif if (ziptic & GZT_XYZ) { g->oldmo.x = READFIXED(g->p); @@ -5106,17 +5772,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; } } @@ -5175,8 +5840,21 @@ void G_GhostTicker(void) } if (ziptic & EZT_SPRITE) g->mo->sprite = READUINT8(g->p); + if (ziptic & EZT_KART) + g->p += 12; // kartitem, kartamount, kartbumpers } +#ifdef DEMO_COMPAT_100 + if (g->version != 0x0001) + { +#endif + if (READUINT8(g->p) != 0xFF) // Make sure there isn't other ghost data here. + I_Error("Ghost is not a record attack ghost"); //@TODO lmao don't blow up like this +#ifdef DEMO_COMPAT_100 + } +#endif + +skippedghosttic: // Tick ghost colors (Super and Mario Invincibility flashing) switch(g->color) { @@ -5206,6 +5884,181 @@ void G_GhostTicker(void) } } +// Demo rewinding functions +typedef struct rewindinfo_s { + tic_t leveltime; + + struct { + boolean ingame; + player_t player; + mobj_t mobj; + } playerinfo[MAXPLAYERS]; + + struct rewindinfo_s *prev; +} rewindinfo_t; + +static tic_t currentrewindnum; +static rewindinfo_t *rewindhead = NULL; // Reverse chronological order + +void G_InitDemoRewind(void) +{ + while (rewindhead) + { + rewindinfo_t *p = rewindhead->prev; + Z_Free(rewindhead); + rewindhead = p; + } + + currentrewindnum = 0; +} + +void G_StoreRewindInfo(void) +{ + static UINT8 timetolog = 8; + rewindinfo_t *info; + size_t i; + + if (timetolog-- > 0) + return; + timetolog = 8; + + info = Z_Calloc(sizeof(rewindinfo_t), PU_STATIC, NULL); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + info->playerinfo[i].ingame = false; + continue; + } + + info->playerinfo[i].ingame = true; + memcpy(&info->playerinfo[i].player, &players[i], sizeof(player_t)); + if (players[i].mo) + memcpy(&info->playerinfo[i].mobj, players[i].mo, sizeof(mobj_t)); + } + + info->leveltime = leveltime; + info->prev = rewindhead; + rewindhead = info; +} + +void G_PreviewRewind(tic_t previewtime) +{ + SINT8 i; + size_t j; + fixed_t tweenvalue = 0; + rewindinfo_t *info = rewindhead, *next_info = rewindhead; + + if (!info) + return; + + while (info->leveltime > previewtime && info->prev) + { + next_info = info; + info = info->prev; + } + if (info != next_info) + tweenvalue = FixedDiv(previewtime - info->leveltime, next_info->leveltime - info->leveltime); + + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + if (info->playerinfo[i].player.mo) + { + //@TODO spawn temp object to act as a player display + } + + continue; + } + + if (!info->playerinfo[i].ingame || !info->playerinfo[i].player.mo) + { + if (players[i].mo) + players[i].mo->flags2 |= MF2_DONTDRAW; + + continue; + } + + if (!players[i].mo) + continue; //@TODO spawn temp object to act as a player display + + players[i].mo->flags2 &= ~MF2_DONTDRAW; + + P_UnsetThingPosition(players[i].mo); +#define TWEEN(pr) info->playerinfo[i].mobj.pr + FixedMul((INT32) (next_info->playerinfo[i].mobj.pr - info->playerinfo[i].mobj.pr), tweenvalue) + players[i].mo->x = TWEEN(x); + players[i].mo->y = TWEEN(y); + players[i].mo->z = TWEEN(z); + players[i].mo->angle = TWEEN(angle); +#undef TWEEN + P_SetThingPosition(players[i].mo); + + players[i].frameangle = info->playerinfo[i].player.frameangle + FixedMul((INT32) (next_info->playerinfo[i].player.frameangle - info->playerinfo[i].player.frameangle), tweenvalue); + + players[i].mo->sprite = info->playerinfo[i].mobj.sprite; + players[i].mo->frame = info->playerinfo[i].mobj.frame; + + players[i].realtime = info->playerinfo[i].player.realtime; + for (j = 0; j < NUMKARTSTUFF; j++) + players[i].kartstuff[j] = info->playerinfo[i].player.kartstuff[j]; + } + + for (i = splitscreen; i >= 0; i--) + P_ResetCamera(&players[displayplayers[i]], &camera[i]); +} + +void G_ConfirmRewind(tic_t rewindtime) +{ + SINT8 i; + tic_t j; + boolean oldmenuactive = menuactive, oldsounddisabled = sound_disabled; + + INT32 olddp1 = displayplayers[0], olddp2 = displayplayers[1], olddp3 = displayplayers[2], olddp4 = displayplayers[3]; + UINT8 oldss = splitscreen; + + menuactive = false; // Prevent loops + + CV_StealthSetValue(&cv_renderview, 0); + + if (rewindtime > starttime) + { + sound_disabled = true; // Prevent sound spam + demo.rewinding = true; + } + else + demo.rewinding = false; + + G_DoPlayDemo(NULL); // Restart the current demo + + for (j = 0; j < rewindtime && leveltime < rewindtime; i++) + { + //TryRunTics(1); + G_Ticker((j % NEWTICRATERATIO) == 0); + } + + demo.rewinding = false; + menuactive = oldmenuactive; // Bring the menu back up + sound_disabled = oldsounddisabled; // Re-enable SFX + + wipegamestate = gamestate; // No fading back in! + + COM_BufInsertText("renderview on\n"); + + splitscreen = oldss; + displayplayers[0] = olddp1; + displayplayers[1] = olddp2; + displayplayers[2] = olddp3; + displayplayers[3] = olddp4; + R_ExecuteSetViewSize(); + G_ResetViews(); + + for (i = splitscreen; i >= 0; i--) + P_ResetCamera(&players[displayplayers[i]], &camera[i]); +} + void G_ReadMetalTic(mobj_t *metal) { UINT8 ziptic; @@ -5271,7 +6124,7 @@ void G_ReadMetalTic(mobj_t *metal) speed = FixedDiv(P_AproxDistance(oldmetal.momx, oldmetal.momy), metal->scale)>>FRACBITS; // Use speed to decide an appropriate state - if (speed > 28) // default skin runspeed + if (speed > 20) // default skin runspeed statetype = 2; else if (speed > 1) // stopspeed statetype = 1; @@ -5420,9 +6273,12 @@ void G_RecordDemo(const char *name) { INT32 maxsize; + CONS_Printf("Recording demo %s.lmp\n", name); + strcpy(demoname, name); strcat(demoname, ".lmp"); - maxsize = 1024*1024; + //@TODO make a maxdemosize cvar + maxsize = 1024*1024*2; if (M_CheckParm("-maxdemo") && M_IsNextParm()) maxsize = atoi(M_GetNextParm()) * 1024; // if (demobuffer) @@ -5431,7 +6287,7 @@ void G_RecordDemo(const char *name) demobuffer = malloc(maxsize); demoend = demobuffer + maxsize; - demorecording = true; + demo.recording = true; } void G_RecordMetal(void) @@ -5448,16 +6304,23 @@ void G_RecordMetal(void) void G_BeginRecording(void) { - UINT8 i; + UINT8 i, p; char name[16]; player_t *player = &players[consoleplayer]; + char *filename; + UINT8 totalfiles; + UINT8 *m; + if (demo_p) return; memset(name,0,sizeof(name)); demo_p = demobuffer; - demoflags = DF_GHOST|(modeattacking<important) + { + nameonly(( filename = va("%s", wadfiles[i]->filename) )); + WRITESTRINGN(demo_p, filename, 64); + WRITEMEM(demo_p, wadfiles[i]->md5sum, 16); + + totalfiles++; + } + + WRITEUINT8(m, totalfiles); + switch ((demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { case ATTACKING_NONE: // 0 @@ -5494,69 +6380,69 @@ void G_BeginRecording(void) WRITEUINT32(demo_p,P_GetInitSeed()); - // Name - for (i = 0; i < 16 && cv_playername.string[i]; i++) - name[i] = cv_playername.string[i]; - for (; i < 16; i++) - name[i] = '\0'; - M_Memcpy(demo_p,name,16); - demo_p += 16; + // Reserved for extrainfo location from start of file + demoinfo_p = demo_p; + WRITEUINT32(demo_p, 0); - // Skin - for (i = 0; i < 16 && cv_skin.string[i]; i++) - name[i] = cv_skin.string[i]; - for (; i < 16; i++) - name[i] = '\0'; - M_Memcpy(demo_p,name,16); - demo_p += 16; + // Save netvars + CV_SaveNetVars(&demo_p, true); - // Color - for (i = 0; i < 16 && cv_playercolor.string[i]; i++) - name[i] = cv_playercolor.string[i]; - for (; i < 16; i++) - name[i] = '\0'; - M_Memcpy(demo_p,name,16); - demo_p += 16; + // Now store some info for each in-game player + for (p = 0; p < MAXPLAYERS; p++) { + if (playeringame[p]) { + player = &players[p]; - // Stats - WRITEUINT8(demo_p,player->charability); - WRITEUINT8(demo_p,player->charability2); - WRITEUINT8(demo_p,player->actionspd>>FRACBITS); - WRITEUINT8(demo_p,player->mindash>>FRACBITS); - WRITEUINT8(demo_p,player->maxdash>>FRACBITS); - // SRB2kart - WRITEUINT8(demo_p,player->kartspeed); - WRITEUINT8(demo_p,player->kartweight); - // - WRITEUINT8(demo_p,player->normalspeed>>FRACBITS); - WRITEUINT8(demo_p,player->runspeed>>FRACBITS); - WRITEUINT8(demo_p,player->thrustfactor); - WRITEUINT8(demo_p,player->accelstart); - WRITEUINT8(demo_p,player->acceleration); + WRITEUINT8(demo_p, p | (player->spectator ? DEMO_SPECTATOR : 0)); - // Trying to convert it back to % causes demo desync due to precision loss. - // Don't do it. - WRITEFIXED(demo_p, player->jumpfactor); + // Name + memset(name, 0, 16); + strncpy(name, player_names[p], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; - // Save netvar data (SONICCD, etc) - CV_SaveNetVars(&demo_p); + // Skin + memset(name, 0, 16); + strncpy(name, skins[player->skin].name, 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + + // Color + memset(name, 0, 16); + strncpy(name, KartColor_Names[player->skincolor], 16); + M_Memcpy(demo_p,name,16); + demo_p += 16; + + // Score, since Kart uses this to determine where you start on the map + WRITEUINT32(demo_p, player->score); + + // Kart speed and weight + WRITEUINT8(demo_p, skins[player->skin].kartspeed); + WRITEUINT8(demo_p, skins[player->skin].kartweight); + } + } + + WRITEUINT8(demo_p, 0xFF); // Denote the end of the player listing memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldghost,0,sizeof(oldghost)); memset(&ghostext,0,sizeof(ghostext)); - ghostext.lastcolor = ghostext.color = GHC_NORMAL; - ghostext.lastscale = ghostext.scale = FRACUNIT; - if (player->mo) + for (i = 0; i < MAXPLAYERS; i++) { - oldghost.x = player->mo->x; - oldghost.y = player->mo->y; - oldghost.z = player->mo->z; - oldghost.angle = player->mo->angle; + ghostext[i].lastcolor = ghostext[i].color = GHC_NORMAL; + ghostext[i].lastscale = ghostext[i].scale = FRACUNIT; - // preticker started us gravity flipped - if (player->mo->eflags & MFE_VERTICALFLIP) - ghostext.flags |= EZT_FLIP; + if (players[i].mo) + { + oldghost[i].x = players[i].mo->x; + oldghost[i].y = players[i].mo->y; + oldghost[i].z = players[i].mo->z; + oldghost[i].angle = players[i].mo->angle; + + // preticker started us gravity flipped + if (players[i].mo->eflags & MFE_VERTICALFLIP) + ghostext[i].flags |= EZT_FLIP; + } } } @@ -5588,9 +6474,44 @@ void G_BeginMetal(void) oldmetal.angle = mo->angle; } +void G_WriteStanding(UINT8 ranking, char *name, INT32 skinnum, UINT8 color, UINT32 val) +{ + char temp[16]; + + if (demoinfo_p && (UINT32)(*demoinfo_p) == 0) + { + WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker + WRITEUINT32(demoinfo_p, demo_p - demobuffer); + } + + WRITEUINT8(demo_p, DW_STANDING); + WRITEUINT8(demo_p, ranking); + + // Name + memset(temp, 0, 16); + strncpy(temp, name, 16); + M_Memcpy(demo_p,temp,16); + demo_p += 16; + + // Skin + memset(temp, 0, 16); + strncpy(temp, skins[skinnum].name, 16); + M_Memcpy(demo_p,temp,16); + demo_p += 16; + + // Color + memset(temp, 0, 16); + strncpy(temp, KartColor_Names[color], 16); + M_Memcpy(demo_p,temp,16); + demo_p += 16; + + // Score/time/whatever + WRITEUINT32(demo_p, val); +} + void G_SetDemoTime(UINT32 ptime, UINT32 plap) { - if (!demorecording || !demotime_p) + if (!demo.recording || !demotime_p) return; if (demoflags & DF_RECORDATTACK) { @@ -5606,6 +6527,165 @@ void G_SetDemoTime(UINT32 ptime, UINT32 plap) }*/ } +static void G_LoadDemoExtraFiles(UINT8 **pp) +{ + UINT8 totalfiles; + char filename[MAX_WADPATH]; + UINT8 md5sum[16]; + filestatus_t ncs; + boolean toomany = false; + boolean alreadyloaded; + UINT8 i, j; + + totalfiles = READUINT8((*pp)); + for (i = 0; i < totalfiles; ++i) + { + if (toomany) + SKIPSTRING((*pp)); + else + { + strlcpy(filename, (char *)(*pp), sizeof filename); + SKIPSTRING((*pp)); + } + READMEM((*pp), md5sum, 16); + + if (!toomany) + { + alreadyloaded = false; + + for (j = 0; j < numwadfiles; ++j) + { + if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0) + { + alreadyloaded = true; + break; + } + } + + if (alreadyloaded) + continue; + + if (numwadfiles >= MAX_WADFILES) + toomany = true; + else + ncs = findfile(filename, md5sum, false); + + if (toomany) + { + CONS_Alert(CONS_WARNING, M_GetText("Too many files loaded to add anymore for demo playback\n")); + if (!CON_Ready()) + M_StartMessage(M_GetText("There are too many files loaded to add this demo's add-ons.\n\nDemo playback may desync.\n\nPress ESC\n"), NULL, MM_NOTHING); + } + else if (ncs != FS_FOUND) + { + if (ncs == FS_NOTFOUND) + CONS_Alert(CONS_NOTICE, M_GetText("You do not have a copy of %s\n"), filename); + else if (ncs == FS_MD5SUMBAD) + CONS_Alert(CONS_NOTICE, M_GetText("Checksum mismatch on %s\n"), filename); + else + CONS_Alert(CONS_NOTICE, M_GetText("Unknown error finding file %s\n"), filename); + + if (!CON_Ready()) + M_StartMessage(M_GetText("There were errors trying to add this demo's add-ons. Check the console for more information.\n\nDemo playback may desync.\n\nPress ESC\n"), NULL, MM_NOTHING); + } + else + { + P_AddWadFile(filename); + } + } + } +} + +static void G_SkipDemoExtraFiles(UINT8 **pp) +{ + UINT8 totalfiles; + UINT8 i; + + totalfiles = READUINT8((*pp)); + for (i = 0; i < totalfiles; ++i) + { + SKIPSTRING((*pp));// file name + (*pp) += 16;// md5 + } +} + +// G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's. +// Enabling quick prevents filesystem checks to see if needed files are available to load. +static UINT8 G_CheckDemoExtraFiles(UINT8 **pp, boolean quick) +{ + UINT8 totalfiles, filesloaded, nmusfilecount; + char filename[MAX_WADPATH]; + UINT8 md5sum[16]; + boolean toomany = false; + boolean alreadyloaded; + UINT8 i, j; + UINT8 error = 0; + + totalfiles = READUINT8((*pp)); + filesloaded = 0; + for (i = 0; i < totalfiles; ++i) + { + if (toomany) + SKIPSTRING((*pp)); + else + { + strlcpy(filename, (char *)(*pp), sizeof filename); + SKIPSTRING((*pp)); + } + READMEM((*pp), md5sum, 16); + + if (!toomany) + { + alreadyloaded = false; + nmusfilecount = 0; + + for (j = 0; j < numwadfiles; ++j) + { + if (wadfiles[j]->important && j > mainwads) + nmusfilecount++; + else + continue; + + if (memcmp(md5sum, wadfiles[j]->md5sum, 16) == 0) + { + alreadyloaded = true; + + if (i != nmusfilecount-1 && error < DFILE_ERROR_OUTOFORDER) + error |= DFILE_ERROR_OUTOFORDER; + + break; + } + } + + if (alreadyloaded) + { + filesloaded++; + continue; + } + + if (numwadfiles >= MAX_WADFILES) + error = DFILE_ERROR_CANNOTLOAD; + else if (!quick && findfile(filename, md5sum, false) != FS_FOUND) + error = DFILE_ERROR_CANNOTLOAD; + else if (error < DFILE_ERROR_INCOMPLETEOUTOFORDER) + error |= DFILE_ERROR_NOTLOADED; + } else + error = DFILE_ERROR_CANNOTLOAD; + } + + // Get final file count + nmusfilecount = 0; + + for (j = 0; j < numwadfiles; ++j) + if (wadfiles[j]->important && j > mainwads) + nmusfilecount++; + + if (!error && filesloaded < nmusfilecount) + error = DFILE_ERROR_EXTRAFILES; + + return error; +} + // Returns bitfield: // 1 == new demo has lower time // 2 == new demo has higher score @@ -5636,12 +6716,15 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) I_Assert(c == SUBVERSION); s = READUINT16(p); I_Assert(s == DEMOVERSION); + p += 64; // full demo title p += 16; // demo checksum I_Assert(!memcmp(p, "PLAY", 4)); p += 4; // PLAY p += 2; // gamemap p += 16; // map md5 flags = READUINT8(p); // demoflags + p++; // gametype + G_SkipDemoExtraFiles(&p); aflags = flags & (DF_RECORDATTACK|DF_NIGHTSATTACK); I_Assert(aflags); @@ -5682,7 +6765,15 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) switch(oldversion) // demoversion { case DEMOVERSION: // latest always supported + p += 64; // full demo title break; +#ifdef DEMO_COMPAT_100 + case 0x0001: + // Old replays gotta go :] + CONS_Alert(CONS_NOTICE, M_GetText("File '%s' outdated version. It will be overwritten. Nyeheheh.\n"), oldname); + Z_Free(buffer); + return UINT8_MAX; +#endif // too old, cannot support. default: CONS_Alert(CONS_NOTICE, M_GetText("File '%s' invalid format. It will be overwritten.\n"), oldname); @@ -5699,6 +6790,8 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) p += 2; // gamemap p += 16; // mapmd5 flags = READUINT8(p); + p++; // gametype + G_SkipDemoExtraFiles(&p); if (!(flags & aflags)) { CONS_Alert(CONS_NOTICE, M_GetText("File '%s' not from same game mode. It will be overwritten.\n"), oldname); @@ -5730,6 +6823,188 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname) return c; } +void G_LoadDemoInfo(menudemo_t *pdemo) +{ + UINT8 *infobuffer, *info_p, *extrainfo_p; + UINT8 version, subversion, pdemoflags; + UINT16 pdemoversion, count; + + if (!FIL_ReadFile(pdemo->filepath, &infobuffer)) + { + CONS_Alert(CONS_ERROR, M_GetText("Failed to read file '%s'.\n"), pdemo->filepath); + pdemo->type = MD_INVALID; + sprintf(pdemo->title, "INVALID REPLAY"); + + return; + } + + info_p = infobuffer; + + if (memcmp(info_p, DEMOHEADER, 12)) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is not a SRB2Kart replay file.\n"), pdemo->filepath); + pdemo->type = MD_INVALID; + sprintf(pdemo->title, "INVALID REPLAY"); + Z_Free(infobuffer); + return; + } + + pdemo->type = MD_LOADED; + + info_p += 12; // DEMOHEADER + + version = READUINT8(info_p); + subversion = READUINT8(info_p); + pdemoversion = READUINT16(info_p); + + switch(pdemoversion) + { + case DEMOVERSION: // latest always supported + // demo title + M_Memcpy(pdemo->title, info_p, 64); + info_p += 64; + + break; +#ifdef DEMO_COMPAT_100 + case 0x0001: + pdemo->type = MD_OUTDATED; + sprintf(pdemo->title, "Legacy Replay"); + break; +#endif + // too old, cannot support. + default: + CONS_Alert(CONS_ERROR, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemo->filepath); + pdemo->type = MD_INVALID; + sprintf(pdemo->title, "INVALID REPLAY"); + Z_Free(infobuffer); + return; + } + + if (version != VERSION || subversion != SUBVERSION) + pdemo->type = MD_OUTDATED; + + info_p += 16; // demo checksum + if (memcmp(info_p, "PLAY", 4)) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is the wrong type of recording and cannot be played.\n"), pdemo->filepath); + pdemo->type = MD_INVALID; + sprintf(pdemo->title, "INVALID REPLAY"); + Z_Free(infobuffer); + return; + } + info_p += 4; // "PLAY" + pdemo->map = READINT16(info_p); + info_p += 16; // mapmd5 + + pdemoflags = READUINT8(info_p); + + // temp? + if (!(pdemoflags & DF_MULTIPLAYER)) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is not a multiplayer replay and can't be listed on this menu fully yet.\n"), pdemo->filepath); + Z_Free(infobuffer); + return; + } +#ifdef DEMO_COMPAT_100 + else if (pdemoversion == 0x0001) + { + CONS_Alert(CONS_ERROR, M_GetText("%s is a legacy multiplayer replay and cannot be played.\n"), pdemo->filepath); + pdemo->type = MD_INVALID; + sprintf(pdemo->title, "INVALID REPLAY"); + Z_Free(infobuffer); + return; + } +#endif + + pdemo->gametype = READUINT8(info_p); + + pdemo->addonstatus = G_CheckDemoExtraFiles(&info_p, true); + info_p += 4; // RNG seed + + extrainfo_p = infobuffer + READUINT32(info_p); + + // Pared down version of CV_LoadNetVars to find the kart speed + pdemo->kartspeed = 1; // Default to normal speed + count = READUINT16(info_p); + while (count--) + { + UINT16 netid; + char *svalue; + + netid = READUINT16(info_p); + svalue = (char *)info_p; + SKIPSTRING(info_p); + info_p++; // stealth + + if (netid == cv_kartspeed.netid) + { + UINT8 j; + for (j = 0; kartspeed_cons_t[j].strvalue; j++) + if (!stricmp(kartspeed_cons_t[j].strvalue, svalue)) + pdemo->kartspeed = kartspeed_cons_t[j].value; + } + else if (netid == cv_basenumlaps.netid && pdemo->gametype == GT_RACE) + pdemo->numlaps = atoi(svalue); + } + + if (pdemoflags & DF_ENCORE) + pdemo->kartspeed |= DF_ENCORE; + + /*// Temporary info until this is actually present in replays. + (void)extrainfo_p; + sprintf(pdemo->winnername, "transrights420"); + pdemo->winnerskin = 1; + pdemo->winnercolor = SKINCOLOR_MOONSLAM; + pdemo->winnertime = 6666;*/ + + // Read standings! + count = 0; + + while (READUINT8(extrainfo_p) == DW_STANDING) // Assume standings are always first in the extrainfo + { + INT32 i; + char temp[16]; + + pdemo->standings[count].ranking = READUINT8(extrainfo_p); + + // Name + M_Memcpy(pdemo->standings[count].name, extrainfo_p, 16); + extrainfo_p += 16; + + // Skin + M_Memcpy(temp,extrainfo_p,16); + extrainfo_p += 16; + pdemo->standings[count].skin = UINT8_MAX; + for (i = 0; i < numskins; i++) + if (stricmp(skins[i].name, temp) == 0) + { + pdemo->standings[count].skin = i; + break; + } + + // Color + M_Memcpy(temp,extrainfo_p,16); + extrainfo_p += 16; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i],temp)) // SRB2kart + { + pdemo->standings[count].color = i; + break; + } + + // Score/time/whatever + pdemo->standings[count].timeorscore = READUINT32(extrainfo_p); + + count++; + + if (count >= MAXPLAYERS) + break; //@TODO still cycle through the rest of these if extra demo data is ever used + } + + // I think that's everything we need? + Z_Free(infobuffer); +} + // // G_PlayDemo // @@ -5737,7 +7012,7 @@ void G_DeferedPlayDemo(const char *name) { COM_BufAddText("playdemo \""); COM_BufAddText(name); - COM_BufAddText("\"\n"); + COM_BufAddText("\" -addfiles\n"); } // @@ -5746,62 +7021,76 @@ void G_DeferedPlayDemo(const char *name) #define SKIPERRORS void G_DoPlayDemo(char *defdemoname) { - UINT8 i; + UINT8 i, p; lumpnum_t l; char skin[17],color[17],*n,*pdemoname; - UINT8 version,subversion,charability,charability2,kartspeed,kartweight,thrustfactor,accelstart,acceleration; + UINT8 version,subversion; UINT32 randseed; - fixed_t actionspd,mindash,maxdash,normalspeed,runspeed,jumpfactor; char msg[1024]; #if defined(SKIPERRORS) && !defined(DEVELOP) boolean skiperrors = false; #endif + boolean spectator; + UINT8 slots[MAXPLAYERS], kartspeed[MAXPLAYERS], kartweight[MAXPLAYERS], numslots = 0; + + G_InitDemoRewind(); skin[16] = '\0'; color[16] = '\0'; - n = defdemoname+strlen(defdemoname); - while (*n != '/' && *n != '\\' && n != defdemoname) - n--; - if (n != defdemoname) - n++; - pdemoname = ZZ_Alloc(strlen(n)+1); - strcpy(pdemoname,n); - - // Internal if no extension, external if one exists - if (FIL_CheckExtension(defdemoname)) + // No demo name means we're restarting the current demo + if (defdemoname == NULL) { - //FIL_DefaultExtension(defdemoname, ".lmp"); - if (!FIL_ReadFile(defdemoname, &demobuffer)) + demo_p = demobuffer; + pdemoname = ZZ_Alloc(1); // Easier than adding checks for this everywhere it's freed + } + else + { + n = defdemoname+strlen(defdemoname); + while (*n != '/' && *n != '\\' && n != defdemoname) + n--; + if (n != defdemoname) + n++; + pdemoname = ZZ_Alloc(strlen(n)+1); + strcpy(pdemoname,n); + + M_SetPlaybackMenuPointer(); + + // Internal if no extension, external if one exists + if (FIL_CheckExtension(defdemoname)) { - snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname); + //FIL_DefaultExtension(defdemoname, ".lmp"); + if (!FIL_ReadFile(defdemoname, &demobuffer)) + { + snprintf(msg, 1024, M_GetText("Failed to read file '%s'.\n"), defdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + gameaction = ga_nothing; + M_StartMessage(msg, NULL, MM_NOTHING); + return; + } + demo_p = demobuffer; + } + // load demo resource from WAD + else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) + { + snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); CONS_Alert(CONS_ERROR, "%s", msg); gameaction = ga_nothing; M_StartMessage(msg, NULL, MM_NOTHING); return; } - demo_p = demobuffer; - } - // load demo resource from WAD - else if ((l = W_CheckNumForName(defdemoname)) == LUMPERROR) - { - snprintf(msg, 1024, M_GetText("Failed to read lump '%s'.\n"), defdemoname); - CONS_Alert(CONS_ERROR, "%s", msg); - gameaction = ga_nothing; - M_StartMessage(msg, NULL, MM_NOTHING); - return; - } - else // it's an internal demo - { - demobuffer = demo_p = W_CacheLumpNum(l, PU_STATIC); + else // it's an internal demo + { + demobuffer = demo_p = W_CacheLumpNum(l, PU_STATIC); #if defined(SKIPERRORS) && !defined(DEVELOP) - skiperrors = true; // SRB2Kart: Don't print warnings for staff ghosts, since they'll inevitably happen when we make bugfixes/changes... + skiperrors = true; // SRB2Kart: Don't print warnings for staff ghosts, since they'll inevitably happen when we make bugfixes/changes... #endif + } } // read demo header gameaction = ga_nothing; - demoplayback = true; + demo.playback = true; if (memcmp(demo_p, DEMOHEADER, 12)) { snprintf(msg, 1024, M_GetText("%s is not a SRB2Kart replay file.\n"), pdemoname); @@ -5809,19 +7098,27 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); - demoplayback = false; - titledemo = false; + demo.playback = false; + demo.title = false; return; } demo_p += 12; // DEMOHEADER version = READUINT8(demo_p); subversion = READUINT8(demo_p); - demoversion = READUINT16(demo_p); - switch(demoversion) + demo.version = READUINT16(demo_p); + switch(demo.version) { case DEMOVERSION: // latest always supported + // demo title + M_Memcpy(demo.titlename, demo_p, 64); + demo_p += 64; + break; +#ifdef DEMO_COMPAT_100 + case 0x0001: + break; +#endif // too old, cannot support. default: snprintf(msg, 1024, M_GetText("%s is an incompatible replay format and cannot be played.\n"), pdemoname); @@ -5829,8 +7126,8 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); - demoplayback = false; - titledemo = false; + demo.playback = false; + demo.title = false; return; } demo_p += 16; // demo checksum @@ -5841,8 +7138,8 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); - demoplayback = false; - titledemo = false; + demo.playback = false; + demo.title = false; return; } demo_p += 4; // "PLAY" @@ -5850,7 +7147,87 @@ void G_DoPlayDemo(char *defdemoname) demo_p += 16; // mapmd5 demoflags = READUINT8(demo_p); +#ifdef DEMO_COMPAT_100 + if (demo.version == 0x0001) + { + if (demoflags & DF_MULTIPLAYER) + { + snprintf(msg, 1024, M_GetText("%s is an alpha multiplayer replay and cannot be played.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + } + else + { +#endif + gametype = READUINT8(demo_p); + + if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running. + G_SkipDemoExtraFiles(&demo_p); + else if (demo.loadfiles) + G_LoadDemoExtraFiles(&demo_p); + else if (demo.ignorefiles) + G_SkipDemoExtraFiles(&demo_p); + else + { + UINT8 error = G_CheckDemoExtraFiles(&demo_p, false); + + if (error) + { + switch (error) + { + case DFILE_ERROR_NOTLOADED: + snprintf(msg, 1024, + M_GetText("Required files for this demo are not loaded.\n\nUse\n\"playdemo %s -addfiles\"\nto load them and play the demo.\n"), + pdemoname); + break; + + case DFILE_ERROR_OUTOFORDER: + snprintf(msg, 1024, + M_GetText("Required files for this demo are loaded out of order.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + break; + + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + snprintf(msg, 1024, + M_GetText("Required files for this demo are not loaded, and some are out of order.\n\nUse\n\"playdemo %s -addfiles\"\nto load needed files and play the demo.\n"), + pdemoname); + break; + + case DFILE_ERROR_CANNOTLOAD: + snprintf(msg, 1024, + M_GetText("Required files for this demo cannot be loaded.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + break; + + case DFILE_ERROR_EXTRAFILES: + snprintf(msg, 1024, + M_GetText("You have additional files loaded beyond the demo's file list.\n\nUse\n\"playdemo %s -force\"\nto play the demo anyway.\n"), + pdemoname); + break; + } + + CONS_Alert(CONS_ERROR, "%s", msg); + if (!CON_Ready()) // In the console they'll just see the notice there! No point pulling them out. + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + } +#ifdef DEMO_COMPAT_100 + } +#endif + modeattacking = (demoflags & DF_ATTACKMASK)>>DF_ATTACKSHIFT; + multiplayer = !!(demoflags & DF_MULTIPLAYER); CON_ToggleOff(); hu_demotime = UINT32_MAX; @@ -5875,34 +7252,109 @@ void G_DoPlayDemo(char *defdemoname) // Random seed randseed = READUINT32(demo_p); +#ifdef DEMO_COMPAT_100 + if (demo.version != 0x0001) +#endif + demo_p += 4; // Extrainfo location - // Player name - M_Memcpy(player_names[0],demo_p,16); - demo_p += 16; +#ifdef DEMO_COMPAT_100 + if (demo.version == 0x0001) + { + // Player name + M_Memcpy(player_names[0],demo_p,16); + demo_p += 16; - // Skin - M_Memcpy(skin,demo_p,16); - demo_p += 16; + // Skin + M_Memcpy(skin,demo_p,16); + demo_p += 16; - // Color - M_Memcpy(color,demo_p,16); - demo_p += 16; + // Color + M_Memcpy(color,demo_p,16); + demo_p += 16; - charability = READUINT8(demo_p); - charability2 = READUINT8(demo_p); - actionspd = (fixed_t)READUINT8(demo_p)< 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); + demo.playback = false; + demo.title = false; + return; + } + + // Set color + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i],color)) // SRB2kart + { + players[0].skincolor = i; + break; + } + + // net var data + CV_LoadNetVars(&demo_p); + + // Sigh ... it's an empty demo. + if (*demo_p == DEMOMARKER) + { + snprintf(msg, 1024, M_GetText("%s contains no data to be played.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + + Z_Free(pdemoname); + + memset(&oldcmd,0,sizeof(oldcmd)); + memset(&oldghost,0,sizeof(oldghost)); + memset(&ghostext,0,sizeof(ghostext)); + + CONS_Alert(CONS_WARNING, M_GetText("Demo version does not match game version. Desyncs may occur.\n")); + + // console warning messages +#if defined(SKIPERRORS) && !defined(DEVELOP) + demosynced = (!skiperrors); +#else + demosynced = true; +#endif + + // didn't start recording right away. + demo.deferstart = false; + + consoleplayer = 0; + memset(displayplayers, 0, sizeof(displayplayers)); + memset(playeringame, 0, sizeof(playeringame)); + playeringame[0] = true; + + goto post_compat; + } +#endif // net var data CV_LoadNetVars(&demo_p); @@ -5915,34 +7367,8 @@ void G_DoPlayDemo(char *defdemoname) M_StartMessage(msg, NULL, MM_NOTHING); Z_Free(pdemoname); Z_Free(demobuffer); - demoplayback = false; - titledemo = false; - 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; + demo.playback = false; + demo.title = false; return; } @@ -5950,6 +7376,7 @@ void G_DoPlayDemo(char *defdemoname) memset(&oldcmd,0,sizeof(oldcmd)); memset(&oldghost,0,sizeof(oldghost)); + memset(&ghostext,0,sizeof(ghostext)); #if defined(SKIPERRORS) && !defined(DEVELOP) if ((VERSION != version || SUBVERSION != subversion) && !skiperrors) @@ -5966,53 +7393,131 @@ void G_DoPlayDemo(char *defdemoname) #endif // didn't start recording right away. - demo_start = false; + demo.deferstart = false; /*#ifdef HAVE_BLUA LUAh_MapChange(gamemap); #endif*/ - displayplayer = consoleplayer = 0; + displayplayers[0] = consoleplayer = 0; memset(playeringame,0,sizeof(playeringame)); - playeringame[0] = true; - 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 color - for (i = 0; i < MAXSKINCOLORS; i++) - if (!stricmp(KartColor_Names[i],color)) // SRB2kart - { - players[0].skincolor = i; - break; - } - //CV_StealthSetValue(&cv_playercolor, players[0].skincolor); -- as far as I can tell this is more trouble than it's worth - if (players[0].mo) + // Load players that were in-game when the map started + p = READUINT8(demo_p); + + for (i = 1; i < MAXSPLITSCREENPLAYERS; i++) + displayplayers[i] = INT32_MAX; + + while (p != 0xFF) { - players[0].mo->color = players[0].skincolor; - oldghost.x = players[0].mo->x; - oldghost.y = players[0].mo->y; - oldghost.z = players[0].mo->z; + spectator = false; + if (p & DEMO_SPECTATOR) + { + spectator = true; + p &= ~DEMO_SPECTATOR; + + if (modeattacking) + { + snprintf(msg, 1024, M_GetText("%s is a record attack replay with spectators, and is thus invalid.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + } + slots[numslots] = p; numslots++; + + if (modeattacking && numslots > 1) + { + snprintf(msg, 1024, M_GetText("%s is a record attack replay with multiple players, and is thus invalid.\n"), pdemoname); + CONS_Alert(CONS_ERROR, "%s", msg); + M_StartMessage(msg, NULL, MM_NOTHING); + Z_Free(pdemoname); + Z_Free(demobuffer); + demo.playback = false; + demo.title = false; + return; + } + + if (!playeringame[displayplayers[0]] || players[displayplayers[0]].spectator) + displayplayers[0] = consoleplayer = serverplayer = p; + + playeringame[p] = true; + players[p].spectator = spectator; + + // Name + M_Memcpy(player_names[p],demo_p,16); + demo_p += 16; + + // Skin + M_Memcpy(skin,demo_p,16); + demo_p += 16; + SetPlayerSkin(p, skin); + + // Color + M_Memcpy(color,demo_p,16); + demo_p += 16; + for (i = 0; i < MAXSKINCOLORS; i++) + if (!stricmp(KartColor_Names[i],color)) // SRB2kart + { + players[p].skincolor = i; + break; + } + + // Score, since Kart uses this to determine where you start on the map + players[p].score = READUINT32(demo_p); + + // Kart stats, temporarily + kartspeed[p] = READUINT8(demo_p); + kartweight[p] = READUINT8(demo_p); + + if (stricmp(skins[players[p].skin].name, skin) != 0) + FindClosestSkinForStats(p, kartspeed[p], kartweight[p]); + + // Look for the next player + p = READUINT8(demo_p); } - // Set saved attribute values - // No cheat checking here, because even if they ARE wrong... - // it would only break the replay if we clipped them. - players[0].charability = charability; - players[0].charability2 = charability2; - players[0].actionspd = actionspd; - players[0].mindash = mindash; - players[0].maxdash = maxdash; - // SRB2kart - players[0].kartspeed = kartspeed; - players[0].kartweight = kartweight; - // - players[0].normalspeed = normalspeed; - players[0].runspeed = runspeed; - players[0].thrustfactor = thrustfactor; - players[0].accelstart = accelstart; - players[0].acceleration = acceleration; - players[0].jumpfactor = jumpfactor; + splitscreen = 0; - demo_start = true; + if (demo.title) + { + splitscreen = M_RandomKey(6)-1; + splitscreen = min(min(3, numslots-1), splitscreen); // Bias toward 1p and 4p views + + for (p = 0; p <= splitscreen; p++) + G_ResetView(p+1, slots[M_RandomKey(numslots)], false); + } + + R_ExecuteSetViewSize(); + +#ifdef DEMO_COMPAT_100 +post_compat: +#endif + + P_SetRandSeed(randseed); + G_InitNew(demoflags & DF_ENCORE, G_BuildMapName(gamemap), true, true); // Doesn't matter whether you reset or not here, given changes to resetplayer. + + for (i = 0; i < MAXPLAYERS; i++) + { + if (players[i].mo) + { + players[i].mo->color = players[i].skincolor; + oldghost[i].x = players[i].mo->x; + oldghost[i].y = players[i].mo->y; + oldghost[i].z = players[i].mo->z; + } + + // Set saved attribute values + // No cheat checking here, because even if they ARE wrong... + // it would only break the replay if we clipped them. + players[i].kartspeed = kartspeed[i]; + players[i].kartweight = kartweight[i]; + } + + demo.deferstart = true; } #undef SKIPERRORS @@ -6027,6 +7532,7 @@ void G_AddGhost(char *defdemoname) mapthing_t *mthing; UINT16 count, ghostversion; skin_t *ghskin = &skins[0]; + UINT8 kartspeed = UINT8_MAX, kartweight = UINT8_MAX; name[16] = '\0'; skin[16] = '\0'; @@ -6069,14 +7575,22 @@ void G_AddGhost(char *defdemoname) Z_Free(pdemoname); Z_Free(buffer); return; - } p += 12; // DEMOHEADER + } + + p += 12; // DEMOHEADER p++; // VERSION p++; // SUBVERSION + ghostversion = READUINT16(p); switch(ghostversion) { case DEMOVERSION: // latest always supported + p += 64; // title break; +#ifdef DEMO_COMPAT_100 + case 0x0001: + break; +#endif // too old, cannot support. default: CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo version incompatible.\n"), pdemoname); @@ -6084,6 +7598,7 @@ void G_AddGhost(char *defdemoname) Z_Free(buffer); return; } + M_Memcpy(md5, p, 16); p += 16; // demo checksum for (gh = ghosts; gh; gh = gh->next) if (!memcmp(md5, gh->checksum, 16)) // another ghost in the game already has this checksum? @@ -6093,16 +7608,21 @@ void G_AddGhost(char *defdemoname) Z_Free(buffer); return; } + if (memcmp(p, "PLAY", 4)) { CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Demo format unacceptable.\n"), pdemoname); Z_Free(pdemoname); Z_Free(buffer); return; - } p += 4; // "PLAY" + } + + p += 4; // "PLAY" p += 2; // gamemap p += 16; // mapmd5 (possibly check for consistency?) + flags = READUINT8(p); + if (!(flags & DF_GHOST)) { CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: No ghost data in this demo.\n"), pdemoname); @@ -6110,6 +7630,16 @@ void G_AddGhost(char *defdemoname) Z_Free(buffer); return; } + +#ifdef DEMO_COMPAT_100 + if (ghostversion != 0x0001) +#endif + p++; // gametype + +#ifdef DEMO_COMPAT_100 + if (ghostversion != 0x0001) +#endif + G_SkipDemoExtraFiles(&p); // Don't wanna modify the file list for ghosts. switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { case ATTACKING_NONE: // 0 @@ -6126,34 +7656,41 @@ void G_AddGhost(char *defdemoname) p += 4; // random seed - // Player name (TODO: Display this somehow if it doesn't match cv_playername!) - M_Memcpy(name, p,16); - p += 16; +#ifdef DEMO_COMPAT_100 + if (ghostversion == 0x0001) + { + // Player name (TODO: Display this somehow if it doesn't match cv_playername!) + M_Memcpy(name, p,16); + p += 16; - // Skin - M_Memcpy(skin, p,16); - p += 16; + // Skin + M_Memcpy(skin, p,16); + p += 16; - // Color - M_Memcpy(color, p,16); - p += 16; + // Color + M_Memcpy(color, p,16); + p += 16; - // Ghosts do not have a player structure to put this in. - p++; // charability - p++; // charability2 - p++; // actionspd - p++; // mindash - p++; // maxdash - // SRB2kart - p++; // kartspeed - p++; // kartweight - // - p++; // normalspeed - p++; // runspeed - p++; // thrustfactor - p++; // accelstart - p++; // acceleration - p += 4; // jumpfactor + // Ghosts do not have a player structure to put this in. + p++; // charability + p++; // charability2 + p++; // actionspd + p++; // mindash + p++; // maxdash + // SRB2kart + p++; // kartspeed + p++; // kartweight + // + p++; // normalspeed + p++; // runspeed + p++; // thrustfactor + p++; // accelstart + p++; // acceleration + p += 4; // jumpfactor + } + else +#endif + p += 4; // Extra data location reference // net var data count = READUINT16(p); @@ -6172,6 +7709,46 @@ void G_AddGhost(char *defdemoname) return; } +#ifdef DEMO_COMPAT_100 + if (ghostversion != 0x0001) + { +#endif + if (READUINT8(p) != 0) + { + CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } + + // Player name (TODO: Display this somehow if it doesn't match cv_playername!) + M_Memcpy(name, p, 16); + p += 16; + + // Skin + M_Memcpy(skin, p, 16); + p += 16; + + // Color + M_Memcpy(color, p, 16); + p += 16; + + p += 4; // score + + kartspeed = READUINT8(p); + kartweight = READUINT8(p); + + if (READUINT8(p) != 0xFF) + { + CONS_Alert(CONS_NOTICE, M_GetText("Failed to add ghost %s: Invalid player slot.\n"), pdemoname); + Z_Free(pdemoname); + Z_Free(buffer); + return; + } +#ifdef DEMO_COMPAT_100 + } +#endif + for (i = 0; i < numskins; i++) if (!stricmp(skins[i].name,skin)) { @@ -6181,10 +7758,10 @@ void G_AddGhost(char *defdemoname) 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; + if (kartspeed != UINT8_MAX && kartweight != UINT8_MAX) + ghskin = &skins[GetSkinNumClosestToStats(kartspeed, kartweight)]; + + CONS_Alert(CONS_NOTICE, M_GetText("Ghost %s: Invalid character. Falling back to %s.\n"), pdemoname, ghskin->name); } gh = Z_Calloc(sizeof(demoghost), PU_LEVEL, NULL); @@ -6261,30 +7838,56 @@ void G_UpdateStaffGhostName(lumpnum_t l) if (memcmp(p, DEMOHEADER, 12)) { goto fail; - } p += 12; // DEMOHEADER + } + + p += 12; // DEMOHEADER p++; // VERSION p++; // SUBVERSION + ghostversion = READUINT16(p); switch(ghostversion) { case DEMOVERSION: // latest always supported + p += 64; // full demo title break; + +#ifdef DEMO_COMPAT_100 + case 0x0001: + break; +#endif + // too old, cannot support. default: goto fail; } + p += 16; // demo checksum + if (memcmp(p, "PLAY", 4)) { goto fail; - } p += 4; // "PLAY" + } + + p += 4; // "PLAY" p += 2; // gamemap p += 16; // mapmd5 (possibly check for consistency?) + flags = READUINT8(p); if (!(flags & DF_GHOST)) { goto fail; // we don't NEED to do it here, but whatever } + +#ifdef DEMO_COMPAT_100 + if (ghostversion != 0x0001) +#endif + p++; // Gametype + +#ifdef DEMO_COMPAT_100 + if (ghostversion != 0x0001) +#endif + G_SkipDemoExtraFiles(&p); + switch ((flags & DF_ATTACKMASK)>>DF_ATTACKSHIFT) { case ATTACKING_NONE: // 0 @@ -6298,9 +7901,34 @@ void G_UpdateStaffGhostName(lumpnum_t l) default: // 3 break; } + p += 4; // random seed - // Player name + +#ifdef DEMO_COMPAT_100 + if (ghostversion == 0x0001) + { + // Player name + M_Memcpy(dummystaffname, p,16); + dummystaffname[16] = '\0'; + goto fail; // Not really a failure but whatever + } +#endif + + p += 4; // Extrainfo location marker + + // Ehhhh don't need ghostversion here (?) so I'll reuse the var here + ghostversion = READUINT16(p); + while (ghostversion--) + { + p += 2; + SKIPSTRING(p); + p++; // stealth + } + + // Assert first player is in and then read name + if (READUINT8(p) != 0) + goto fail; M_Memcpy(dummystaffname, p,16); dummystaffname[16] = '\0'; @@ -6323,7 +7951,7 @@ void G_TimeDemo(const char *name) restorecv_vidwait = cv_vidwait.value; if (cv_vidwait.value) CV_Set(&cv_vidwait, "0"); - timingdemo = true; + demo.timing = true; singletics = true; framecount = 0; demostarttime = I_GetTime(); @@ -6371,6 +7999,10 @@ void G_DoPlayMetal(void) { case DEMOVERSION: // latest always supported break; +#ifdef DEMO_COMPAT_100 + case 0x0001: + I_Error("You need to implement demo compat here, doofus! %s:%d", __FILE__, __LINE__); +#endif // too old, cannot support. default: CONS_Alert(CONS_WARNING, M_GetText("Failed to load bot recording for this map, format version incompatible.\n")); @@ -6453,13 +8085,16 @@ void G_StopDemo(void) { Z_Free(demobuffer); demobuffer = NULL; - demoplayback = false; - if (titledemo) + demo.playback = false; + if (demo.title) modeattacking = false; - titledemo = false; - timingdemo = false; + demo.title = false; + demo.timing = false; singletics = false; + CV_SetValue(&cv_playbackspeed, 1); + demo.rewinding = false; + if (gamestate == GS_LEVEL && rendermode != render_none) { V_SetPaletteLump("PLAYPAL"); // Reset the palette @@ -6478,8 +8113,6 @@ void G_StopDemo(void) boolean G_CheckDemoStatus(void) { - boolean saved; - while (ghosts) { demoghost *next = ghosts->next; @@ -6490,7 +8123,7 @@ boolean G_CheckDemoStatus(void) // DO NOT end metal sonic demos here - if (timingdemo) + if (demo.timing) { INT32 demotime; double f1, f2; @@ -6498,7 +8131,7 @@ boolean G_CheckDemoStatus(void) if (!demotime) return true; G_StopDemo(); - timingdemo = false; + demo.timing = false; f1 = (double)demotime; f2 = (double)framecount*TICRATE; CONS_Printf(M_GetText("timed %u gametics in %d realtics\n%f seconds, %f avg fps\n"), leveltime,demotime,f1/TICRATE,f2/f1); @@ -6508,49 +8141,165 @@ boolean G_CheckDemoStatus(void) return true; } - if (demoplayback) + if (demo.playback) { - if (singledemo) + if (demo.quitafterplaying) I_Quit(); - G_StopDemo(); - if (modeattacking) - M_EndModeAttackRun(); + if (multiplayer && !demo.title) + G_ExitLevel(); else - D_AdvanceDemo(); - - return true; - } - - if (demorecording) - { - UINT8 *p = demobuffer+16; // checksum position -#ifdef NOMD5 - UINT8 i; - WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - for (i = 0; i < 16; i++, p++) - *p = P_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. -#else - WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker - md5_buffer((char *)p+16, demo_p - (p+16), p); // make a checksum of everything after the checksum in the file. -#endif - saved = FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer); // finally output the file. - free(demobuffer); - demorecording = false; - - if (modeattacking != ATTACKING_RECORD) { - if (saved) - CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); + G_StopDemo(); + + if (modeattacking) + M_EndModeAttackRun(); else - CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname); + D_AdvanceDemo(); } + return true; } + if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) + { + G_SaveDemo(); + return true; + } + demo.recording = false; + return false; } +void G_SaveDemo(void) +{ + UINT8 *p = demobuffer+16; // checksum position +#ifdef NOMD5 + UINT8 i; +#endif + + // Ensure extrainfo pointer is always available, even if no info is present. + if (demoinfo_p && (UINT32)(*demoinfo_p) == 0) + { + WRITEUINT8(demo_p, DEMOMARKER); // add the demo end marker + WRITEUINT32(demoinfo_p, (UINT32)(demo_p - demobuffer)); + } + WRITEUINT8(demo_p, DW_END); // Mark end of demo extra data. + + M_Memcpy(p, demo.titlename, 64); // Write demo title here + p += 64; + + if (multiplayer) + { + // Change the demo's name to be a slug of the title + char demo_slug[128]; + char *writepoint; + size_t i, strindex = 0; + boolean dash = true; + + for (i = 0; demo.titlename[i] && i < 127; i++) + { + if ((demo.titlename[i] >= 'a' && demo.titlename[i] <= 'z') || + (demo.titlename[i] >= '0' && demo.titlename[i] <= '9')) + { + demo_slug[strindex] = demo.titlename[i]; + strindex++; + dash = false; + } + else if (demo.titlename[i] >= 'A' && demo.titlename[i] <= 'Z') + { + demo_slug[strindex] = demo.titlename[i] + 'a' - 'A'; + strindex++; + dash = false; + } + else if (!dash) + { + demo_slug[strindex] = '-'; + strindex++; + dash = true; + } + } + + demo_slug[strindex] = 0; + if (dash) demo_slug[strindex-1] = 0; + + writepoint = strstr(demoname, "-") + 1; + demo_slug[128 - (writepoint - demoname) - 4] = 0; + sprintf(writepoint, "%s.lmp", demo_slug); + } + +#ifdef NOMD5 + for (i = 0; i < 16; i++, p++) + *p = M_RandomByte(); // This MD5 was chosen by fair dice roll and most likely < 50% correct. +#else + // Make a checksum of everything after the checksum in the file up to the end of the standard data. Extrainfo is freely modifiable. + md5_buffer((char *)p+16, (demobuffer + (UINT32)*demoinfo_p) - (p+16), p); +#endif + + + if (FIL_WriteFile(va(pandf, srb2home, demoname), demobuffer, demo_p - demobuffer)) // finally output the file. + demo.savemode = DSM_SAVED; + free(demobuffer); + demo.recording = false; + + if (modeattacking != ATTACKING_RECORD) + { + if (demo.savemode == DSM_SAVED) + CONS_Printf(M_GetText("Demo %s recorded\n"), demoname); + else + CONS_Alert(CONS_WARNING, M_GetText("Demo %s not saved\n"), demoname); + } +} + +boolean G_DemoTitleResponder(event_t *ev) +{ + size_t len; + INT32 ch; + + if (ev->type != ev_keydown) + return false; + + ch = (INT32)ev->data1; + + // Only ESC and non-keyboard keys abort connection + if (ch == KEY_ESCAPE) + { + demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING; + return true; + } + + if (ch == KEY_ENTER || ch >= KEY_MOUSE1) + { + demo.savemode = DSM_WILLSAVE; + return true; + } + + if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART]) + || ch == ' ') // Allow spaces, of course + { + len = strlen(demo.titlename); + if (len < 64) + { + demo.titlename[len+1] = 0; + demo.titlename[len] = CON_ShiftChar(ch); + } + } + else if (ch == KEY_BACKSPACE) + { + if (shiftdown) + memset(demo.titlename, 0, sizeof(demo.titlename)); + else + { + len = strlen(demo.titlename); + + if (len > 0) + demo.titlename[len-1] = 0; + } + } + + return true; +} + // // G_SetGamestate // diff --git a/src/g_game.h b/src/g_game.h index fc7a4a4f5..a69f91421 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -36,11 +36,61 @@ extern boolean playeringame[MAXPLAYERS]; // ====================================== // demoplaying back and demo recording -extern boolean demoplayback, titledemo, fromtitledemo, demorecording, timingdemo; +extern consvar_t cv_recordmultiplayerdemos, cv_netdemosyncquality; + +// Publicly-accessible demo vars +struct demovars_s { + char titlename[65]; + boolean recording, playback, timing; + UINT16 version; // Current file format of the demo being played + boolean title; // Title Screen demo can be cancelled by any key + boolean rewinding; // Rewind in progress + + boolean loadfiles, ignorefiles; // Demo file loading options + boolean fromtitle; // SRB2Kart: Don't stop the music + boolean inreplayhut; // Go back to replayhut after demos + boolean quitafterplaying; // quit after playing a demo from cmdline + boolean deferstart; // don't start playing demo right away + + tic_t savebutton; // Used to determine when the local player can choose to save the replay while the race is still going + enum { + DSM_NOTSAVING, + DSM_WILLAUTOSAVE, + DSM_TITLEENTRY, + DSM_WILLSAVE, + DSM_SAVED + } savemode; +}; + +extern struct demovars_s demo; + +typedef enum { + MD_NOTLOADED, + MD_LOADED, + MD_SUBDIR, + MD_OUTDATED, + MD_INVALID +} menudemotype_e; + +typedef struct menudemo_s { + char filepath[256]; + menudemotype_e type; + + char title[65]; // Null-terminated for string prints + UINT16 map; + UINT8 addonstatus; // What do we need to do addon-wise to play this demo? + UINT8 gametype; + UINT8 kartspeed; // Add OR DF_ENCORE for encore mode, idk + UINT8 numlaps; + + struct { + UINT8 ranking; + char name[17]; + UINT8 skin, color; + UINT32 timeorscore; + } standings[MAXPLAYERS]; +} menudemo_t; -// Quit after playing a demo from cmdline. -extern boolean singledemo; -extern boolean demo_start; extern mobj_t *metalplayback; @@ -56,15 +106,16 @@ 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*/; extern consvar_t cv_useranalog, cv_useranalog2, cv_useranalog3, cv_useranalog4; extern consvar_t cv_analog, cv_analog2, cv_analog3, cv_analog4; -extern consvar_t cv_turnaxis,cv_moveaxis,cv_brakeaxis,cv_aimaxis,cv_lookaxis,cv_fireaxis,cv_driftaxis; -extern consvar_t cv_turnaxis2,cv_moveaxis2,cv_brakeaxis2,cv_aimaxis2,cv_lookaxis2,cv_fireaxis2,cv_driftaxis2; -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_turnaxis,cv_moveaxis,cv_brakeaxis,cv_aimaxis,cv_lookaxis,cv_fireaxis,cv_driftaxis,cv_deadzone; +extern consvar_t cv_turnaxis2,cv_moveaxis2,cv_brakeaxis2,cv_aimaxis2,cv_lookaxis2,cv_fireaxis2,cv_driftaxis2,cv_deadzone2; +extern consvar_t cv_turnaxis3,cv_moveaxis3,cv_brakeaxis3,cv_aimaxis3,cv_lookaxis3,cv_fireaxis3,cv_driftaxis3,cv_deadzone3; +extern consvar_t cv_turnaxis4,cv_moveaxis4,cv_brakeaxis4,cv_aimaxis4,cv_lookaxis4,cv_fireaxis4,cv_driftaxis4,cv_deadzone4; extern consvar_t cv_ghost_besttime, cv_ghost_bestlap, cv_ghost_last, cv_ghost_guest, cv_ghost_staff; typedef enum @@ -101,9 +152,9 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming); boolean InputDown(INT32 gc, UINT8 p); INT32 JoyAxis(axis_input_e axissel, UINT8 p); -extern angle_t localangle, localangle2, localangle3, localangle4; -extern INT32 localaiming, localaiming2, localaiming3, localaiming4; // should be an angle_t but signed -extern boolean camspin, camspin2, camspin3, camspin4; // SRB2Kart +extern angle_t localangle[MAXSPLITSCREENPLAYERS]; +extern INT32 localaiming[MAXSPLITSCREENPLAYERS]; // should be an angle_t but signed +extern boolean camspin[MAXSPLITSCREENPLAYERS]; // SRB2Kart // // GAME @@ -127,6 +178,7 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar UINT8 ssplayers, boolean FLS); void G_DoLoadLevel(boolean resetplayer); +void G_LoadDemoInfo(menudemo_t *pdemo); void G_DeferedPlayDemo(const char *demo); // Can be called by the startup code or M_Responder, calls P_SetupLevel. @@ -143,6 +195,7 @@ void G_BeginRecording(void); void G_BeginMetal(void); // Only called by shutdown code. +void G_WriteStanding(UINT8 ranking, char *name, INT32 skinnum, UINT8 color, UINT32 val); void G_SetDemoTime(UINT32 ptime, UINT32 plap); UINT8 G_CmpDemoTime(char *oldname, char *newname); @@ -154,19 +207,41 @@ typedef enum GHC_INVINCIBLE } ghostcolor_t; +extern UINT8 demo_extradata[MAXPLAYERS]; +extern UINT8 demo_writerng; +#define DXD_RESPAWN 0x01 // "respawn" command in console +#define DXD_SKIN 0x02 // skin changed +#define DXD_NAME 0x04 // name changed +#define DXD_COLOR 0x08 // color changed +#define DXD_PLAYSTATE 0x10 // state changed between playing, spectating, or not in-game + +#define DXD_PST_PLAYING 0x01 +#define DXD_PST_SPECTATING 0x02 +#define DXD_PST_LEFT 0x03 + // Record/playback tics +void G_ReadDemoExtraData(void); +void G_WriteDemoExtraData(void); void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum); void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum); -void G_GhostAddThok(void); -void G_GhostAddSpin(void); -void G_GhostAddRev(void); -void G_GhostAddColor(ghostcolor_t color); -void G_GhostAddFlip(void); -void G_GhostAddScale(fixed_t scale); -void G_GhostAddHit(mobj_t *victim); -void G_WriteGhostTic(mobj_t *ghost); -void G_ConsGhostTic(void); +void G_GhostAddThok(INT32 playernum); +void G_GhostAddSpin(INT32 playernum); +void G_GhostAddRev(INT32 playernum); +void G_GhostAddColor(INT32 playernum, ghostcolor_t color); +void G_GhostAddFlip(INT32 playernum); +void G_GhostAddScale(INT32 playernum, fixed_t scale); +void G_GhostAddHit(INT32 playernum, mobj_t *victim); +void G_WriteAllGhostTics(void); +void G_WriteGhostTic(mobj_t *ghost, INT32 playernum); +void G_ConsAllGhostTics(void); +void G_ConsGhostTic(INT32 playernum); void G_GhostTicker(void); + +void G_InitDemoRewind(void); +void G_StoreRewindInfo(void); +void G_PreviewRewind(tic_t previewtime); +void G_ConfirmRewind(tic_t rewindtime); + void G_ReadMetalTic(mobj_t *metal); void G_WriteMetalTic(mobj_t *metal); void G_SaveMetal(UINT8 **buffer); @@ -183,6 +258,13 @@ typedef struct demoghost { } demoghost; extern demoghost *ghosts; +// G_CheckDemoExtraFiles: checks if our loaded WAD list matches the demo's. +#define DFILE_ERROR_NOTLOADED 0x01 // Files are not loaded, but can be without a restart. +#define DFILE_ERROR_OUTOFORDER 0x02 // Files are loaded, but out of order. +#define DFILE_ERROR_INCOMPLETEOUTOFORDER 0x03 // Some files are loaded out of order, but others are not. +#define DFILE_ERROR_CANNOTLOAD 0x04 // Files are missing and cannot be loaded. +#define DFILE_ERROR_EXTRAFILES 0x05 // Extra files outside of the replay's file list are loaded. + void G_DoPlayDemo(char *defdemoname); void G_TimeDemo(const char *name); void G_AddGhost(char *defdemoname); @@ -193,7 +275,10 @@ void G_StopMetalDemo(void); ATTRNORETURN void FUNCNORETURN G_StopMetalRecording(void); void G_StopDemo(void); boolean G_CheckDemoStatus(void); +void G_SaveDemo(void); +boolean G_DemoTitleResponder(event_t *ev); +INT32 G_GetGametypeByName(const char *gametypestr); boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); boolean G_GametypeHasTeams(void); @@ -213,6 +298,16 @@ void G_EndGame(void); // moved from y_inter.c/h and renamed void G_Ticker(boolean run); boolean G_Responder(event_t *ev); +boolean G_CouldView(INT32 playernum); +boolean G_CanView(INT32 playernum, UINT8 viewnum, boolean onlyactive); + +INT32 G_FindView(INT32 startview, UINT8 viewnum, boolean onlyactive, boolean reverse); +INT32 G_CountPlayersPotentiallyViewable(boolean active); + +void G_ResetViews(void); +void G_ResetView(UINT8 viewnum, INT32 playernum, boolean onlyactive); +void G_AdjustView(UINT8 viewnum, INT32 offset, boolean onlyactive); + void G_AddPlayer(INT32 playernum); void G_SetExitGameFlag(void); diff --git a/src/g_input.c b/src/g_input.c index cab358303..08a323c78 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -1239,6 +1239,8 @@ void G_ClearAllControlKeys(void) { G_ClearControlKeys(gamecontrol, i); G_ClearControlKeys(gamecontrolbis, i); + G_ClearControlKeys(gamecontrol3, i); + G_ClearControlKeys(gamecontrol4, i); } } diff --git a/src/hardware/hw3sound.c b/src/hardware/hw3sound.c index f7c6e1da0..2594a5df9 100644 --- a/src/hardware/hw3sound.c +++ b/src/hardware/hw3sound.c @@ -296,7 +296,7 @@ static void HW3S_FillSourceParameters data->max_distance = MAX_DISTANCE; data->min_distance = MIN_DISTANCE; - if (origin && origin != players[displayplayer].mo) + if (origin && origin != players[displayplayers[0]].mo) { data->head_relative = false; @@ -356,10 +356,10 @@ INT32 HW3S_I_StartSound(const void *origin_p, source3D_data_t *source_parm, chan source3D_data_t source3d_data; INT32 s_num = 0; source_t *source = NULL; - mobj_t *listenmobj = players[displayplayer].mo; + mobj_t *listenmobj = players[displayplayers[0]].mo; // TODO: Kart 4P does not support sounds properly here mobj_t *listenmobj2 = NULL; - if (splitscreen) listenmobj2 = players[secondarydisplayplayer].mo; + if (splitscreen) listenmobj2 = players[displayplayers[1]].mo; if (sound_disabled) return -1; @@ -876,12 +876,12 @@ static void HW3S_Update3DSource(source_t *src) void HW3S_UpdateSources(void) { - mobj_t *listener = players[displayplayer].mo; + mobj_t *listener = players[displayplayers[0]].mo; mobj_t *listener2 = NULL; source_t *src; INT32 audible, snum, volume, sep, pitch; - if (splitscreen) listener2 = players[secondarydisplayplayer].mo; + if (splitscreen) listener2 = players[displayplayers[1]].mo; HW3S_UpdateListener2(listener2); HW3S_UpdateListener(listener); diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c index 483932492..9e454bcd5 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_cache.c b/src/hardware/hw_cache.c index 78fc31afc..d49531bdf 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -241,43 +241,6 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight, if (blockheight < 1) I_Error("3D GenerateTexture : too small"); } - else if (cv_voodoocompatibility.value) - { - if (originalwidth > 256 || originalheight > 256) - { - blockwidth = 256; - while (originalwidth < blockwidth) - blockwidth >>= 1; - if (blockwidth < 1) - I_Error("3D GenerateTexture : too small"); - - blockheight = 256; - while (originalheight < blockheight) - blockheight >>= 1; - if (blockheight < 1) - I_Error("3D GenerateTexture : too small"); - } - else - { - //size up to nearest power of 2 - blockwidth = 1; - while (blockwidth < originalwidth) - blockwidth <<= 1; - // scale down the original graphics to fit in 256 - if (blockwidth > 256) - blockwidth = 256; - //I_Error("3D GenerateTexture : too big"); - - //size up to nearest power of 2 - blockheight = 1; - while (blockheight < originalheight) - blockheight <<= 1; - // scale down the original graphics to fit in 256 - if (blockheight > 256) - blockheight = 255; - //I_Error("3D GenerateTexture : too big"); - } - } else { //size up to nearest power of 2 @@ -508,18 +471,6 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm newwidth = blockwidth; newheight = blockheight; } - else if (cv_voodoocompatibility.value) // Only scales down textures that exceed 256x256. - { - // no rounddown, do not size up patches, so they don't look 'scaled' - newwidth = min(grPatch->width, blockwidth); - newheight = min(grPatch->height, blockheight); - - if (newwidth > 256 || newheight > 256) - { - newwidth = blockwidth; - newheight = blockheight; - } - } else { // no rounddown, do not size up patches, so they don't look 'scaled' @@ -935,18 +886,6 @@ GLPatch_t *HWR_GetPic(lumpnum_t lumpnum) newwidth = blockwidth; newheight = blockheight; } - else if (cv_voodoocompatibility.value) // Only scales down textures that exceed 256x256. - { - // no rounddown, do not size up patches, so they don't look 'scaled' - newwidth = min(SHORT(pic->width),blockwidth); - newheight = min(SHORT(pic->height),blockheight); - - if (newwidth > 256 || newheight > 256) - { - newwidth = blockwidth; - newheight = blockheight; - } - } else { // no rounddown, do not size up patches, so they don't look 'scaled' diff --git a/src/hardware/hw_clip.c b/src/hardware/hw_clip.c index 6d120efe7..a63527083 100644 --- a/src/hardware/hw_clip.c +++ b/src/hardware/hw_clip.c @@ -72,13 +72,14 @@ #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" #ifdef HAVE_SPHEREFRUSTRUM -static GLdouble viewMatrix[16]; -static GLdouble projMatrix[16]; +static GLfloat viewMatrix[16]; +static GLfloat projMatrix[16]; float frustum[6][4]; #endif @@ -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; @@ -380,8 +381,8 @@ void gld_FrustrumSetup(void) float t; float clip[16]; - pglGetDoublev(GL_PROJECTION_MATRIX, projMatrix); - pglGetDoublev(GL_MODELVIEW_MATRIX, viewMatrix); + pglGeFloatv(GL_PROJECTION_MATRIX, projMatrix); + pglGetFloatv(GL_MODELVIEW_MATRIX, viewMatrix); clip[0] = CALCMATRIX(0, 0, 1, 4, 2, 8, 3, 12); clip[1] = CALCMATRIX(0, 1, 1, 5, 2, 9, 3, 13); diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index ece627d3c..9ecb16a56 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -46,8 +46,8 @@ typedef unsigned char FBOOLEAN; #define HWR_PATCHES_CHROMAKEY_COLORINDEX 0 #define HWR_CHROMAKEY_EQUIVALENTCOLORINDEX 1 #else -#define HWR_PATCHES_CHROMAKEY_COLORINDEX 247 -#define HWR_CHROMAKEY_EQUIVALENTCOLORINDEX 220 +#define HWR_PATCHES_CHROMAKEY_COLORINDEX 255 +#define HWR_CHROMAKEY_EQUIVALENTCOLORINDEX 130 #endif // the chroma key color shows on border sprites, set it to black @@ -101,15 +101,29 @@ typedef struct //Hurdler: Transform (coords + angles) //BP: transform order : scale(rotation_x(rotation_y(translation(v)))) + +// Kart features +#define USE_FTRANSFORM_ANGLEZ +#define USE_FTRANSFORM_MIRROR + +// Vanilla features +//#define USE_MODEL_NEXTFRAME + typedef struct { FLOAT x,y,z; // position +#ifdef USE_FTRANSFORM_ANGLEZ FLOAT anglex,angley,anglez; // aimingangle / viewangle +#else + FLOAT anglex,angley; // aimingangle / viewangle +#endif FLOAT scalex,scaley,scalez; FLOAT fovxangle, fovyangle; UINT8 splitscreen; boolean flip; // screenflip +#ifdef USE_FTRANSFORM_MIRROR boolean mirror; // SRB2Kart: Encore Mode +#endif } FTransform; // Transformed vector, as passed to HWR API @@ -152,7 +166,7 @@ enum EPolyFlags // When set, pass the color constant into the FSurfaceInfo -> FlatColor PF_NoTexture = 0x00002000, // Use the small white texture PF_Corona = 0x00004000, // Tell the rendrer we are drawing a corona - PF_MD2 = 0x00008000, // Tell the rendrer we are drawing an MD2 + PF_Unused = 0x00008000, // Unused PF_RemoveYWrap = 0x00010000, // Force clamp texture on Y PF_ForceWrapX = 0x00020000, // Force repeat texture on X PF_ForceWrapY = 0x00040000, // Force repeat texture on Y @@ -210,8 +224,6 @@ enum hwdsetspecialstate HWD_SET_FOG_COLOR, HWD_SET_FOG_DENSITY, HWD_SET_FOV, - HWD_SET_POLYGON_SMOOTH, - HWD_SET_PALETTECOLOR, HWD_SET_TEXTUREFILTERMODE, HWD_SET_TEXTUREANISOTROPICMODE, HWD_NUMSTATE diff --git a/src/hardware/hw_dll.h b/src/hardware/hw_dll.h index 6b9f4d538..466452783 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_drv.h b/src/hardware/hw_drv.h index e2fa90eb0..54bd9e786 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -58,20 +58,18 @@ EXPORT void HWRAPI(ClearMipMapCache) (void); EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value); //Hurdler: added for new development -EXPORT void HWRAPI(DrawMD2) (INT32 *gl_cmd_buffer, md2_frame_t *frame, FTransform *pos, float scale); -EXPORT void HWRAPI(DrawMD2i) (INT32 *gl_cmd_buffer, md2_frame_t *frame, INT32 duration, INT32 tics, md2_frame_t *nextframe, FTransform *pos, float scale, UINT8 flipped, UINT8 *color); +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 *color); +EXPORT void HWRAPI(CreateModelVBOs) (model_t *model); EXPORT void HWRAPI(SetTransform) (FTransform *ptransform); EXPORT INT32 HWRAPI(GetTextureUsed) (void); EXPORT INT32 HWRAPI(GetRenderVersion) (void); -#ifdef SHUFFLE #define SCREENVERTS 10 EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]); -#endif EXPORT void HWRAPI(FlushScreenTextures) (void); EXPORT void HWRAPI(StartScreenWipe) (void); EXPORT void HWRAPI(EndScreenWipe) (void); -EXPORT void HWRAPI(DoScreenWipe) (float alpha); +EXPORT void HWRAPI(DoScreenWipe) (void); EXPORT void HWRAPI(DrawIntermissionBG) (void); EXPORT void HWRAPI(MakeScreenTexture) (void); EXPORT void HWRAPI(MakeScreenFinalTexture) (void); @@ -96,8 +94,8 @@ struct hwdriver_s GClipRect pfnGClipRect; ClearMipMapCache pfnClearMipMapCache; SetSpecialState pfnSetSpecialState;//Hurdler: added for backward compatibility - DrawMD2 pfnDrawMD2; - DrawMD2i pfnDrawMD2i; + DrawModel pfnDrawModel; + CreateModelVBOs pfnCreateModelVBOs; SetTransform pfnSetTransform; GetTextureUsed pfnGetTextureUsed; GetRenderVersion pfnGetRenderVersion; @@ -107,9 +105,7 @@ struct hwdriver_s #ifndef HAVE_SDL Shutdown pfnShutdown; #endif -#ifdef SHUFFLE PostImgRedraw pfnPostImgRedraw; -#endif FlushScreenTextures pfnFlushScreenTextures; StartScreenWipe pfnStartScreenWipe; EndScreenWipe pfnEndScreenWipe; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index c45814bbc..63cde0ca0 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -63,7 +63,7 @@ struct hwdriver_s hwdriver; // ========================================================================== -static void HWR_AddSprites(sector_t *sec, UINT8 ssplayer); +static void HWR_AddSprites(sector_t *sec); static void HWR_ProjectSprite(mobj_t *thing); #ifdef HWPRECIP static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing); @@ -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}; @@ -3392,7 +3389,7 @@ static void HWR_AddPolyObjectPlanes(void) // : Draw one or more line segments. // Notes : Sets gr_cursectorlight to the light of the parent sector, to modulate wall textures // -----------------+ -static void HWR_Subsector(size_t num, UINT8 ssplayer) +static void HWR_Subsector(size_t num) { INT16 count; seg_t *line; @@ -3757,7 +3754,7 @@ static void HWR_Subsector(size_t num, UINT8 ssplayer) { // draw sprites first, coz they are clipped to the solidsegs of // subsectors more 'in front' - HWR_AddSprites(gr_frontsector, ssplayer); + HWR_AddSprites(gr_frontsector); //Hurdler: at this point validcount must be the same, but is not because // gr_frontsector doesn't point anymore to sub->sector due to @@ -3809,7 +3806,7 @@ static boolean HWR_CheckHackBBox(fixed_t *bb) // BP: big hack for a test in lighning ref : 1249753487AB fixed_t *hwbbox; -static void HWR_RenderBSPNode(INT32 bspnum, UINT8 ssplayer) +static void HWR_RenderBSPNode(INT32 bspnum) { /*//GZDoom code if(bspnum == -1) @@ -3849,12 +3846,12 @@ static void HWR_RenderBSPNode(INT32 bspnum, UINT8 ssplayer) if (bspnum == -1) { //*(gr_drawsubsector_p++) = 0; - HWR_Subsector(0, ssplayer); + HWR_Subsector(0); } else { //*(gr_drawsubsector_p++) = bspnum&(~NF_SUBSECTOR); - HWR_Subsector(bspnum&(~NF_SUBSECTOR), ssplayer); + HWR_Subsector(bspnum&(~NF_SUBSECTOR)); } return; } @@ -3866,14 +3863,14 @@ static void HWR_RenderBSPNode(INT32 bspnum, UINT8 ssplayer) hwbbox = bsp->bbox[side]; // Recursively divide front space. - HWR_RenderBSPNode(bsp->children[side], ssplayer); + HWR_RenderBSPNode(bsp->children[side]); // Possibly divide back space. if (HWR_CheckBBox(bsp->bbox[side^1])) { // BP: big hack for a test in lighning ref : 1249753487AB hwbbox = bsp->bbox[side^1]; - HWR_RenderBSPNode(bsp->children[side^1], ssplayer); + HWR_RenderBSPNode(bsp->children[side^1]); } } @@ -4100,14 +4097,14 @@ static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float t angle_t shadowdir; // Set direction - if (splitscreen && stplyr == &players[secondarydisplayplayer]) - shadowdir = localangle2 + FixedAngle(cv_cam2_rotate.value); - else if (splitscreen > 1 && stplyr == &players[thirddisplayplayer]) - shadowdir = localangle3 + FixedAngle(cv_cam3_rotate.value); - else if (splitscreen > 2 && stplyr == &players[fourthdisplayplayer]) - shadowdir = localangle4 + FixedAngle(cv_cam4_rotate.value); + if (splitscreen && stplyr == &players[displayplayers[1]]) + shadowdir = localangle[1] + FixedAngle(cv_cam2_rotate.value); + else if (splitscreen > 1 && stplyr == &players[displayplayers[2]]) + shadowdir = localangle[2] + FixedAngle(cv_cam3_rotate.value); + else if (splitscreen > 2 && stplyr == &players[displayplayers[3]]) + shadowdir = localangle[3] + FixedAngle(cv_cam4_rotate.value); else - shadowdir = localangle + FixedAngle(cv_cam_rotate.value); + shadowdir = localangle[0] + FixedAngle(cv_cam_rotate.value); // Find floorheight floorheight = HWR_OpaqueFloorAtPos( @@ -4262,10 +4259,45 @@ static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float t } } +// This is expecting a pointer to an array containing 4 wallVerts for a sprite +static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts) +{ + if (cv_grspritebillboarding.value + && spr && spr->mobj && !(spr->mobj->frame & FF_PAPERSPRITE) + && wallVerts) + { + float basey = FIXED_TO_FLOAT(spr->mobj->z); + float lowy = wallVerts[0].y; + if (P_MobjFlip(spr->mobj) == -1) + { + basey = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height); + } + // Rotate sprites to fully billboard with the camera + // X, Y, AND Z need to be manipulated for the polys to rotate around the + // origin, because of how the origin setting works I believe that should + // be mobj->z or mobj->z + mobj->height + wallVerts[2].y = wallVerts[3].y = (spr->ty - basey) * gr_viewludsin + basey; + wallVerts[0].y = wallVerts[1].y = (lowy - basey) * gr_viewludsin + basey; + // translate back to be around 0 before translating back + wallVerts[3].x += ((spr->ty - basey) * gr_viewludcos) * gr_viewcos; + wallVerts[2].x += ((spr->ty - basey) * gr_viewludcos) * gr_viewcos; + + wallVerts[0].x += ((lowy - basey) * gr_viewludcos) * gr_viewcos; + wallVerts[1].x += ((lowy - basey) * gr_viewludcos) * gr_viewcos; + + wallVerts[3].z += ((spr->ty - basey) * gr_viewludcos) * gr_viewsin; + wallVerts[2].z += ((spr->ty - basey) * gr_viewludcos) * gr_viewsin; + + wallVerts[0].z += ((lowy - basey) * gr_viewludcos) * gr_viewsin; + wallVerts[1].z += ((lowy - basey) * gr_viewludcos) * gr_viewsin; + } +} + static void HWR_SplitSprite(gr_vissprite_t *spr) { float this_scale = 1.0f; FOutVector wallVerts[4]; + FOutVector baseWallVerts[4]; // This is what the verts should end up as GLPatch_t *gpatch; FSurfaceInfo Surf; const boolean hires = (spr->mobj && spr->mobj->skin && ((skin_t *)spr->mobj->skin)->flags & SF_HIRES); @@ -4278,11 +4310,13 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) float realtop, realbot, top, bot; float towtop, towbot, towmult; float bheight; + float realheight, heightmult; const sector_t *sector = spr->mobj->subsector->sector; const lightlist_t *list = sector->lightlist; #ifdef ESLOPE float endrealtop, endrealbot, endtop, endbot; float endbheight; + float endrealheight; fixed_t temp; fixed_t v1x, v1y, v2x, v2y; #endif @@ -4315,16 +4349,16 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) HWR_DrawSpriteShadow(spr, gpatch, this_scale); } - wallVerts[0].x = wallVerts[3].x = spr->x1; - wallVerts[2].x = wallVerts[1].x = spr->x2; - wallVerts[0].z = wallVerts[3].z = spr->z1; - wallVerts[1].z = wallVerts[2].z = spr->z2; + baseWallVerts[0].x = baseWallVerts[3].x = spr->x1; + baseWallVerts[2].x = baseWallVerts[1].x = spr->x2; + baseWallVerts[0].z = baseWallVerts[3].z = spr->z1; + baseWallVerts[1].z = baseWallVerts[2].z = spr->z2; - wallVerts[2].y = wallVerts[3].y = spr->ty; + baseWallVerts[2].y = baseWallVerts[3].y = spr->ty; if (spr->mobj && fabsf(this_scale - 1.0f) > 1.0E-36f) - wallVerts[0].y = wallVerts[1].y = spr->ty - gpatch->height * this_scale; + baseWallVerts[0].y = baseWallVerts[1].y = spr->ty - gpatch->height * this_scale; else - wallVerts[0].y = wallVerts[1].y = spr->ty - gpatch->height; + baseWallVerts[0].y = baseWallVerts[1].y = spr->ty - gpatch->height; v1x = FLOAT_TO_FIXED(spr->x1); v1y = FLOAT_TO_FIXED(spr->z1); @@ -4333,44 +4367,56 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) if (spr->flip) { - wallVerts[0].sow = wallVerts[3].sow = gpatch->max_s; - wallVerts[2].sow = wallVerts[1].sow = 0; - }else{ - wallVerts[0].sow = wallVerts[3].sow = 0; - wallVerts[2].sow = wallVerts[1].sow = gpatch->max_s; + baseWallVerts[0].sow = baseWallVerts[3].sow = gpatch->max_s; + baseWallVerts[2].sow = baseWallVerts[1].sow = 0; + } + else + { + baseWallVerts[0].sow = baseWallVerts[3].sow = 0; + baseWallVerts[2].sow = baseWallVerts[1].sow = gpatch->max_s; } // flip the texture coords (look familiar?) if (spr->vflip) { - wallVerts[3].tow = wallVerts[2].tow = gpatch->max_t; - wallVerts[0].tow = wallVerts[1].tow = 0; - }else{ - wallVerts[3].tow = wallVerts[2].tow = 0; - wallVerts[0].tow = wallVerts[1].tow = gpatch->max_t; + baseWallVerts[3].tow = baseWallVerts[2].tow = gpatch->max_t; + baseWallVerts[0].tow = baseWallVerts[1].tow = 0; + } + else + { + baseWallVerts[3].tow = baseWallVerts[2].tow = 0; + baseWallVerts[0].tow = baseWallVerts[1].tow = gpatch->max_t; } // if it has a dispoffset, push it a little towards the camera if (spr->dispoffset) { float co = -gr_viewcos*(0.05f*spr->dispoffset); float si = -gr_viewsin*(0.05f*spr->dispoffset); - wallVerts[0].z = wallVerts[3].z = wallVerts[0].z+si; - wallVerts[1].z = wallVerts[2].z = wallVerts[1].z+si; - wallVerts[0].x = wallVerts[3].x = wallVerts[0].x+co; - wallVerts[1].x = wallVerts[2].x = wallVerts[1].x+co; + baseWallVerts[0].z = baseWallVerts[3].z = baseWallVerts[0].z+si; + baseWallVerts[1].z = baseWallVerts[2].z = baseWallVerts[1].z+si; + baseWallVerts[0].x = baseWallVerts[3].x = baseWallVerts[0].x+co; + baseWallVerts[1].x = baseWallVerts[2].x = baseWallVerts[1].x+co; } - realtop = top = wallVerts[3].y; - realbot = bot = wallVerts[0].y; - towtop = wallVerts[3].tow; - towbot = wallVerts[0].tow; + // Let dispoffset work first since this adjust each vertex + HWR_RotateSpritePolyToAim(spr, baseWallVerts); + + realtop = top = baseWallVerts[3].y; + realbot = bot = baseWallVerts[0].y; + towtop = baseWallVerts[3].tow; + towbot = baseWallVerts[0].tow; towmult = (towbot - towtop) / (top - bot); #ifdef ESLOPE - endrealtop = endtop = wallVerts[2].y; - endrealbot = endbot = wallVerts[1].y; + endrealtop = endtop = baseWallVerts[2].y; + endrealbot = endbot = baseWallVerts[1].y; #endif + // copy the contents of baseWallVerts into the drawn wallVerts array + // baseWallVerts is used to know the final shape to easily get the vertex + // co-ordinates + memcpy(wallVerts, baseWallVerts, sizeof(baseWallVerts)); + if (!cv_translucency.value) // translucency disabled { Surf.FlatColor.s.alpha = 0xFF; @@ -4497,12 +4543,55 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) wallVerts[2].y = endtop; wallVerts[0].y = bot; wallVerts[1].y = endbot; + + // The x and y only need to be adjusted in the case that it's not a papersprite + if (cv_grspritebillboarding.value + && spr->mobj && !(spr->mobj->frame & FF_PAPERSPRITE)) + { + // Get the x and z of the vertices so billboarding draws correctly + realheight = realbot - realtop; + endrealheight = endrealbot - endrealtop; + heightmult = (realtop - top) / realheight; + wallVerts[3].x = baseWallVerts[3].x + (baseWallVerts[3].x - baseWallVerts[0].x) * heightmult; + wallVerts[3].z = baseWallVerts[3].z + (baseWallVerts[3].z - baseWallVerts[0].z) * heightmult; + + heightmult = (endrealtop - endtop) / endrealheight; + wallVerts[2].x = baseWallVerts[2].x + (baseWallVerts[2].x - baseWallVerts[1].x) * heightmult; + wallVerts[2].z = baseWallVerts[2].z + (baseWallVerts[2].z - baseWallVerts[1].z) * heightmult; + + heightmult = (realtop - bot) / realheight; + wallVerts[0].x = baseWallVerts[3].x + (baseWallVerts[3].x - baseWallVerts[0].x) * heightmult; + wallVerts[0].z = baseWallVerts[3].z + (baseWallVerts[3].z - baseWallVerts[0].z) * heightmult; + + heightmult = (endrealtop - endbot) / endrealheight; + wallVerts[1].x = baseWallVerts[2].x + (baseWallVerts[2].x - baseWallVerts[1].x) * heightmult; + wallVerts[1].z = baseWallVerts[2].z + (baseWallVerts[2].z - baseWallVerts[1].z) * heightmult; + } #else wallVerts[3].tow = wallVerts[2].tow = towtop + ((realtop - top) * towmult); wallVerts[0].tow = wallVerts[1].tow = towtop + ((realtop - bot) * towmult); wallVerts[2].y = wallVerts[3].y = top; wallVerts[0].y = wallVerts[1].y = bot; + + // The x and y only need to be adjusted in the case that it's not a papersprite + if (cv_grspritebillboarding.value + && spr->mobj && !(spr->mobj->frame & FF_PAPERSPRITE)) + { + // Get the x and z of the vertices so billboarding draws correctly + realheight = realbot - realtop; + heightmult = (realtop - top) / realheight; + wallVerts[3].x = baseWallVerts[3].x + (baseWallVerts[3].x - baseWallVerts[0].x) * heightmult; + wallVerts[3].z = baseWallVerts[3].z + (baseWallVerts[3].z - baseWallVerts[0].z) * heightmult; + wallVerts[2].x = baseWallVerts[2].x + (baseWallVerts[2].x - baseWallVerts[1].x) * heightmult; + wallVerts[2].z = baseWallVerts[2].z + (baseWallVerts[2].z - baseWallVerts[1].z) * heightmult; + + heightmult = (realtop - bot) / realheight; + wallVerts[0].x = baseWallVerts[3].x + (baseWallVerts[3].x - baseWallVerts[0].x) * heightmult; + wallVerts[0].z = baseWallVerts[3].z + (baseWallVerts[3].z - baseWallVerts[0].z) * heightmult; + wallVerts[1].x = baseWallVerts[2].x + (baseWallVerts[2].x - baseWallVerts[1].x) * heightmult; + wallVerts[1].z = baseWallVerts[2].z + (baseWallVerts[2].z - baseWallVerts[1].z) * heightmult; + } #endif if (colormap) @@ -4672,6 +4761,9 @@ static void HWR_DrawSprite(gr_vissprite_t *spr) wallVerts[1].x = wallVerts[2].x = wallVerts[1].x+co; } + // Let dispoffset work first since this adjust each vertex + HWR_RotateSpritePolyToAim(spr, wallVerts); + // This needs to be AFTER the shadows so that the regular sprites aren't drawn completely black. // sprite lighting by modulating the RGB components /// \todo coloured @@ -4753,6 +4845,9 @@ static inline void HWR_DrawPrecipitationSprite(gr_vissprite_t *spr) wallVerts[0].z = wallVerts[3].z = spr->z1; wallVerts[1].z = wallVerts[2].z = spr->z2; + // Let dispoffset work first since this adjust each vertex + HWR_RotateSpritePolyToAim(spr, wallVerts); + wallVerts[0].sow = wallVerts[3].sow = 0; wallVerts[2].sow = wallVerts[1].sow = gpatch->max_s; @@ -5264,14 +5359,14 @@ static void HWR_DrawSprites(void) if (spr->mobj && spr->mobj->skin && spr->mobj->sprite == SPR_PLAY) { // 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))) + if (!cv_grmdls.value || ((md2_playermodels[(skin_t*)spr->mobj->skin-skins].notfound || md2_playermodels[(skin_t*)spr->mobj->skin-skins].scale < 0.0f) && ((!cv_grfallbackplayermodel.value) || md2_models[SPR_PLAY].notfound || md2_models[SPR_PLAY].scale < 0.0f))) HWR_DrawSprite(spr); else HWR_DrawMD2(spr); } else { - if (!cv_grmd2.value || md2_models[spr->mobj->sprite].notfound || md2_models[spr->mobj->sprite].scale < 0.0f) + if (!cv_grmdls.value || md2_models[spr->mobj->sprite].notfound || md2_models[spr->mobj->sprite].scale < 0.0f) HWR_DrawSprite(spr); else HWR_DrawMD2(spr); @@ -5286,7 +5381,7 @@ static void HWR_DrawSprites(void) // During BSP traversal, this adds sprites by sector. // -------------------------------------------------------------------------- static UINT8 sectorlight; -static void HWR_AddSprites(sector_t *sec, UINT8 ssplayer) +static void HWR_AddSprites(sector_t *sec) { mobj_t *thing; #ifdef HWPRECIP @@ -5319,19 +5414,19 @@ static void HWR_AddSprites(sector_t *sec, UINT8 ssplayer) if (splitscreen) { if (thing->eflags & MFE_DRAWONLYFORP1) - if (ssplayer != 1) + if (viewssnum != 0) continue; if (thing->eflags & MFE_DRAWONLYFORP2) - if (ssplayer != 2) + if (viewssnum != 1) continue; if (thing->eflags & MFE_DRAWONLYFORP3 && splitscreen > 1) - if (ssplayer != 3) + if (viewssnum != 2) continue; if (thing->eflags & MFE_DRAWONLYFORP4 && splitscreen > 2) - if (ssplayer != 4) + if (viewssnum != 3) continue; } @@ -5354,19 +5449,19 @@ static void HWR_AddSprites(sector_t *sec, UINT8 ssplayer) if (splitscreen) { if (thing->eflags & MFE_DRAWONLYFORP1) - if (ssplayer != 1) + if (viewssnum != 0) continue; if (thing->eflags & MFE_DRAWONLYFORP2) - if (ssplayer != 2) + if (viewssnum != 1) continue; if (thing->eflags & MFE_DRAWONLYFORP3 && splitscreen > 1) - if (ssplayer != 3) + if (viewssnum != 2) continue; if (thing->eflags & MFE_DRAWONLYFORP4 && splitscreen > 2) - if (ssplayer != 4) + if (viewssnum != 3) continue; } @@ -5438,7 +5533,7 @@ static void HWR_ProjectSprite(mobj_t *thing) tz = (tr_x * gr_viewcos) + (tr_y * gr_viewsin); // thing is behind view plane? - if (tz < ZCLIP_PLANE && !papersprite && (!cv_grmd2.value || md2_models[thing->sprite].notfound == true)) //Yellow: Only MD2's dont disappear + if (tz < ZCLIP_PLANE && !papersprite && (!cv_grmdls.value || md2_models[thing->sprite].notfound == true)) //Yellow: Only MD2's dont disappear return; // The above can stay as it works for cutting sprites that are too close @@ -5907,35 +6002,10 @@ 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; - if (splitscreen) - { - if (player == &players[secondarydisplayplayer]) - { - type = &postimgtype2; - ssplayer = 2; - } - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - { - type = &postimgtype3; - ssplayer = 3; - } - else if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - { - type = &postimgtype4; - ssplayer = 4; - } - else - { - type = &postimgtype; - ssplayer = 1; - } - } - else - type = &postimgtype; + type = &postimgtype[viewnumber]; { // do we really need to save player (is it not the same)? @@ -6059,36 +6129,36 @@ if (0) validcount++; - HWR_RenderBSPNode((INT32)numnodes-1, ssplayer); + HWR_RenderBSPNode((INT32)numnodes-1); #ifndef NEWCLIP // Make a viewangle int so we can render things based on mouselook if (player == &players[consoleplayer]) - viewangle = localaiming; - else if (splitscreen && player == &players[secondarydisplayplayer]) - viewangle = localaiming2; - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - viewangle = localaiming3; - else if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - viewangle = localaiming4; + viewangle = localaiming[0]; + else if (splitscreen && player == &players[displayplayers[1]]) + viewangle = localaiming[1]; + else if (splitscreen > 1 && player == &players[displayplayers[2]]) + viewangle = localaiming[2]; + else if (splitscreen > 2 && player == &players[displayplayers[3]]) + viewangle = localaiming[3]; // 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(); - HWR_RenderBSPNode((INT32)numnodes-1, ssplayer); //left + HWR_RenderBSPNode((INT32)numnodes-1); //left dup_viewangle += ANGLE_90; if (((INT32)aimingangle > ANGLE_45 || (INT32)aimingangle<-ANGLE_45)) { HWR_ClearClipSegs(); - HWR_RenderBSPNode((INT32)numnodes-1, ssplayer); //back + HWR_RenderBSPNode((INT32)numnodes-1); //back } dup_viewangle += ANGLE_90; HWR_ClearClipSegs(); - HWR_RenderBSPNode((INT32)numnodes-1, ssplayer); //right + HWR_RenderBSPNode((INT32)numnodes-1); //right dup_viewangle += ANGLE_90; } @@ -6151,39 +6221,14 @@ if (0) // ========================================================================== void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) { - const float fpov = FIXED_TO_FLOAT(cv_grfov.value+player->fovadd); - postimg_t *type; - UINT8 ssplayer = 0; + const float fpov = FIXED_TO_FLOAT(cv_fov.value+player->fovadd); + postimg_t *type = &postimgtype[viewnumber]; const boolean skybox = (skyboxmo[0] && cv_skybox.value); // True if there's a skybox object and skyboxes are on FRGBAFloat ClearColor; - if (splitscreen) - { - if (player == &players[secondarydisplayplayer]) - { - type = &postimgtype2; - ssplayer = 2; - } - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - { - type = &postimgtype3; - ssplayer = 3; - } - else if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - { - type = &postimgtype4; - ssplayer = 4; - } - else - { - type = &postimgtype; - ssplayer = 1; - } - } - else - type = &postimgtype; + type = &postimgtype[viewnumber]; ClearColor.red = 0.0f; ClearColor.green = 0.0f; @@ -6318,36 +6363,36 @@ if (0) validcount++; - HWR_RenderBSPNode((INT32)numnodes-1, ssplayer); + HWR_RenderBSPNode((INT32)numnodes-1); #ifndef NEWCLIP // Make a viewangle int so we can render things based on mouselook if (player == &players[consoleplayer]) - viewangle = localaiming; - else if (splitscreen && player == &players[secondarydisplayplayer]) - viewangle = localaiming2; - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - viewangle = localaiming3; - else if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - viewangle = localaiming4; + viewangle = localaiming[0]; + else if (splitscreen && player == &players[displayplayers[1]]) + viewangle = localaiming[1]; + else if (splitscreen > 1 && player == &players[displayplayers[2]]) + viewangle = localaiming[2]; + else if (splitscreen > 2 && player == &players[displayplayers[3]]) + viewangle = localaiming[3]; // 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(); - HWR_RenderBSPNode((INT32)numnodes-1, ssplayer); //left + HWR_RenderBSPNode((INT32)numnodes-1); //left dup_viewangle += ANGLE_90; if (((INT32)aimingangle > ANGLE_45 || (INT32)aimingangle<-ANGLE_45)) { HWR_ClearClipSegs(); - HWR_RenderBSPNode((INT32)numnodes-1, ssplayer); //back + HWR_RenderBSPNode((INT32)numnodes-1); //back } dup_viewangle += ANGLE_90; HWR_ClearClipSegs(); - HWR_RenderBSPNode((INT32)numnodes-1, ssplayer); //right + HWR_RenderBSPNode((INT32)numnodes-1); //right dup_viewangle += ANGLE_90; } @@ -6455,11 +6500,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) { @@ -6482,7 +6522,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); @@ -6804,11 +6843,6 @@ static void HWR_RenderWall(wallVert3D *wallVerts, FSurfaceInfo *pSurf, FBITFIE #endif } -void HWR_SetPaletteColor(INT32 palcolor) -{ - HWD.pfnSetSpecialState(HWD_SET_PALETTECOLOR, palcolor); -} - INT32 HWR_GetTextureUsed(void) { return HWD.pfnGetTextureUsed(); @@ -6816,16 +6850,17 @@ INT32 HWR_GetTextureUsed(void) void HWR_DoPostProcessor(player_t *player) { - postimg_t *type; + postimg_t *type = &postimgtype[0]; + UINT8 i; - if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - type = &postimgtype4; - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - type = &postimgtype3; - else if (splitscreen && player == &players[secondarydisplayplayer]) - type = &postimgtype2; - else - type = &postimgtype; + for (i = splitscreen; i > 0; i--) + { + if (player == &players[displayplayers[i]]) + { + type = &postimgtype[i]; + break; + } + } // Armageddon Blast Flash! // Could this even be considered postprocessor? @@ -6859,7 +6894,6 @@ void HWR_DoPostProcessor(player_t *player) if (splitscreen) // Not supported in splitscreen - someone want to add support? return; -#ifdef SHUFFLE // Drunken vision! WooOOooo~ if (*type == postimg_water || *type == postimg_heat) { @@ -6902,7 +6936,6 @@ void HWR_DoPostProcessor(player_t *player) HWD.pfnMakeScreenTexture(); } // Flipping of the screen isn't done here anymore -#endif // SHUFFLE } void HWR_StartScreenWipe(void) @@ -6949,7 +6982,7 @@ void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum) HWR_GetFadeMask(lumpnum); - HWD.pfnDoScreenWipe(HWRWipeCounter); // Still send in wipecounter since old stuff might not support multitexturing + HWD.pfnDoScreenWipe(); HWRWipeCounter += 0.05f; // increase opacity of end screen diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 198780f9f..7bc361d95 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -58,7 +58,6 @@ void HWR_AddCommands(void); void HWR_CorrectSWTricks(void); void transform(float *cx, float *cy, float *cz); FBITFIELD HWR_TranstableToAlpha(INT32 transtablenum, FSurfaceInfo *pSurf); -void HWR_SetPaletteColor(INT32 palcolor); INT32 HWR_GetTextureUsed(void); void HWR_DoPostProcessor(player_t *player); void HWR_StartScreenWipe(void); @@ -80,8 +79,8 @@ 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_grmdls; +extern consvar_t cv_grfallbackplayermodel; extern consvar_t cv_grfog; extern consvar_t cv_grfogcolor; extern consvar_t cv_grfogdensity; @@ -92,9 +91,9 @@ extern consvar_t cv_grgammablue; extern consvar_t cv_grfiltermode; extern consvar_t cv_granisotropicmode; extern consvar_t cv_grcorrecttricks; -extern consvar_t cv_voodoocompatibility; extern consvar_t cv_grfovchange; extern consvar_t cv_grsolvetjoin; +extern consvar_t cv_grspritebillboarding; extern float gr_viewwidth, gr_viewheight, gr_baseviewwindowx, gr_baseviewwindowy; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index e50c13652..d217f4094 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -43,6 +43,7 @@ #include "../r_draw.h" #include "../p_tick.h" #include "../k_kart.h" // colortranslations +#include "hw_model.h" #include "hw_main.h" #include "../v_video.h" @@ -75,172 +76,6 @@ #include "errno.h" #endif -#define NUMVERTEXNORMALS 162 -float avertexnormals[NUMVERTEXNORMALS][3] = { -{-0.525731f, 0.000000f, 0.850651f}, -{-0.442863f, 0.238856f, 0.864188f}, -{-0.295242f, 0.000000f, 0.955423f}, -{-0.309017f, 0.500000f, 0.809017f}, -{-0.162460f, 0.262866f, 0.951056f}, -{0.000000f, 0.000000f, 1.000000f}, -{0.000000f, 0.850651f, 0.525731f}, -{-0.147621f, 0.716567f, 0.681718f}, -{0.147621f, 0.716567f, 0.681718f}, -{0.000000f, 0.525731f, 0.850651f}, -{0.309017f, 0.500000f, 0.809017f}, -{0.525731f, 0.000000f, 0.850651f}, -{0.295242f, 0.000000f, 0.955423f}, -{0.442863f, 0.238856f, 0.864188f}, -{0.162460f, 0.262866f, 0.951056f}, -{-0.681718f, 0.147621f, 0.716567f}, -{-0.809017f, 0.309017f, 0.500000f}, -{-0.587785f, 0.425325f, 0.688191f}, -{-0.850651f, 0.525731f, 0.000000f}, -{-0.864188f, 0.442863f, 0.238856f}, -{-0.716567f, 0.681718f, 0.147621f}, -{-0.688191f, 0.587785f, 0.425325f}, -{-0.500000f, 0.809017f, 0.309017f}, -{-0.238856f, 0.864188f, 0.442863f}, -{-0.425325f, 0.688191f, 0.587785f}, -{-0.716567f, 0.681718f, -0.147621f}, -{-0.500000f, 0.809017f, -0.309017f}, -{-0.525731f, 0.850651f, 0.000000f}, -{0.000000f, 0.850651f, -0.525731f}, -{-0.238856f, 0.864188f, -0.442863f}, -{0.000000f, 0.955423f, -0.295242f}, -{-0.262866f, 0.951056f, -0.162460f}, -{0.000000f, 1.000000f, 0.000000f}, -{0.000000f, 0.955423f, 0.295242f}, -{-0.262866f, 0.951056f, 0.162460f}, -{0.238856f, 0.864188f, 0.442863f}, -{0.262866f, 0.951056f, 0.162460f}, -{0.500000f, 0.809017f, 0.309017f}, -{0.238856f, 0.864188f, -0.442863f}, -{0.262866f, 0.951056f, -0.162460f}, -{0.500000f, 0.809017f, -0.309017f}, -{0.850651f, 0.525731f, 0.000000f}, -{0.716567f, 0.681718f, 0.147621f}, -{0.716567f, 0.681718f, -0.147621f}, -{0.525731f, 0.850651f, 0.000000f}, -{0.425325f, 0.688191f, 0.587785f}, -{0.864188f, 0.442863f, 0.238856f}, -{0.688191f, 0.587785f, 0.425325f}, -{0.809017f, 0.309017f, 0.500000f}, -{0.681718f, 0.147621f, 0.716567f}, -{0.587785f, 0.425325f, 0.688191f}, -{0.955423f, 0.295242f, 0.000000f}, -{1.000000f, 0.000000f, 0.000000f}, -{0.951056f, 0.162460f, 0.262866f}, -{0.850651f, -0.525731f, 0.000000f}, -{0.955423f, -0.295242f, 0.000000f}, -{0.864188f, -0.442863f, 0.238856f}, -{0.951056f, -0.162460f, 0.262866f}, -{0.809017f, -0.309017f, 0.500000f}, -{0.681718f, -0.147621f, 0.716567f}, -{0.850651f, 0.000000f, 0.525731f}, -{0.864188f, 0.442863f, -0.238856f}, -{0.809017f, 0.309017f, -0.500000f}, -{0.951056f, 0.162460f, -0.262866f}, -{0.525731f, 0.000000f, -0.850651f}, -{0.681718f, 0.147621f, -0.716567f}, -{0.681718f, -0.147621f, -0.716567f}, -{0.850651f, 0.000000f, -0.525731f}, -{0.809017f, -0.309017f, -0.500000f}, -{0.864188f, -0.442863f, -0.238856f}, -{0.951056f, -0.162460f, -0.262866f}, -{0.147621f, 0.716567f, -0.681718f}, -{0.309017f, 0.500000f, -0.809017f}, -{0.425325f, 0.688191f, -0.587785f}, -{0.442863f, 0.238856f, -0.864188f}, -{0.587785f, 0.425325f, -0.688191f}, -{0.688191f, 0.587785f, -0.425325f}, -{-0.147621f, 0.716567f, -0.681718f}, -{-0.309017f, 0.500000f, -0.809017f}, -{0.000000f, 0.525731f, -0.850651f}, -{-0.525731f, 0.000000f, -0.850651f}, -{-0.442863f, 0.238856f, -0.864188f}, -{-0.295242f, 0.000000f, -0.955423f}, -{-0.162460f, 0.262866f, -0.951056f}, -{0.000000f, 0.000000f, -1.000000f}, -{0.295242f, 0.000000f, -0.955423f}, -{0.162460f, 0.262866f, -0.951056f}, -{-0.442863f, -0.238856f, -0.864188f}, -{-0.309017f, -0.500000f, -0.809017f}, -{-0.162460f, -0.262866f, -0.951056f}, -{0.000000f, -0.850651f, -0.525731f}, -{-0.147621f, -0.716567f, -0.681718f}, -{0.147621f, -0.716567f, -0.681718f}, -{0.000000f, -0.525731f, -0.850651f}, -{0.309017f, -0.500000f, -0.809017f}, -{0.442863f, -0.238856f, -0.864188f}, -{0.162460f, -0.262866f, -0.951056f}, -{0.238856f, -0.864188f, -0.442863f}, -{0.500000f, -0.809017f, -0.309017f}, -{0.425325f, -0.688191f, -0.587785f}, -{0.716567f, -0.681718f, -0.147621f}, -{0.688191f, -0.587785f, -0.425325f}, -{0.587785f, -0.425325f, -0.688191f}, -{0.000000f, -0.955423f, -0.295242f}, -{0.000000f, -1.000000f, 0.000000f}, -{0.262866f, -0.951056f, -0.162460f}, -{0.000000f, -0.850651f, 0.525731f}, -{0.000000f, -0.955423f, 0.295242f}, -{0.238856f, -0.864188f, 0.442863f}, -{0.262866f, -0.951056f, 0.162460f}, -{0.500000f, -0.809017f, 0.309017f}, -{0.716567f, -0.681718f, 0.147621f}, -{0.525731f, -0.850651f, 0.000000f}, -{-0.238856f, -0.864188f, -0.442863f}, -{-0.500000f, -0.809017f, -0.309017f}, -{-0.262866f, -0.951056f, -0.162460f}, -{-0.850651f, -0.525731f, 0.000000f}, -{-0.716567f, -0.681718f, -0.147621f}, -{-0.716567f, -0.681718f, 0.147621f}, -{-0.525731f, -0.850651f, 0.000000f}, -{-0.500000f, -0.809017f, 0.309017f}, -{-0.238856f, -0.864188f, 0.442863f}, -{-0.262866f, -0.951056f, 0.162460f}, -{-0.864188f, -0.442863f, 0.238856f}, -{-0.809017f, -0.309017f, 0.500000f}, -{-0.688191f, -0.587785f, 0.425325f}, -{-0.681718f, -0.147621f, 0.716567f}, -{-0.442863f, -0.238856f, 0.864188f}, -{-0.587785f, -0.425325f, 0.688191f}, -{-0.309017f, -0.500000f, 0.809017f}, -{-0.147621f, -0.716567f, 0.681718f}, -{-0.425325f, -0.688191f, 0.587785f}, -{-0.162460f, -0.262866f, 0.951056f}, -{0.442863f, -0.238856f, 0.864188f}, -{0.162460f, -0.262866f, 0.951056f}, -{0.309017f, -0.500000f, 0.809017f}, -{0.147621f, -0.716567f, 0.681718f}, -{0.000000f, -0.525731f, 0.850651f}, -{0.425325f, -0.688191f, 0.587785f}, -{0.587785f, -0.425325f, 0.688191f}, -{0.688191f, -0.587785f, 0.425325f}, -{-0.955423f, 0.295242f, 0.000000f}, -{-0.951056f, 0.162460f, 0.262866f}, -{-1.000000f, 0.000000f, 0.000000f}, -{-0.850651f, 0.000000f, 0.525731f}, -{-0.955423f, -0.295242f, 0.000000f}, -{-0.951056f, -0.162460f, 0.262866f}, -{-0.864188f, 0.442863f, -0.238856f}, -{-0.951056f, 0.162460f, -0.262866f}, -{-0.809017f, 0.309017f, -0.500000f}, -{-0.864188f, -0.442863f, -0.238856f}, -{-0.951056f, -0.162460f, -0.262866f}, -{-0.809017f, -0.309017f, -0.500000f}, -{-0.681718f, 0.147621f, -0.716567f}, -{-0.681718f, -0.147621f, -0.716567f}, -{-0.850651f, 0.000000f, -0.525731f}, -{-0.688191f, 0.587785f, -0.425325f}, -{-0.587785f, 0.425325f, -0.688191f}, -{-0.425325f, 0.688191f, -0.587785f}, -{-0.425325f, -0.688191f, -0.587785f}, -{-0.587785f, -0.425325f, -0.688191f}, -{-0.688191f, -0.587785f, -0.425325f}, -}; - md2_t md2_models[NUMSPRITES]; md2_t md2_playermodels[MAXSKINS]; @@ -248,198 +83,29 @@ md2_t md2_playermodels[MAXSKINS]; /* * free model */ -static void md2_freeModel (md2_model_t *model) +#if 0 +static void md2_freeModel (model_t *model) { - if (model) - { - if (model->skins) - free(model->skins); - - if (model->texCoords) - free(model->texCoords); - - if (model->triangles) - free(model->triangles); - - if (model->frames) - { - size_t i; - - for (i = 0; i < model->header.numFrames; i++) - { - if (model->frames[i].vertices) - free(model->frames[i].vertices); - } - free(model->frames); - } - - if (model->glCommandBuffer) - free(model->glCommandBuffer); - - free(model); - } + UnloadModel(model); } +#endif // // load model // // Hurdler: the current path is the Legacy.exe path -static md2_model_t *md2_readModel(const char *filename) +static model_t *md2_readModel(const char *filename) { - FILE *file; - md2_model_t *model; - UINT8 buffer[MD2_MAX_FRAMESIZE]; - size_t i; - - model = calloc(1, sizeof (*model)); - if (model == NULL) - return 0; - //Filename checking fixed ~Monster Iestyn and Golden - file = fopen(va("%s"PATHSEP"%s", srb2home, filename), "rb"); - if (!file) - { - file = fopen(va("%s"PATHSEP"%s", srb2path, filename), "rb"); - if (!file) - { - free(model); - return 0; - } - } - - // initialize model and read header - - if (fread(&model->header, sizeof (model->header), 1, file) != 1 - || model->header.magic != MD2_IDENT - || model->header.version != MD2_VERSION) - { - fclose(file); - free(model); - return 0; - } - - model->header.numSkins = 1; - -#define MD2LIMITCHECK(field, max, msgname) \ - if (field > max) \ - { \ - CONS_Alert(CONS_ERROR, "md2_readModel: %s has too many " msgname " (# found: %d, maximum: %d)\n", filename, field, max); \ - md2_freeModel (model); \ - fclose(file); \ - return 0; \ - } - - // Uncomment if these are actually needed -// MD2LIMITCHECK(model->header.numSkins, MD2_MAX_SKINS, "skins") -// MD2LIMITCHECK(model->header.numTexCoords, MD2_MAX_TEXCOORDS, "texture coordinates") - MD2LIMITCHECK(model->header.numTriangles, MD2_MAX_TRIANGLES, "triangles") - MD2LIMITCHECK(model->header.numFrames, MD2_MAX_FRAMES, "frames") - MD2LIMITCHECK(model->header.numVertices, MD2_MAX_VERTICES, "vertices") - -#undef MD2LIMITCHECK - - // read skins - fseek(file, model->header.offsetSkins, SEEK_SET); - if (model->header.numSkins > 0) - { - model->skins = calloc(sizeof (md2_skin_t), model->header.numSkins); - if (!model->skins || model->header.numSkins != - fread(model->skins, sizeof (md2_skin_t), model->header.numSkins, file)) - { - md2_freeModel (model); - fclose(file); - return 0; - } - } - - // read texture coordinates - fseek(file, model->header.offsetTexCoords, SEEK_SET); - if (model->header.numTexCoords > 0) - { - model->texCoords = calloc(sizeof (md2_textureCoordinate_t), model->header.numTexCoords); - if (!model->texCoords || model->header.numTexCoords != - fread(model->texCoords, sizeof (md2_textureCoordinate_t), model->header.numTexCoords, file)) - { - md2_freeModel (model); - fclose(file); - return 0; - } - } - - // read triangles - fseek(file, model->header.offsetTriangles, SEEK_SET); - if (model->header.numTriangles > 0) - { - model->triangles = calloc(sizeof (md2_triangle_t), model->header.numTriangles); - if (!model->triangles || model->header.numTriangles != - fread(model->triangles, sizeof (md2_triangle_t), model->header.numTriangles, file)) - { - md2_freeModel (model); - fclose(file); - return 0; - } - } - - // read alias frames - fseek(file, model->header.offsetFrames, SEEK_SET); - if (model->header.numFrames > 0) - { - model->frames = calloc(sizeof (md2_frame_t), model->header.numFrames); - if (!model->frames) - { - md2_freeModel (model); - fclose(file); - return 0; - } - - for (i = 0; i < model->header.numFrames; i++) - { - md2_alias_frame_t *frame = (md2_alias_frame_t *)(void *)buffer; - size_t j; - - model->frames[i].vertices = calloc(sizeof (md2_triangleVertex_t), model->header.numVertices); - if (!model->frames[i].vertices || model->header.frameSize != - fread(frame, 1, model->header.frameSize, file)) - { - md2_freeModel (model); - fclose(file); - return 0; - } - - strcpy(model->frames[i].name, frame->name); - for (j = 0; j < model->header.numVertices; j++) - { - model->frames[i].vertices[j].vertex[0] = (float) ((INT32) frame->alias_vertices[j].vertex[0]) * frame->scale[0] + frame->translate[0]; - model->frames[i].vertices[j].vertex[2] = -1* ((float) ((INT32) frame->alias_vertices[j].vertex[1]) * frame->scale[1] + frame->translate[1]); - model->frames[i].vertices[j].vertex[1] = (float) ((INT32) frame->alias_vertices[j].vertex[2]) * frame->scale[2] + frame->translate[2]; - model->frames[i].vertices[j].normal[0] = avertexnormals[frame->alias_vertices[j].lightNormalIndex][0]; - model->frames[i].vertices[j].normal[1] = avertexnormals[frame->alias_vertices[j].lightNormalIndex][1]; - model->frames[i].vertices[j].normal[2] = avertexnormals[frame->alias_vertices[j].lightNormalIndex][2]; - } - } - } - - // read gl commands - fseek(file, model->header.offsetGlCommands, SEEK_SET); - if (model->header.numGlCommands) - { - model->glCommandBuffer = calloc(sizeof (INT32), model->header.numGlCommands); - if (!model->glCommandBuffer || model->header.numGlCommands != - fread(model->glCommandBuffer, sizeof (INT32), model->header.numGlCommands, file)) - { - md2_freeModel (model); - fclose(file); - return 0; - } - } - - fclose(file); - - return model; + if (FIL_FileExists(va("%s"PATHSEP"%s", srb2home, filename))) + return LoadModel(va("%s"PATHSEP"%s", srb2home, filename), PU_STATIC); + else if (FIL_FileExists(va("%s"PATHSEP"%s", srb2path, filename))) + return LoadModel(va("%s"PATHSEP"%s", srb2path, filename), PU_STATIC); + return NULL; } -static inline void md2_printModelInfo (md2_model_t *model) +static inline void md2_printModelInfo (model_t *model) { #if 0 INT32 i; @@ -498,13 +164,13 @@ static GrTextureFormat_t PNG_Load(const char *filename, int *w, int *h, GLPatch_ #endif volatile png_FILE_p png_FILE; //Filename checking fixed ~Monster Iestyn and Golden - char *pngfilename = va("%s"PATHSEP"md2"PATHSEP"%s", srb2home, filename); + char *pngfilename = va("%s"PATHSEP"mdls"PATHSEP"%s", srb2home, filename); FIL_ForceExtension(pngfilename, ".png"); png_FILE = fopen(pngfilename, "rb"); if (!png_FILE) { - pngfilename = va("%s"PATHSEP"md2"PATHSEP"%s", srb2path, filename); + pngfilename = va("%s"PATHSEP"mdls"PATHSEP"%s", srb2path, filename); FIL_ForceExtension(pngfilename, ".png"); png_FILE = fopen(pngfilename, "rb"); //CONS_Debug(DBG_RENDER, "M_SavePNG: Error on opening %s for loading\n", filename); @@ -631,13 +297,13 @@ static GrTextureFormat_t PCX_Load(const char *filename, int *w, int *h, INT32 ch, rep; FILE *file; //Filename checking fixed ~Monster Iestyn and Golden - char *pcxfilename = va("%s"PATHSEP"md2"PATHSEP"%s", srb2home, filename); + char *pcxfilename = va("%s"PATHSEP"mdls"PATHSEP"%s", srb2home, filename); FIL_ForceExtension(pcxfilename, ".pcx"); file = fopen(pcxfilename, "rb"); if (!file) { - pcxfilename = va("%s"PATHSEP"md2"PATHSEP"%s", srb2path, filename); + pcxfilename = va("%s"PATHSEP"mdls"PATHSEP"%s", srb2path, filename); FIL_ForceExtension(pcxfilename, ".pcx"); file = fopen(pcxfilename, "rb"); if (!file) @@ -826,16 +492,16 @@ void HWR_InitMD2(void) md2_models[i].error = false; } - // read the md2.dat file + // read the mdls.dat file //Filename checking fixed ~Monster Iestyn and Golden - f = fopen(va("%s"PATHSEP"%s", srb2home, "kmd2.dat"), "rt"); + f = fopen(va("%s"PATHSEP"%s", srb2home, "mdls.dat"), "rt"); if (!f) { - f = fopen(va("%s"PATHSEP"%s", srb2path, "kmd2.dat"), "rt"); + f = fopen(va("%s"PATHSEP"%s", srb2path, "mdls.dat"), "rt"); if (!f) { - CONS_Printf("%s %s\n", M_GetText("Error while loading kmd2.dat:"), strerror(errno)); + CONS_Printf("%s %s\n", M_GetText("Error while loading mdls.dat:"), strerror(errno)); nomd2s = true; return; } @@ -844,7 +510,7 @@ void HWR_InitMD2(void) { /*if (stricmp(name, "PLAY") == 0) { - CONS_Printf("MD2 for sprite PLAY detected in kmd2.dat, use a player skin instead!\n"); + CONS_Printf("MD2 for sprite PLAY detected in mdls.dat, use a player skin instead!\n"); continue; }*/ // 8/1/19: Allow PLAY to load for default MD2. @@ -879,7 +545,7 @@ void HWR_InitMD2(void) } } // no sprite/player skin name found?!? - CONS_Printf("Unknown sprite/player skin %s detected in kmd2.dat\n", name); + CONS_Printf("Unknown sprite/player skin %s detected in mdls.dat\n", name); md2found: // move on to next line... continue; @@ -898,16 +564,16 @@ void HWR_AddPlayerMD2(int skin) // For MD2's that were added after startup CONS_Printf("AddPlayerMD2()...\n"); - // read the md2.dat file + // read the mdls.dat file //Filename checking fixed ~Monster Iestyn and Golden - f = fopen(va("%s"PATHSEP"%s", srb2home, "kmd2.dat"), "rt"); + f = fopen(va("%s"PATHSEP"%s", srb2home, "mdls.dat"), "rt"); if (!f) { - f = fopen(va("%s"PATHSEP"%s", srb2path, "kmd2.dat"), "rt"); + f = fopen(va("%s"PATHSEP"%s", srb2path, "mdls.dat"), "rt"); if (!f) { - CONS_Printf("%s %s\n", M_GetText("Error while loading kmd2.dat:"), strerror(errno)); + CONS_Printf("%s %s\n", M_GetText("Error while loading mdls.dat:"), strerror(errno)); nomd2s = true; return; } @@ -937,7 +603,7 @@ playermd2found: void HWR_AddSpriteMD2(size_t spritenum) // For MD2s that were added after startup { FILE *f; - // name[18] is used to check for names in the kmd2.dat file that match with sprites or player skins + // name[18] is used to check for names in the mdls.dat file that match with sprites or player skins // sprite names are always 4 characters long, and names is for player skins can be up to 19 characters long char name[18], filename[32]; float scale, offset; @@ -950,20 +616,20 @@ void HWR_AddSpriteMD2(size_t spritenum) // For MD2s that were added after startu // Read the md2.dat file //Filename checking fixed ~Monster Iestyn and Golden - f = fopen(va("%s"PATHSEP"%s", srb2home, "kmd2.dat"), "rt"); + f = fopen(va("%s"PATHSEP"%s", srb2home, "mdls.dat"), "rt"); if (!f) { - f = fopen(va("%s"PATHSEP"%s", srb2path, "kmd2.dat"), "rt"); + f = fopen(va("%s"PATHSEP"%s", srb2path, "mdls.dat"), "rt"); if (!f) { - CONS_Printf("%s %s\n", M_GetText("Error while loading kmd2.dat:"), strerror(errno)); + CONS_Printf("%s %s\n", M_GetText("Error while loading mdls.dat:"), strerror(errno)); nomd2s = true; return; } } - // Check for any MD2s that match the names of player skins! + // Check for any MD2s that match the names of sprite names! while (fscanf(f, "%19s %31s %f %f", name, filename, &scale, &offset) == 4) { if (stricmp(name, sprnames[spritenum]) == 0) @@ -1195,12 +861,13 @@ void HWR_DrawMD2(gr_vissprite_t *spr) FSurfaceInfo Surf; char filename[64]; - INT32 frame; + INT32 frame = 0; + INT32 nextFrame = -1; FTransform p; md2_t *md2; UINT8 color[4]; - if (!cv_grmd2.value) + if (!cv_grmdls.value) return; if (spr->precip) @@ -1248,10 +915,9 @@ void HWR_DrawMD2(gr_vissprite_t *spr) // Look at HWR_ProjectSprite for more { GLPatch_t *gpatch; - INT32 *buff; INT32 durs = spr->mobj->state->tics; INT32 tics = spr->mobj->tics; - md2_frame_t *curr, *next = NULL; + //mdlframe_t *next = NULL; const UINT8 flip = (UINT8)((spr->mobj->eflags & MFE_VERTICALFLIP) == MFE_VERTICALFLIP); spritedef_t *sprdef; spriteframe_t *sprframe; @@ -1285,13 +951,14 @@ void HWR_DrawMD2(gr_vissprite_t *spr) return; // we already failed loading this before :( if (!md2->model) { - CONS_Debug(DBG_RENDER, "Loading MD2... (%s, %s)", sprnames[spr->mobj->sprite], md2->filename); - sprintf(filename, "md2/%s", md2->filename); + CONS_Debug(DBG_RENDER, "Loading model... (%s, %s)", sprnames[spr->mobj->sprite], md2->filename); + sprintf(filename, "mdls/%s", md2->filename); md2->model = md2_readModel(filename); if (md2->model) { md2_printModelInfo(md2->model); + HWD.pfnCreateModelVBOs(md2->model); } else { @@ -1364,27 +1031,27 @@ void HWR_DrawMD2(gr_vissprite_t *spr) } //FIXME: this is not yet correct - frame = (spr->mobj->frame & FF_FRAMEMASK) % md2->model->header.numFrames; - buff = md2->model->glCommandBuffer; - curr = &md2->model->frames[frame]; -#if 0 - if (cv_grmd2.value == 1 && tics <= durs) + frame = (spr->mobj->frame & FF_FRAMEMASK) % md2->model->meshes[0].numFrames; + +#ifdef USE_MODEL_NEXTFRAME + if (cv_grmdls.value == 1 && tics <= durs) { // frames are handled differently for states with FF_ANIMATE, so get the next frame differently for the interpolation if (spr->mobj->frame & FF_ANIMATE) { - UINT32 nextframe = (spr->mobj->frame & FF_FRAMEMASK) + 1; - if (nextframe >= (UINT32)spr->mobj->state->var1) - nextframe = (spr->mobj->state->frame & FF_FRAMEMASK); - nextframe %= md2->model->header.numFrames; - next = &md2->model->frames[nextframe]; + nextFrame = (spr->mobj->frame & FF_FRAMEMASK) + 1; + if (nextFrame >= spr->mobj->state->var1) + nextFrame = (spr->mobj->state->frame & FF_FRAMEMASK); + nextFrame %= md2->model->meshes[0].numFrames; + //next = &md2->model->meshes[0].frames[nextFrame]; } else { - if (spr->mobj->state->nextstate != S_NULL && states[spr->mobj->state->nextstate].sprite != SPR_NULL) + if (spr->mobj->state->nextstate != S_NULL && states[spr->mobj->state->nextstate].sprite != SPR_NULL + && !(spr->mobj->player && (spr->mobj->state->nextstate == S_PLAY_TAP1 || spr->mobj->state->nextstate == S_PLAY_TAP2) && spr->mobj->state == &states[S_PLAY_STND])) { - const UINT32 nextframe = (states[spr->mobj->state->nextstate].frame & FF_FRAMEMASK) % md2->model->header.numFrames; - next = &md2->model->frames[nextframe]; + nextFrame = (states[spr->mobj->state->nextstate].frame & FF_FRAMEMASK) % md2->model->meshes[0].numFrames; + //next = &md2->model->meshes[0].frames[nextFrame]; } } } @@ -1421,6 +1088,8 @@ void HWR_DrawMD2(gr_vissprite_t *spr) p.angley = FIXED_TO_FLOAT(anglef); } p.anglex = 0.0f; +#ifdef USE_FTRANSFORM_ANGLEZ + // Slope rotation from Kart p.anglez = 0.0f; if (spr->mobj->standingslope) { @@ -1432,7 +1101,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr) tempangle = -AngleFixed(R_PointToAngle2(0, 0, tempz, tempy)); p.anglex = FIXED_TO_FLOAT(tempangle); } - +#endif color[0] = Surf.FlatColor.s.red; color[1] = Surf.FlatColor.s.green; @@ -1443,9 +1112,11 @@ void HWR_DrawMD2(gr_vissprite_t *spr) finalscale *= FIXED_TO_FLOAT(spr->mobj->scale); p.flip = atransform.flip; - p.mirror = atransform.mirror; +#ifdef USE_FTRANSFORM_MIRROR + p.mirror = atransform.mirror; // from Kart +#endif - HWD.pfnDrawMD2i(buff, curr, durs, tics, next, &p, finalscale, flip, color); + HWD.pfnDrawModel(md2->model, frame, durs, tics, nextFrame, &p, finalscale, flip, color); } } diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h index ca43c7b4f..57d8026b9 100644 --- a/src/hardware/hw_md2.h +++ b/src/hardware/hw_md2.h @@ -22,97 +22,7 @@ #define _HW_MD2_H_ #include "hw_glob.h" - -// magic number "IDP2" or 844121161 -#define MD2_IDENT (INT32)(('2' << 24) + ('P' << 16) + ('D' << 8) + 'I') -// model version -#define MD2_VERSION 8 - -#define MD2_MAX_TRIANGLES 16384 -#define MD2_MAX_VERTICES 4096 -#define MD2_MAX_TEXCOORDS 4096 -#define MD2_MAX_FRAMES 512 -#define MD2_MAX_SKINS 32 -#define MD2_MAX_FRAMESIZE (MD2_MAX_VERTICES * 4 + 128) - -#if defined(_MSC_VER) -#pragma pack(1) -#endif -typedef struct -{ - UINT32 magic; - UINT32 version; - UINT32 skinWidth; - UINT32 skinHeight; - UINT32 frameSize; - UINT32 numSkins; - UINT32 numVertices; - UINT32 numTexCoords; - UINT32 numTriangles; - UINT32 numGlCommands; - UINT32 numFrames; - UINT32 offsetSkins; - UINT32 offsetTexCoords; - UINT32 offsetTriangles; - UINT32 offsetFrames; - UINT32 offsetGlCommands; - UINT32 offsetEnd; -} ATTRPACK md2_header_t; //NOTE: each of md2_header's members are 4 unsigned bytes - -typedef struct -{ - UINT8 vertex[3]; - UINT8 lightNormalIndex; -} ATTRPACK md2_alias_triangleVertex_t; - -typedef struct -{ - float vertex[3]; - float normal[3]; -} ATTRPACK md2_triangleVertex_t; - -typedef struct -{ - INT16 vertexIndices[3]; - INT16 textureIndices[3]; -} ATTRPACK md2_triangle_t; - -typedef struct -{ - INT16 s, t; -} ATTRPACK md2_textureCoordinate_t; - -typedef struct -{ - float scale[3]; - float translate[3]; - char name[16]; - md2_alias_triangleVertex_t alias_vertices[1]; -} ATTRPACK md2_alias_frame_t; - -typedef struct -{ - char name[16]; - md2_triangleVertex_t *vertices; -} ATTRPACK md2_frame_t; - -typedef char md2_skin_t[64]; - -typedef struct -{ - float s, t; - INT32 vertexIndex; -} ATTRPACK md2_glCommandVertex_t; - -typedef struct -{ - md2_header_t header; - md2_skin_t *skins; - md2_textureCoordinate_t *texCoords; - md2_triangle_t *triangles; - md2_frame_t *frames; - INT32 *glCommandBuffer; -} ATTRPACK md2_model_t; +#include "hw_model.h" #if defined(_MSC_VER) #pragma pack() @@ -123,7 +33,7 @@ typedef struct char filename[32]; float scale; float offset; - md2_model_t *model; + model_t *model; void *grpatch; void *blendgrpatch; boolean notfound; diff --git a/src/hardware/hw_md2load.c b/src/hardware/hw_md2load.c new file mode 100644 index 000000000..3805deffb --- /dev/null +++ b/src/hardware/hw_md2load.c @@ -0,0 +1,564 @@ +/* + From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) + An experimental work-in-progress. + + Donated to Sonic Team Junior and adapted to work with + Sonic Robo Blast 2. The license of this code matches whatever + the licensing is for Sonic Robo Blast 2. +*/ + +#include +#include +#include +#include "../doomdef.h" +#include "hw_md2load.h" +#include "hw_model.h" +#include "../z_zone.h" + +#define NUMVERTEXNORMALS 162 + +// Quake 2 normals are indexed. Use avertexnormals[normalindex][x/y/z] and +// you'll have your normals. +float avertexnormals[NUMVERTEXNORMALS][3] = { +{-0.525731f, 0.000000f, 0.850651f}, +{-0.442863f, 0.238856f, 0.864188f}, +{-0.295242f, 0.000000f, 0.955423f}, +{-0.309017f, 0.500000f, 0.809017f}, +{-0.162460f, 0.262866f, 0.951056f}, +{0.000000f, 0.000000f, 1.000000f}, +{0.000000f, 0.850651f, 0.525731f}, +{-0.147621f, 0.716567f, 0.681718f}, +{0.147621f, 0.716567f, 0.681718f}, +{0.000000f, 0.525731f, 0.850651f}, +{0.309017f, 0.500000f, 0.809017f}, +{0.525731f, 0.000000f, 0.850651f}, +{0.295242f, 0.000000f, 0.955423f}, +{0.442863f, 0.238856f, 0.864188f}, +{0.162460f, 0.262866f, 0.951056f}, +{-0.681718f, 0.147621f, 0.716567f}, +{-0.809017f, 0.309017f, 0.500000f}, +{-0.587785f, 0.425325f, 0.688191f}, +{-0.850651f, 0.525731f, 0.000000f}, +{-0.864188f, 0.442863f, 0.238856f}, +{-0.716567f, 0.681718f, 0.147621f}, +{-0.688191f, 0.587785f, 0.425325f}, +{-0.500000f, 0.809017f, 0.309017f}, +{-0.238856f, 0.864188f, 0.442863f}, +{-0.425325f, 0.688191f, 0.587785f}, +{-0.716567f, 0.681718f, -0.147621f}, +{-0.500000f, 0.809017f, -0.309017f}, +{-0.525731f, 0.850651f, 0.000000f}, +{0.000000f, 0.850651f, -0.525731f}, +{-0.238856f, 0.864188f, -0.442863f}, +{0.000000f, 0.955423f, -0.295242f}, +{-0.262866f, 0.951056f, -0.162460f}, +{0.000000f, 1.000000f, 0.000000f}, +{0.000000f, 0.955423f, 0.295242f}, +{-0.262866f, 0.951056f, 0.162460f}, +{0.238856f, 0.864188f, 0.442863f}, +{0.262866f, 0.951056f, 0.162460f}, +{0.500000f, 0.809017f, 0.309017f}, +{0.238856f, 0.864188f, -0.442863f}, +{0.262866f, 0.951056f, -0.162460f}, +{0.500000f, 0.809017f, -0.309017f}, +{0.850651f, 0.525731f, 0.000000f}, +{0.716567f, 0.681718f, 0.147621f}, +{0.716567f, 0.681718f, -0.147621f}, +{0.525731f, 0.850651f, 0.000000f}, +{0.425325f, 0.688191f, 0.587785f}, +{0.864188f, 0.442863f, 0.238856f}, +{0.688191f, 0.587785f, 0.425325f}, +{0.809017f, 0.309017f, 0.500000f}, +{0.681718f, 0.147621f, 0.716567f}, +{0.587785f, 0.425325f, 0.688191f}, +{0.955423f, 0.295242f, 0.000000f}, +{1.000000f, 0.000000f, 0.000000f}, +{0.951056f, 0.162460f, 0.262866f}, +{0.850651f, -0.525731f, 0.000000f}, +{0.955423f, -0.295242f, 0.000000f}, +{0.864188f, -0.442863f, 0.238856f}, +{0.951056f, -0.162460f, 0.262866f}, +{0.809017f, -0.309017f, 0.500000f}, +{0.681718f, -0.147621f, 0.716567f}, +{0.850651f, 0.000000f, 0.525731f}, +{0.864188f, 0.442863f, -0.238856f}, +{0.809017f, 0.309017f, -0.500000f}, +{0.951056f, 0.162460f, -0.262866f}, +{0.525731f, 0.000000f, -0.850651f}, +{0.681718f, 0.147621f, -0.716567f}, +{0.681718f, -0.147621f, -0.716567f}, +{0.850651f, 0.000000f, -0.525731f}, +{0.809017f, -0.309017f, -0.500000f}, +{0.864188f, -0.442863f, -0.238856f}, +{0.951056f, -0.162460f, -0.262866f}, +{0.147621f, 0.716567f, -0.681718f}, +{0.309017f, 0.500000f, -0.809017f}, +{0.425325f, 0.688191f, -0.587785f}, +{0.442863f, 0.238856f, -0.864188f}, +{0.587785f, 0.425325f, -0.688191f}, +{0.688191f, 0.587785f, -0.425325f}, +{-0.147621f, 0.716567f, -0.681718f}, +{-0.309017f, 0.500000f, -0.809017f}, +{0.000000f, 0.525731f, -0.850651f}, +{-0.525731f, 0.000000f, -0.850651f}, +{-0.442863f, 0.238856f, -0.864188f}, +{-0.295242f, 0.000000f, -0.955423f}, +{-0.162460f, 0.262866f, -0.951056f}, +{0.000000f, 0.000000f, -1.000000f}, +{0.295242f, 0.000000f, -0.955423f}, +{0.162460f, 0.262866f, -0.951056f}, +{-0.442863f, -0.238856f, -0.864188f}, +{-0.309017f, -0.500000f, -0.809017f}, +{-0.162460f, -0.262866f, -0.951056f}, +{0.000000f, -0.850651f, -0.525731f}, +{-0.147621f, -0.716567f, -0.681718f}, +{0.147621f, -0.716567f, -0.681718f}, +{0.000000f, -0.525731f, -0.850651f}, +{0.309017f, -0.500000f, -0.809017f}, +{0.442863f, -0.238856f, -0.864188f}, +{0.162460f, -0.262866f, -0.951056f}, +{0.238856f, -0.864188f, -0.442863f}, +{0.500000f, -0.809017f, -0.309017f}, +{0.425325f, -0.688191f, -0.587785f}, +{0.716567f, -0.681718f, -0.147621f}, +{0.688191f, -0.587785f, -0.425325f}, +{0.587785f, -0.425325f, -0.688191f}, +{0.000000f, -0.955423f, -0.295242f}, +{0.000000f, -1.000000f, 0.000000f}, +{0.262866f, -0.951056f, -0.162460f}, +{0.000000f, -0.850651f, 0.525731f}, +{0.000000f, -0.955423f, 0.295242f}, +{0.238856f, -0.864188f, 0.442863f}, +{0.262866f, -0.951056f, 0.162460f}, +{0.500000f, -0.809017f, 0.309017f}, +{0.716567f, -0.681718f, 0.147621f}, +{0.525731f, -0.850651f, 0.000000f}, +{-0.238856f, -0.864188f, -0.442863f}, +{-0.500000f, -0.809017f, -0.309017f}, +{-0.262866f, -0.951056f, -0.162460f}, +{-0.850651f, -0.525731f, 0.000000f}, +{-0.716567f, -0.681718f, -0.147621f}, +{-0.716567f, -0.681718f, 0.147621f}, +{-0.525731f, -0.850651f, 0.000000f}, +{-0.500000f, -0.809017f, 0.309017f}, +{-0.238856f, -0.864188f, 0.442863f}, +{-0.262866f, -0.951056f, 0.162460f}, +{-0.864188f, -0.442863f, 0.238856f}, +{-0.809017f, -0.309017f, 0.500000f}, +{-0.688191f, -0.587785f, 0.425325f}, +{-0.681718f, -0.147621f, 0.716567f}, +{-0.442863f, -0.238856f, 0.864188f}, +{-0.587785f, -0.425325f, 0.688191f}, +{-0.309017f, -0.500000f, 0.809017f}, +{-0.147621f, -0.716567f, 0.681718f}, +{-0.425325f, -0.688191f, 0.587785f}, +{-0.162460f, -0.262866f, 0.951056f}, +{0.442863f, -0.238856f, 0.864188f}, +{0.162460f, -0.262866f, 0.951056f}, +{0.309017f, -0.500000f, 0.809017f}, +{0.147621f, -0.716567f, 0.681718f}, +{0.000000f, -0.525731f, 0.850651f}, +{0.425325f, -0.688191f, 0.587785f}, +{0.587785f, -0.425325f, 0.688191f}, +{0.688191f, -0.587785f, 0.425325f}, +{-0.955423f, 0.295242f, 0.000000f}, +{-0.951056f, 0.162460f, 0.262866f}, +{-1.000000f, 0.000000f, 0.000000f}, +{-0.850651f, 0.000000f, 0.525731f}, +{-0.955423f, -0.295242f, 0.000000f}, +{-0.951056f, -0.162460f, 0.262866f}, +{-0.864188f, 0.442863f, -0.238856f}, +{-0.951056f, 0.162460f, -0.262866f}, +{-0.809017f, 0.309017f, -0.500000f}, +{-0.864188f, -0.442863f, -0.238856f}, +{-0.951056f, -0.162460f, -0.262866f}, +{-0.809017f, -0.309017f, -0.500000f}, +{-0.681718f, 0.147621f, -0.716567f}, +{-0.681718f, -0.147621f, -0.716567f}, +{-0.850651f, 0.000000f, -0.525731f}, +{-0.688191f, 0.587785f, -0.425325f}, +{-0.587785f, 0.425325f, -0.688191f}, +{-0.425325f, 0.688191f, -0.587785f}, +{-0.425325f, -0.688191f, -0.587785f}, +{-0.587785f, -0.425325f, -0.688191f}, +{-0.688191f, -0.587785f, -0.425325f}, +}; + +typedef struct +{ + int ident; // A "magic number" that's used to identify the .md2 file + int version; // The version of the file, always 8 + int skinwidth; // Width of the skin(s) in pixels + int skinheight; // Height of the skin(s) in pixels + int framesize; // Size of each frame in bytes + int numSkins; // Number of skins with the model + int numXYZ; // Number of vertices in each frame + int numST; // Number of texture coordinates in each frame. + int numTris; // Number of triangles in each frame + int numGLcmds; // Number of dwords (4 bytes) in the gl command list. + int numFrames; // Number of frames + int offsetSkins; // Offset, in bytes from the start of the file, to the list of skin names. + int offsetST; // Offset, in bytes from the start of the file, to the list of texture coordinates + int offsetTris; // Offset, in bytes from the start of the file, to the list of triangles + int offsetFrames; // Offset, in bytes from the start of the file, to the list of frames + int offsetGLcmds; // Offset, in bytes from the start of the file, to the list of gl commands + int offsetEnd; // Offset, in bytes from the start of the file, to the end of the file (filesize) +} md2header_t; + +typedef struct +{ + unsigned short meshIndex[3]; // indices into the array of vertices in each frames + unsigned short stIndex[3]; // indices into the array of texture coordinates +} md2triangle_t; + +typedef struct +{ + short s; + short t; +} md2texcoord_t; + +typedef struct +{ + unsigned char v[3]; // Scaled vertices. You'll need to multiply them with scale[x] to make them normal. + unsigned char lightNormalIndex; // Index to the array of normals +} md2vertex_t; + +typedef struct +{ + float scale[3]; // Used by the v member in the md2framePoint structure + float translate[3]; // Used by the v member in the md2framePoint structure + char name[16]; // Name of the frame +} md2frame_t; + +// Load the model +model_t *MD2_LoadModel(const char *fileName, int ztag, boolean useFloat) +{ + FILE *f; + + model_t *retModel = NULL; + md2header_t *header; + + size_t fileLen; + int i, j; + size_t namelen; + char *texturefilename; + const char *texPos; + + char *buffer; + + const float WUNITS = 1.0f; + float dataScale = WUNITS; + + md2triangle_t *tris; + md2texcoord_t *texcoords; + md2frame_t *frames; + + int t; + + // MD2 currently does not work with tinyframes, so force useFloat = true + // + // + // the UV coordinates in MD2 are not compatible with glDrawElements like MD3 is. So they need to be loaded as full float. + // + // MD2 is intended to be draw in triangle strips and fans + // not very compatible with a modern GL implementation, either + // so the idea would be to full float expand it, and put it in a vertex buffer object + // I'm sure there's a way to convert the UVs to 'tinyframes', but maybe that's a job for someone else. + // You'd have to decompress the model, then recompress, reindexing the triangles and weeding out duplicate coordinates + // I already have the decompression work done + + useFloat = true; + + f = fopen(fileName, "rb"); + + if (!f) + return NULL; + + retModel = (model_t*)Z_Calloc(sizeof(model_t), ztag, 0); + + //size_t fileLen; + + //int i, j; + + //size_t namelen; + //char *texturefilename; + texPos = strchr(fileName, '/'); + + if (texPos) + { + texPos++; + namelen = strlen(texPos) + 1; + texturefilename = (char*)Z_Malloc(namelen, PU_CACHE, 0); + strcpy(texturefilename, texPos); + } + else + { + namelen = strlen(fileName) + 1; + texturefilename = (char*)Z_Malloc(namelen, PU_CACHE, 0); + strcpy(texturefilename, fileName); + } + + texturefilename[namelen - 2] = 'z'; + texturefilename[namelen - 3] = 'u'; + texturefilename[namelen - 4] = 'b'; + + // find length of file + fseek(f, 0, SEEK_END); + fileLen = ftell(f); + fseek(f, 0, SEEK_SET); + + // read in file + buffer = malloc(fileLen); + if (fread(buffer, fileLen, 1, f)) { } // squash ignored fread error + fclose(f); + + // get pointer to file header + header = (md2header_t*)buffer; + + retModel->numMeshes = 1; // MD2 only has one mesh + retModel->meshes = (mesh_t*)Z_Calloc(sizeof(mesh_t) * retModel->numMeshes, ztag, 0); + retModel->meshes[0].numFrames = header->numFrames; + // const float WUNITS = 1.0f; + // float dataScale = WUNITS; + + // Tris and ST are simple structures that can be straight-copied + tris = (md2triangle_t*)&buffer[header->offsetTris]; + texcoords = (md2texcoord_t*)&buffer[header->offsetST]; + frames = (md2frame_t*)&buffer[header->offsetFrames]; + + // Read in textures + retModel->numMaterials = header->numSkins; + + if (retModel->numMaterials <= 0) // Always at least one skin, duh + retModel->numMaterials = 1; + + retModel->materials = (material_t*)Z_Calloc(sizeof(material_t)*retModel->numMaterials, ztag, 0); + + // int t; + for (t = 0; t < retModel->numMaterials; t++) + { + retModel->materials[t].ambient[0] = 0.8f; + retModel->materials[t].ambient[1] = 0.8f; + retModel->materials[t].ambient[2] = 0.8f; + retModel->materials[t].ambient[3] = 1.0f; + retModel->materials[t].diffuse[0] = 0.8f; + retModel->materials[t].diffuse[1] = 0.8f; + retModel->materials[t].diffuse[2] = 0.8f; + retModel->materials[t].diffuse[3] = 1.0f; + retModel->materials[t].emissive[0] = 0.0f; + retModel->materials[t].emissive[1] = 0.0f; + retModel->materials[t].emissive[2] = 0.0f; + retModel->materials[t].emissive[3] = 1.0f; + retModel->materials[t].specular[0] = 0.0f; + retModel->materials[t].specular[1] = 0.0f; + retModel->materials[t].specular[2] = 0.0f; + retModel->materials[t].specular[3] = 1.0f; + retModel->materials[t].shininess = 0.0f; + retModel->materials[t].spheremap = false; + + /* retModel->materials[t].texture = Texture::ReadTexture((char*)texturefilename, ZT_TEXTURE); + + if (!systemSucks) + { + // Check for a normal map...?? + char openfilename[1024]; + char normalMapName[1024]; + strcpy(normalMapName, texturefilename); + size_t len = strlen(normalMapName); + char *ptr = &normalMapName[len]; + ptr--; // z + ptr--; // u + ptr--; // b + ptr--; // . + *ptr++ = '_'; + *ptr++ = 'n'; + *ptr++ = '.'; + *ptr++ = 'b'; + *ptr++ = 'u'; + *ptr++ = 'z'; + *ptr++ = '\0'; + + sprintf(openfilename, "%s/%s", "textures", normalMapName); + // Convert backslashes to forward slashes + for (int k = 0; k < 1024; k++) + { + if (openfilename[k] == '\0') + break; + + if (openfilename[k] == '\\') + openfilename[k] = '/'; + } + + Resource::resource_t *res = Resource::Open(openfilename); + if (res) + { + Resource::Close(res); + retModel->materials[t].lightmap = Texture::ReadTexture(normalMapName, ZT_TEXTURE); + } + }*/ + } + + retModel->meshes[0].numTriangles = header->numTris; + + if (!useFloat) // Decompress to MD3 'tinyframe' space + { + char *ptr; + + md2triangle_t *trisPtr; + unsigned short *indexptr; + float *uvptr; + + dataScale = 0.015624f; // 1 / 64.0f + retModel->meshes[0].tinyframes = (tinyframe_t*)Z_Calloc(sizeof(tinyframe_t)*header->numFrames, ztag, 0); + retModel->meshes[0].numVertices = header->numXYZ; + retModel->meshes[0].uvs = (float*)Z_Malloc(sizeof(float) * 2 * retModel->meshes[0].numVertices, ztag, 0); + + ptr = (char*)frames; + for (i = 0; i < header->numFrames; i++, ptr += header->framesize) + { + short *vertptr; + char *normptr; + // char *tanptr; + + md2vertex_t *vertex; + + md2frame_t *framePtr = (md2frame_t*)ptr; + retModel->meshes[0].tinyframes[i].vertices = (short*)Z_Malloc(sizeof(short) * 3 * header->numXYZ, ztag, 0); + retModel->meshes[0].tinyframes[i].normals = (char*)Z_Malloc(sizeof(char) * 3 * header->numXYZ, ztag, 0); + + // if (retModel->materials[0].lightmap) + // retModel->meshes[0].tinyframes[i].tangents = (char*)malloc(sizeof(char));//(char*)Z_Malloc(sizeof(char)*3*header->numVerts, ztag); + retModel->meshes[0].indices = (unsigned short*)Z_Malloc(sizeof(unsigned short) * 3 * header->numTris, ztag, 0); + + vertptr = retModel->meshes[0].tinyframes[i].vertices; + normptr = retModel->meshes[0].tinyframes[i].normals; + + // tanptr = retModel->meshes[0].tinyframes[i].tangents; + retModel->meshes[0].tinyframes[i].material = &retModel->materials[0]; + + framePtr++; // Advance to vertex list + vertex = (md2vertex_t*)framePtr; + framePtr--; + for (j = 0; j < header->numXYZ; j++, vertex++) + { + *vertptr = (short)(((vertex->v[0] * framePtr->scale[0]) + framePtr->translate[0]) / dataScale); + vertptr++; + *vertptr = (short)(((vertex->v[2] * framePtr->scale[2]) + framePtr->translate[2]) / dataScale); + vertptr++; + *vertptr = -1.0f * (short)(((vertex->v[1] * framePtr->scale[1]) + framePtr->translate[1]) / dataScale); + vertptr++; + + // Normal + *normptr++ = (char)(avertexnormals[vertex->lightNormalIndex][0] * 127); + *normptr++ = (char)(avertexnormals[vertex->lightNormalIndex][2] * 127); + *normptr++ = (char)(avertexnormals[vertex->lightNormalIndex][1] * 127); + } + } + + // This doesn't need to be done every frame! + trisPtr = tris; + indexptr = retModel->meshes[0].indices; + uvptr = (float*)retModel->meshes[0].uvs; + for (j = 0; j < header->numTris; j++, trisPtr++) + { + *indexptr = trisPtr->meshIndex[0]; + indexptr++; + *indexptr = trisPtr->meshIndex[1]; + indexptr++; + *indexptr = trisPtr->meshIndex[2]; + indexptr++; + + uvptr[trisPtr->meshIndex[0] * 2] = texcoords[trisPtr->stIndex[0]].s / (float)header->skinwidth; + uvptr[trisPtr->meshIndex[0] * 2 + 1] = (texcoords[trisPtr->stIndex[0]].t / (float)header->skinheight); + uvptr[trisPtr->meshIndex[1] * 2] = texcoords[trisPtr->stIndex[1]].s / (float)header->skinwidth; + uvptr[trisPtr->meshIndex[1] * 2 + 1] = (texcoords[trisPtr->stIndex[1]].t / (float)header->skinheight); + uvptr[trisPtr->meshIndex[2] * 2] = texcoords[trisPtr->stIndex[2]].s / (float)header->skinwidth; + uvptr[trisPtr->meshIndex[2] * 2 + 1] = (texcoords[trisPtr->stIndex[2]].t / (float)header->skinheight); + } + } + else // Full float loading method + { + md2triangle_t *trisPtr; + float *uvptr; + + char *ptr; + + retModel->meshes[0].numVertices = header->numTris * 3; + retModel->meshes[0].frames = (mdlframe_t*)Z_Calloc(sizeof(mdlframe_t)*header->numFrames, ztag, 0); + retModel->meshes[0].uvs = (float*)Z_Malloc(sizeof(float) * 2 * retModel->meshes[0].numVertices, ztag, 0); + + trisPtr = tris; + uvptr = retModel->meshes[0].uvs; + for (i = 0; i < retModel->meshes[0].numTriangles; i++, trisPtr++) + { + *uvptr++ = texcoords[trisPtr->stIndex[0]].s / (float)header->skinwidth; + *uvptr++ = (texcoords[trisPtr->stIndex[0]].t / (float)header->skinheight); + *uvptr++ = texcoords[trisPtr->stIndex[1]].s / (float)header->skinwidth; + *uvptr++ = (texcoords[trisPtr->stIndex[1]].t / (float)header->skinheight); + *uvptr++ = texcoords[trisPtr->stIndex[2]].s / (float)header->skinwidth; + *uvptr++ = (texcoords[trisPtr->stIndex[2]].t / (float)header->skinheight); + } + + ptr = (char*)frames; + for (i = 0; i < header->numFrames; i++, ptr += header->framesize) + { + float *vertptr, *normptr; + + md2vertex_t *vertex; + + md2frame_t *framePtr = (md2frame_t*)ptr; + retModel->meshes[0].frames[i].normals = (float*)Z_Malloc(sizeof(float) * 3 * header->numTris * 3, ztag, 0); + retModel->meshes[0].frames[i].vertices = (float*)Z_Malloc(sizeof(float) * 3 * header->numTris * 3, ztag, 0); + // if (retModel->materials[0].lightmap) + // retModel->meshes[0].frames[i].tangents = (float*)malloc(sizeof(float));//(float*)Z_Malloc(sizeof(float)*3*header->numTris*3, ztag); + //float *vertptr, *normptr; + normptr = (float*)retModel->meshes[0].frames[i].normals; + vertptr = (float*)retModel->meshes[0].frames[i].vertices; + trisPtr = tris; + + retModel->meshes[0].frames[i].material = &retModel->materials[0]; + + framePtr++; // Advance to vertex list + vertex = (md2vertex_t*)framePtr; + framePtr--; + for (j = 0; j < header->numTris; j++, trisPtr++) + { + *vertptr = ((vertex[trisPtr->meshIndex[0]].v[0] * framePtr->scale[0]) + framePtr->translate[0]) * WUNITS; + vertptr++; + *vertptr = ((vertex[trisPtr->meshIndex[0]].v[2] * framePtr->scale[2]) + framePtr->translate[2]) * WUNITS; + vertptr++; + *vertptr = -1.0f * ((vertex[trisPtr->meshIndex[0]].v[1] * framePtr->scale[1]) + framePtr->translate[1]) * WUNITS; + vertptr++; + + *vertptr = ((vertex[trisPtr->meshIndex[1]].v[0] * framePtr->scale[0]) + framePtr->translate[0]) * WUNITS; + vertptr++; + *vertptr = ((vertex[trisPtr->meshIndex[1]].v[2] * framePtr->scale[2]) + framePtr->translate[2]) * WUNITS; + vertptr++; + *vertptr = -1.0f * ((vertex[trisPtr->meshIndex[1]].v[1] * framePtr->scale[1]) + framePtr->translate[1]) * WUNITS; + vertptr++; + + *vertptr = ((vertex[trisPtr->meshIndex[2]].v[0] * framePtr->scale[0]) + framePtr->translate[0]) * WUNITS; + vertptr++; + *vertptr = ((vertex[trisPtr->meshIndex[2]].v[2] * framePtr->scale[2]) + framePtr->translate[2]) * WUNITS; + vertptr++; + *vertptr = -1.0f * ((vertex[trisPtr->meshIndex[2]].v[1] * framePtr->scale[1]) + framePtr->translate[1]) * WUNITS; + vertptr++; + + *normptr++ = avertexnormals[vertex[trisPtr->meshIndex[0]].lightNormalIndex][0]; + *normptr++ = avertexnormals[vertex[trisPtr->meshIndex[0]].lightNormalIndex][2]; + *normptr++ = avertexnormals[vertex[trisPtr->meshIndex[0]].lightNormalIndex][1]; + + *normptr++ = avertexnormals[vertex[trisPtr->meshIndex[1]].lightNormalIndex][0]; + *normptr++ = avertexnormals[vertex[trisPtr->meshIndex[1]].lightNormalIndex][2]; + *normptr++ = avertexnormals[vertex[trisPtr->meshIndex[1]].lightNormalIndex][1]; + + *normptr++ = avertexnormals[vertex[trisPtr->meshIndex[2]].lightNormalIndex][0]; + *normptr++ = avertexnormals[vertex[trisPtr->meshIndex[2]].lightNormalIndex][2]; + *normptr++ = avertexnormals[vertex[trisPtr->meshIndex[2]].lightNormalIndex][1]; + } + } + } + + free(buffer); + return retModel; +} diff --git a/src/hardware/hw_md2load.h b/src/hardware/hw_md2load.h new file mode 100644 index 000000000..1662d6471 --- /dev/null +++ b/src/hardware/hw_md2load.h @@ -0,0 +1,19 @@ +/* + From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) + An experimental work-in-progress. + + Donated to Sonic Team Junior and adapted to work with + Sonic Robo Blast 2. The license of this code matches whatever + the licensing is for Sonic Robo Blast 2. +*/ + +#ifndef _HW_MD2LOAD_H_ +#define _HW_MD2LOAD_H_ + +#include "hw_model.h" +#include "../doomtype.h" + +// Load the Model +model_t *MD2_LoadModel(const char *fileName, int ztag, boolean useFloat); + +#endif diff --git a/src/hardware/hw_md3load.c b/src/hardware/hw_md3load.c new file mode 100644 index 000000000..53f6034c0 --- /dev/null +++ b/src/hardware/hw_md3load.c @@ -0,0 +1,510 @@ +/* + From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) + An experimental work-in-progress. + + Donated to Sonic Team Junior and adapted to work with + Sonic Robo Blast 2. The license of this code matches whatever + the licensing is for Sonic Robo Blast 2. +*/ + +#include +#include +#include +#include "../doomdef.h" +#include "hw_md3load.h" +#include "hw_model.h" +#include "../z_zone.h" + +typedef struct +{ + int ident; // A "magic number" that's used to identify the .md3 file + int version; // The version of the file, always 15 + char name[64]; + int flags; + int numFrames; // Number of frames + int numTags; + int numSurfaces; + int numSkins; // Number of skins with the model + int offsetFrames; + int offsetTags; + int offsetSurfaces; + int offsetEnd; // Offset, in bytes from the start of the file, to the end of the file (filesize) +} md3modelHeader; + +typedef struct +{ + float minBounds[3]; // First corner of the bounding box + float maxBounds[3]; // Second corner of the bounding box + float localOrigin[3]; // Local origin, usually (0, 0, 0) + float radius; // Radius of bounding sphere + char name[16]; // Name of frame +} md3Frame; + +typedef struct +{ + char name[64]; // Name of tag + float origin[3]; // Coordinates of tag + float axis[9]; // Orientation of tag object +} md3Tag; + +typedef struct +{ + int ident; + char name[64]; // Name of this surface + int flags; + int numFrames; // # of keyframes + int numShaders; // # of shaders + int numVerts; // # of vertices + int numTriangles; // # of triangles + int offsetTriangles; // Relative offset from start of this struct to where the list of Triangles start + int offsetShaders; // Relative offset from start of this struct to where the list of Shaders start + int offsetST; // Relative offset from start of this struct to where the list of tex coords start + int offsetXYZNormal; // Relative offset from start of this struct to where the list of vertices start + int offsetEnd; // Relative offset from start of this struct to where this surface ends +} md3Surface; + +typedef struct +{ + char name[64]; // Name of this shader + int shaderIndex; // Shader index number +} md3Shader; + +typedef struct +{ + int index[3]; // List of offset values into the list of Vertex objects that constitute the corners of the Triangle object. +} md3Triangle; + +typedef struct +{ + float st[2]; +} md3TexCoord; + +typedef struct +{ + short x, y, z, n; +} md3Vertex; + +static float latlnglookup[256][256][3]; + +static void GetNormalFromLatLong(short latlng, float *out) +{ + float *lookup = latlnglookup[(unsigned char)(latlng >> 8)][(unsigned char)(latlng & 255)]; + + out[0] = *lookup++; + out[1] = *lookup++; + out[2] = *lookup++; +} + +#if 0 +static void NormalToLatLng(float *n, short *out) +{ + // Special cases + if (0.0f == n[0] && 0.0f == n[1]) + { + if (n[2] > 0.0f) + *out = 0; + else + *out = 128; + } + else + { + char x, y; + + x = (char)(57.2957795f * (atan2(n[1], n[0])) * (255.0f / 360.0f)); + y = (char)(57.2957795f * (acos(n[2])) * (255.0f / 360.0f)); + + *out = (x << 8) + y; + } +} +#endif + +static inline void LatLngToNormal(short n, float *out) +{ + const float PI = (3.1415926535897932384626433832795f); + float lat = (float)(n >> 8); + float lng = (float)(n & 255); + + lat *= PI / 128.0f; + lng *= PI / 128.0f; + + out[0] = cosf(lat) * sinf(lng); + out[1] = sinf(lat) * sinf(lng); + out[2] = cosf(lng); +} + +static void LatLngInit(void) +{ + int i, j; + for (i = 0; i < 256; i++) + { + for (j = 0; j < 256; j++) + LatLngToNormal((short)((i << 8) + j), latlnglookup[i][j]); + } +} + +static boolean latlnginit = false; + +model_t *MD3_LoadModel(const char *fileName, int ztag, boolean useFloat) +{ + const float WUNITS = 1.0f; + model_t *retModel = NULL; + md3modelHeader *mdh; + long fileLen; + long fileReadLen; + char *buffer; + int surfEnd; + int i, t; + int matCount; + FILE *f; + + if (!latlnginit) + { + LatLngInit(); + latlnginit = true; + } + + f = fopen(fileName, "rb"); + + if (!f) + return NULL; + + retModel = (model_t*)Z_Calloc(sizeof(model_t), ztag, 0); + + // find length of file + fseek(f, 0, SEEK_END); + fileLen = ftell(f); + fseek(f, 0, SEEK_SET); + + // read in file + buffer = malloc(fileLen); + fileReadLen = fread(buffer, fileLen, 1, f); + fclose(f); + + (void)fileReadLen; // intentionally ignore return value, per buildbot + + // get pointer to file header + mdh = (md3modelHeader*)buffer; + + retModel->numMeshes = mdh->numSurfaces; + + retModel->numMaterials = 0; + surfEnd = 0; + for (i = 0; i < mdh->numSurfaces; i++) + { + md3Surface *mdS = (md3Surface*)&buffer[mdh->offsetSurfaces]; + surfEnd += mdS->offsetEnd; + + retModel->numMaterials += mdS->numShaders; + } + + // Initialize materials + if (retModel->numMaterials <= 0) // Always at least one skin, duh + retModel->numMaterials = 1; + + retModel->materials = (material_t*)Z_Calloc(sizeof(material_t)*retModel->numMaterials, ztag, 0); + + for (t = 0; t < retModel->numMaterials; t++) + { + retModel->materials[t].ambient[0] = 0.3686f; + retModel->materials[t].ambient[1] = 0.3684f; + retModel->materials[t].ambient[2] = 0.3684f; + retModel->materials[t].ambient[3] = 1.0f; + retModel->materials[t].diffuse[0] = 0.8863f; + retModel->materials[t].diffuse[1] = 0.8850f; + retModel->materials[t].diffuse[2] = 0.8850f; + retModel->materials[t].diffuse[3] = 1.0f; + retModel->materials[t].emissive[0] = 0.0f; + retModel->materials[t].emissive[1] = 0.0f; + retModel->materials[t].emissive[2] = 0.0f; + retModel->materials[t].emissive[3] = 1.0f; + retModel->materials[t].specular[0] = 0.4902f; + retModel->materials[t].specular[1] = 0.4887f; + retModel->materials[t].specular[2] = 0.4887f; + retModel->materials[t].specular[3] = 1.0f; + retModel->materials[t].shininess = 25.0f; + retModel->materials[t].spheremap = false; + } + + retModel->meshes = (mesh_t*)Z_Calloc(sizeof(mesh_t)*retModel->numMeshes, ztag, 0); + + matCount = 0; + for (i = 0, surfEnd = 0; i < mdh->numSurfaces; i++) + { + int j; + md3Shader *mdShader; + md3Surface *mdS = (md3Surface*)&buffer[mdh->offsetSurfaces + surfEnd]; + surfEnd += mdS->offsetEnd; + + mdShader = (md3Shader*)((char*)mdS + mdS->offsetShaders); + + for (j = 0; j < mdS->numShaders; j++, matCount++) + { + size_t len = strlen(mdShader[j].name); + mdShader[j].name[len-1] = 'z'; + mdShader[j].name[len-2] = 'u'; + mdShader[j].name[len-3] = 'b'; + + // Load material +/* retModel->materials[matCount].texture = Texture::ReadTexture(mdShader[j].name, ZT_TEXTURE); + + if (!systemSucks) + { + // Check for a normal map...?? + char openfilename[1024]; + char normalMapName[1024]; + strcpy(normalMapName, mdShader[j].name); + len = strlen(normalMapName); + char *ptr = &normalMapName[len]; + ptr--; // z + ptr--; // u + ptr--; // b + ptr--; // . + *ptr++ = '_'; + *ptr++ = 'n'; + *ptr++ = '.'; + *ptr++ = 'b'; + *ptr++ = 'u'; + *ptr++ = 'z'; + *ptr++ = '\0'; + + sprintf(openfilename, "%s/%s", "textures", normalMapName); + // Convert backslashes to forward slashes + for (int k = 0; k < 1024; k++) + { + if (openfilename[k] == '\0') + break; + + if (openfilename[k] == '\\') + openfilename[k] = '/'; + } + + Resource::resource_t *res = Resource::Open(openfilename); + if (res) + { + Resource::Close(res); + retModel->materials[matCount].lightmap = Texture::ReadTexture(normalMapName, ZT_TEXTURE); + } + }*/ + } + + retModel->meshes[i].numFrames = mdS->numFrames; + retModel->meshes[i].numTriangles = mdS->numTriangles; + + if (!useFloat) // 'tinyframe' mode with indices + { + float tempNormal[3]; + float *uvptr; + md3TexCoord *mdST; + unsigned short *indexptr; + md3Triangle *mdT; + + retModel->meshes[i].tinyframes = (tinyframe_t*)Z_Calloc(sizeof(tinyframe_t)*mdS->numFrames, ztag, 0); + retModel->meshes[i].numVertices = mdS->numVerts; + retModel->meshes[i].uvs = (float*)Z_Malloc(sizeof(float)*2*mdS->numVerts, ztag, 0); + for (j = 0; j < mdS->numFrames; j++) + { + short *vertptr; + char *normptr; + // char *tanptr; + int k; + md3Vertex *mdV = (md3Vertex*)((char*)mdS + mdS->offsetXYZNormal + (mdS->numVerts*j*sizeof(md3Vertex))); + retModel->meshes[i].tinyframes[j].vertices = (short*)Z_Malloc(sizeof(short)*3*mdS->numVerts, ztag, 0); + retModel->meshes[i].tinyframes[j].normals = (char*)Z_Malloc(sizeof(char)*3*mdS->numVerts, ztag, 0); + +// if (retModel->materials[0].lightmap) +// retModel->meshes[i].tinyframes[j].tangents = (char*)malloc(sizeof(char));//(char*)Z_Malloc(sizeof(char)*3*mdS->numVerts, ztag); + retModel->meshes[i].indices = (unsigned short*)Z_Malloc(sizeof(unsigned short) * 3 * mdS->numTriangles, ztag, 0); + vertptr = retModel->meshes[i].tinyframes[j].vertices; + normptr = retModel->meshes[i].tinyframes[j].normals; + +// tanptr = retModel->meshes[i].tinyframes[j].tangents; + retModel->meshes[i].tinyframes[j].material = &retModel->materials[i]; + + for (k = 0; k < mdS->numVerts; k++) + { + // Vertex + *vertptr = mdV[k].x; + vertptr++; + *vertptr = mdV[k].z; + vertptr++; + *vertptr = 1.0f - mdV[k].y; + vertptr++; + + // Normal + GetNormalFromLatLong(mdV[k].n, tempNormal); + *normptr = (char)(tempNormal[0] * 127); + normptr++; + *normptr = (char)(tempNormal[2] * 127); + normptr++; + *normptr = (char)(tempNormal[1] * 127); + normptr++; + } + } + + uvptr = (float*)retModel->meshes[i].uvs; + mdST = (md3TexCoord*)((char*)mdS + mdS->offsetST); + for (j = 0; j < mdS->numVerts; j++) + { + *uvptr = mdST[j].st[0]; + uvptr++; + *uvptr = mdST[j].st[1]; + uvptr++; + } + + indexptr = retModel->meshes[i].indices; + mdT = (md3Triangle*)((char*)mdS + mdS->offsetTriangles); + for (j = 0; j < mdS->numTriangles; j++, mdT++) + { + // Indices + *indexptr = (unsigned short)mdT->index[0]; + indexptr++; + *indexptr = (unsigned short)mdT->index[1]; + indexptr++; + *indexptr = (unsigned short)mdT->index[2]; + indexptr++; + } + } + else // Traditional full-float loading method + { + float dataScale = 0.015624f * WUNITS; + float tempNormal[3]; + md3TexCoord *mdST; + md3Triangle *mdT; + float *uvptr; + int k; + + retModel->meshes[i].numVertices = mdS->numTriangles * 3;//mdS->numVerts; + retModel->meshes[i].frames = (mdlframe_t*)Z_Calloc(sizeof(mdlframe_t)*mdS->numFrames, ztag, 0); + retModel->meshes[i].uvs = (float*)Z_Malloc(sizeof(float)*2*mdS->numTriangles*3, ztag, 0); + + for (j = 0; j < mdS->numFrames; j++) + { + float *vertptr; + float *normptr; + md3Vertex *mdV = (md3Vertex*)((char*)mdS + mdS->offsetXYZNormal + (mdS->numVerts*j*sizeof(md3Vertex))); + retModel->meshes[i].frames[j].vertices = (float*)Z_Malloc(sizeof(float)*3*mdS->numTriangles*3, ztag, 0); + retModel->meshes[i].frames[j].normals = (float*)Z_Malloc(sizeof(float)*3*mdS->numTriangles*3, ztag, 0); +// if (retModel->materials[i].lightmap) +// retModel->meshes[i].frames[j].tangents = (float*)malloc(sizeof(float));//(float*)Z_Malloc(sizeof(float)*3*mdS->numTriangles*3, ztag); + vertptr = retModel->meshes[i].frames[j].vertices; + normptr = retModel->meshes[i].frames[j].normals; + retModel->meshes[i].frames[j].material = &retModel->materials[i]; + + mdT = (md3Triangle*)((char*)mdS + mdS->offsetTriangles); + + for (k = 0; k < mdS->numTriangles; k++) + { + // Vertex 1 + *vertptr = mdV[mdT->index[0]].x * dataScale; + vertptr++; + *vertptr = mdV[mdT->index[0]].z * dataScale; + vertptr++; + *vertptr = 1.0f - mdV[mdT->index[0]].y * dataScale; + vertptr++; + + GetNormalFromLatLong(mdV[mdT->index[0]].n, tempNormal); + *normptr = tempNormal[0]; + normptr++; + *normptr = tempNormal[2]; + normptr++; + *normptr = tempNormal[1]; + normptr++; + + // Vertex 2 + *vertptr = mdV[mdT->index[1]].x * dataScale; + vertptr++; + *vertptr = mdV[mdT->index[1]].z * dataScale; + vertptr++; + *vertptr = 1.0f - mdV[mdT->index[1]].y * dataScale; + vertptr++; + + GetNormalFromLatLong(mdV[mdT->index[1]].n, tempNormal); + *normptr = tempNormal[0]; + normptr++; + *normptr = tempNormal[2]; + normptr++; + *normptr = tempNormal[1]; + normptr++; + + // Vertex 3 + *vertptr = mdV[mdT->index[2]].x * dataScale; + vertptr++; + *vertptr = mdV[mdT->index[2]].z * dataScale; + vertptr++; + *vertptr = 1.0f - mdV[mdT->index[2]].y * dataScale; + vertptr++; + + GetNormalFromLatLong(mdV[mdT->index[2]].n, tempNormal); + *normptr = tempNormal[0]; + normptr++; + *normptr = tempNormal[2]; + normptr++; + *normptr = tempNormal[1]; + normptr++; + + mdT++; // Advance to next triangle + } + } + + mdST = (md3TexCoord*)((char*)mdS + mdS->offsetST); + uvptr = (float*)retModel->meshes[i].uvs; + mdT = (md3Triangle*)((char*)mdS + mdS->offsetTriangles); + + for (k = 0; k < mdS->numTriangles; k++) + { + *uvptr = mdST[mdT->index[0]].st[0]; + uvptr++; + *uvptr = mdST[mdT->index[0]].st[1]; + uvptr++; + + *uvptr = mdST[mdT->index[1]].st[0]; + uvptr++; + *uvptr = mdST[mdT->index[1]].st[1]; + uvptr++; + + *uvptr = mdST[mdT->index[2]].st[0]; + uvptr++; + *uvptr = mdST[mdT->index[2]].st[1]; + uvptr++; + + mdT++; // Advance to next triangle + } + } + } + /* + // Tags? + retModel->numTags = mdh->numTags; + retModel->maxNumFrames = mdh->numFrames; + retModel->tags = (tag_t*)Z_Calloc(sizeof(tag_t) * retModel->numTags * mdh->numFrames, ztag); + md3Tag *mdTag = (md3Tag*)&buffer[mdh->offsetTags]; + tag_t *curTag = retModel->tags; + for (i = 0; i < mdh->numFrames; i++) + { + int j; + for (j = 0; j < retModel->numTags; j++, mdTag++) + { + strcpys(curTag->name, mdTag->name, sizeof(curTag->name) / sizeof(char)); + curTag->transform.m[0][0] = mdTag->axis[0]; + curTag->transform.m[0][1] = mdTag->axis[1]; + curTag->transform.m[0][2] = mdTag->axis[2]; + curTag->transform.m[1][0] = mdTag->axis[3]; + curTag->transform.m[1][1] = mdTag->axis[4]; + curTag->transform.m[1][2] = mdTag->axis[5]; + curTag->transform.m[2][0] = mdTag->axis[6]; + curTag->transform.m[2][1] = mdTag->axis[7]; + curTag->transform.m[2][2] = mdTag->axis[8]; + curTag->transform.m[3][0] = mdTag->origin[0] * WUNITS; + curTag->transform.m[3][1] = mdTag->origin[1] * WUNITS; + curTag->transform.m[3][2] = mdTag->origin[2] * WUNITS; + curTag->transform.m[3][3] = 1.0f; + + Matrix::Rotate(&curTag->transform, 90.0f, &Vector::Xaxis); + curTag++; + } + }*/ + + + free(buffer); + + return retModel; +} diff --git a/src/hardware/hw_md3load.h b/src/hardware/hw_md3load.h new file mode 100644 index 000000000..c0e0522ff --- /dev/null +++ b/src/hardware/hw_md3load.h @@ -0,0 +1,19 @@ +/* + From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) + An experimental work-in-progress. + + Donated to Sonic Team Junior and adapted to work with + Sonic Robo Blast 2. The license of this code matches whatever + the licensing is for Sonic Robo Blast 2. +*/ + +#ifndef _HW_MD3LOAD_H_ +#define _HW_MD3LOAD_H_ + +#include "hw_model.h" +#include "../doomtype.h" + +// Load the Model +model_t *MD3_LoadModel(const char *fileName, int ztag, boolean useFloat); + +#endif diff --git a/src/hardware/hw_model.c b/src/hardware/hw_model.c new file mode 100644 index 000000000..2c36f9744 --- /dev/null +++ b/src/hardware/hw_model.c @@ -0,0 +1,593 @@ +/* + From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) + An experimental work-in-progress. + + Donated to Sonic Team Junior and adapted to work with + Sonic Robo Blast 2. The license of this code matches whatever + the licensing is for Sonic Robo Blast 2. +*/ + +#include "../z_zone.h" +#include "../doomdef.h" +#include "hw_model.h" +#include "hw_md2load.h" +#include "hw_md3load.h" +#include "u_list.h" +#include + +static float PI = (3.1415926535897932384626433832795f); +static float U_Deg2Rad(float deg) +{ + return deg * ((float)PI / 180.0f); +} + +vector_t vectorXaxis = { 1.0f, 0.0f, 0.0f }; +vector_t vectorYaxis = { 0.0f, 1.0f, 0.0f }; +vector_t vectorZaxis = { 0.0f, 0.0f, 1.0f }; + +void VectorRotate(vector_t *rotVec, const vector_t *axisVec, float angle) +{ + float ux, uy, uz, vx, vy, vz, wx, wy, wz, sa, ca; + + angle = U_Deg2Rad(angle); + + // Rotate the point (x,y,z) around the vector (u,v,w) + ux = axisVec->x * rotVec->x; + uy = axisVec->x * rotVec->y; + uz = axisVec->x * rotVec->z; + vx = axisVec->y * rotVec->x; + vy = axisVec->y * rotVec->y; + vz = axisVec->y * rotVec->z; + wx = axisVec->z * rotVec->x; + wy = axisVec->z * rotVec->y; + wz = axisVec->z * rotVec->z; + sa = sinf(angle); + ca = cosf(angle); + + rotVec->x = axisVec->x*(ux + vy + wz) + (rotVec->x*(axisVec->y*axisVec->y + axisVec->z*axisVec->z) - axisVec->x*(vy + wz))*ca + (-wy + vz)*sa; + rotVec->y = axisVec->y*(ux + vy + wz) + (rotVec->y*(axisVec->x*axisVec->x + axisVec->z*axisVec->z) - axisVec->y*(ux + wz))*ca + (wx - uz)*sa; + rotVec->z = axisVec->z*(ux + vy + wz) + (rotVec->z*(axisVec->x*axisVec->x + axisVec->y*axisVec->y) - axisVec->z*(ux + vy))*ca + (-vx + uy)*sa; +} + +void UnloadModel(model_t *model) +{ + // Wouldn't it be great if C just had destructors? + int i; + for (i = 0; i < model->numMeshes; i++) + { + mesh_t *mesh = &model->meshes[i]; + + if (mesh->frames) + { + int j; + for (j = 0; j < mesh->numFrames; j++) + { + if (mesh->frames[j].normals) + Z_Free(mesh->frames[j].normals); + + if (mesh->frames[j].tangents) + Z_Free(mesh->frames[j].tangents); + + if (mesh->frames[j].vertices) + Z_Free(mesh->frames[j].vertices); + + if (mesh->frames[j].colors) + Z_Free(mesh->frames[j].colors); + } + + Z_Free(mesh->frames); + } + else if (mesh->tinyframes) + { + int j; + for (j = 0; j < mesh->numFrames; j++) + { + if (mesh->tinyframes[j].normals) + Z_Free(mesh->tinyframes[j].normals); + + if (mesh->tinyframes[j].tangents) + Z_Free(mesh->tinyframes[j].tangents); + + if (mesh->tinyframes[j].vertices) + Z_Free(mesh->tinyframes[j].vertices); + } + + if (mesh->indices) + Z_Free(mesh->indices); + + Z_Free(mesh->tinyframes); + } + + if (mesh->uvs) + Z_Free(mesh->uvs); + + if (mesh->lightuvs) + Z_Free(mesh->lightuvs); + } + + if (model->meshes) + Z_Free(model->meshes); + + if (model->tags) + Z_Free(model->tags); + + if (model->materials) + Z_Free(model->materials); + + DeleteVBOs(model); + Z_Free(model); +} + +tag_t *GetTagByName(model_t *model, char *name, int frame) +{ + if (frame < model->maxNumFrames) + { + tag_t *iterator = &model->tags[frame * model->numTags]; + + int i; + for (i = 0; i < model->numTags; i++) + { + if (!stricmp(iterator[i].name, name)) + return &iterator[i]; + } + } + + return NULL; +} + +// +// LoadModel +// +// Load a model and +// convert it to the +// internal format. +// +model_t *LoadModel(const char *filename, int ztag) +{ + model_t *model; + + // What type of file? + const char *extension = NULL; + int i; + for (i = (int)strlen(filename)-1; i >= 0; i--) + { + if (filename[i] != '.') + continue; + + extension = &filename[i]; + break; + } + + if (!extension) + { + CONS_Printf("Model %s is lacking a file extension, unable to determine type!\n", filename); + return NULL; + } + + if (!strcmp(extension, ".md3")) + { + if (!(model = MD3_LoadModel(filename, ztag, false))) + return NULL; + } + else if (!strcmp(extension, ".md3s")) // MD3 that will be converted in memory to use full floats + { + if (!(model = MD3_LoadModel(filename, ztag, true))) + return NULL; + } + else if (!strcmp(extension, ".md2")) + { + if (!(model = MD2_LoadModel(filename, ztag, false))) + return NULL; + } + else if (!strcmp(extension, ".md2s")) + { + if (!(model = MD2_LoadModel(filename, ztag, true))) + return NULL; + } + else + { + CONS_Printf("Unknown model format: %s\n", extension); + return NULL; + } + + model->mdlFilename = (char*)Z_Malloc(strlen(filename)+1, ztag, 0); + strcpy(model->mdlFilename, filename); + + Optimize(model); + GeneratePolygonNormals(model, ztag); + + // Default material properties + for (i = 0 ; i < model->numMaterials; i++) + { + material_t *material = &model->materials[i]; + material->ambient[0] = 0.7686f; + material->ambient[1] = 0.7686f; + material->ambient[2] = 0.7686f; + material->ambient[3] = 1.0f; + material->diffuse[0] = 0.5863f; + material->diffuse[1] = 0.5863f; + material->diffuse[2] = 0.5863f; + material->diffuse[3] = 1.0f; + material->specular[0] = 0.4902f; + material->specular[1] = 0.4902f; + material->specular[2] = 0.4902f; + material->specular[3] = 1.0f; + material->shininess = 25.0f; + } + + return model; +} + +// +// GenerateVertexNormals +// +// Creates a new normal for a vertex using the average of all of the polygons it belongs to. +// +void GenerateVertexNormals(model_t *model) +{ + int i; + for (i = 0; i < model->numMeshes; i++) + { + int j; + + mesh_t *mesh = &model->meshes[i]; + + if (!mesh->frames) + continue; + + for (j = 0; j < mesh->numFrames; j++) + { + mdlframe_t *frame = &mesh->frames[j]; + int memTag = PU_STATIC; + float *newNormals = (float*)Z_Malloc(sizeof(float)*3*mesh->numTriangles*3, memTag, 0); + int k; + float *vertPtr = frame->vertices; + float *oldNormals; + + M_Memcpy(newNormals, frame->normals, sizeof(float)*3*mesh->numTriangles*3); + +/* if (!systemSucks) + { + memTag = Z_GetTag(frame->tangents); + float *newTangents = (float*)Z_Malloc(sizeof(float)*3*mesh->numTriangles*3, memTag); + M_Memcpy(newTangents, frame->tangents, sizeof(float)*3*mesh->numTriangles*3); + }*/ + + for (k = 0; k < mesh->numVertices; k++) + { + float x, y, z; + int vCount = 0; + vector_t normal; + int l; + float *testPtr = frame->vertices; + + x = *vertPtr++; + y = *vertPtr++; + z = *vertPtr++; + + normal.x = normal.y = normal.z = 0; + + for (l = 0; l < mesh->numVertices; l++) + { + float testX, testY, testZ; + testX = *testPtr++; + testY = *testPtr++; + testZ = *testPtr++; + + if (fabsf(x - testX) > FLT_EPSILON + || fabsf(y - testY) > FLT_EPSILON + || fabsf(z - testZ) > FLT_EPSILON) + continue; + + // Found a vertex match! Add it... + normal.x += frame->normals[3 * l + 0]; + normal.y += frame->normals[3 * l + 1]; + normal.z += frame->normals[3 * l + 2]; + vCount++; + } + + if (vCount > 1) + { +// Vector::Normalize(&normal); + newNormals[3 * k + 0] = (float)normal.x; + newNormals[3 * k + 1] = (float)normal.y; + newNormals[3 * k + 2] = (float)normal.z; + +/* if (!systemSucks) + { + Vector::vector_t tangent; + Vector::Tangent(&normal, &tangent); + newTangents[3 * k + 0] = tangent.x; + newTangents[3 * k + 1] = tangent.y; + newTangents[3 * k + 2] = tangent.z; + }*/ + } + } + + oldNormals = frame->normals; + frame->normals = newNormals; + Z_Free(oldNormals); + +/* if (!systemSucks) + { + float *oldTangents = frame->tangents; + frame->tangents = newTangents; + Z_Free(oldTangents); + }*/ + } + } +} + +typedef struct materiallist_s +{ + struct materiallist_s *next; + struct materiallist_s *prev; + material_t *material; +} materiallist_t; + +static boolean AddMaterialToList(materiallist_t **head, material_t *material) +{ + materiallist_t *node, *newMatNode; + for (node = *head; node; node = node->next) + { + if (node->material == material) + return false; + } + + // Didn't find it, so add to the list + newMatNode = (materiallist_t*)Z_Malloc(sizeof(materiallist_t), PU_CACHE, 0); + newMatNode->material = material; + ListAdd(newMatNode, (listitem_t**)head); + return true; +} + +// +// Optimize +// +// Groups triangles from meshes in the model +// Only works for models with 1 frame +// +void Optimize(model_t *model) +{ + int numMeshes = 0; + int i; + materiallist_t *matListHead = NULL; + int memTag; + mesh_t *newMeshes; + materiallist_t *node; + + if (model->numMeshes <= 1) + return; // No need + + for (i = 0; i < model->numMeshes; i++) + { + mesh_t *curMesh = &model->meshes[i]; + + if (curMesh->numFrames > 1) + return; // Can't optimize models with > 1 frame + + if (!curMesh->frames) + return; // Don't optimize tinyframe models (no need) + + // We are condensing to 1 mesh per material, so + // the # of materials we use will be the new + // # of meshes + if (AddMaterialToList(&matListHead, curMesh->frames[0].material)) + numMeshes++; + } + + memTag = PU_STATIC; + newMeshes = (mesh_t*)Z_Calloc(sizeof(mesh_t) * numMeshes, memTag, 0); + + i = 0; + for (node = matListHead; node; node = node->next) + { + material_t *curMat = node->material; + mesh_t *newMesh = &newMeshes[i]; + mdlframe_t *curFrame; + int uvCount; + int vertCount; + int colorCount; + + // Find all triangles with this material and count them + int numTriangles = 0; + int j; + for (j = 0; j < model->numMeshes; j++) + { + mesh_t *curMesh = &model->meshes[j]; + + if (curMesh->frames[0].material == curMat) + numTriangles += curMesh->numTriangles; + } + + newMesh->numFrames = 1; + newMesh->numTriangles = numTriangles; + newMesh->numVertices = numTriangles * 3; + newMesh->uvs = (float*)Z_Malloc(sizeof(float)*2*numTriangles*3, memTag, 0); +// if (node->material->lightmap) +// newMesh->lightuvs = (float*)Z_Malloc(sizeof(float)*2*numTriangles*3, memTag, 0); + newMesh->frames = (mdlframe_t*)Z_Calloc(sizeof(mdlframe_t), memTag, 0); + curFrame = &newMesh->frames[0]; + + curFrame->material = curMat; + curFrame->normals = (float*)Z_Malloc(sizeof(float)*3*numTriangles*3, memTag, 0); +// if (!systemSucks) +// curFrame->tangents = (float*)Z_Malloc(sizeof(float)*3*numTriangles*3, memTag, 0); + curFrame->vertices = (float*)Z_Malloc(sizeof(float)*3*numTriangles*3, memTag, 0); + curFrame->colors = (char*)Z_Malloc(sizeof(char)*4*numTriangles*3, memTag, 0); + + // Now traverse the meshes of the model, adding in + // vertices/normals/uvs that match the current material + uvCount = 0; + vertCount = 0; + colorCount = 0; + for (j = 0; j < model->numMeshes; j++) + { + mesh_t *curMesh = &model->meshes[j]; + + if (curMesh->frames[0].material == curMat) + { + float *dest; + float *src; + char *destByte; + char *srcByte; + + M_Memcpy(&newMesh->uvs[uvCount], + curMesh->uvs, + sizeof(float)*2*curMesh->numTriangles*3); + +/* if (node->material->lightmap) + { + M_Memcpy(&newMesh->lightuvs[uvCount], + curMesh->lightuvs, + sizeof(float)*2*curMesh->numTriangles*3); + }*/ + uvCount += 2*curMesh->numTriangles*3; + + dest = (float*)newMesh->frames[0].vertices; + src = (float*)curMesh->frames[0].vertices; + M_Memcpy(&dest[vertCount], + src, + sizeof(float)*3*curMesh->numTriangles*3); + + dest = (float*)newMesh->frames[0].normals; + src = (float*)curMesh->frames[0].normals; + M_Memcpy(&dest[vertCount], + src, + sizeof(float)*3*curMesh->numTriangles*3); + +/* if (!systemSucks) + { + dest = (float*)newMesh->frames[0].tangents; + src = (float*)curMesh->frames[0].tangents; + M_Memcpy(&dest[vertCount], + src, + sizeof(float)*3*curMesh->numTriangles*3); + }*/ + + vertCount += 3 * curMesh->numTriangles * 3; + + destByte = (char*)newMesh->frames[0].colors; + srcByte = (char*)curMesh->frames[0].colors; + + if (srcByte) + { + M_Memcpy(&destByte[colorCount], + srcByte, + sizeof(char)*4*curMesh->numTriangles*3); + } + else + { + memset(&destByte[colorCount], + 255, + sizeof(char)*4*curMesh->numTriangles*3); + } + + colorCount += 4 * curMesh->numTriangles * 3; + } + } + + i++; + } + + CONS_Printf("Model::Optimize(): Model reduced from %d to %d meshes.\n", model->numMeshes, numMeshes); + model->meshes = newMeshes; + model->numMeshes = numMeshes; +} + +void GeneratePolygonNormals(model_t *model, int ztag) +{ + int i; + for (i = 0; i < model->numMeshes; i++) + { + int j; + mesh_t *mesh = &model->meshes[i]; + + if (!mesh->frames) + continue; + + for (j = 0; j < mesh->numFrames; j++) + { + int k; + mdlframe_t *frame = &mesh->frames[j]; + const float *vertices = frame->vertices; + vector_t *polyNormals; + + frame->polyNormals = (vector_t*)Z_Malloc(sizeof(vector_t) * mesh->numTriangles, ztag, 0); + + polyNormals = frame->polyNormals; + + for (k = 0; k < mesh->numTriangles; k++) + { +// Vector::Normal(vertices, polyNormals); + vertices += 3 * 3; + polyNormals++; + } + } + } +} + +// +// Reload +// +// Reload VBOs +// +#if 0 +static void Reload(void) +{ +/* model_t *node; + for (node = modelHead; node; node = node->next) + { + int i; + for (i = 0; i < node->numMeshes; i++) + { + mesh_t *mesh = &node->meshes[i]; + + if (mesh->frames) + { + int j; + for (j = 0; j < mesh->numFrames; j++) + CreateVBO(mesh, &mesh->frames[j]); + } + else if (mesh->tinyframes) + { + int j; + for (j = 0; j < mesh->numFrames; j++) + CreateVBO(mesh, &mesh->tinyframes[j]); + } + } + }*/ +} +#endif + +void DeleteVBOs(model_t *model) +{ + (void)model; +/* for (int i = 0; i < model->numMeshes; i++) + { + mesh_t *mesh = &model->meshes[i]; + + if (mesh->frames) + { + for (int j = 0; j < mesh->numFrames; j++) + { + mdlframe_t *frame = &mesh->frames[j]; + if (!frame->vboID) + continue; + bglDeleteBuffers(1, &frame->vboID); + frame->vboID = 0; + } + } + else if (mesh->tinyframes) + { + for (int j = 0; j < mesh->numFrames; j++) + { + tinyframe_t *frame = &mesh->tinyframes[j]; + if (!frame->vboID) + continue; + bglDeleteBuffers(1, &frame->vboID); + frame->vboID = 0; + } + } + }*/ +} diff --git a/src/hardware/hw_model.h b/src/hardware/hw_model.h new file mode 100644 index 000000000..1803f4c5c --- /dev/null +++ b/src/hardware/hw_model.h @@ -0,0 +1,104 @@ +/* + From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) + An experimental work-in-progress. + + Donated to Sonic Team Junior and adapted to work with + Sonic Robo Blast 2. The license of this code matches whatever + the licensing is for Sonic Robo Blast 2. +*/ + +#ifndef _HW_MODEL_H_ +#define _HW_MODEL_H_ + +#include "../doomtype.h" + +typedef struct +{ + float x, y, z; +} vector_t; + +extern vector_t vectorXaxis; +extern vector_t vectorYaxis; +extern vector_t vectorZaxis; + +void VectorRotate(vector_t *rotVec, const vector_t *axisVec, float angle); + +typedef struct +{ + float ambient[4], diffuse[4], specular[4], emissive[4]; + float shininess; + boolean spheremap; +// Texture::texture_t *texture; +// Texture::texture_t *lightmap; +} material_t; + +typedef struct +{ + material_t *material; // Pointer to the allocated 'materials' list in model_t + float *vertices; + float *normals; + float *tangents; + char *colors; + unsigned int vboID; + vector_t *polyNormals; +} mdlframe_t; + +typedef struct +{ + material_t *material; + short *vertices; + char *normals; + char *tangents; + unsigned int vboID; +} tinyframe_t; + +// Equivalent to MD3's many 'surfaces' +typedef struct mesh_s +{ + int numVertices; + int numTriangles; + + float *uvs; + float *lightuvs; + + int numFrames; + mdlframe_t *frames; + tinyframe_t *tinyframes; + unsigned short *indices; +} mesh_t; + +typedef struct tag_s +{ + char name[64]; +// matrix_t transform; +} tag_t; + +typedef struct model_s +{ + int maxNumFrames; + + int numMaterials; + material_t *materials; + int numMeshes; + mesh_t *meshes; + int numTags; + tag_t *tags; + + char *mdlFilename; + boolean unloaded; +} model_t; + +extern int numModels; +extern model_t *modelHead; + +tag_t *GetTagByName(model_t *model, char *name, int frame); +model_t *LoadModel(const char *filename, int ztag); +void UnloadModel(model_t *model); +void Optimize(model_t *model); +void GenerateVertexNormals(model_t *model); +void GeneratePolygonNormals(model_t *model, int ztag); +void CreateVBOTiny(mesh_t *mesh, tinyframe_t *frame); +void CreateVBO(mesh_t *mesh, mdlframe_t *frame); +void DeleteVBOs(model_t *model); + +#endif diff --git a/src/hardware/r_opengl/ogl_win.c b/src/hardware/r_opengl/ogl_win.c index eb9a31a7d..562afe998 100644 --- a/src/hardware/r_opengl/ogl_win.c +++ b/src/hardware/r_opengl/ogl_win.c @@ -347,13 +347,6 @@ static INT32 WINAPI SetRes(viddef_t *lvid, vmode_t *pcurrentmode) if (strstr(renderer, "810")) oglflags |= GLF_NOZBUFREAD; DBG_Printf("oglflags : 0x%X\n", oglflags); -#ifdef USE_PALETTED_TEXTURE - if (isExtAvailable("GL_EXT_paletted_texture",gl_extensions)) - glColorTableEXT = GetGLFunc("glColorTableEXT"); - else - glColorTableEXT = NULL; -#endif - #ifdef USE_WGL_SWAP if (isExtAvailable("WGL_EXT_swap_control",gl_extensions)) wglSwapIntervalEXT = GetGLFunc("wglSwapIntervalEXT"); @@ -582,19 +575,8 @@ EXPORT void HWRAPI(SetPalette) (RGBA_t *pal, RGBA_t *gamma) myPaletteData[i].s.blue = (UINT8)MIN((pal[i].s.blue*gamma->s.blue)/127, 255); myPaletteData[i].s.alpha = pal[i].s.alpha; } -#ifdef USE_PALETTED_TEXTURE - if (glColorTableEXT) - { - for (i = 0; i < 256; i++) - { - palette_tex[3*i+0] = pal[i].s.red; - palette_tex[3*i+1] = pal[i].s.green; - palette_tex[3*i+2] = pal[i].s.blue; - } - glColorTableEXT(GL_TEXTURE_2D, GL_RGB8, 256, GL_RGB, GL_UNSIGNED_BYTE, palette_tex); - } -#endif - // on a chang� de palette, il faut recharger toutes les textures + + // on a palette change, you have to reload all of the textures Flush(); } diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 848353896..9fcc8d157 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -29,15 +29,10 @@ #include #include -#ifndef SHUFFLE -#ifndef KOS_GL_COMPATIBILITY -#define SHUFFLE -#endif -#endif #include "r_opengl.h" +#include "r_vbo.h" #if defined (HWRENDER) && !defined (NOROPENGL) -// for KOS: GL_TEXTURE_ENV, glAlphaFunc, glColorMask, glPolygonOffset, glReadPixels, GL_ALPHA_TEST, GL_POLYGON_OFFSET_FILL struct GLRGBAFloat { @@ -47,6 +42,7 @@ struct GLRGBAFloat GLfloat alpha; }; typedef struct GLRGBAFloat GLRGBAFloat; +static const GLubyte white[4] = { 255, 255, 255, 255 }; // ========================================================================== // CONSTANTS @@ -70,8 +66,10 @@ static float NEAR_CLIPPING_PLANE = NZCLIP_PLANE; static GLuint NextTexAvail = FIRST_TEX_AVAIL; static GLuint tex_downloaded = 0; static GLfloat fov = 90.0f; +#if 0 static GLuint pal_col = 0; static FRGBAFloat const_pal_col; +#endif static FBITFIELD CurrentPolyFlags; static FTextureInfo* gr_cachetail = NULL; @@ -83,9 +81,7 @@ GLint screen_height = 0; GLbyte screen_depth = 0; GLint textureformatGL = 0; GLint maximumAnisotropy = 0; -#ifndef KOS_GL_COMPATIBILITY static GLboolean MipMap = GL_FALSE; -#endif static GLint min_filter = GL_LINEAR; static GLint mag_filter = GL_LINEAR; static GLint anisotropic_filter = 0; @@ -94,17 +90,9 @@ static FTransform md2_transform; const GLubyte *gl_extensions = NULL; //Hurdler: 04/10/2000: added for the kick ass coronas as Boris wanted;-) -#ifndef MINI_GL_COMPATIBILITY -static GLdouble modelMatrix[16]; -static GLdouble projMatrix[16]; +static GLfloat modelMatrix[16]; +static GLfloat projMatrix[16]; static GLint viewport[4]; -#endif - - -#ifdef USE_PALETTED_TEXTURE - PFNGLCOLORTABLEEXTPROC glColorTableEXT = NULL; - GLubyte palette_tex[256*3]; -#endif // Yay for arbitrary numbers! NextTexAvail is buggy for some reason. // Sryder: NextTexAvail is broken for these because palette changes or changes to the texture filter or antialiasing @@ -167,11 +155,6 @@ float byteasfloat(UINT8 fbyte) static I_Error_t I_Error_GL = NULL; -#ifndef MINI_GL_COMPATIBILITY -static boolean gl13 = false; // whether we can use opengl 1.3 functions -#endif - - // -----------------+ // DBG_Printf : Output error messages to debug log if DEBUG_TO_FILE is defined, // : else do nothing @@ -202,19 +185,14 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...) #define pglAlphaFunc glAlphaFunc #define pglBlendFunc glBlendFunc #define pglCullFace glCullFace -#define pglPolygonMode glPolygonMode #define pglPolygonOffset glPolygonOffset #define pglScissor glScissor #define pglEnable glEnable #define pglDisable glDisable -#ifndef MINI_GL_COMPATIBILITY -#define pglGetDoublev glGetDoublev -#endif +#define pglGetFloatv glGetFloatv //glGetIntegerv //glGetString -#ifdef KOS_GL_COMPATIBILITY #define pglHint glHint -#endif /* Depth Buffer */ #define pglClearDepth glClearDepth @@ -228,23 +206,25 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...) #define pglPushMatrix glPushMatrix #define pglPopMatrix glPopMatrix #define pglLoadIdentity glLoadIdentity -#ifdef MINI_GL_COMPATIBILITY #define pglMultMatrixf glMultMatrixf -#else -#define pglMultMatrixd glMultMatrixd -#endif #define pglRotatef glRotatef #define pglScalef glScalef #define pglTranslatef glTranslatef /* Drawing Functions */ -#define pglBegin glBegin -#define pglEnd glEnd -#define pglVertex3f glVertex3f -#define pglNormal3f glNormal3f -#define pglColor4f glColor4f -#define pglColor4fv glColor4fv -#define pglTexCoord2f glTexCoord2f +#define pglColor4ubv glColor4ubv +#define pglVertexPointer glVertexPointer +#define pglNormalPointer glNormalPointer +#define pglTexCoordPointer glTexCoordPointer +#define pglDrawArrays glDrawArrays +#define pglDrawElements glDrawElements +#define pglEnableClientState glEnableClientState +#define pglDisableClientState glDisableClientState +#define pglClientActiveTexture glClientActiveTexture +#define pglGenBuffers glGenBuffers +#define pglBindBuffer glBindBuffer +#define pglBufferData glBufferData +#define pglDeleteBuffers glDeleteBuffers /* Lighting */ #define pglShadeModel glShadeModel @@ -270,10 +250,8 @@ FUNCPRINTF void DBG_Printf(const char *lpFmt, ...) #define pglDeleteTextures glDeleteTextures #define pglBindTexture glBindTexture /* texture mapping */ //GL_EXT_copy_texture -#ifndef KOS_GL_COMPATIBILITY #define pglCopyTexImage2D glCopyTexImage2D #define pglCopyTexSubImage2D glCopyTexSubImage2D -#endif #else //!STATIC_OPENGL @@ -290,8 +268,6 @@ typedef void (APIENTRY * PFNglBlendFunc) (GLenum sfactor, GLenum dfactor); static PFNglBlendFunc pglBlendFunc; typedef void (APIENTRY * PFNglCullFace) (GLenum mode); static PFNglCullFace pglCullFace; -typedef void (APIENTRY * PFNglPolygonMode) (GLenum face, GLenum mode); -static PFNglPolygonMode pglPolygonMode; typedef void (APIENTRY * PFNglPolygonOffset) (GLfloat factor, GLfloat units); static PFNglPolygonOffset pglPolygonOffset; typedef void (APIENTRY * PFNglScissor) (GLint x, GLint y, GLsizei width, GLsizei height); @@ -300,10 +276,8 @@ typedef void (APIENTRY * PFNglEnable) (GLenum cap); static PFNglEnable pglEnable; typedef void (APIENTRY * PFNglDisable) (GLenum cap); static PFNglDisable pglDisable; -#ifndef MINI_GL_COMPATIBILITY -typedef void (APIENTRY * PFNglGetDoublev) (GLenum pname, GLdouble *params); -static PFNglGetDoublev pglGetDoublev; -#endif +typedef void (APIENTRY * PFNglGetFloatv) (GLenum pname, GLfloat *params); +static PFNglGetFloatv pglGetFloatv; //glGetIntegerv //glGetString @@ -328,13 +302,8 @@ typedef void (APIENTRY * PFNglPopMatrix) (void); static PFNglPopMatrix pglPopMatrix; typedef void (APIENTRY * PFNglLoadIdentity) (void); static PFNglLoadIdentity pglLoadIdentity; -#ifdef MINI_GL_COMPATIBILITY typedef void (APIENTRY * PFNglMultMatrixf) (const GLfloat *m); static PFNglMultMatrixf pglMultMatrixf; -#else -typedef void (APIENTRY * PFNglMultMatrixd) (const GLdouble *m); -static PFNglMultMatrixd pglMultMatrixd; -#endif typedef void (APIENTRY * PFNglRotatef) (GLfloat angle, GLfloat x, GLfloat y, GLfloat z); static PFNglRotatef pglRotatef; typedef void (APIENTRY * PFNglScalef) (GLfloat x, GLfloat y, GLfloat z); @@ -343,20 +312,31 @@ typedef void (APIENTRY * PFNglTranslatef) (GLfloat x, GLfloat y, GLfloat z); static PFNglTranslatef pglTranslatef; /* Drawing Functions */ -typedef void (APIENTRY * PFNglBegin) (GLenum mode); -static PFNglBegin pglBegin; -typedef void (APIENTRY * PFNglEnd) (void); -static PFNglEnd pglEnd; -typedef void (APIENTRY * PFNglVertex3f) (GLfloat x, GLfloat y, GLfloat z); -static PFNglVertex3f pglVertex3f; -typedef void (APIENTRY * PFNglNormal3f) (GLfloat x, GLfloat y, GLfloat z); -static PFNglNormal3f pglNormal3f; -typedef void (APIENTRY * PFNglColor4f) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); -static PFNglColor4f pglColor4f; -typedef void (APIENTRY * PFNglColor4fv) (const GLfloat *v); -static PFNglColor4fv pglColor4fv; -typedef void (APIENTRY * PFNglTexCoord2f) (GLfloat s, GLfloat t); -static PFNglTexCoord2f pglTexCoord2f; +typedef void (APIENTRY * PFNglColor4ubv) (const GLubyte *v); +static PFNglColor4ubv pglColor4ubv; +typedef void (APIENTRY * PFNglVertexPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +static PFNglVertexPointer pglVertexPointer; +typedef void (APIENTRY * PFNglNormalPointer) (GLenum type, GLsizei stride, const GLvoid *pointer); +static PFNglNormalPointer pglNormalPointer; +typedef void (APIENTRY * PFNglTexCoordPointer) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +static PFNglTexCoordPointer pglTexCoordPointer; +typedef void (APIENTRY * PFNglDrawArrays) (GLenum mode, GLint first, GLsizei count); +static PFNglDrawArrays pglDrawArrays; +typedef void (APIENTRY * PFNglDrawElements) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +static PFNglDrawElements pglDrawElements; +typedef void (APIENTRY * PFNglEnableClientState) (GLenum cap); +static PFNglEnableClientState pglEnableClientState; +typedef void (APIENTRY * PFNglDisableClientState) (GLenum cap); +static PFNglDisableClientState pglDisableClientState; +typedef void (APIENTRY * PFNglGenBuffers) (GLsizei n, GLuint *buffers); +static PFNglGenBuffers pglGenBuffers; +typedef void (APIENTRY * PFNglBindBuffer) (GLenum target, GLuint buffer); +static PFNglBindBuffer pglBindBuffer; +typedef void (APIENTRY * PFNglBufferData) (GLenum target, GLsizei size, const GLvoid *data, GLenum usage); +static PFNglBufferData pglBufferData; +typedef void (APIENTRY * PFNglDeleteBuffers) (GLsizei n, const GLuint *buffers); +static PFNglDeleteBuffers pglDeleteBuffers; + /* Lighting */ typedef void (APIENTRY * PFNglShadeModel) (GLenum mode); @@ -404,15 +384,16 @@ static PFNglCopyTexSubImage2D pglCopyTexSubImage2D; typedef GLint (APIENTRY * PFNgluBuild2DMipmaps) (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data); static PFNgluBuild2DMipmaps pgluBuild2DMipmaps; -#ifndef MINI_GL_COMPATIBILITY /* 1.3 functions for multitexturing */ typedef void (APIENTRY *PFNglActiveTexture) (GLenum); static PFNglActiveTexture pglActiveTexture; typedef void (APIENTRY *PFNglMultiTexCoord2f) (GLenum, GLfloat, GLfloat); static PFNglMultiTexCoord2f pglMultiTexCoord2f; -#endif +typedef void (APIENTRY *PFNglMultiTexCoord2fv) (GLenum target, const GLfloat *v); +static PFNglMultiTexCoord2fv pglMultiTexCoord2fv; +typedef void (APIENTRY *PFNglClientActiveTexture) (GLenum); +static PFNglClientActiveTexture pglClientActiveTexture; -#ifndef MINI_GL_COMPATIBILITY /* 1.2 Parms */ /* GL_CLAMP_TO_EDGE_EXT */ #ifndef GL_CLAMP_TO_EDGE @@ -433,14 +414,6 @@ static PFNglMultiTexCoord2f pglMultiTexCoord2f; #define GL_TEXTURE1 0x84C1 #endif -#endif - -#ifdef MINI_GL_COMPATIBILITY -#undef GL_CLAMP_TO_EDGE -#undef GL_TEXTURE_MIN_LOD -#undef GL_TEXTURE_MAX_LOD -#endif - boolean SetupGLfunc(void) { #ifndef STATIC_OPENGL @@ -453,21 +426,18 @@ boolean SetupGLfunc(void) GETOPENGLFUNC(pglClearColor, glClearColor) - GETOPENGLFUNC(pglClear , glClear) - GETOPENGLFUNC(pglColorMask , glColorMask) - GETOPENGLFUNC(pglAlphaFunc , glAlphaFunc) - GETOPENGLFUNC(pglBlendFunc , glBlendFunc) - GETOPENGLFUNC(pglCullFace , glCullFace) - GETOPENGLFUNC(pglPolygonMode , glPolygonMode) - GETOPENGLFUNC(pglPolygonOffset , glPolygonOffset) - GETOPENGLFUNC(pglScissor , glScissor) - GETOPENGLFUNC(pglEnable , glEnable) - GETOPENGLFUNC(pglDisable , glDisable) -#ifndef MINI_GL_COMPATIBILITY - GETOPENGLFUNC(pglGetDoublev , glGetDoublev) -#endif - GETOPENGLFUNC(pglGetIntegerv , glGetIntegerv) - GETOPENGLFUNC(pglGetString , glGetString) + GETOPENGLFUNC(pglClear, glClear) + GETOPENGLFUNC(pglColorMask, glColorMask) + GETOPENGLFUNC(pglAlphaFunc, glAlphaFunc) + GETOPENGLFUNC(pglBlendFunc, glBlendFunc) + GETOPENGLFUNC(pglCullFace, glCullFace) + GETOPENGLFUNC(pglPolygonOffset, glPolygonOffset) + GETOPENGLFUNC(pglScissor, glScissor) + GETOPENGLFUNC(pglEnable, glEnable) + GETOPENGLFUNC(pglDisable, glDisable) + GETOPENGLFUNC(pglGetFloatv, glGetFloatv) + GETOPENGLFUNC(pglGetIntegerv, glGetIntegerv) + GETOPENGLFUNC(pglGetString, glGetString) GETOPENGLFUNC(pglClearDepth , glClearDepth) GETOPENGLFUNC(pglDepthFunc , glDepthFunc) @@ -479,22 +449,19 @@ boolean SetupGLfunc(void) GETOPENGLFUNC(pglPushMatrix , glPushMatrix) GETOPENGLFUNC(pglPopMatrix , glPopMatrix) GETOPENGLFUNC(pglLoadIdentity , glLoadIdentity) -#ifdef MINI_GL_COMPATIBILITY GETOPENGLFUNC(pglMultMatrixf , glMultMatrixf) -#else - GETOPENGLFUNC(pglMultMatrixd , glMultMatrixd) -#endif GETOPENGLFUNC(pglRotatef , glRotatef) GETOPENGLFUNC(pglScalef , glScalef) GETOPENGLFUNC(pglTranslatef , glTranslatef) - GETOPENGLFUNC(pglBegin , glBegin) - GETOPENGLFUNC(pglEnd , glEnd) - GETOPENGLFUNC(pglVertex3f , glVertex3f) - GETOPENGLFUNC(pglNormal3f , glNormal3f) - GETOPENGLFUNC(pglColor4f , glColor4f) - GETOPENGLFUNC(pglColor4fv , glColor4fv) - GETOPENGLFUNC(pglTexCoord2f , glTexCoord2f) + GETOPENGLFUNC(pglColor4ubv, glColor4ubv) + GETOPENGLFUNC(pglVertexPointer, glVertexPointer) + GETOPENGLFUNC(pglNormalPointer, glNormalPointer) + GETOPENGLFUNC(pglTexCoordPointer, glTexCoordPointer) + GETOPENGLFUNC(pglDrawArrays, glDrawArrays) + GETOPENGLFUNC(pglDrawElements, glDrawElements) + GETOPENGLFUNC(pglEnableClientState, glEnableClientState) + GETOPENGLFUNC(pglDisableClientState, glDisableClientState) GETOPENGLFUNC(pglShadeModel , glShadeModel) GETOPENGLFUNC(pglLightfv, glLightfv) @@ -526,47 +493,19 @@ boolean SetupGLfunc(void) } // This has to be done after the context is created so the version number can be obtained +// This is stupid -- even some of the oldest usable OpenGL hardware today supports 1.3-level featureset. boolean SetupGLFunc13(void) { -#ifdef MINI_GL_COMPATIBILITY - return false; -#else - const GLubyte *version = pglGetString(GL_VERSION); - int glmajor, glminor; + pglActiveTexture = GetGLFunc("glActiveTexture"); + pglMultiTexCoord2f = GetGLFunc("glMultiTexCoord2f"); + pglClientActiveTexture = GetGLFunc("glClientActiveTexture"); + pglMultiTexCoord2fv = GetGLFunc("glMultiTexCoord2fv"); + pglGenBuffers = GetGLFunc("glGenBuffers"); + pglBindBuffer = GetGLFunc("glBindBuffer"); + pglBufferData = GetGLFunc("glBufferData"); + pglDeleteBuffers = GetGLFunc("glDeleteBuffers"); - gl13 = false; - // Parse the GL version - if (version != NULL) - { - if (sscanf((const char*)version, "%d.%d", &glmajor, &glminor) == 2) - { - // Look, we gotta prepare for the inevitable arrival of GL 2.0 code... - if (glmajor == 1 && glminor >= 3) - gl13 = true; - else if (glmajor > 1) - gl13 = true; - } - } - - if (gl13) - { - pglActiveTexture = GetGLFunc("glActiveTexture"); - pglMultiTexCoord2f = GetGLFunc("glMultiTexCoord2f"); - } - else if (isExtAvailable("GL_ARB_multitexture", gl_extensions)) - { - // Get the functions - pglActiveTexture = GetGLFunc("glActiveTextureARB"); - pglMultiTexCoord2f = GetGLFunc("glMultiTexCoord2fARB"); - - gl13 = true; // This is now true, so the new fade mask stuff can be done, if OpenGL version is less than 1.3, it still uses the old fade stuff. - DBG_Printf("GL_ARB_multitexture support: enabled\n"); - - } - else - DBG_Printf("GL_ARB_multitexture support: disabled\n"); return true; -#endif } // -----------------+ @@ -582,48 +521,40 @@ static void SetNoTexture(void) } } -static void GLPerspective(GLdouble fovy, GLdouble aspect) +static void GLPerspective(GLfloat fovy, GLfloat aspect) { -#ifdef MINI_GL_COMPATIBILITY GLfloat m[4][4] = -#else - GLdouble m[4][4] = -#endif { { 1.0f, 0.0f, 0.0f, 0.0f}, { 0.0f, 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f,-1.0f}, { 0.0f, 0.0f, 0.0f, 0.0f}, }; - const GLdouble zNear = NEAR_CLIPPING_PLANE; - const GLdouble zFar = FAR_CLIPPING_PLANE; - const GLdouble radians = (GLdouble)(fovy / 2.0f * M_PIl / 180.0f); - const GLdouble sine = sin(radians); - const GLdouble deltaZ = zFar - zNear; - GLdouble cotangent; + const GLfloat zNear = NEAR_CLIPPING_PLANE; + const GLfloat zFar = FAR_CLIPPING_PLANE; + const GLfloat radians = (GLfloat)(fovy / 2.0f * M_PIl / 180.0f); + const GLfloat sine = sinf(radians); + const GLfloat deltaZ = zFar - zNear; + GLfloat cotangent; if ((fabsf((float)deltaZ) < 1.0E-36f) || fpclassify(sine) == FP_ZERO || fpclassify(aspect) == FP_ZERO) { return; } - cotangent = cos(radians) / sine; + cotangent = cosf(radians) / sine; m[0][0] = cotangent / aspect; m[1][1] = cotangent; m[2][2] = -(zFar + zNear) / deltaZ; m[3][2] = -2.0f * zNear * zFar / deltaZ; -#ifdef MINI_GL_COMPATIBILITY + pglMultMatrixf(&m[0][0]); -#else - pglMultMatrixd(&m[0][0]); -#endif } -#ifndef MINI_GL_COMPATIBILITY -static void GLProject(GLdouble objX, GLdouble objY, GLdouble objZ, - GLdouble* winX, GLdouble* winY, GLdouble* winZ) +static void GLProject(GLfloat objX, GLfloat objY, GLfloat objZ, + GLfloat* winX, GLfloat* winY, GLfloat* winZ) { - GLdouble in[4], out[4]; + GLfloat in[4], out[4]; int i; for (i=0; i<4; i++) @@ -659,7 +590,6 @@ static void GLProject(GLdouble objX, GLdouble objY, GLdouble objZ, *winY=in[1]; *winZ=in[2]; } -#endif // -----------------+ // SetModelView : @@ -690,10 +620,8 @@ void SetModelView(GLint w, GLint h) //pglScalef(1.0f, 320.0f/200.0f, 1.0f); // gr_scalefrustum (ORIGINAL_ASPECT) // added for new coronas' code (without depth buffer) -#ifndef MINI_GL_COMPATIBILITY pglGetIntegerv(GL_VIEWPORT, viewport); - pglGetDoublev(GL_PROJECTION_MATRIX, projMatrix); -#endif + pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix); } @@ -718,17 +646,15 @@ void SetStates(void) //pglShadeModel(GL_FLAT); pglEnable(GL_TEXTURE_2D); // two-dimensional texturing -#ifndef KOS_GL_COMPATIBILITY + pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); pglAlphaFunc(GL_NOTEQUAL, 0.0f); -#endif + //pglBlendFunc(GL_ONE, GL_ZERO); // copy pixel to frame buffer (opaque) pglEnable(GL_BLEND); // enable color blending -#ifndef KOS_GL_COMPATIBILITY pglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); -#endif //pglDisable(GL_DITHER); // faB: ??? (undocumented in OpenGL 1.1) // Hurdler: yes, it is! @@ -753,14 +679,10 @@ void SetStates(void) //tex_downloaded = NOTEXTURE_NUM; //pglTexImage2D(GL_TEXTURE_2D, 0, 4, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, Data); -#ifndef KOS_GL_COMPATIBILITY pglPolygonOffset(-1.0f, -1.0f); -#endif //pglEnable(GL_CULL_FACE); //pglCullFace(GL_FRONT); - //pglPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - //pglPolygonMode(GL_FRONT, GL_LINE); //glFogi(GL_FOG_MODE, GL_EXP); //pglHint(GL_FOG_HINT, GL_FASTEST); @@ -776,9 +698,7 @@ void SetStates(void) // bp : when no t&l :) pglLoadIdentity(); pglScalef(1.0f, 1.0f, -1.0f); -#ifndef MINI_GL_COMPATIBILITY - pglGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); // added for new coronas' code (without depth buffer) -#endif + pglGetFloatv(GL_MODELVIEW_MATRIX, modelMatrix); // added for new coronas' code (without depth buffer) } @@ -882,14 +802,6 @@ EXPORT void HWRAPI(ClearMipMapCache) (void) EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 * dst_data) { -#ifdef KOS_GL_COMPATIBILITY - (void)x; - (void)y; - (void)width; - (void)height; - (void)dst_stride; - (void)dst_data; -#else INT32 i; // DBG_Printf ("ReadRect()\n"); if (dst_stride == width*3) @@ -931,7 +843,6 @@ EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, } free(image); } -#endif } @@ -952,10 +863,8 @@ EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, f pglMatrixMode(GL_MODELVIEW); // added for new coronas' code (without depth buffer) -#ifndef MINI_GL_COMPATIBILITY pglGetIntegerv(GL_VIEWPORT, viewport); - pglGetDoublev(GL_PROJECTION_MATRIX, projMatrix); -#endif + pglGetFloatv(GL_PROJECTION_MATRIX, projMatrix); } @@ -989,6 +898,8 @@ EXPORT void HWRAPI(ClearBuffer) (FBOOLEAN ColorMask, SetBlend(DepthMask ? PF_Occlude | CurrentPolyFlags : CurrentPolyFlags&~PF_Occlude); pglClear(ClearMask); + pglEnableClientState(GL_VERTEX_ARRAY); // We always use this one + pglEnableClientState(GL_TEXTURE_COORD_ARRAY); // And mostly this one, too } @@ -999,54 +910,35 @@ EXPORT void HWRAPI(Draw2DLine) (F2DCoord * v1, F2DCoord * v2, RGBA_t Color) { - GLRGBAFloat c; - // DBG_Printf ("DrawLine() (%f %f %f) %d\n", v1->x, -v1->y, -v1->z, v1->argb); -#ifdef MINI_GL_COMPATIBILITY - GLfloat px1, px2, px3, px4; - GLfloat py1, py2, py3, py4; + GLfloat p[12]; GLfloat dx, dy; GLfloat angle; -#endif // BP: we should reflect the new state in our variable //SetBlend(PF_Modulated|PF_NoTexture); pglDisable(GL_TEXTURE_2D); - c.red = byte2float[Color.s.red]; - c.green = byte2float[Color.s.green]; - c.blue = byte2float[Color.s.blue]; - c.alpha = byte2float[Color.s.alpha]; - -#ifndef MINI_GL_COMPATIBILITY - pglColor4fv(&c.red); // is in RGBA float format - pglBegin(GL_LINES); - pglVertex3f(v1->x, -v1->y, 1.0f); - pglVertex3f(v2->x, -v2->y, 1.0f); - pglEnd(); -#else - if (v2->x != v1->x) + // This is the preferred, 'modern' way of rendering lines -- creating a polygon. + if (fabsf(v2->x - v1->x) > FLT_EPSILON) angle = (float)atan((v2->y-v1->y)/(v2->x-v1->x)); else - angle = N_PI_DEMI; + angle = (float)N_PI_DEMI; dx = (float)sin(angle) / (float)screen_width; dy = (float)cos(angle) / (float)screen_height; - px1 = v1->x - dx; py1 = v1->y + dy; - px2 = v2->x - dx; py2 = v2->y + dy; - px3 = v2->x + dx; py3 = v2->y - dy; - px4 = v1->x + dx; py4 = v1->y - dy; + p[0] = v1->x - dx; p[1] = -(v1->y + dy); p[2] = 1; + p[3] = v2->x - dx; p[4] = -(v2->y + dy); p[5] = 1; + p[6] = v2->x + dx; p[7] = -(v2->y - dy); p[8] = 1; + p[9] = v1->x + dx; p[10] = -(v1->y - dy); p[11] = 1; - pglColor4f(c.red, c.green, c.blue, c.alpha); - pglBegin(GL_TRIANGLE_FAN); - pglVertex3f(px1, -py1, 1); - pglVertex3f(px2, -py2, 1); - pglVertex3f(px3, -py3, 1); - pglVertex3f(px4, -py4, 1); - pglEnd(); -#endif + pglDisableClientState(GL_TEXTURE_COORD_ARRAY); + pglColor4ubv((GLubyte*)&Color.s); + pglVertexPointer(3, GL_FLOAT, 0, p); + pglDrawArrays(GL_TRIANGLE_FAN, 0, 4); + pglEnableClientState(GL_TEXTURE_COORD_ARRAY); pglEnable(GL_TEXTURE_2D); } @@ -1075,60 +967,42 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags) switch (PolyFlags & PF_Blending) { case PF_Translucent & PF_Blending: pglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // alpha = level of transparency -#ifndef KOS_GL_COMPATIBILITY pglAlphaFunc(GL_NOTEQUAL, 0.0f); -#endif break; case PF_Masked & PF_Blending: // Hurdler: does that mean lighting is only made by alpha src? // it sounds ok, but not for polygonsmooth pglBlendFunc(GL_SRC_ALPHA, GL_ZERO); // 0 alpha = holes in texture -#ifndef KOS_GL_COMPATIBILITY pglAlphaFunc(GL_GREATER, 0.5f); -#endif break; case PF_Additive & PF_Blending: -#ifdef ATI_RAGE_PRO_COMPATIBILITY - pglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // alpha = level of transparency -#else pglBlendFunc(GL_SRC_ALPHA, GL_ONE); // src * alpha + dest -#endif -#ifndef KOS_GL_COMPATIBILITY pglAlphaFunc(GL_NOTEQUAL, 0.0f); -#endif break; case PF_Environment & PF_Blending: pglBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); -#ifndef KOS_GL_COMPATIBILITY pglAlphaFunc(GL_NOTEQUAL, 0.0f); -#endif break; case PF_Substractive & PF_Blending: // good for shadow - // not realy but what else ? + // not really but what else ? pglBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); -#ifndef KOS_GL_COMPATIBILITY pglAlphaFunc(GL_NOTEQUAL, 0.0f); -#endif break; case PF_Fog & PF_Fog: // Sryder: Fog // multiplies input colour by input alpha, and destination colour by input colour, then adds them pglBlendFunc(GL_SRC_ALPHA, GL_SRC_COLOR); -#ifndef KOS_GL_COMPATIBILITY pglAlphaFunc(GL_NOTEQUAL, 0.0f); -#endif break; default : // must be 0, otherwise it's an error // No blending pglBlendFunc(GL_ONE, GL_ZERO); // the same as no blending -#ifndef KOS_GL_COMPATIBILITY pglAlphaFunc(GL_GREATER, 0.5f); -#endif break; } } -#ifndef KOS_GL_COMPATIBILITY + if (Xor & PF_NoAlphaTest) { if (PolyFlags & PF_NoAlphaTest) @@ -1144,7 +1018,7 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags) else pglDisable(GL_POLYGON_OFFSET_FILL); } -#endif + if (Xor&PF_NoDepthTest) { if (PolyFlags & PF_NoDepthTest) @@ -1171,17 +1045,13 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags) pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } -#ifdef KOS_GL_COMPATIBILITY - if (Xor&PF_Modulated && !(PolyFlags & PF_Modulated)) - pglColor4f(1.0f, 1.0f, 1.0f, 1.0f); -#else if (Xor&PF_Modulated) { #if defined (__unix__) || defined (UNIXCOMMON) if (oglflags & GLF_NOTEXENV) { if (!(PolyFlags & PF_Modulated)) - pglColor4f(1.0f, 1.0f, 1.0f, 1.0f); + pglColor4ubv(white); } else #endif @@ -1194,7 +1064,6 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags) pglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); } } -#endif if (Xor & PF_Occlude) // depth test but (no) depth write { @@ -1248,11 +1117,7 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) else { // Download a mipmap -#ifdef KOS_GL_COMPATIBILITY - static GLushort tex[2048*2048]; -#else static RGBA_t tex[2048*2048]; -#endif const GLvoid *ptex = tex; INT32 w, h; @@ -1261,112 +1126,6 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) w = pTexInfo->width; h = pTexInfo->height; -#ifdef USE_PALETTED_TEXTURE - if (glColorTableEXT && - (pTexInfo->grInfo.format == GR_TEXFMT_P_8) && - !(pTexInfo->flags & TF_CHROMAKEYED)) - { - // do nothing here. - // Not a problem with MiniGL since we don't use paletted texture - } - else -#endif -#ifdef KOS_GL_COMPATIBILITY - if ((pTexInfo->grInfo.format == GR_TEXFMT_P_8) || - (pTexInfo->grInfo.format == GR_TEXFMT_AP_88)) - { - const GLubyte *pImgData = (const GLubyte *)pTexInfo->grInfo.data; - INT32 i, j; - - for (j = 0; j < h; j++) - { - for (i = 0; i < w; i++) - { - if ((*pImgData == HWR_PATCHES_CHROMAKEY_COLORINDEX) && - (pTexInfo->flags & TF_CHROMAKEYED)) - { - tex[w*j+i] = 0; - } - else - { - if (pTexInfo->grInfo.format == GR_TEXFMT_AP_88 && !(pTexInfo->flags & TF_CHROMAKEYED)) - tex[w*j+i] = 0; - else - tex[w*j+i] = (myPaletteData[*pImgData].s.alpha>>4)<<12; - - tex[w*j+i] |= (myPaletteData[*pImgData].s.red >>4)<<8; - tex[w*j+i] |= (myPaletteData[*pImgData].s.green>>4)<<4; - tex[w*j+i] |= (myPaletteData[*pImgData].s.blue >>4); - } - - pImgData++; - - if (pTexInfo->grInfo.format == GR_TEXFMT_AP_88) - { - if (!(pTexInfo->flags & TF_CHROMAKEYED)) - tex[w*j+i] |= ((*pImgData)>>4)<<12; - pImgData++; - } - - } - } - } - else if (pTexInfo->grInfo.format == GR_RGBA) - { - // corona test : passed as ARGB 8888, which is not in glide formats - // Hurdler: not used for coronas anymore, just for dynamic lighting - const RGBA_t *pImgData = (const RGBA_t *)pTexInfo->grInfo.data; - INT32 i, j; - - for (j = 0; j < h; j++) - { - for (i = 0; i < w; i++) - { - tex[w*j+i] = (pImgData->s.alpha>>4)<<12; - tex[w*j+i] |= (pImgData->s.red >>4)<<8; - tex[w*j+i] |= (pImgData->s.green>>4)<<4; - tex[w*j+i] |= (pImgData->s.blue >>4); - pImgData++; - } - } - } - else if (pTexInfo->grInfo.format == GR_TEXFMT_ALPHA_INTENSITY_88) - { - const GLubyte *pImgData = (const GLubyte *)pTexInfo->grInfo.data; - INT32 i, j; - - for (j = 0; j < h; j++) - { - for (i = 0; i < w; i++) - { - const GLubyte sID = (*pImgData)>>4; - tex[w*j+i] = sID<<8 | sID<<4 | sID; - pImgData++; - tex[w*j+i] |= ((*pImgData)>>4)<<12; - pImgData++; - } - } - } - else if (pTexInfo->grInfo.format == GR_TEXFMT_ALPHA_8) // Used for fade masks - { - const GLubyte *pImgData = (const GLubyte *)pTexInfo->grInfo.data; - INT32 i, j; - - for (j = 0; j < h; j++) - { - for (i = 0; i < w; i++) - { - tex[w*j+i] = (pImgData>>4)<<12; - tex[w*j+i] |= (255>>4)<<8; - tex[w*j+i] |= (255>>4)<<4; - tex[w*j+i] |= (255>>4); - pImgData++; - } - } - } - else - DBG_Printf ("SetTexture(bad format) %ld\n", pTexInfo->grInfo.format); -#else if ((pTexInfo->grInfo.format == GR_TEXFMT_P_8) || (pTexInfo->grInfo.format == GR_TEXFMT_AP_88)) { @@ -1449,7 +1208,6 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) } else DBG_Printf ("SetTexture(bad format) %ld\n", pTexInfo->grInfo.format); -#endif pTexInfo->downloaded = NextTexAvail++; tex_downloaded = pTexInfo->downloaded; @@ -1458,13 +1216,8 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) // disable texture filtering on any texture that has holes so there's no dumb borders or blending issues if (pTexInfo->flags & TF_TRANSPARENT) { -#ifdef KOS_GL_COMPATIBILITY - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NONE); - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NONE); -#else pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); -#endif } else { @@ -1472,29 +1225,6 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter); } -#ifdef KOS_GL_COMPATIBILITY - pglTexImage2D(GL_TEXTURE_2D, 0, GL_ARGB4444, w, h, 0, GL_ARGB4444, GL_UNSIGNED_BYTE, ptex); -#else -#ifdef MINI_GL_COMPATIBILITY - //if (pTexInfo->grInfo.format == GR_TEXFMT_ALPHA_INTENSITY_88) - //pglTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex); - //else - if (MipMap) - pgluBuild2DMipmaps(GL_TEXTURE_2D, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, ptex); - else - pglTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex); -#else -#ifdef USE_PALETTED_TEXTURE - //Hurdler: not really supported and not tested recently - if (glColorTableEXT && - (pTexInfo->grInfo.format == GR_TEXFMT_P_8) && - !(pTexInfo->flags & TF_CHROMAKEYED)) - { - glColorTableEXT(GL_TEXTURE_2D, GL_RGB8, 256, GL_RGB, GL_UNSIGNED_BYTE, palette_tex); - pglTexImage2D(GL_TEXTURE_2D, 0, GL_COLOR_INDEX8_EXT, w, h, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, pTexInfo->grInfo.data); - } - else -#endif if (pTexInfo->grInfo.format == GR_TEXFMT_ALPHA_INTENSITY_88) { //pglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex); @@ -1554,8 +1284,6 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) else pglTexImage2D(GL_TEXTURE_2D, 0, textureformatGL, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, ptex); } -#endif -#endif if (pTexInfo->flags & TF_WRAPX) pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); @@ -1579,19 +1307,6 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) else // initialisation de la liste gr_cachetail = gr_cachehead = pTexInfo; } -#ifdef MINI_GL_COMPATIBILITY - switch (pTexInfo->flags) - { - case 0 : - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - break; - default: - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - break; - } -#endif } @@ -1605,57 +1320,31 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FBITFIELD PolyFlags) { FUINT i; -#ifndef MINI_GL_COMPATIBILITY FUINT j; -#endif - GLRGBAFloat c = {0,0,0,0}; -#ifdef MINI_GL_COMPATIBILITY - if (PolyFlags & PF_Corona) - PolyFlags &= ~PF_NoDepthTest; -#else if ((PolyFlags & PF_Corona) && (oglflags & GLF_NOZBUFREAD)) PolyFlags &= ~(PF_NoDepthTest|PF_Corona); -#endif SetBlend(PolyFlags); //TODO: inline (#pragma..) // If Modulated, mix the surface colour to the texture if ((CurrentPolyFlags & PF_Modulated) && pSurf) - { - if (pal_col) - { // hack for non-palettized mode - c.red = (const_pal_col.red +byte2float[pSurf->FlatColor.s.red]) /2.0f; - c.green = (const_pal_col.green+byte2float[pSurf->FlatColor.s.green])/2.0f; - c.blue = (const_pal_col.blue +byte2float[pSurf->FlatColor.s.blue]) /2.0f; - c.alpha = byte2float[pSurf->FlatColor.s.alpha]; - } - else - { - c.red = byte2float[pSurf->FlatColor.s.red]; - c.green = byte2float[pSurf->FlatColor.s.green]; - c.blue = byte2float[pSurf->FlatColor.s.blue]; - c.alpha = byte2float[pSurf->FlatColor.s.alpha]; - } - -#ifdef MINI_GL_COMPATIBILITY - pglColor4f(c.red, c.green, c.blue, c.alpha); -#else - pglColor4fv(&c.red); // is in RGBA float format -#endif - } + pglColor4ubv((GLubyte*)&pSurf->FlatColor.s); // this test is added for new coronas' code (without depth buffer) // I think I should do a separate function for drawing coronas, so it will be a little faster -#ifndef MINI_GL_COMPATIBILITY if (PolyFlags & PF_Corona) // check to see if we need to draw the corona { //rem: all 8 (or 8.0f) values are hard coded: it can be changed to a higher value GLfloat buf[8][8]; - GLdouble cx, cy, cz; - GLdouble px = 0.0f, py = 0.0f, pz = -1.0f; + GLfloat cx, cy, cz; + GLfloat px = 0.0f, py = 0.0f, pz = -1.0f; GLfloat scalef = 0.0f; + GLubyte c[4]; + + float alpha; + cx = (pOutVerts[0].x + pOutVerts[2].x) / 2.0f; // we should change the coronas' ... cy = (pOutVerts[0].y + pOutVerts[2].y) / 2.0f; // ... code so its only done once. cz = pOutVerts[0].z; @@ -1691,22 +1380,20 @@ EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, if (scalef < 0.05f) return; - c.alpha *= scalef; // change the alpha value (it seems better than changing the size of the corona) - pglColor4fv(&c.red); - } -#endif - if (PolyFlags & PF_MD2) - return; + // GLubyte c[4]; + c[0] = pSurf->FlatColor.s.red; + c[1] = pSurf->FlatColor.s.green; + c[2] = pSurf->FlatColor.s.blue; - pglBegin(GL_TRIANGLE_FAN); - for (i = 0; i < iNumPts; i++) - { - pglTexCoord2f(pOutVerts[i].sow, pOutVerts[i].tow); - //Hurdler: test code: -pOutVerts[i].z => pOutVerts[i].z - pglVertex3f(pOutVerts[i].x, pOutVerts[i].y, pOutVerts[i].z); - //pglVertex3f(pOutVerts[i].x, pOutVerts[i].y, -pOutVerts[i].z); + alpha = byte2float[pSurf->FlatColor.s.alpha]; + alpha *= scalef; // change the alpha value (it seems better than changing the size of the corona) + c[3] = (unsigned char)(alpha * 255); + pglColor4ubv(c); } - pglEnd(); + + pglVertexPointer(3, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].x); + pglTexCoordPointer(2, GL_FLOAT, sizeof(FOutVector), &pOutVerts[0].sow); + pglDrawArrays(GL_TRIANGLE_FAN, 0, iNumPts); if (PolyFlags & PF_RemoveYWrap) pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); @@ -1737,15 +1424,6 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value) } #endif - case HWD_SET_PALETTECOLOR: - { - pal_col = Value; - const_pal_col.blue = byte2float[((Value>>16)&0xff)]; - const_pal_col.green = byte2float[((Value>>8)&0xff)]; - const_pal_col.red = byte2float[((Value)&0xff)]; - break; - } - case HWD_SET_FOG_COLOR: { GLfloat fogcolor[4]; @@ -1787,42 +1465,9 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value) pglDisable(GL_FOG); break; - case HWD_SET_POLYGON_SMOOTH: -#ifdef KOS_GL_COMPATIBILITY // GL_POLYGON_SMOOTH_HINT - if (Value) - pglHint(GL_POLYGON_SMOOTH_HINT,GL_NICEST); - else - pglHint(GL_POLYGON_SMOOTH_HINT,GL_FASTEST); -#else - if (Value) - pglEnable(GL_POLYGON_SMOOTH); - else - pglDisable(GL_POLYGON_SMOOTH); -#endif - break; - case HWD_SET_TEXTUREFILTERMODE: switch (Value) { -#ifdef KOS_GL_COMPATIBILITY - case HWD_SET_TEXTUREFILTER_TRILINEAR: - case HWD_SET_TEXTUREFILTER_BILINEAR: - min_filter = mag_filter = GL_FILTER_BILINEAR; - break; - case HWD_SET_TEXTUREFILTER_POINTSAMPLED: - min_filter = mag_filter = GL_FILTER_NONE; - case HWD_SET_TEXTUREFILTER_MIXED1: - min_filter = GL_FILTER_NONE; - mag_filter = GL_LINEAR; - case HWD_SET_TEXTUREFILTER_MIXED2: - min_filter = GL_LINEAR; - mag_filter = GL_FILTER_NONE; - break; - case HWD_SET_TEXTUREFILTER_MIXED3: - min_filter = GL_FILTER_BILINEAR; - mag_filter = GL_FILTER_NONE; - break; -#elif !defined (MINI_GL_COMPATIBILITY) case HWD_SET_TEXTUREFILTER_TRILINEAR: min_filter = GL_LINEAR_MIPMAP_LINEAR; mag_filter = GL_LINEAR; @@ -1851,14 +1496,9 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value) mag_filter = GL_NEAREST; MipMap = GL_TRUE; break; -#endif default: -#ifdef KOS_GL_COMPATIBILITY - min_filter = mag_filter = GL_FILTER_NONE; -#else mag_filter = GL_LINEAR; min_filter = GL_NEAREST; -#endif } if (!pgluBuild2DMipmaps) { @@ -1879,20 +1519,218 @@ EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value) } } -static void DrawMD2Ex(INT32 *gl_cmd_buffer, md2_frame_t *frame, INT32 duration, INT32 tics, md2_frame_t *nextframe, FTransform *pos, float scale, UINT8 flipped, UINT8 *color) +static float *vertBuffer = NULL; +static float *normBuffer = NULL; +static size_t lerpBufferSize = 0; +static short *vertTinyBuffer = NULL; +static char *normTinyBuffer = NULL; +static size_t lerpTinyBufferSize = 0; + +// Static temporary buffer for doing frame interpolation +// 'size' is the vertex size +static void AllocLerpBuffer(size_t size) +{ + if (lerpBufferSize >= size) + return; + + if (vertBuffer != NULL) + free(vertBuffer); + + if (normBuffer != NULL) + free(normBuffer); + + lerpBufferSize = size; + vertBuffer = malloc(lerpBufferSize); + normBuffer = malloc(lerpBufferSize); +} + +// Static temporary buffer for doing frame interpolation +// 'size' is the vertex size +static void AllocLerpTinyBuffer(size_t size) +{ + if (lerpTinyBufferSize >= size) + return; + + if (vertTinyBuffer != NULL) + free(vertTinyBuffer); + + if (normTinyBuffer != NULL) + free(normTinyBuffer); + + lerpTinyBufferSize = size; + vertTinyBuffer = malloc(lerpTinyBufferSize); + normTinyBuffer = malloc(lerpTinyBufferSize / 2); +} + +#ifndef GL_STATIC_DRAW +#define GL_STATIC_DRAW 0x88E4 +#endif + +#ifndef GL_ARRAY_BUFFER +#define GL_ARRAY_BUFFER 0x8892 +#endif + +static void CreateModelVBO(mesh_t *mesh, mdlframe_t *frame) +{ + int bufferSize = sizeof(vbo64_t)*mesh->numTriangles * 3; + vbo64_t *buffer = (vbo64_t*)malloc(bufferSize); + vbo64_t *bufPtr = buffer; + + float *vertPtr = frame->vertices; + float *normPtr = frame->normals; + float *tanPtr = frame->tangents; + float *uvPtr = mesh->uvs; + float *lightPtr = mesh->lightuvs; + char *colorPtr = frame->colors; + + int i; + for (i = 0; i < mesh->numTriangles * 3; i++) + { + bufPtr->x = *vertPtr++; + bufPtr->y = *vertPtr++; + bufPtr->z = *vertPtr++; + + bufPtr->nx = *normPtr++; + bufPtr->ny = *normPtr++; + bufPtr->nz = *normPtr++; + + bufPtr->s0 = *uvPtr++; + bufPtr->t0 = *uvPtr++; + + if (tanPtr != NULL) + { + bufPtr->tan0 = *tanPtr++; + bufPtr->tan1 = *tanPtr++; + bufPtr->tan2 = *tanPtr++; + } + + if (lightPtr != NULL) + { + bufPtr->s1 = *lightPtr++; + bufPtr->t1 = *lightPtr++; + } + + if (colorPtr) + { + bufPtr->r = *colorPtr++; + bufPtr->g = *colorPtr++; + bufPtr->b = *colorPtr++; + bufPtr->a = *colorPtr++; + } + else + { + bufPtr->r = 255; + bufPtr->g = 255; + bufPtr->b = 255; + bufPtr->a = 255; + } + + bufPtr++; + } + + pglGenBuffers(1, &frame->vboID); + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglBufferData(GL_ARRAY_BUFFER, bufferSize, buffer, GL_STATIC_DRAW); + free(buffer); +} + +static void CreateModelVBOTiny(mesh_t *mesh, tinyframe_t *frame) +{ + int bufferSize = sizeof(vbotiny_t)*mesh->numTriangles * 3; + vbotiny_t *buffer = (vbotiny_t*)malloc(bufferSize); + vbotiny_t *bufPtr = buffer; + + short *vertPtr = frame->vertices; + char *normPtr = frame->normals; + float *uvPtr = mesh->uvs; + char *tanPtr = frame->tangents; + + int i; + for (i = 0; i < mesh->numVertices; i++) + { + bufPtr->x = *vertPtr++; + bufPtr->y = *vertPtr++; + bufPtr->z = *vertPtr++; + + bufPtr->nx = *normPtr++; + bufPtr->ny = *normPtr++; + bufPtr->nz = *normPtr++; + + bufPtr->s0 = *uvPtr++; + bufPtr->t0 = *uvPtr++; + + if (tanPtr) + { + bufPtr->tanx = *tanPtr++; + bufPtr->tany = *tanPtr++; + bufPtr->tanz = *tanPtr++; + } + + bufPtr++; + } + + pglGenBuffers(1, &frame->vboID); + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglBufferData(GL_ARRAY_BUFFER, bufferSize, buffer, GL_STATIC_DRAW); + free(buffer); +} + +EXPORT void HWRAPI(CreateModelVBOs) (model_t *model) +{ + int i; + for (i = 0; i < model->numMeshes; i++) + { + mesh_t *mesh = &model->meshes[i]; + + if (mesh->frames) + { + int j; + for (j = 0; j < model->meshes[i].numFrames; j++) + { + mdlframe_t *frame = &mesh->frames[j]; + if (frame->vboID) + pglDeleteBuffers(1, &frame->vboID); + frame->vboID = 0; + CreateModelVBO(mesh, frame); + } + } + else if (mesh->tinyframes) + { + int j; + for (j = 0; j < model->meshes[i].numFrames; j++) + { + tinyframe_t *frame = &mesh->tinyframes[j]; + if (frame->vboID) + pglDeleteBuffers(1, &frame->vboID); + frame->vboID = 0; + CreateModelVBOTiny(mesh, frame); + } + } + } +} + +#define BUFFER_OFFSET(i) ((char*)NULL + (i)) + +static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 *color) { - INT32 val, count, pindex; - GLfloat s, t; GLfloat ambient[4]; GLfloat diffuse[4]; float pol = 0.0f; - float scalex = scale, scaley = scale, scalez = scale; + float scalex, scaley, scalez; + + boolean useTinyFrames; + + int i; // Because Otherwise, scaling the screen negatively vertically breaks the lighting -#ifndef KOS_GL_COMPATIBILITY GLfloat LightPos[] = {0.0f, 1.0f, 0.0f, 0.0f}; -#endif + + // Affect input model scaling + scale *= 0.5f; + scalex = scale; + scaley = scale; + scalez = scale; if (duration != 0 && duration != -1 && tics != -1) // don't interpolate if instantaneous or infinite in length { @@ -1927,7 +1765,9 @@ static void DrawMD2Ex(INT32 *gl_cmd_buffer, md2_frame_t *frame, INT32 duration, } pglEnable(GL_CULL_FACE); + pglEnable(GL_NORMALIZE); +#ifdef USE_FTRANSFORM_MIRROR // flipped is if the object is flipped // pos->flip is if the screen is flipped vertically // pos->mirror is if the screen is flipped horizontally @@ -1939,11 +1779,20 @@ static void DrawMD2Ex(INT32 *gl_cmd_buffer, md2_frame_t *frame, INT32 duration, else pglCullFace(GL_BACK); } - -#ifndef KOS_GL_COMPATIBILITY - pglLightfv(GL_LIGHT0, GL_POSITION, LightPos); +#else + // pos->flip is if the screen is flipped too + if (flipped != pos->flip) // If either are active, but not both, invert the model's culling + { + pglCullFace(GL_FRONT); + } + else + { + pglCullFace(GL_BACK); + } #endif + pglLightfv(GL_LIGHT0, GL_POSITION, LightPos); + pglShadeModel(GL_SMOOTH); if (color) { @@ -1963,109 +1812,160 @@ static void DrawMD2Ex(INT32 *gl_cmd_buffer, md2_frame_t *frame, INT32 duration, pglTranslatef(pos->x, pos->z, pos->y); if (flipped) scaley = -scaley; - pglRotatef(pos->anglez, 0.0f, 0.0f, -1.0f); +#ifdef USE_FTRANSFORM_ANGLEZ + pglRotatef(pos->anglez, 0.0f, 0.0f, -1.0f); // rotate by slope from Kart +#endif pglRotatef(pos->anglex, -1.0f, 0.0f, 0.0f); pglRotatef(pos->angley, 0.0f, -1.0f, 0.0f); - val = *gl_cmd_buffer++; + pglScalef(scalex, scaley, scalez); - while (val != 0) + useTinyFrames = model->meshes[0].tinyframes != NULL; + + if (useTinyFrames) + pglScalef(1 / 64.0f, 1 / 64.0f, 1 / 64.0f); + + pglEnableClientState(GL_NORMAL_ARRAY); + + for (i = 0; i < model->numMeshes; i++) { - if (val < 0) - { - pglBegin(GL_TRIANGLE_FAN); - count = -val; - } - else - { - pglBegin(GL_TRIANGLE_STRIP); - count = val; - } + mesh_t *mesh = &model->meshes[i]; - while (count--) + if (useTinyFrames) { - s = *(float *) gl_cmd_buffer++; - t = *(float *) gl_cmd_buffer++; - pindex = *gl_cmd_buffer++; + tinyframe_t *frame = &mesh->tinyframes[frameIndex % mesh->numFrames]; + tinyframe_t *nextframe = NULL; - pglTexCoord2f(s, t); + if (nextFrameIndex != -1) + nextframe = &mesh->tinyframes[nextFrameIndex % mesh->numFrames]; if (!nextframe || fpclassify(pol) == FP_ZERO) { - pglNormal3f(frame->vertices[pindex].normal[0], - frame->vertices[pindex].normal[1], - frame->vertices[pindex].normal[2]); + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglVertexPointer(3, GL_SHORT, sizeof(vbotiny_t), BUFFER_OFFSET(0)); + pglNormalPointer(GL_BYTE, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short)*3)); + pglTexCoordPointer(2, GL_FLOAT, sizeof(vbotiny_t), BUFFER_OFFSET(sizeof(short) * 3 + sizeof(char) * 6)); - pglVertex3f(frame->vertices[pindex].vertex[0]*scalex/2.0f, - frame->vertices[pindex].vertex[1]*scaley/2.0f, - frame->vertices[pindex].vertex[2]*scalez/2.0f); + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + pglBindBuffer(GL_ARRAY_BUFFER, 0); } else { - // Interpolate - float px1 = frame->vertices[pindex].vertex[0]*scalex/2.0f; - float px2 = nextframe->vertices[pindex].vertex[0]*scalex/2.0f; - float py1 = frame->vertices[pindex].vertex[1]*scaley/2.0f; - float py2 = nextframe->vertices[pindex].vertex[1]*scaley/2.0f; - float pz1 = frame->vertices[pindex].vertex[2]*scalez/2.0f; - float pz2 = nextframe->vertices[pindex].vertex[2]*scalez/2.0f; - float nx1 = frame->vertices[pindex].normal[0]; - float nx2 = nextframe->vertices[pindex].normal[0]; - float ny1 = frame->vertices[pindex].normal[1]; - float ny2 = nextframe->vertices[pindex].normal[1]; - float nz1 = frame->vertices[pindex].normal[2]; - float nz2 = nextframe->vertices[pindex].normal[2]; + short *vertPtr; + char *normPtr; + int j; - pglNormal3f((nx1 + pol * (nx2 - nx1)), - (ny1 + pol * (ny2 - ny1)), - (nz1 + pol * (nz2 - nz1))); - pglVertex3f((px1 + pol * (px2 - px1)), - (py1 + pol * (py2 - py1)), - (pz1 + pol * (pz2 - pz1))); + // Dangit, I soooo want to do this in a GLSL shader... + AllocLerpTinyBuffer(mesh->numVertices * sizeof(short) * 3); + vertPtr = vertTinyBuffer; + normPtr = normTinyBuffer; + j = 0; + + for (j = 0; j < mesh->numVertices * 3; j++) + { + // Interpolate + *vertPtr++ = (short)(frame->vertices[j] + (pol * (nextframe->vertices[j] - frame->vertices[j]))); + *normPtr++ = (char)(frame->normals[j] + (pol * (nextframe->normals[j] - frame->normals[j]))); + } + + pglVertexPointer(3, GL_SHORT, 0, vertTinyBuffer); + pglNormalPointer(GL_BYTE, 0, normTinyBuffer); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); } } + else + { + mdlframe_t *frame = &mesh->frames[frameIndex % mesh->numFrames]; + mdlframe_t *nextframe = NULL; - pglEnd(); + if (nextFrameIndex != -1) + nextframe = &mesh->frames[nextFrameIndex % mesh->numFrames]; - val = *gl_cmd_buffer++; + if (!nextframe || fpclassify(pol) == FP_ZERO) + { + // Zoom! Take advantage of just shoving the entire arrays to the GPU. +/* pglVertexPointer(3, GL_FLOAT, 0, frame->vertices); + pglNormalPointer(GL_FLOAT, 0, frame->normals); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3);*/ + + pglBindBuffer(GL_ARRAY_BUFFER, frame->vboID); + pglVertexPointer(3, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(0)); + pglNormalPointer(GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 3)); + pglTexCoordPointer(2, GL_FLOAT, sizeof(vbo64_t), BUFFER_OFFSET(sizeof(float) * 6)); + + pglDrawArrays(GL_TRIANGLES, 0, mesh->numTriangles * 3); + // No tinyframes, no mesh indices + //pglDrawElements(GL_TRIANGLES, mesh->numTriangles * 3, GL_UNSIGNED_SHORT, mesh->indices); + pglBindBuffer(GL_ARRAY_BUFFER, 0); + } + else + { + float *vertPtr; + float *normPtr; + int j = 0; + + // Dangit, I soooo want to do this in a GLSL shader... + AllocLerpBuffer(mesh->numVertices * sizeof(float) * 3); + vertPtr = vertBuffer; + normPtr = normBuffer; + //int j = 0; + + for (j = 0; j < mesh->numVertices * 3; j++) + { + // Interpolate + *vertPtr++ = frame->vertices[j] + (pol * (nextframe->vertices[j] - frame->vertices[j])); + *normPtr++ = frame->normals[j] + (pol * (nextframe->normals[j] - frame->normals[j])); + } + + pglVertexPointer(3, GL_FLOAT, 0, vertBuffer); + pglNormalPointer(GL_FLOAT, 0, normBuffer); + pglTexCoordPointer(2, GL_FLOAT, 0, mesh->uvs); + pglDrawArrays(GL_TRIANGLES, 0, mesh->numVertices); + } + } } + + pglDisableClientState(GL_NORMAL_ARRAY); + pglPopMatrix(); // should be the same as glLoadIdentity if (color) pglDisable(GL_LIGHTING); pglShadeModel(GL_FLAT); pglDisable(GL_CULL_FACE); + pglDisable(GL_NORMALIZE); } // -----------------+ // HWRAPI DrawMD2 : Draw an MD2 model with glcommands // -----------------+ -EXPORT void HWRAPI(DrawMD2i) (INT32 *gl_cmd_buffer, md2_frame_t *frame, INT32 duration, INT32 tics, md2_frame_t *nextframe, FTransform *pos, float scale, UINT8 flipped, UINT8 *color) +EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 *color) { - DrawMD2Ex(gl_cmd_buffer, frame, duration, tics, nextframe, pos, scale, flipped, color); + DrawModelEx(model, frameIndex, duration, tics, nextFrameIndex, pos, scale, flipped, color); } -EXPORT void HWRAPI(DrawMD2) (INT32 *gl_cmd_buffer, md2_frame_t *frame, FTransform *pos, float scale) -{ - DrawMD2Ex(gl_cmd_buffer, frame, 0, 0, NULL, pos, scale, false, NULL); -} - - // -----------------+ // SetTransform : // -----------------+ 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)); +#ifdef USE_FTRANSFORM_MIRROR + // mirroring from Kart if (stransform->mirror) pglScalef(-stransform->scalex, stransform->scaley, -stransform->scalez); - else if (stransform->flip) + else +#endif + if (stransform->flip) pglScalef(stransform->scalex, -stransform->scaley, -stransform->scalez); else pglScalef(stransform->scalex, stransform->scaley, -stransform->scalez); @@ -2074,39 +1974,28 @@ 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.13l, 2*ASPECT_RATIO); // 53.13 = 2*atan(0.5) - else - GLPerspective(stransform->fovxangle, ASPECT_RATIO); -#ifndef MINI_GL_COMPATIBILITY - pglGetDoublev(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer) -#endif - 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.13l, 2*ASPECT_RATIO); // 53.13 = 2*atan(0.5) - else - //Hurdler: is "fov" correct? - GLPerspective(fov, ASPECT_RATIO); -#ifndef MINI_GL_COMPATIBILITY - pglGetDoublev(GL_PROJECTION_MATRIX, projMatrix); // added for new coronas' code (without depth buffer) -#endif - pglMatrixMode(GL_MODELVIEW); } -#ifndef MINI_GL_COMPATIBILITY - pglGetDoublev(GL_MODELVIEW_MATRIX, modelMatrix); // added for new coronas' code (without depth buffer) -#endif + pglMatrixMode(GL_PROJECTION); + pglLoadIdentity(); + if (special_splitscreen) + { + used_fov = atan(tan(used_fov*M_PIl/360.0l)*0.8l)*360/M_PIl; + GLPerspective((GLfloat)used_fov, 2*ASPECT_RATIO); + } + else + GLPerspective((GLfloat)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) } EXPORT INT32 HWRAPI(GetTextureUsed) (void) @@ -2127,7 +2016,6 @@ EXPORT INT32 HWRAPI(GetRenderVersion) (void) return VERSION; } -#ifdef SHUFFLE EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]) { INT32 x, y; @@ -2135,6 +2023,14 @@ EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]) float xfix, yfix; INT32 texsize = 2048; + const float blackBack[16] = + { + -16.0f, -16.0f, 6.0f, + -16.0f, 16.0f, 6.0f, + 16.0f, 16.0f, 6.0f, + 16.0f, -16.0f, 6.0f + }; + // Use a power of two texture, dammit if(screen_width <= 1024) texsize = 1024; @@ -2147,47 +2043,66 @@ EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]) pglDisable(GL_DEPTH_TEST); pglDisable(GL_BLEND); - pglBegin(GL_QUADS); - // Draw a black square behind the screen texture, - // so nothing shows through the edges - pglColor4f(1.0f, 1.0f, 1.0f, 1.0f); - pglVertex3f(-16.0f, -16.0f, 6.0f); - pglVertex3f(-16.0f, 16.0f, 6.0f); - pglVertex3f(16.0f, 16.0f, 6.0f); - pglVertex3f(16.0f, -16.0f, 6.0f); + // const float blackBack[16] - for(x=0;x #include -#ifndef MINI_GL_COMPATIBILITY #ifdef STATIC_OPENGL // Because of the 1.3 functions, you'll need GLext to compile it if static #define GL_GLEXT_PROTOTYPES #include #endif #endif -#endif #define _CREATE_DLL_ // necessary for Unix AND Windows #include "../../doomdef.h" @@ -73,7 +71,6 @@ extern FILE *gllogstream; #endif #ifndef DRIVER_STRING -// #define USE_PALETTED_TEXTURE #define DRIVER_STRING "HWRAPI Init(): SRB2Kart OpenGL renderer" // Tails #endif @@ -91,10 +88,6 @@ int SetupPixelFormat(INT32 WantColorBits, INT32 WantStencilBits, INT32 WantDepth void SetModelView(GLint w, GLint h); void SetStates(void); FUNCMATH float byteasfloat(UINT8 fbyte); -#ifdef USE_PALETTED_TEXTURE -extern PFNGLCOLORTABLEEXTPROC glColorTableEXT; -extern GLubyte palette_tex[256*3]; -#endif #ifndef GL_EXT_texture_filter_anisotropic #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE @@ -120,6 +113,10 @@ typedef void (APIENTRY * PFNglGetIntegerv) (GLenum pname, GLint *params); extern PFNglGetIntegerv pglGetIntegerv; typedef const GLubyte* (APIENTRY * PFNglGetString) (GLenum name); extern PFNglGetString pglGetString; +#if 0 +typedef void (APIENTRY * PFNglEnableClientState) (GLenum cap); // redefined in r_opengl.c +static PFNglEnableClientState pglEnableClientState; +#endif #endif // ========================================================================== diff --git a/src/hardware/r_opengl/r_vbo.h b/src/hardware/r_opengl/r_vbo.h new file mode 100644 index 000000000..ca1a974da --- /dev/null +++ b/src/hardware/r_opengl/r_vbo.h @@ -0,0 +1,52 @@ +/* + From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) + An experimental work-in-progress. + + Donated to Sonic Team Junior and adapted to work with + Sonic Robo Blast 2. The license of this code matches whatever + the licensing is for Sonic Robo Blast 2. +*/ +#ifndef _R_VBO_H_ +#define _R_VBO_H_ + +typedef struct +{ + float x, y, z; // Vertex + float nx, ny, nz; // Normal + float s0, t0; // Texcoord0 +} vbo32_t; + +typedef struct +{ + float x, y, z; // Vertex + float s0, t0; // Texcoord0 + unsigned char r, g, b, a; // Color + float pad[2]; // Pad +} vbo2d32_t; + +typedef struct +{ + float x, y; // Vertex + float s0, t0; // Texcoord0 +} vbofont_t; + +typedef struct +{ + short x, y, z; // Vertex + char nx, ny, nz; // Normal + char tanx, tany, tanz; // Tangent + float s0, t0; // Texcoord0 +} vbotiny_t; + +typedef struct +{ + float x, y, z; // Vertex + float nx, ny, nz; // Normal + float s0, t0; // Texcoord0 + float s1, t1; // Texcoord1 + float s2, t2; // Texcoord2 + float tan0, tan1, tan2; // Tangent + unsigned char r, g, b, a; // Color +} vbo64_t; + +#endif diff --git a/src/hardware/u_list.c b/src/hardware/u_list.c new file mode 100644 index 000000000..dc49a74e7 --- /dev/null +++ b/src/hardware/u_list.c @@ -0,0 +1,230 @@ +/* + From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) + An experimental work-in-progress. + + Donated to Sonic Team Junior and adapted to work with + Sonic Robo Blast 2. The license of this code matches whatever + the licensing is for Sonic Robo Blast 2. +*/ + +#include "u_list.h" +#include "../z_zone.h" + +// Utility for managing +// structures in a linked +// list. +// +// Struct must have "next" and "prev" pointers +// as its first two variables. +// + +// +// ListAdd +// +// Adds an item to the list +// +void ListAdd(void *pItem, listitem_t **itemHead) +{ + listitem_t *item = (listitem_t*)pItem; + + if (*itemHead == NULL) + { + *itemHead = item; + (*itemHead)->prev = (*itemHead)->next = NULL; + } + else + { + listitem_t *tail; + tail = *itemHead; + + while (tail->next != NULL) + tail = tail->next; + + tail->next = item; + + tail->next->prev = tail; + + item->next = NULL; + } +} + +// +// ListAddFront +// +// Adds an item to the front of the list +// (This is much faster) +// +void ListAddFront(void *pItem, listitem_t **itemHead) +{ + listitem_t *item = (listitem_t*)pItem; + + if (*itemHead == NULL) + { + *itemHead = item; + (*itemHead)->prev = (*itemHead)->next = NULL; + } + else + { + (*itemHead)->prev = item; + item->next = (*itemHead); + item->prev = NULL; + *itemHead = item; + } +} + +// +// ListAddBefore +// +// Adds an item before the item specified in the list +// +void ListAddBefore(void *pItem, void *pSpot, listitem_t **itemHead) +{ + listitem_t *item = (listitem_t*)pItem; + listitem_t *spot = (listitem_t*)pSpot; + + listitem_t *prev = spot->prev; + + if (!prev) + ListAddFront(pItem, itemHead); + else + { + item->next = spot; + spot->prev = item; + item->prev = prev; + prev->next = item; + } +} + +// +// ListAddAfter +// +// Adds an item after the item specified in the list +// +void ListAddAfter(void *pItem, void *pSpot, listitem_t **itemHead) +{ + listitem_t *item = (listitem_t*)pItem; + listitem_t *spot = (listitem_t*)pSpot; + + listitem_t *next = spot->next; + + if (!next) + ListAdd(pItem, itemHead); + else + { + item->prev = spot; + spot->next = item; + item->next = next; + next->prev = item; + } +} + +// +// ListRemove +// +// Take an item out of the list and free its memory. +// +void ListRemove(void *pItem, listitem_t **itemHead) +{ + listitem_t *item = (listitem_t*)pItem; + + if (item == *itemHead) // Start of list + { + *itemHead = item->next; + + if (*itemHead) + (*itemHead)->prev = NULL; + } + else if (item->next == NULL) // end of list + { + item->prev->next = NULL; + } + else // Somewhere in between + { + item->prev->next = item->next; + item->next->prev = item->prev; + } + + Z_Free (item); +} + +// +// ListRemoveAll +// +// Removes all items from the list, freeing their memory. +// +void ListRemoveAll(listitem_t **itemHead) +{ + listitem_t *item; + listitem_t *next; + for (item = *itemHead; item; item = next) + { + next = item->next; + ListRemove(item, itemHead); + } +} + +// +// ListRemoveNoFree +// +// Take an item out of the list, but don't free its memory. +// +void ListRemoveNoFree(void *pItem, listitem_t **itemHead) +{ + listitem_t *item = (listitem_t*)pItem; + + if (item == *itemHead) // Start of list + { + *itemHead = item->next; + + if (*itemHead) + (*itemHead)->prev = NULL; + } + else if (item->next == NULL) // end of list + { + item->prev->next = NULL; + } + else // Somewhere in between + { + item->prev->next = item->next; + item->next->prev = item->prev; + } +} + +// +// ListGetCount +// +// Counts the # of items in a list +// Should not be used in performance-minded code +// +unsigned int ListGetCount(void *itemHead) +{ + listitem_t *item = (listitem_t*)itemHead; + + unsigned int count = 0; + for (; item; item = item->next) + count++; + + return count; +} + +// +// ListGetByIndex +// +// Gets an item in the list by its index +// Should not be used in performance-minded code +// +listitem_t *ListGetByIndex(void *itemHead, unsigned int index) +{ + listitem_t *head = (listitem_t*)itemHead; + unsigned int count = 0; + listitem_t *node; + for (node = head; node; node = node->next) + { + if (count == index) + return node; + + count++; + } + + return NULL; +} diff --git a/src/hardware/u_list.h b/src/hardware/u_list.h new file mode 100644 index 000000000..7e9a3cabd --- /dev/null +++ b/src/hardware/u_list.h @@ -0,0 +1,29 @@ +/* + From the 'Wizard2' engine by Spaddlewit Inc. ( http://www.spaddlewit.com ) + An experimental work-in-progress. + + Donated to Sonic Team Junior and adapted to work with + Sonic Robo Blast 2. The license of this code matches whatever + the licensing is for Sonic Robo Blast 2. +*/ + +#ifndef _U_LIST_H_ +#define _U_LIST_H_ + +typedef struct listitem_s +{ + struct listitem_s *next; + struct listitem_s *prev; +} listitem_t; + +void ListAdd(void *pItem, listitem_t **itemHead); +void ListAddFront(void *pItem, listitem_t **itemHead); +void ListAddBefore(void *pItem, void *pSpot, listitem_t **itemHead); +void ListAddAfter(void *pItem, void *pSpot, listitem_t **itemHead); +void ListRemove(void *pItem, listitem_t **itemHead); +void ListRemoveAll(listitem_t **itemHead); +void ListRemoveNoFree(void *pItem, listitem_t **itemHead); +unsigned int ListGetCount(void *itemHead); +listitem_t *ListGetByIndex(void *itemHead, unsigned int index); + +#endif diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 3d8f2383b..7a912e010 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -39,7 +39,7 @@ #include "am_map.h" #include "d_main.h" -#include "p_local.h" // camera, camera2, camera3, camera4 +#include "p_local.h" // camera[] #include "p_tick.h" #ifdef HWRENDER @@ -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,150 @@ 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_PLATINUM: + case SKINCOLOR_JET: + cstart = "\x86"; // V_GRAYMAP + break; + case SKINCOLOR_SEPIA: + case SKINCOLOR_BEIGE: + case SKINCOLOR_CARAMEL: + case SKINCOLOR_PEACH: + case SKINCOLOR_BROWN: + case SKINCOLOR_LEATHER: + case SKINCOLOR_RUST: + case SKINCOLOR_WRISTWATCH: + cstart = "\x8e"; // V_BROWNMAP + break; + case SKINCOLOR_FAIRY: + case SKINCOLOR_SALMON: + case SKINCOLOR_PINK: + case SKINCOLOR_ROSE: + case SKINCOLOR_LEMONADE: + case SKINCOLOR_BUBBLEGUM: + case SKINCOLOR_LILAC: + case SKINCOLOR_TAFFY: + cstart = "\x8d"; // V_PINKMAP + break; + case SKINCOLOR_CINNAMON: + case SKINCOLOR_RUBY: + case SKINCOLOR_RASPBERRY: + case SKINCOLOR_RED: + case SKINCOLOR_CRIMSON: + case SKINCOLOR_MAROON: + 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_ROSEWOOD: + case SKINCOLOR_TANGERINE: + cstart = "\x87"; // V_ORANGEMAP + break; + case SKINCOLOR_TAN: + case SKINCOLOR_CREAM: + cstart = "\x8f"; // V_TANMAP + break; + case SKINCOLOR_GOLD: + case SKINCOLOR_ROYAL: + case SKINCOLOR_BRONZE: + case SKINCOLOR_COPPER: + case SKINCOLOR_THUNDER: + cstart = "\x8A"; // V_GOLDMAP + break; + case SKINCOLOR_POPCORN: + case SKINCOLOR_YELLOW: + case SKINCOLOR_MUSTARD: + case SKINCOLOR_BANANA: + case SKINCOLOR_OLIVE: + case SKINCOLOR_CROCODILE: + cstart = "\x82"; // V_YELLOWMAP + break; + case SKINCOLOR_ARTICHOKE: + case SKINCOLOR_PERIDOT: + case SKINCOLOR_VOMIT: + case SKINCOLOR_GARDEN: + case SKINCOLOR_LIME: + case SKINCOLOR_HANDHELD: + case SKINCOLOR_TEA: + case SKINCOLOR_PISTACHIO: + case SKINCOLOR_MOSS: + case SKINCOLOR_CAMOUFLAGE: + case SKINCOLOR_ROBOHOOD: + case SKINCOLOR_MINT: + case SKINCOLOR_GREEN: + case SKINCOLOR_PINETREE: + case SKINCOLOR_TURTLE: + case SKINCOLOR_SWAMP: + case SKINCOLOR_DREAM: + case SKINCOLOR_PLAGUE: + case SKINCOLOR_EMERALD: + case SKINCOLOR_ALGAE: + cstart = "\x83"; // V_GREENMAP + break; + case SKINCOLOR_CARIBBEAN: + case SKINCOLOR_AZURE: + case SKINCOLOR_AQUAMARINE: + case SKINCOLOR_TURQUOISE: + case SKINCOLOR_TEAL: + cstart = "\x8b"; // V_AQUAMAP + break; + case SKINCOLOR_PIGEON: + case SKINCOLOR_CYAN: + case SKINCOLOR_JAWZ: + case SKINCOLOR_CERULEAN: + case SKINCOLOR_NAVY: + case SKINCOLOR_SAPPHIRE: + cstart = "\x88"; // V_SKYMAP + break; + case SKINCOLOR_STEEL: + case SKINCOLOR_ULTRAMARINE: + case SKINCOLOR_PERIWINKLE: + case SKINCOLOR_BLUE: + case SKINCOLOR_BLUEBERRY: + case SKINCOLOR_NOVA: + cstart = "\x84"; // V_BLUEMAP + break; + case SKINCOLOR_THISTLE: + case SKINCOLOR_PURPLE: + case SKINCOLOR_PASTEL: + cstart = "\x81"; // V_PURPLEMAP + break; + case SKINCOLOR_MAGENTA: + case SKINCOLOR_FUCHSIA: + case SKINCOLOR_MOONSLAM: + cstart = "\x8c"; // V_MAGENTAMAP + break; + 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 +1236,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 +1261,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 +1288,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 @@ -1190,20 +1304,7 @@ boolean HU_Responder(event_t *ev) && ev->data1 != gamecontrol[gc_talkkey][1])) return false; - c = (INT32)ev->data1; - - // 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) @@ -1480,7 +1581,7 @@ static void HU_drawMiniChat(void) else { if (cv_chatbacktint.value) // on request of wolfy - V_DrawFillConsoleMap(x + dx + 2, y+dy, charwidth, charheight, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT); + V_DrawFillConsoleMap(x + dx + 2, y+dy, charwidth, charheight, 159|V_SNAPTOBOTTOM|V_SNAPTOLEFT); V_DrawChatCharacter(x + dx + 2, y+dy, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|transflag, !cv_allcaps.value, colormap); } @@ -1544,7 +1645,7 @@ static void HU_drawChatLog(INT32 offset) chat_topy = y + chat_scroll*charheight; chat_bottomy = chat_topy + boxh*charheight; - V_DrawFillConsoleMap(chatx, chat_topy, boxw, boxh*charheight +2, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT); // log box + V_DrawFillConsoleMap(chatx, chat_topy, boxw, boxh*charheight +2, 159|V_SNAPTOBOTTOM|V_SNAPTOLEFT); // log box for (i=0; i 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; @@ -1669,7 +1770,7 @@ static void HU_DrawChat(void) cflag = V_GRAYMAP; // set text in gray if chat is muted. } - V_DrawFillConsoleMap(chatx, y-1, boxw, (typelines*charheight), 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); + V_DrawFillConsoleMap(chatx, y-1, boxw, (typelines*charheight), 159 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); while (talk[i]) { @@ -1796,14 +1897,14 @@ static void HU_DrawChat(void) { char name[MAXPLAYERNAME+1]; strlcpy(name, player_names[i], 7); // shorten name to 7 characters. - V_DrawFillConsoleMap(chatx+ boxw + 2, p_dispy- (6*count), 48, 6, 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); // fill it like the chat so the text doesn't become hard to read because of the hud. + V_DrawFillConsoleMap(chatx+ boxw + 2, p_dispy- (6*count), 48, 6, 159 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); // fill it like the chat so the text doesn't become hard to read because of the hud. V_DrawSmallString(chatx+ boxw + 4, p_dispy- (6*count), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, va("\x82%d\x80 - %s", i, name)); count++; } } if (count == 0) // no results. { - V_DrawFillConsoleMap(chatx+boxw+2, p_dispy- (6*count), 48, 6, 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); // fill it like the chat so the text doesn't become hard to read because of the hud. + V_DrawFillConsoleMap(chatx+boxw+2, p_dispy- (6*count), 48, 6, 159 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); // fill it like the chat so the text doesn't become hard to read because of the hud. V_DrawSmallString(chatx+boxw+4, p_dispy- (6*count), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, "NO RESULT."); } } @@ -1897,7 +1998,7 @@ static void HU_DrawChat_Old(void) if (!i) return; - if ((netgame || multiplayer) && players[displayplayer].spectator) + if ((netgame || multiplayer) && players[displayplayers[0]].spectator) return; #ifdef HWRENDER @@ -1924,7 +2025,7 @@ static inline void HU_DrawCrosshair2(void) if (!i) return; - if ((netgame || multiplayer) && players[secondarydisplayplayer].spectator) + if ((netgame || multiplayer) && players[displayplayers[1]].spectator) return; #ifdef HWRENDER @@ -1971,7 +2072,7 @@ static inline void HU_DrawCrosshair3(void) if (!i) return; - if ((netgame || multiplayer) && players[thirddisplayplayer].spectator) + if ((netgame || multiplayer) && players[displayplayers[2]].spectator) return; #ifdef HWRENDER @@ -2008,7 +2109,7 @@ static inline void HU_DrawCrosshair4(void) if (!i) return; - if ((netgame || multiplayer) && players[fourthdisplayplayer].spectator) + if ((netgame || multiplayer) && players[displayplayers[3]].spectator) return; #ifdef HWRENDER @@ -2109,8 +2210,16 @@ UINT32 hu_demolap; static void HU_DrawDemoInfo(void) { - V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-40, 0, M_GetText("Replay:")); - V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_ALLOWLOWERCASE, player_names[0]); + if (!multiplayer)/* netreplay */ + { + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-40, 0, M_GetText("Replay:")); + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_ALLOWLOWERCASE, player_names[0]); + } + else + { + V_DrawRightAlignedThinString(BASEVIDWIDTH-2, BASEVIDHEIGHT-10, V_ALLOWLOWERCASE, demo.titlename); + } + if (modeattacking) { V_DrawRightAlignedString((BASEVIDWIDTH/2)-4, BASEVIDHEIGHT-24, V_YELLOWMAP|V_MONOSPACE, "BEST TIME:"); @@ -2137,7 +2246,7 @@ static void HU_DrawDemoInfo(void) // // Song credits // -static void HU_DrawSongCredits(void) +void HU_DrawSongCredits(void) { char *str; INT32 len, destx; @@ -2178,10 +2287,14 @@ static void HU_DrawSongCredits(void) V_DrawRightAlignedThinString(cursongcredit.x, y, V_ALLOWLOWERCASE|V_6WIDTHSPACE|V_SNAPTOLEFT|(cursongcredit.trans< 1))) + V_DrawVhsEffect(demo.rewinding); + #ifndef NONET // draw chat string plus cursor if (chat_on) @@ -2227,10 +2340,7 @@ void HU_Drawer(void) if (cechotimer) HU_DrawCEcho(); - if (demoplayback && hu_showscores) - HU_DrawDemoInfo(); - - if (!Playing() + if (!( Playing() || demo.playback ) || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_GAMEEND @@ -2250,24 +2360,28 @@ void HU_Drawer(void) LUAh_ScoresHUD(); #endif } + if (demo.playback) + { + HU_DrawDemoInfo(); + } } if (gamestate != GS_LEVEL) return; // draw the crosshair, not when viewing demos nor with chasecam - /*if (!automapactive && !demoplayback) + /*if (!automapactive && !demo.playback) { - if (cv_crosshair.value && !camera.chase && !players[displayplayer].spectator) + if (cv_crosshair.value && !camera[0].chase && !players[displayplayers[0]].spectator) HU_DrawCrosshair(); - if (cv_crosshair2.value && !camera2.chase && !players[secondarydisplayplayer].spectator) + if (cv_crosshair2.value && !camera[1].chase && !players[displayplayers[1]].spectator) HU_DrawCrosshair2(); - if (cv_crosshair3.value && !camera3.chase && !players[thirddisplayplayer].spectator) + if (cv_crosshair3.value && !camera[2].chase && !players[displayplayers[2]].spectator) HU_DrawCrosshair3(); - if (cv_crosshair4.value && !camera4.chase && !players[fourthdisplayplayer].spectator) + if (cv_crosshair4.value && !camera[3].chase && !players[displayplayers[3]].spectator) HU_DrawCrosshair4(); }*/ @@ -2369,36 +2483,25 @@ void HU_Erase(void) // // HU_drawPing // -void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext) +void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags) { - UINT8 numbars = 1; // how many ping bars do we draw? - UINT8 barcolor = 128; // color we use for the bars (green, yellow or red) - SINT8 i = 0; - SINT8 yoffset = 6; - INT32 dx = x+1 - (V_SmallStringWidth(va("%dms", ping), V_ALLOWLOWERCASE)/2); + INT32 gfxnum = 4; // gfx to draw + UINT8 const *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SALMON, GTC_CACHE); - if (ping < 128) - { - numbars = 3; - barcolor = 184; - } + if (ping < 76) + gfxnum = 0; + else if (ping < 137) + gfxnum = 1; else if (ping < 256) - { - numbars = 2; // Apparently ternaries w/ multiple statements don't look good in C so I decided against it. - barcolor = 103; - } + gfxnum = 2; + else if (ping < 500) + gfxnum = 3; - if (!notext || vid.width >= 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); } // @@ -2920,7 +3023,7 @@ static void HU_DrawRankings(void) // When you play, you quickly see your score because your name is displayed in white. // When playing back a demo, you quickly see who's the view. if (!splitscreen) - whiteplayer = demoplayback ? displayplayer : consoleplayer; + whiteplayer = demo.playback ? displayplayers[0] : consoleplayer; scorelines = 0; memset(completed, 0, sizeof (completed)); diff --git a/src/hu_stuff.h b/src/hu_stuff.h index f1ecb2ff1..be6798a82 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -80,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]; @@ -105,11 +109,12 @@ void HU_Start(void); boolean HU_Responder(event_t *ev); void HU_Ticker(void); +void HU_DrawSongCredits(void); 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/i_sound.h b/src/i_sound.h index fd73d1454..9a5c2930a 100644 --- a/src/i_sound.h +++ b/src/i_sound.h @@ -146,6 +146,18 @@ boolean I_SongPaused(void); boolean I_SetSongSpeed(float speed); +/// ------------------------ +// MUSIC SEEKING +/// ------------------------ + +UINT32 I_GetSongLength(void); + +boolean I_SetSongLoopPoint(UINT32 looppoint); +UINT32 I_GetSongLoopPoint(void); + +boolean I_SetSongPosition(UINT32 position); +UINT32 I_GetSongPosition(void); + /// ------------------------ // MUSIC PLAYBACK /// ------------------------ @@ -216,6 +228,17 @@ void I_SetMusicVolume(UINT8 volume); boolean I_SetSongTrack(INT32 track); +/// ------------------------ +/// MUSIC FADING +/// ------------------------ + +void I_SetInternalMusicVolume(UINT8 volume); +void I_StopFadingSong(void); +boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void)); +boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void)); +boolean I_FadeOutStopSong(UINT32 ms); +boolean I_FadeInPlaySong(UINT32 ms, boolean looping); + /// ------------------------ // CD MUSIC I/O /// ------------------------ diff --git a/src/i_tcp.c b/src/i_tcp.c index f8a65b754..fb0e5852d 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -612,7 +612,7 @@ static boolean SOCK_Get(void) if (c != ERRSOCKET) { // find remote node number - for (j = 0; j <= MAXNETNODES; j++) //include LAN + for (j = 1; j <= MAXNETNODES; j++) //include LAN { if (SOCK_cmpaddr(&fromaddress, &clientaddress[j], 0)) { @@ -813,6 +813,8 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen #endif #endif mysockaddr_t straddr; + struct sockaddr_in sin; + socklen_t len = sizeof(sin); if (s == (SOCKET_TYPE)ERRSOCKET) return (SOCKET_TYPE)ERRSOCKET; @@ -906,12 +908,16 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen CONS_Printf(M_GetText("Network system buffer set to: %dKb\n"), opt>>10); } + if (getsockname(s, (struct sockaddr *)&sin, &len) == -1) + CONS_Alert(CONS_WARNING, M_GetText("Failed to get port number\n")); + else + current_port = (UINT16)ntohs(sin.sin_port); + return s; } static boolean UDP_Socket(void) { - const char *sock_port = NULL; size_t s; struct my_addrinfo *ai, *runp, hints; int gaie; @@ -933,20 +939,11 @@ static boolean UDP_Socket(void) hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; - if (M_CheckParm("-clientport")) - { - if (!M_IsNextParm()) - I_Error("syntax: -clientport "); - sock_port = M_GetNextParm(); - } - else - sock_port = port_name; - if (M_CheckParm("-bindaddr")) { while (M_IsNextParm()) { - gaie = I_getaddrinfo(M_GetNextParm(), sock_port, &hints, &ai); + gaie = I_getaddrinfo(M_GetNextParm(), port_name, &hints, &ai); if (gaie == 0) { runp = ai; @@ -967,7 +964,7 @@ static boolean UDP_Socket(void) } else { - gaie = I_getaddrinfo("0.0.0.0", sock_port, &hints, &ai); + gaie = I_getaddrinfo("0.0.0.0", port_name, &hints, &ai); if (gaie == 0) { runp = ai; @@ -982,8 +979,8 @@ static boolean UDP_Socket(void) #ifdef HAVE_MINIUPNPC if (UPNP_support) { - I_UPnP_rem(sock_port, "UDP"); - I_UPnP_add(NULL, sock_port, "UDP"); + I_UPnP_rem(port_name, "UDP"); + I_UPnP_add(NULL, port_name, "UDP"); } #endif } @@ -1000,7 +997,7 @@ static boolean UDP_Socket(void) { while (M_IsNextParm()) { - gaie = I_getaddrinfo(M_GetNextParm(), sock_port, &hints, &ai); + gaie = I_getaddrinfo(M_GetNextParm(), port_name, &hints, &ai); if (gaie == 0) { runp = ai; @@ -1021,7 +1018,7 @@ static boolean UDP_Socket(void) } else { - gaie = I_getaddrinfo("::", sock_port, &hints, &ai); + gaie = I_getaddrinfo("::", port_name, &hints, &ai); if (gaie == 0) { runp = ai; @@ -1340,8 +1337,12 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) while (runp != NULL) { // find ip of the server - memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen); - runp = NULL; + if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0) + { + memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen); + break; + } + runp = runp->ai_next; } I_freeaddrinfo(ai); return newnode; @@ -1474,14 +1475,15 @@ boolean I_InitTcpNetwork(void) if (!I_InitTcpDriver()) return false; - if (M_CheckParm("-udpport")) + if (M_CheckParm("-port")) + // Combined -udpport and -clientport into -port + // As it was really redundant having two seperate parms that does the same thing { if (M_IsNextParm()) strcpy(port_name, M_GetNextParm()); else strcpy(port_name, "0"); } - current_port = (UINT16)atoi(port_name); // parse network game options, if (M_CheckParm("-server") || dedicated) diff --git a/src/info.c b/src/info.c index 3f90f5e48..13fb628da 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 @@ -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 @@ -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 diff --git a/src/info.h b/src/info.h index 157c72e1b..0e23a07c5 100644 --- a/src/info.h +++ b/src/info.h @@ -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 diff --git a/src/k_kart.c b/src/k_kart.c index ac077164b..20b1dee34 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -37,224 +37,370 @@ //{ SRB2kart Color Code #define SKIN_RAMP_LENGTH 16 -#define DEFAULT_STARTTRANSCOLOR 160 +#define DEFAULT_STARTTRANSCOLOR 96 #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 + "None", // SKINCOLOR_NONE + "White", // SKINCOLOR_WHITE + "Silver", // SKINCOLOR_SILVER + "Grey", // SKINCOLOR_GREY + "Nickel", // SKINCOLOR_NICKEL + "Black", // SKINCOLOR_BLACK + "Fairy", // SKINCOLOR_FAIRY + "Popcorn", // SKINCOLOR_POPCORN + "Artichoke", // SKINCOLOR_ARTICHOKE + "Pigeon", // SKINCOLOR_PIGEON + "Sepia", // SKINCOLOR_SEPIA + "Beige", // SKINCOLOR_BEIGE + "Caramel", // SKINCOLOR_CARAMEL + "Peach", // SKINCOLOR_PEACH + "Brown", // SKINCOLOR_BROWN + "Leather", // SKINCOLOR_LEATHER + "Salmon", // SKINCOLOR_SALMON + "Pink", // SKINCOLOR_PINK + "Rose", // SKINCOLOR_ROSE + "Cinnamon", // SKINCOLOR_CINNAMON + "Ruby", // SKINCOLOR_RUBY + "Raspberry", // SKINCOLOR_RASPBERRY + "Red", // SKINCOLOR_RED + "Crimson", // SKINCOLOR_CRIMSON + "Maroon", // SKINCOLOR_MAROON + "Lemonade", // SKINCOLOR_LEMONADE + "Scarlet", // SKINCOLOR_SCARLET + "Ketchup", // SKINCOLOR_KETCHUP + "Dawn", // SKINCOLOR_DAWN + "Sunset", // SKINCOLOR_SUNSET + "Creamsicle", // SKINCOLOR_CREAMSICLE + "Orange", // SKINCOLOR_ORANGE + "Rosewood", // SKINCOLOR_ROSEWOOD + "Tangerine", // SKINCOLOR_TANGERINE + "Tan", // SKINCOLOR_TAN + "Cream", // SKINCOLOR_CREAM + "Gold", // SKINCOLOR_GOLD + "Royal", // SKINCOLOR_ROYAL + "Bronze", // SKINCOLOR_BRONZE + "Copper", // SKINCOLOR_COPPER + "Yellow", // SKINCOLOR_YELLOW + "Mustard", // SKINCOLOR_MUSTARD + "Banana", // SKINCOLOR_BANANA + "Olive", // SKINCOLOR_OLIVE + "Crocodile", // SKINCOLOR_CROCODILE + "Peridot", // SKINCOLOR_PERIDOT + "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 + "Turtle", // SKINCOLOR_TURTLE + "Swamp", // SKINCOLOR_SWAMP + "Dream", // SKINCOLOR_DREAM + "Plague", // SKINCOLOR_PLAGUE + "Emerald", // SKINCOLOR_EMERALD + "Algae", // SKINCOLOR_ALGAE + "Caribbean", // SKINCOLOR_CARIBBEAN + "Azure", // SKINCOLOR_AZURE + "Aquamarine", // SKINCOLOR_AQUAMARINE + "Turquoise", // SKINCOLOR_TURQUOISE + "Teal", // SKINCOLOR_TEAL + "Cyan", // SKINCOLOR_CYAN + "Jawz", // SKINCOLOR_JAWZ + "Cerulean", // SKINCOLOR_CERULEAN + "Navy", // SKINCOLOR_NAVY + "Platinum", // SKINCOLOR_PLATINUM + "Slate", // SKINCOLOR_SLATE + "Steel", // SKINCOLOR_STEEL + "Thunder", // SKINCOLOR_THUNDER + "Nova", // SKINCOLOR_NOVA + "Rust", // SKINCOLOR_RUST + "Wristwatch", // SKINCOLOR_WRISTWATCH + "Jet", // SKINCOLOR_JET + "Sapphire", // SKINCOLOR_SAPPHIRE + "Ultramarine", // SKINCOLOR_ULTRAMARINE + "Periwinkle", // SKINCOLOR_PERIWINKLE + "Blue", // SKINCOLOR_BLUE + "Blueberry", // SKINCOLOR_BLUEBERRY + "Thistle", // SKINCOLOR_THISTLE + "Purple", // SKINCOLOR_PURPLE + "Pastel", // SKINCOLOR_PASTEL + "Moonslam", // SKINCOLOR_MOONSLAM + "Dusk", // SKINCOLOR_DUSK + "Bubblegum", // SKINCOLOR_BUBBLEGUM + "Magenta", // SKINCOLOR_MAGENTA + "Fuchsia", // SKINCOLOR_FUCHSIA + "Toxic", // SKINCOLOR_TOXIC + "Mauve", // SKINCOLOR_MAUVE + "Lavender", // SKINCOLOR_LAVENDER + "Byzantium", // SKINCOLOR_BYZANTIUM + "Pomegranate", // SKINCOLOR_POMEGRANATE + "Lilac", // SKINCOLOR_LILAC + "Taffy" // SKINCOLOR_TAFFY }; // 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 + 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_ARTICHOKE,12, // SKINCOLOR_FAIRY + SKINCOLOR_PIGEON,12, // SKINCOLOR_POPCORN + SKINCOLOR_FAIRY,12, // SKINCOLOR_ARTICHOKE + SKINCOLOR_POPCORN,12, // SKINCOLOR_PIGEON + SKINCOLOR_LEATHER,6, // SKINCOLOR_SEPIA + SKINCOLOR_BROWN,2, // SKINCOLOR_BEIGE + SKINCOLOR_CERULEAN,8, // SKINCOLOR_CARAMEL + SKINCOLOR_CYAN,8, // SKINCOLOR_PEACH + 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_WRISTWATCH,6, // SKINCOLOR_CINNAMON + SKINCOLOR_SAPPHIRE,8, // SKINCOLOR_RUBY + SKINCOLOR_MINT,8, // SKINCOLOR_RASPBERRY + SKINCOLOR_GREEN,6, // SKINCOLOR_RED + SKINCOLOR_PINETREE,6, // SKINCOLOR_CRIMSON + SKINCOLOR_TOXIC,8, // SKINCOLOR_MAROON + SKINCOLOR_THUNDER,8, // SKINCOLOR_LEMONADE + 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,6, // SKINCOLOR_ROSEWOOD + SKINCOLOR_LIME,8, // SKINCOLOR_TANGERINE + SKINCOLOR_RUST,8, // SKINCOLOR_TAN + 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_AQUAMARINE,8, // SKINCOLOR_YELLOW + SKINCOLOR_KETCHUP,8, // SKINCOLOR_MUSTARD + SKINCOLOR_EMERALD,8, // SKINCOLOR_BANANA + SKINCOLOR_TEAL,8, // SKINCOLOR_OLIVE + SKINCOLOR_BUBBLEGUM,8, // SKINCOLOR_CROCODILE + SKINCOLOR_NAVY,6, // SKINCOLOR_PERIDOT + SKINCOLOR_ROBOHOOD,8, // SKINCOLOR_VOMIT + SKINCOLOR_LAVENDER,6, // SKINCOLOR_GARDEN + SKINCOLOR_TANGERINE,8, // SKINCOLOR_LIME + SKINCOLOR_ULTRAMARINE,8, // SKINCOLOR_HANDHELD + SKINCOLOR_SALMON,8, // SKINCOLOR_TEA + SKINCOLOR_PINK,6, // SKINCOLOR_PISTACHIO + SKINCOLOR_ROSE,8, // SKINCOLOR_MOSS + SKINCOLOR_CAMOUFLAGE,8, // SKINCOLOR_CAMOUFLAGE + SKINCOLOR_VOMIT,8, // SKINCOLOR_ROBOHOOD + SKINCOLOR_RASPBERRY,8, // SKINCOLOR_MINT + SKINCOLOR_RED,8, // SKINCOLOR_GREEN + SKINCOLOR_CRIMSON,8, // SKINCOLOR_PINETREE + SKINCOLOR_MAGENTA,8, // SKINCOLOR_TURTLE + SKINCOLOR_BYZANTIUM,8, // SKINCOLOR_SWAMP + SKINCOLOR_POMEGRANATE,8, // SKINCOLOR_DREAM + SKINCOLOR_NOVA,8, // SKINCOLOR_PLAGUE + SKINCOLOR_BANANA,8, // SKINCOLOR_EMERALD + SKINCOLOR_SCARLET,10, // SKINCOLOR_ALGAE + SKINCOLOR_PURPLE,8, // SKINCOLOR_CARIBBEAN + SKINCOLOR_THISTLE,8, // SKINCOLOR_AZURE + SKINCOLOR_YELLOW,8, // SKINCOLOR_AQUAMARINE + SKINCOLOR_MAUVE,10, // SKINCOLOR_TURQUOISE + SKINCOLOR_OLIVE,8, // SKINCOLOR_TEAL + SKINCOLOR_PEACH,8, // SKINCOLOR_CYAN + SKINCOLOR_LILAC,10, // SKINCOLOR_JAWZ + SKINCOLOR_CARAMEL,8, // SKINCOLOR_CERULEAN + SKINCOLOR_PERIDOT,8, // SKINCOLOR_NAVY + SKINCOLOR_ROYAL,8, // SKINCOLOR_PLATINUM + SKINCOLOR_GOLD,10, // SKINCOLOR_SLATE + SKINCOLOR_BRONZE,10, // SKINCOLOR_STEEL + SKINCOLOR_LEMONADE,8, // SKINCOLOR_THUNDER + SKINCOLOR_PLAGUE,10, // SKINCOLOR_NOVA + SKINCOLOR_TAN,8, // SKINCOLOR_RUST + SKINCOLOR_CINNAMON,8, // SKINCOLOR_WRISTWATCH + SKINCOLOR_TAFFY,8, // SKINCOLOR_JET + SKINCOLOR_RUBY,6, // SKINCOLOR_SAPPHIRE + SKINCOLOR_HANDHELD,10, // SKINCOLOR_ULTRAMARINE + SKINCOLOR_CREAMSICLE,8, // SKINCOLOR_PERIWINKLE + SKINCOLOR_ORANGE,8, // SKINCOLOR_BLUE + SKINCOLOR_ROSEWOOD,8, // SKINCOLOR_BLUEBERRY + SKINCOLOR_AZURE,8, // SKINCOLOR_THISTLE + SKINCOLOR_CARIBBEAN,10, // SKINCOLOR_PURPLE + SKINCOLOR_FUCHSIA,11, // SKINCOLOR_PASTEL + SKINCOLOR_SUNSET,10, // SKINCOLOR_MOONSLAM + SKINCOLOR_DAWN,6, // SKINCOLOR_DUSK + SKINCOLOR_CROCODILE,8, // SKINCOLOR_BUBBLEGUM + SKINCOLOR_TURTLE,8, // SKINCOLOR_MAGENTA + SKINCOLOR_PASTEL,11, // SKINCOLOR_FUCHSIA + SKINCOLOR_MAROON,8, // SKINCOLOR_TOXIC + SKINCOLOR_TURQUOISE,8, // SKINCOLOR_MAUVE + SKINCOLOR_GARDEN,6, // SKINCOLOR_LAVENDER + SKINCOLOR_SWAMP,8, // SKINCOLOR_BYZANTIUM + SKINCOLOR_DREAM,8, // SKINCOLOR_POMEGRANATE + SKINCOLOR_JAWZ,6, // SKINCOLOR_LILAC + SKINCOLOR_JET,8 // SKINCOLOR_TAFFY }; -UINT8 colortranslations[MAXSKINCOLORS][16] = { +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, 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 + { 0, 0, 0, 0, 1, 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 - { 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 - */ + { 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 + { 0, 1, 208, 208, 209, 210, 10, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_FAIRY + { 0, 80, 80, 81, 82, 218, 240, 11, 13, 16, 18, 21, 23, 26, 28, 31}, // SKINCOLOR_POPCORN + { 80, 88, 89, 98, 99, 91, 12, 14, 16, 18, 20, 22, 24, 26, 28, 31}, // SKINCOLOR_ARTICHOKE + { 0, 128, 129, 130, 146, 170, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31}, // SKINCOLOR_PIGEON + { 0, 1, 3, 5, 7, 9, 241, 242, 243, 245, 247, 249, 236, 237, 238, 239}, // SKINCOLOR_SEPIA + { 0, 208, 216, 217, 240, 241, 242, 243, 245, 247, 249, 250, 251, 237, 238, 239}, // SKINCOLOR_BEIGE + {208, 48, 216, 217, 218, 220, 221, 223, 224, 226, 228, 230, 232, 234, 236, 239}, // SKINCOLOR_CARAMEL + { 0, 208, 48, 216, 218, 221, 212, 213, 214, 215, 206, 207, 197, 198, 199, 254}, // SKINCOLOR_PEACH + {216, 217, 219, 221, 224, 225, 227, 229, 230, 232, 234, 235, 237, 239, 29, 30}, // SKINCOLOR_BROWN + {218, 221, 224, 227, 229, 231, 233, 235, 237, 239, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_LEATHER + { 0, 0, 0, 208, 208, 209, 210, 32, 34, 35, 36, 38, 40, 42, 44, 46}, // SKINCOLOR_SALMON + { 0, 208, 208, 209, 209, 210, 211, 211, 212, 213, 214, 215, 41, 43, 45, 46}, // SKINCOLOR_PINK + {209, 210, 211, 211, 212, 213, 214, 215, 41, 42, 43, 44, 45, 71, 46, 47}, // SKINCOLOR_ROSE + {216, 221, 224, 226, 228, 60, 61, 43, 44, 45, 71, 46, 47, 29, 30, 31}, // SKINCOLOR_CINNAMON + { 0, 208, 209, 210, 211, 213, 39, 40, 41, 43, 186, 186, 169, 169, 253, 254}, // SKINCOLOR_RUBY + { 0, 208, 209, 210, 32, 33, 34, 35, 37, 39, 41, 43, 44, 45, 46, 47}, // SKINCOLOR_RASPBERRY + {209, 210, 32, 34, 36, 38, 39, 40, 41, 42, 43, 44 , 45, 71, 46, 47}, // SKINCOLOR_RED + {210, 33, 35, 38, 40, 42, 43, 45, 71, 71, 46, 46, 47, 47, 30, 31}, // SKINCOLOR_CRIMSON + { 32, 33, 35, 37, 39, 41, 43, 237, 26, 26, 27, 27, 28, 29, 30, 31}, // SKINCOLOR_MAROON + { 0, 80, 81, 82, 83, 216, 210, 211, 212, 213, 214, 215, 43, 44, 71, 47}, // SKINCOLOR_LEMONADE + { 48, 49, 50, 51, 53, 34, 36, 38, 184, 185, 168, 168, 169, 169, 254, 31}, // SKINCOLOR_SCARLET + { 72, 73, 64, 51, 52, 54, 34, 36, 38, 40, 42, 43, 44, 71, 46, 47}, // SKINCOLOR_KETCHUP + { 0, 208, 216, 209, 210, 211, 212, 57, 58, 59, 60, 61, 63, 71, 47, 31}, // SKINCOLOR_DAWN + { 82, 72, 73, 64, 51, 53, 55, 213, 214, 195, 195, 173, 174, 175, 253, 254}, // SKINCOLOR_SUNSET + { 0, 0, 208, 208, 48, 49, 50, 52, 53, 54, 56, 57, 58, 60, 61, 63}, // SKINCOLOR_CREAMSICLE + {208, 48, 49, 50, 51, 52, 53, 54, 55, 57, 59, 60, 62, 44, 71, 47}, // SKINCOLOR_ORANGE + { 50, 52, 55, 56, 58, 59, 60, 61, 62, 63, 44, 45, 71, 46, 47, 30}, // SKINCOLOR_ROSEWOOD + { 80, 81, 82, 83, 64, 51, 52, 54, 55, 57, 58, 60, 61, 63, 71, 47}, // SKINCOLOR_TANGERINE + { 0, 80, 81, 82, 83, 84, 85, 86, 87, 245, 246, 248, 249, 251, 237, 239}, // SKINCOLOR_TAN + { 0, 80, 80, 81, 81, 49, 51, 222, 224, 227, 230, 233, 236, 239, 29, 31}, // SKINCOLOR_CREAM + { 0, 80, 81, 83, 64, 65, 66, 67, 68, 215, 69, 70, 44, 71, 46, 47}, // SKINCOLOR_GOLD + { 80, 81, 83, 64, 65, 223, 229, 196, 196, 197, 197, 198, 199, 29, 30, 31}, // SKINCOLOR_ROYAL + { 83, 64, 65, 66, 67, 215, 69, 70, 44, 44, 45, 71, 46, 47, 29, 31}, // SKINCOLOR_BRONZE + { 0, 82, 64, 65, 67, 68, 70, 237, 239, 28, 28, 29, 29, 30, 30, 31}, // SKINCOLOR_COPPER + { 0, 80, 81, 82, 83, 73, 84, 74, 64, 65, 66, 67, 68, 69, 70, 71}, // SKINCOLOR_YELLOW + { 80, 81, 82, 83, 64, 65, 65, 76, 76, 77, 77, 78, 79, 237, 239, 29}, // SKINCOLOR_MUSTARD + { 80, 81, 83, 72, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 30}, // SKINCOLOR_BANANA + { 80, 82, 73, 74, 75, 76, 77, 78, 79, 236, 237, 238, 239, 28, 29, 31}, // SKINCOLOR_OLIVE + { 0, 80, 81, 88, 88, 188, 189, 76, 76, 77, 78, 79, 236, 237, 238, 239}, // SKINCOLOR_CROCODILE + { 0, 80, 81, 88, 188, 189, 190, 191, 94, 94, 95, 95, 109, 110, 111, 31}, // SKINCOLOR_PERIDOT + { 0, 208, 216, 209, 218, 51, 65, 76, 191, 191, 126, 143, 138, 175, 169, 254}, // SKINCOLOR_VOMIT + { 81, 82, 83, 73, 64, 65, 66, 92, 92, 93, 93, 94, 95, 173, 174, 175}, // SKINCOLOR_GARDEN + { 0, 80, 81, 82, 83, 88, 89, 99, 100, 102, 104, 126, 143, 138, 139, 31}, // SKINCOLOR_LIME + { 83, 72, 73, 74, 75, 76, 102, 104, 105, 106, 107, 108, 109, 110, 111, 31}, // SKINCOLOR_HANDHELD + { 0, 80, 80, 81, 88, 89, 90, 91, 92, 93, 94, 95, 109, 110, 111, 31}, // SKINCOLOR_TEA + { 0, 80, 88, 88, 89, 90, 91, 102, 103, 104, 105, 106, 107, 108, 109, 110}, // SKINCOLOR_PISTACHIO + { 88, 89, 90, 91, 91, 92, 93, 94, 107, 107, 108, 108, 109, 109, 110, 111}, // SKINCOLOR_MOSS + {208, 84, 85, 240, 241, 243, 245, 94, 107, 108, 108, 109, 109, 110, 110, 111}, // SKINCOLOR_CAMOUFLAGE + { 0, 88, 98, 101, 103, 104, 105, 94, 94, 107, 95, 109, 110, 111, 30, 31}, // SKINCOLOR_ROBOHOOD + { 0, 88, 88, 89, 89, 100, 101, 102, 125, 126, 143, 143, 138, 175, 169, 254}, // SKINCOLOR_MINT + { 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111}, // SKINCOLOR_GREEN + { 97, 99, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 30, 30, 31}, // SKINCOLOR_PINETREE + { 96, 112, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 119, 111}, // SKINCOLOR_TURTLE + { 96, 112, 113, 114, 115, 116, 117, 118, 119, 119, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_SWAMP + { 0, 0, 208, 208, 48, 89, 98, 100, 148, 148, 172, 172, 173, 173, 174, 175}, // SKINCOLOR_DREAM + { 80, 88, 96, 112, 113, 124, 142, 149, 149, 173, 174, 175, 169, 253, 254, 31}, // SKINCOLOR_PLAGUE + { 0, 120, 121, 112, 113, 114, 115, 125, 125, 126, 126, 127, 138, 175, 253, 254}, // SKINCOLOR_EMERALD + {128, 128, 129, 129, 130, 140, 124, 103, 104, 116, 116, 117, 118, 119, 111, 31}, // SKINCOLOR_ALGAE + { 0, 88, 89, 97, 113, 141, 135, 136, 136, 173, 173, 174, 174, 175, 199, 31}, // SKINCOLOR_CARIBBEAN + { 0, 80, 81, 82, 89, 140, 134, 135, 136, 172, 196, 197, 198, 199, 30, 31}, // SKINCOLOR_AZURE + { 0, 128, 120, 121, 122, 123, 124, 125, 126, 126, 127, 127, 118, 118, 119, 111}, // SKINCOLOR_AQUAMARINE + {128, 120, 121, 122, 123, 141, 141, 142, 142, 143, 143, 138, 138, 139, 139, 31}, // SKINCOLOR_TURQUOISE + { 0, 120, 120, 121, 140, 141, 142, 143, 143, 138, 138, 139, 139, 254, 254, 31}, // SKINCOLOR_TEAL + { 0, 0, 128, 128, 255, 131, 132, 134, 142, 142, 143, 127, 118, 119, 110, 111}, // SKINCOLOR_CYAN + { 0, 0, 128, 128, 129, 146, 133, 134, 135, 149, 149, 173, 173, 174, 175, 31}, // SKINCOLOR_JAWZ + { 0, 128, 129, 130, 131, 132, 133, 135, 136, 136, 137, 137, 138, 138, 139, 31}, // SKINCOLOR_CERULEAN + {128, 129, 130, 132, 134, 135, 136, 137, 137, 138, 138, 139, 139, 29, 30, 31}, // SKINCOLOR_NAVY + { 0, 0, 0, 144, 144, 145, 9, 11, 14, 142, 136, 137, 138, 138, 139, 31}, // SKINCOLOR_PLATINUM + { 0, 0, 144, 144, 144, 145, 145, 145, 170, 170, 171, 171, 172, 173, 174, 175}, // SKINCOLOR_SLATE + { 0, 144, 144, 145, 145, 170, 170, 171, 171, 172, 172, 173, 173, 174, 175, 31}, // SKINCOLOR_STEEL + { 80, 81, 82, 83, 64, 65, 11, 171, 172, 173, 173, 157, 158, 159, 254, 31}, // SKINCOLOR_THUNDER + { 0, 83, 49, 50, 51, 32, 192, 148, 148, 172, 173, 174, 175, 29, 30, 31}, // SKINCOLOR_NOVA + {208, 48, 216, 217, 240, 241, 242, 171, 172, 173, 24, 25, 26, 28, 29, 31}, // SKINCOLOR_RUST + { 48, 218, 221, 224, 227, 231, 196, 173, 173, 174, 159, 159, 253, 253, 254, 31}, // SKINCOLOR_WRISTWATCH + {145, 146, 147, 148, 149, 173, 173, 174, 175, 175, 28, 28, 29, 29, 30, 31}, // SKINCOLOR_JET + { 0, 128, 129, 131, 133, 135, 149, 150, 152, 154, 156, 158, 159, 253, 254, 31}, // SKINCOLOR_SAPPHIRE + { 0, 0, 120, 120, 121, 133, 135, 149, 149, 166, 166, 167, 168, 169, 254, 31}, // SKINCOLOR_ULTRAMARINE + { 0, 0, 144, 144, 145, 146, 147, 149, 150, 152, 154, 155, 157, 159, 253, 254}, // SKINCOLOR_PERIWINKLE + {144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 155, 156, 158, 253, 254, 31}, // SKINCOLOR_BLUE + {146, 148, 149, 150, 152, 153, 155, 157, 159, 253, 253, 254, 254, 31, 31, 31}, // SKINCOLOR_BLUEBERRY + { 0, 0, 0, 252, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_THISTLE + { 0, 252, 160, 161, 162, 163, 164, 165, 166, 167, 168, 168, 169, 169, 253, 254}, // SKINCOLOR_PURPLE + { 0, 128, 128, 129, 129, 146, 170, 162, 163, 164, 165, 166, 167, 168, 169, 254}, // SKINCOLOR_PASTEL + { 0, 144, 145, 146, 170, 162, 163, 184, 184, 207, 207, 44, 45, 46, 47, 31}, // SKINCOLOR_MOONSLAM + {252, 200, 201, 192, 193, 194, 172, 172, 173, 173, 174, 174, 175, 169, 253, 254}, // SKINCOLOR_DUSK + { 0, 252, 252, 200, 201, 181, 182, 183, 184, 166, 167, 168, 169, 253, 254, 31}, // SKINCOLOR_BUBBLEGUM + {176, 177, 178, 179, 180, 181, 182, 183, 184, 184, 185, 185, 186, 187, 30, 31}, // SKINCOLOR_MAGENTA + {208, 209, 209, 32, 33, 182, 183, 184, 185, 185, 186, 186, 187, 253, 254, 31}, // SKINCOLOR_FUCHSIA + { 0, 0, 88, 88, 89, 6, 8, 10, 193, 194, 195, 184, 185, 186, 187, 31}, // SKINCOLOR_TOXIC + { 80, 81, 82, 83, 64, 50, 201, 192, 193, 194, 195, 173, 174, 175, 253, 254}, // SKINCOLOR_MAUVE + {252, 177, 179, 192, 193, 194, 195, 196, 196, 197, 197, 198, 198, 199, 30, 31}, // SKINCOLOR_LAVENDER + {145, 192, 193, 194, 195, 196, 197, 198, 199, 199, 29, 29, 30, 30, 31, 31}, // SKINCOLOR_BYZANTIUM + {208, 209, 210, 211, 212, 213, 214, 195, 195, 196, 196, 197, 198, 199, 29, 30}, // SKINCOLOR_POMEGRANATE + { 0, 0, 0, 252, 252, 176, 200, 201, 179, 192, 193, 194, 195, 196, 197, 198}, // SKINCOLOR_LILAC + { 0, 252, 252, 200, 200, 201, 202, 203, 204, 204, 205, 206, 207, 43, 45, 47}, // SKINCOLOR_TAFFY + + // THESE STILL NEED CONVERTED!!! + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 100, 104, 113, 116, 119}, // SKINCOLOR_SUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 96, 98, 101, 104, 113, 115, 117, 119}, // SKINCOLOR_SUPER2 + { 0, 0, 0, 0, 0, 0, 96, 98, 100, 102, 104, 113, 114, 116, 117, 119}, // SKINCOLOR_SUPER3 + { 0, 0, 0, 0, 96, 97, 99, 100, 102, 104, 113, 114, 115, 116, 117, 119}, // SKINCOLOR_SUPER4 + { 0, 0, 96, 0, 0, 0, 0, 0, 104, 113, 114, 115, 116, 117, 118, 119}, // SKINCOLOR_SUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 82, 85, 115, 117, 119}, // SKINCOLOR_TSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 80, 81, 83, 85, 115, 116, 117, 119}, // SKINCOLOR_TSUPER2 + { 0, 0, 0, 0, 0, 0, 80, 81, 82, 83, 85, 115, 116, 117, 118, 119}, // SKINCOLOR_TSUPER3 + { 0, 0, 0, 0, 80, 81, 82, 83, 84, 85, 115, 115, 116, 117, 118, 119}, // SKINCOLOR_TSUPER4 + { 0, 0, 80, 80, 81, 82, 83, 84, 85, 115, 115, 116, 117, 117, 118, 119}, // SKINCOLOR_TSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 121, 123, 125, 127, 129, 132}, // SKINCOLOR_KSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 121, 122, 124, 125, 127, 128, 130, 132}, // SKINCOLOR_KSUPER2 + { 0, 0, 0, 0, 0, 0, 121, 122, 123, 124, 125, 127, 128, 129, 130, 132}, // SKINCOLOR_KSUPER3 + { 0, 0, 0, 0, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132}, // SKINCOLOR_KSUPER4 + { 0, 0, 121, 121, 122, 123, 124, 125, 126, 126, 127, 128, 129, 130, 131, 132}, // SKINCOLOR_KSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 122, 124, 248, 251, 255}, // SKINCOLOR_PSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 1, 121, 122, 124, 248, 250, 252, 255}, // SKINCOLOR_PSUPER2 + { 0, 0, 0, 0, 0, 0, 1, 121, 122, 123, 124, 248, 249, 251, 253, 255}, // SKINCOLOR_PSUPER3 + { 0, 0, 0, 0, 1, 121, 122, 123, 124, 248, 249, 250, 251, 252, 253, 255}, // SKINCOLOR_PSUPER4 + { 0, 0, 1, 121, 122, 123, 124, 248, 248, 249, 250, 251, 252, 253, 254, 255}, // SKINCOLOR_PSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 225, 227, 228, 230, 232}, // SKINCOLOR_BSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 224, 225, 226, 227, 228, 229, 230, 232}, // SKINCOLOR_BSUPER2 + { 0, 0, 0, 0, 0, 0, 224, 224, 225, 226, 227, 228, 229, 230, 231, 232}, // SKINCOLOR_BSUPER3 + { 0, 0, 0, 0, 224, 224, 225, 226, 226, 227, 228, 229, 229, 230, 231, 232}, // SKINCOLOR_BSUPER4 + { 0, 0, 224, 224, 225, 225, 226, 227, 227, 228, 228, 229, 230, 230, 231, 232}, // SKINCOLOR_BSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 208, 210, 212, 215, 220, 222}, // SKINCOLOR_ASUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 208, 209, 211, 213, 215, 220, 221, 223}, // SKINCOLOR_ASUPER2 + { 0, 0, 0, 0, 0, 0, 208, 209, 210, 211, 212, 213, 215, 220, 221, 223}, // SKINCOLOR_ASUPER3 + { 0, 0, 0, 0, 208, 209, 210, 211, 212, 213, 214, 215, 220, 221, 222, 223}, // SKINCOLOR_ASUPER4 + { 0, 0, 208, 208, 209, 210, 211, 211, 212, 213, 214, 215, 220, 221, 222, 223}, // SKINCOLOR_ASUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 176, 160, 163, 167, 171, 175}, // SKINCOLOR_GSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 176, 176, 160, 163, 166, 169, 172, 175}, // SKINCOLOR_GSUPER2 + { 0, 0, 0, 0, 0, 0, 176, 176, 160, 162, 164, 166, 168, 170, 172, 175}, // SKINCOLOR_GSUPER3 + { 0, 0, 0, 0, 176, 176, 176, 160, 161, 163, 165, 167, 169, 171, 173, 175}, // SKINCOLOR_GSUPER4 + { 0, 0, 176, 176, 176, 160, 161, 163, 164, 166, 167, 169, 170, 172, 173, 175}, // SKINCOLOR_GSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // SKINCOLOR_WSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 4, 9}, // SKINCOLOR_WSUPER2 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 8, 11}, // SKINCOLOR_WSUPER3 + { 0, 0, 0, 0, 0, 0, 0, 1, 1, 3, 4, 6, 8, 9, 11, 13}, // SKINCOLOR_WSUPER4 + { 0, 0, 0, 0, 1, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 15}, // SKINCOLOR_WSUPER5 + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 98, 99, 81, 73, 79}, // SKINCOLOR_CSUPER1 + { 0, 0, 0, 0, 0, 0, 0, 0, 96, 97, 98, 81, 81, 71, 75, 79}, // SKINCOLOR_CSUPER2 + { 0, 0, 0, 0, 0, 0, 96, 97, 98, 99, 81, 81, 70, 73, 76, 79}, // SKINCOLOR_CSUPER3 + { 0, 0, 0, 0, 96, 96, 97, 98, 99, 81, 81, 70, 72, 74, 76, 79}, // SKINCOLOR_CSUPER4 + { 0, 0, 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. @@ -291,7 +437,7 @@ void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor) // 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 + if (i == 0 || i == 31) // pure black and pure white don't change { dest_colormap[i] = (UINT8)i; continue; @@ -347,7 +493,7 @@ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color) if (skinnum == TC_BOSS) dest_colormap[31] = 0; else if (skinnum == TC_METALSONIC) - dest_colormap[239] = 0; + dest_colormap[143] = 0; return; } @@ -444,6 +590,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartdebugcheckpoint); CV_RegisterVar(&cv_kartdebugnodes); + CV_RegisterVar(&cv_kartdebugcolorize); } //} @@ -497,17 +644,17 @@ boolean K_IsPlayerWanted(player_t *player) 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,10, 4, 2, 1, 0, 0, 0, 0, 0 }, // Banana + /*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, 8, 6, 4, 2, 0, 0, 0, 0, 0 }, // Orbinaut + /*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, 1, 3, 5, 3, 0 }, // Grow + /*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 @@ -557,6 +704,11 @@ static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][6] = */ 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 @@ -584,10 +736,6 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) 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) { @@ -609,12 +757,13 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) \return void */ -static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed) +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, pinvin = 0; + UINT8 pingame = 0, pexiting = 0; + boolean thunderisout = false; SINT8 first = -1, second = -1; INT32 secondist = 0; boolean itemenabled[NUMKARTRESULTS-1] = { @@ -652,21 +801,25 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed) 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_INVINCIBILITY - || players[i].kartstuff[k_itemtype] == KITEM_GROW - || players[i].kartstuff[k_invincibilitytimer] - || players[i].kartstuff[k_growshrinktimer] > 0) - pinvin++; + if (players[i].kartstuff[k_itemtype] == KITEM_THUNDERSHIELD) + thunderisout = true; + if (!G_BattleGametype()) { if (players[i].kartstuff[k_position] == 1 && first == -1) @@ -690,29 +843,29 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed) // 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 5 players in game. - // This is done to make low player count races more fair & interesting. (2P normal would be about halfway between 8P normal and 8P frantic) - // Lastly, it *divides* it by your mashed value, which was determined in K_KartItemRoulette, to punish those who are impatient. -#define POWERITEMODDS(odds) \ + // 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; \ + odds = FixedMul(odds<> FRACBITS; \ if (mashed > 0) \ - odds = FixedDiv(odds<> FRACBITS \ + odds = FixedDiv(odds<> FRACBITS; \ +} + +#define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime) switch (item) { - case KITEM_INVINCIBILITY: - case KITEM_GROW: - if (pinvin >= max(1, (pingame+2) / 4)) - newodds = 0; - else - /* FALLTHRU */ case KITEM_ROCKETSNEAKER: case KITEM_JAWZ: - case KITEM_MINE: case KITEM_BALLHOG: - case KITEM_THUNDERSHIELD: case KRITEM_TRIPLESNEAKER: case KRITEM_TRIPLEBANANA: case KRITEM_TENFOLDBANANA: @@ -721,17 +874,34 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed) 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: - //POWERITEMODDS(newodds); if (((indirectitemcooldown > 0) || (pexiting > 0) || (secondist/distvar < 3)) && (pos != 9)) // Force SPB newodds = 0; else - newodds *= min((secondist/distvar)-4, 3); + newodds *= min((secondist/distvar)-4, 3); // POWERITEMODDS(newodds); break; case KITEM_SHRINK: - POWERITEMODDS(newodds); - if ((indirectitemcooldown > 0) || (pingame-1 <= pexiting)) + 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: @@ -767,7 +937,7 @@ static INT32 K_FindUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT3 for (j = 1; j < NUMKARTRESULTS; j++) { - if (K_KartGetItemOdds(i, j, mashed) > 0) + if (K_KartGetItemOdds(i, j, mashed, spbrush) > 0) { available = true; break; @@ -868,11 +1038,12 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) UINT8 pingame = 0; UINT8 roulettestop; INT32 useodds = 0; - INT32 spawnchance[NUMKARTRESULTS * NUMKARTODDS]; - INT32 chance = 0, numchoices = 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]) @@ -893,34 +1064,14 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) } // This makes the roulette produce the random noises. - if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsLocalPlayer(player)) + if ((player->kartstuff[k_itemroulette] % 3) == 1 && P_IsDisplayPlayer(player)) { -#define PLAYROULETTESND S_StartSound(NULL, sfx_itrol1 + ((player->kartstuff[k_itemroulette] / 3) % 8)); - if (splitscreen) +#define PLAYROULETTESND S_StartSound(NULL, sfx_itrol1 + ((player->kartstuff[k_itemroulette] / 3) % 8)) + for (i = 0; i <= splitscreen; i++) { - 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; - } + if (player == &players[displayplayers[i]] && players[displayplayers[i]].kartstuff[k_itemroulette]) + PLAYROULETTESND; } - else - PLAYROULETTESND; #undef PLAYROULETTESND } @@ -947,7 +1098,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) //player->kartstuff[k_itemblinkmode] = 1; player->kartstuff[k_itemroulette] = 0; player->kartstuff[k_roulettetype] = 0; - if (P_IsLocalPlayer(player)) + if (P_IsDisplayPlayer(player)) S_StartSound(NULL, sfx_itrole); return; } @@ -960,37 +1111,39 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) player->kartstuff[k_itemblinkmode] = 2; player->kartstuff[k_itemroulette] = 0; player->kartstuff[k_roulettetype] = 0; - if (P_IsLocalPlayer(player)) + if (P_IsDisplayPlayer(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 * NUMKARTODDS); i++) + 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, (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 + useodds = K_FindUseodds(player, mashed, pingame, bestbumper, spbrush, dontforcespb); for (i = 1; i < NUMKARTRESULTS; i++) - SETITEMRESULT(i); - -#undef SETITEMRESULT + spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush)); // Award the player whatever power is rolled - if (numchoices > 0) - K_KartGetItemResult(player, spawnchance[P_RandomKey(numchoices)]); + 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)) + if (P_IsDisplayPlayer(player)) S_StartSound(NULL, ((player->kartstuff[k_roulettetype] == 1) ? sfx_itrolk : (mashed ? sfx_itrolm : sfx_itrolf))); player->kartstuff[k_itemblink] = TICRATE; @@ -1124,10 +1277,11 @@ void K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid) 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) ?: 1; + 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); @@ -1282,11 +1436,23 @@ static void K_UpdateOffroad(player_t *player) 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 - mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP)|(master->eflags & MFE_VERTICALFLIP); + // 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); @@ -1400,7 +1566,7 @@ void K_RespawnChecker(player_t *player) mo->eflags |= MFE_VERTICALFLIP; P_SetTarget(&mo->target, player->mo); mo->angle = newangle+ANGLE_90; - mo->momz = (8*FRACUNIT)*P_MobjFlip(player->mo); + mo->momz = (8<mo); P_SetScale(mo, (mo->destscale = FRACUNIT)); } } @@ -1418,7 +1584,7 @@ void K_RespawnChecker(player_t *player) if (!P_IsObjectOnGround(player->mo) && !mapreset) { - player->powers[pw_flashing] = 2; + player->powers[pw_flashing] = K_GetKartFlashing(player); // Sal: The old behavior was stupid and prone to accidental usage. // Let's rip off Mania instead, and turn this into a Drop Dash! @@ -1484,7 +1650,7 @@ void K_KartMoveAnimation(player_t *player) 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)) + else if (player->speed > (20*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); @@ -1494,7 +1660,7 @@ void K_KartMoveAnimation(player_t *player) 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)) + else if (player->speed <= (20*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); @@ -1974,17 +2140,25 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto static void K_RemoveGrowShrink(player_t *player) { - player->kartstuff[k_growshrinktimer] = 0; - if (player->mo && !P_MobjWasRemoved(player->mo)) { + if (player->kartstuff[k_growshrinktimer] > 0) // Play Shrink noise + S_StartSound(player->mo, sfx_kc59); + else if (player->kartstuff[k_growshrinktimer] < 0) // Play Grow noise + S_StartSound(player->mo, sfx_kc5a); + 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; } + + player->kartstuff[k_growshrinktimer] = 0; + player->kartstuff[k_growcancel] = -1; + P_RestoreMusic(player); } @@ -2112,6 +2286,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b #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) @@ -2122,6 +2297,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b #else static const boolean force = false; #endif + if (G_BattleGametype()) { if (K_IsPlayerWanted(player)) @@ -2152,7 +2328,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b if (source && source != player->mo && source->player) K_PlayHitEmSound(source); - player->mo->momz = 18*mapobjectscale; + 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; @@ -2203,16 +2379,19 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b } player->kartstuff[k_spinouttype] = 1; - player->kartstuff[k_spinouttimer] = 2*TICRATE+(TICRATE/2); + 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] = ((3*player->kartstuff[k_spinouttimer])/2)+1; + 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); @@ -2389,12 +2568,14 @@ void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 // 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; + 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; @@ -2405,6 +2586,7 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) 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; @@ -2414,6 +2596,7 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) 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; @@ -2421,7 +2604,9 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) 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->momz = P_RandomRange(-speed, speed)*FRACUNIT*P_MobjFlip(truc); + if (truc->eflags & MFE_UNDERWATER) + truc->momz = (117 * truc->momz) / 200; truc->color = color; } @@ -2430,6 +2615,7 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) 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; @@ -2439,6 +2625,7 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) 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; @@ -2447,9 +2634,11 @@ void K_SpawnMineExplosion(mobj_t *source, UINT8 color) 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; + 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; } @@ -2721,7 +2910,8 @@ void K_SpawnBoostTrail(player_t *player) 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 + // not K_MatchGenericExtraFlags so that a stolen sneaker can be seen + K_FlipFromObject(flame, player->mo); flame->momx = 8; P_XYMovement(flame); @@ -2754,6 +2944,7 @@ void K_SpawnSparkleTrail(mobj_t *mo) fixed_t newz = mo->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; } @@ -2772,17 +2962,30 @@ void K_SpawnSparkleTrail(mobj_t *mo) void K_SpawnWipeoutTrail(mobj_t *mo, boolean translucent) { mobj_t *dust; + angle_t aoff; 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); + 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); - dust->eflags = (dust->eflags & ~MFE_VERTICALFLIP)|(mo->eflags & MFE_VERTICALFLIP); // not K_MatchGenericExtraFlags because hyudoro shouldn't be able to wipeout + K_FlipFromObject(dust, mo); if (translucent) // offroad effect { @@ -2848,10 +3051,6 @@ void K_DriftDustHandling(mobj_t *spawner) fixed_t spawny = 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)); @@ -2884,7 +3083,8 @@ static mobj_t *K_FindLastTrailMobj(player_t *player) static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t mapthing, INT32 defaultDir, INT32 altthrow) { mobj_t *mo; - INT32 dir, PROJSPEED; + INT32 dir; + fixed_t PROJSPEED; angle_t newangle; fixed_t newx, newy, newz; mobj_t *throwmo; @@ -2922,12 +3122,19 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map { 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 { @@ -2992,6 +3199,14 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map { // 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); @@ -3001,18 +3216,29 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map if (mo) { angle_t fa = player->mo->angle>>ANGLETOFINESHIFT; - INT32 HEIGHT = (20 + (dir*10))*mapobjectscale + player->mo->momz; + fixed_t HEIGHT = (20 + (dir*10))*mapobjectscale + (player->mo->momz*P_MobjFlip(player->mo)); - 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; + 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); - if (player->mo->eflags & MFE_VERTICALFLIP) - mo->eflags |= MFE_VERTICALFLIP; + 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 @@ -3038,9 +3264,7 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map } mo = P_SpawnMobj(newx, newy, newz, mapthing); // this will never return null because collision isn't processed here - - if (P_MobjFlip(player->mo) < 0) - mo->z = player->mo->z + player->mo->height - mo->height; + K_FlipFromObject(mo, player->mo); mo->threshold = 10; P_SetTarget(&mo->target, player->mo); @@ -3071,12 +3295,68 @@ static mobj_t *K_ThrowKartItem(player_t *player, boolean missile, mobjtype_t map 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) @@ -3246,6 +3526,7 @@ void K_DoSneaker(player_t *player, INT32 type) 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; } @@ -3256,6 +3537,7 @@ void K_DoSneaker(player_t *player, INT32 type) 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); } } @@ -3265,7 +3547,6 @@ void K_DoSneaker(player_t *player, INT32 type) { player->pflags |= PF_ATTACKDOWN; K_PlayBoostTaunt(player->mo); - player->powers[pw_flashing] = 0; // Stop flashing after boosting } } @@ -3284,30 +3565,29 @@ static void K_DoShrink(player_t *user) continue; if (players[i].kartstuff[k_position] < user->kartstuff[k_position]) { + //P_FlashPal(&players[i], PAL_NUKE, 10); + + // Grow should get taken away. + if (players[i].kartstuff[k_growshrinktimer] > 0) + K_RemoveGrowShrink(&players[i]); // Don't hit while invulnerable! - if (!players[i].kartstuff[k_invincibilitytimer] + else 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 (!P_MobjWasRemoved(players[i].mo)) + 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; - players[i].kartstuff[k_growshrinktimer] = -(200+(40*(MAXPLAYERS-players[i].kartstuff[k_position]))); + S_StartSound(players[i].mo, sfx_kc59); } } - - // 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); } } } @@ -3360,11 +3640,14 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) thrust = 32<momz = FixedMul(FINESINE(ANGLE_22h>>ANGLETOFINESHIFT), FixedMul(thrust, vscale)); + mo->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)); } @@ -3514,6 +3797,8 @@ void K_DropHnextList(player_t *player) 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; @@ -3572,6 +3857,8 @@ void K_DropItems(player_t *player) 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]; @@ -3783,6 +4070,7 @@ static void K_MoveHeldObjects(player_t *player) 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; @@ -3873,8 +4161,11 @@ static void K_MoveHeldObjects(player_t *player) { // bobbing, copy pasted from my kimokawaiii entry const fixed_t pi = (22<>ANGLETOFINESHIFT) & FINEMASK); + fixed_t sine = FixedMul(player->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) @@ -3891,7 +4182,7 @@ static void K_MoveHeldObjects(player_t *player) } P_TeleportMove(cur, targx, targy, targz); - + K_FlipFromObject(cur, player->mo); // Update graviflip in real time thanks. num = (num+1) % 2; cur = cur->hnext; } @@ -4060,10 +4351,7 @@ static void K_UpdateEngineSounds(player_t *player, ticcmd_t *cmd) 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)) + if (P_IsDisplayPlayer(&players[i])) { volumedampen += FRACUNIT; // We already know what this is gonna be, let's not waste our time. continue; @@ -4436,8 +4724,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) 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 (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; @@ -4694,6 +4985,7 @@ static void K_KartDrift(player_t *player, boolean onground) 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) { @@ -4756,6 +5048,7 @@ static void K_KartDrift(player_t *player, boolean onground) if ((!player->kartstuff[k_sneakertimer]) || (!player->cmd.driftturn) + || (!player->kartstuff[k_aizdriftstrat]) || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0)) { if (!player->kartstuff[k_drift]) @@ -5030,6 +5323,28 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->kartstuff[k_rocketsneakertimer] < 1) player->kartstuff[k_rocketsneakertimer] = 1; } + // Grow Canceling + else if (player->kartstuff[k_growshrinktimer] > 0) + { + if (player->kartstuff[k_growcancel] >= 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 ((cmd->buttons & BT_ATTACK) || (player->pflags & PF_ATTACKDOWN)) + player->kartstuff[k_growcancel] = -1; + else + player->kartstuff[k_growcancel] = 0; + } + } else if (player->kartstuff[k_itemamount] <= 0) { player->kartstuff[k_itemamount] = player->kartstuff[k_itemheld] = 0; @@ -5067,6 +5382,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) 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; @@ -5283,16 +5599,21 @@ void K_MoveKartPlayer(player_t *player, boolean onground) 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); + if (player->kartstuff[k_growshrinktimer] < 0) // If you're shrunk, then "grow" will just make you normal again. + K_RemoveGrowShrink(player); + else + { + 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; @@ -5386,6 +5707,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->kartstuff[k_itemtype] != KITEM_THUNDERSHIELD) player->kartstuff[k_curshield] = 0; + if (player->kartstuff[k_growshrinktimer] <= 0) + player->kartstuff[k_growcancel] = -1; + if (player->kartstuff[k_itemtype] == KITEM_SPB || player->kartstuff[k_itemtype] == KITEM_SHRINK || player->kartstuff[k_growshrinktimer] < 0) @@ -5402,13 +5726,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->kartstuff[k_hyudorotimer] >= (1*TICRATE/2) && player->kartstuff[k_hyudorotimer] <= hyudorotime-(1*TICRATE/2)) { - if (player == &players[secondarydisplayplayer]) + if (player == &players[displayplayers[1]]) player->mo->eflags |= MFE_DRAWONLYFORP2; - else if (player == &players[thirddisplayplayer] && splitscreen > 1) + else if (player == &players[displayplayers[2]] && splitscreen > 1) player->mo->eflags |= MFE_DRAWONLYFORP3; - else if (player == &players[fourthdisplayplayer] && splitscreen > 2) + else if (player == &players[displayplayers[3]] && splitscreen > 2) player->mo->eflags |= MFE_DRAWONLYFORP4; - else if (player == &players[consoleplayer]) + else if (player == &players[displayplayers[0]]) player->mo->eflags |= MFE_DRAWONLYFORP1; else player->mo->flags2 |= MF2_DONTDRAW; @@ -5418,8 +5742,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } else { - if (player == &players[displayplayer] - || (player != &players[displayplayer] && (player->kartstuff[k_hyudorotimer] < (1*TICRATE/2) || player->kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2)))) + if (P_IsDisplayPlayer(player) + || (!P_IsDisplayPlayer(player) && (player->kartstuff[k_hyudorotimer] < (1*TICRATE/2) || player->kartstuff[k_hyudorotimer] > hyudorotime-(1*TICRATE/2)))) { if (leveltime & 1) player->mo->flags2 |= MF2_DONTDRAW; @@ -5519,7 +5843,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } // Play the starting countdown sounds - if (player == &players[displayplayer]) // Don't play louder in splitscreen + if (player == &players[displayplayers[0]]) // Don't play louder in splitscreen { if ((leveltime == starttime-(3*TICRATE)) || (leveltime == starttime-(2*TICRATE)) || (leveltime == starttime-TICRATE)) S_StartSound(NULL, sfx_s3ka7); @@ -6404,17 +6728,17 @@ INT32 K_calcSplitFlags(INT32 snapflags) if (splitscreen == 0) return snapflags; - if (stplyr != &players[displayplayer]) + if (stplyr != &players[displayplayers[0]]) { - if (splitscreen == 1 && stplyr == &players[secondarydisplayplayer]) + if (splitscreen == 1 && stplyr == &players[displayplayers[1]]) { splitflags |= V_SPLITSCREEN; } else if (splitscreen > 1) { - if (stplyr == &players[thirddisplayplayer] || stplyr == &players[fourthdisplayplayer]) + if (stplyr == &players[displayplayers[2]] || (splitscreen == 3 && stplyr == &players[displayplayers[3]])) splitflags |= V_SPLITSCREEN; - if (stplyr == &players[secondarydisplayplayer] || stplyr == &players[fourthdisplayplayer]) + if (stplyr == &players[displayplayers[1]] || (splitscreen == 3 && stplyr == &players[displayplayers[3]])) splitflags |= V_HORZSCREEN; } } @@ -6450,6 +6774,8 @@ static void K_drawKartItem(void) //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; @@ -6558,6 +6884,8 @@ static void K_drawKartItem(void) else if (stplyr->kartstuff[k_rocketsneakertimer] > 1) { itembar = stplyr->kartstuff[k_rocketsneakertimer]; + maxl = (itemtime*3) - barlength; + if (leveltime & 1) localpatch = kp_rocketsneaker[offset]; else @@ -6565,6 +6893,12 @@ static void K_drawKartItem(void) } else if (stplyr->kartstuff[k_growshrinktimer] > 0) { + if (stplyr->kartstuff[k_growcancel] > 0) + { + itembar = stplyr->kartstuff[k_growcancel]; + maxl = 26; + } + if (leveltime & 1) localpatch = kp_grow[offset]; else @@ -6666,41 +7000,41 @@ static void K_drawKartItem(void) } // pain and suffering defined below - if (splitscreen < 2) // don't change shit for THIS splitscreen. + 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. + else // now we're having a fun game. { - if (stplyr == &players[displayplayer] || stplyr == &players[thirddisplayplayer]) // If we are P1 or P3... + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // 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. + fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? 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 + fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN : V_SNAPTOTOP); // flip P4 to the bottom flipamount = true; } } if (localcolor != SKINCOLOR_NONE) - colmap = R_GetTranslationColormap(colormode, localcolor, 0); + 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_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])); @@ -6716,8 +7050,6 @@ static void K_drawKartItem(void) // Extensible meter, currently only used for rocket sneaker... if (itembar && hudtrans) { - const INT32 barlength = (splitscreen > 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); @@ -6732,7 +7064,7 @@ static void K_drawKartItem(void) 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 + V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 0|fflags); // the shine } } @@ -6818,7 +7150,7 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UI else if ((drawtime/TICRATE) & 1) V_DrawKartString(TX, TY+3, splitflags, va("99'59\"99")); - if (emblemmap && (modeattacking || (mode == 1)) && !demoplayback) // emblem time! + if (emblemmap && (modeattacking || (mode == 1)) && !demo.playback) // emblem time! { INT32 workx = TX + 96, worky = TY+18; SINT8 curemb = 0; @@ -6929,7 +7261,7 @@ static void K_DrawKartPositionNum(INT32 num) 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. + if (stplyr == &players[displayplayers[0]]) // for player 1: display this at the top right, above the minimap. { fy = 30; fflags = V_SNAPTOTOP|V_SNAPTORIGHT; @@ -6944,11 +7276,11 @@ static void K_DrawKartPositionNum(INT32 num) } else { - if (stplyr == &players[displayplayer] || stplyr == &players[thirddisplayplayer]) // If we are P1 or P3... + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // 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. + fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? 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. @@ -6957,7 +7289,7 @@ static void K_DrawKartPositionNum(INT32 num) { fx = POSI2_X; fy = POSI2_Y; - fflags = V_SNAPTORIGHT|((stplyr == &players[fourthdisplayplayer]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom + fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom } } @@ -7185,7 +7517,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I 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); + HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); STRBUFCPY(strtime, tab[i].name); @@ -7283,17 +7615,17 @@ static void K_drawKartLaps(void) } else { - if (stplyr == &players[displayplayer] || stplyr == &players[thirddisplayplayer]) // If we are P1 or P3... + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // 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. + fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? 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 + fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom flipstring = true; // make the string right aligned and other shit } } @@ -7354,7 +7686,7 @@ static void K_drawKartSpeedometer(void) static void K_drawKartBumpersOrKarma(void) { - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, 0); + 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 @@ -7365,17 +7697,17 @@ static void K_drawKartBumpersOrKarma(void) // 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... + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // 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. + fflags = V_SNAPTOLEFT|((stplyr == &players[displayplayers[2]]) ? 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 + fflags = V_SNAPTORIGHT|((stplyr == &players[displayplayers[3]]) ? V_SPLITSCREEN|V_SNAPTOBOTTOM : 0); // flip P4 to the bottom flipstring = true; } @@ -7452,7 +7784,7 @@ static void K_drawKartWanted(void) UINT8 *colormap = NULL; INT32 basex = 0, basey = 0; - if (stplyr != &players[displayplayer]) + if (stplyr != &players[displayplayers[0]]) return; for (i = 0; i < 4; i++) @@ -7532,7 +7864,7 @@ static void K_drawKartPlayerCheck(void) if (stplyr->awayviewtics) return; - if (camspin) + if (camspin[0]) return; for (i = 0; i < MAXPLAYERS; i++) @@ -7562,7 +7894,7 @@ static void K_drawKartPlayerCheck(void) else if (x > 306) x = 306; - colormap = R_GetTranslationColormap(TC_DEFAULT, players[i].mo->color, 0); + colormap = R_GetTranslationColormap(TC_DEFAULT, players[i].mo->color, GTC_CACHE); V_DrawMappedPatch(x, CHEK_Y, V_HUDTRANS|splitflags, kp_check[pnum], colormap); } } @@ -7654,8 +7986,12 @@ static void K_drawKartMinimapHead(mobj_t *mo, INT32 x, INT32 y, INT32 flags, pat else colormap = R_GetTranslationColormap(skin, mo->color, GTC_CACHE); V_DrawFixedPatch(amxpos, amypos, FRACUNIT, flags, facemmapprefix[skin], colormap); - if (mo->player && K_IsPlayerWanted(mo->player)) + if (mo->player + && ((G_RaceGametype() && mo->player->kartstuff[k_position] == spbplace) + || (G_BattleGametype() && K_IsPlayerWanted(mo->player)))) + { V_DrawFixedPatch(amxpos - (4<width)<kartstuff[k_cardanimation])*(xval > x ? xval : x))/TICRATE; - if (splitscreen && stplyr == &players[secondarydisplayplayer]) + if (splitscreen && stplyr == &players[displayplayers[1]]) x = -x; V_DrawFixedPatch(x + (STCD_X<>1), @@ -7851,9 +8187,9 @@ static void K_drawBattleFullscreen(void) if (splitscreen) { - if ((splitscreen == 1 && stplyr == &players[secondarydisplayplayer]) - || (splitscreen > 1 && (stplyr == &players[thirddisplayplayer] - || (stplyr == &players[fourthdisplayplayer] && splitscreen > 2)))) + if ((splitscreen == 1 && stplyr == &players[displayplayers[1]]) + || (splitscreen > 1 && (stplyr == &players[displayplayers[2]] + || (stplyr == &players[displayplayers[3]] && splitscreen > 2)))) { y = 232-(stplyr->kartstuff[k_cardanimation]/2); splitflags = V_SNAPTOBOTTOM; @@ -7865,8 +8201,8 @@ static void K_drawBattleFullscreen(void) { scale /= 2; - if (stplyr == &players[secondarydisplayplayer] - || (stplyr == &players[fourthdisplayplayer] && splitscreen > 2)) + if (stplyr == &players[displayplayers[1]] + || (stplyr == &players[displayplayers[3]] && splitscreen > 2)) x = 3*BASEVIDWIDTH/4; else x = BASEVIDWIDTH/4; @@ -7875,7 +8211,7 @@ static void K_drawBattleFullscreen(void) { if (stplyr->exiting) { - if (stplyr == &players[secondarydisplayplayer]) + if (stplyr == &players[displayplayers[1]]) x = BASEVIDWIDTH-96; else x = 96; @@ -7887,7 +8223,7 @@ static void K_drawBattleFullscreen(void) if (stplyr->exiting) { - if (stplyr == &players[displayplayer]) + if (stplyr == &players[displayplayers[0]]) V_DrawFadeScreen(0xFF00, 16); if (stplyr->exiting < 6*TICRATE && !stplyr->spectator) { @@ -7917,9 +8253,9 @@ static void K_drawBattleFullscreen(void) { if (splitscreen > 1) ty = (BASEVIDHEIGHT/4)+33; - if ((splitscreen == 1 && stplyr == &players[secondarydisplayplayer]) - || (stplyr == &players[thirddisplayplayer] && splitscreen > 1) - || (stplyr == &players[fourthdisplayplayer] && splitscreen > 2)) + if ((splitscreen == 1 && stplyr == &players[displayplayers[1]]) + || (stplyr == &players[displayplayers[2]] && splitscreen > 1) + || (stplyr == &players[displayplayers[3]] && splitscreen > 2)) ty += (BASEVIDHEIGHT/2); } else @@ -7946,7 +8282,7 @@ static void K_drawBattleFullscreen(void) // check to see if there's anyone else at all for (i = 0; i < MAXPLAYERS; i++) { - if (i == displayplayer) + if (i == displayplayers[0]) continue; if (playeringame[i] && !stplyr->spectator) return; @@ -7972,11 +8308,11 @@ static void K_drawKartFirstPerson(void) if (stplyr->spectator || !stplyr->mo || (stplyr->mo->flags2 & MF2_DONTDRAW)) return; - if (stplyr == &players[secondarydisplayplayer] && splitscreen) + if (stplyr == &players[displayplayers[1]] && splitscreen) { pn = pnum[1]; tn = turn[1]; dr = drift[1]; } - else if (stplyr == &players[thirddisplayplayer] && splitscreen > 1) + else if (stplyr == &players[displayplayers[2]] && splitscreen > 1) { pn = pnum[2]; tn = turn[2]; dr = drift[2]; } - else if (stplyr == &players[fourthdisplayplayer] && splitscreen > 2) + else if (stplyr == &players[displayplayers[3]] && splitscreen > 2) { pn = pnum[3]; tn = turn[3]; dr = drift[3]; } else { pn = pnum[0]; tn = turn[0]; dr = drift[0]; } @@ -7989,7 +8325,7 @@ static void K_drawKartFirstPerson(void) } { - if (stplyr->speed < FixedMul(stplyr->runspeed, stplyr->mo->scale) && (leveltime & 1) && !splitscreen) + if (stplyr->speed < (20*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) @@ -8087,25 +8423,25 @@ static void K_drawKartFirstPerson(void) // drift sparks! if ((leveltime & 1) && (stplyr->kartstuff[k_driftcharge] >= dsthree)) - colmap = R_GetTranslationColormap(TC_RAINBOW, (UINT8)(1 + (leveltime % (MAXSKINCOLORS-1))), 0); + 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, 0); + 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, 0); + 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, 0); + 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) + if (stplyr == &players[displayplayers[1]] && splitscreen) { pnum[1] = pn; turn[1] = tn; drift[1] = dr; } - else if (stplyr == &players[thirddisplayplayer] && splitscreen > 1) + else if (stplyr == &players[displayplayers[2]] && splitscreen > 1) { pnum[2] = pn; turn[2] = tn; drift[2] = dr; } - else if (stplyr == &players[fourthdisplayplayer] && splitscreen > 2) + else if (stplyr == &players[displayplayers[3]] && splitscreen > 2) { pnum[3] = pn; turn[3] = tn; drift[3] = dr; } else { pnum[0] = pn; turn[0] = tn; drift[0] = dr; } @@ -8127,19 +8463,12 @@ static void K_drawInput(void) if (timeinmap < 113) { INT32 count = ((INT32)(timeinmap) - 105); - offs = (titledemo ? 128 : 64); + offs = 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 @@ -8206,7 +8535,7 @@ static void K_drawInput(void) else { UINT8 *colormap; - colormap = R_GetTranslationColormap(0, stplyr->skincolor, 0); + colormap = R_GetTranslationColormap(0, stplyr->skincolor, GTC_CACHE); V_DrawFixedPatch(x<kartstuff[k_lapanimation]; - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, 0); + 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, @@ -8331,8 +8660,9 @@ static void K_drawDistributionDebugger(void) INT32 i; INT32 x = -9, y = -9; boolean dontforcespb = false; + boolean spbrush = false; - if (stplyr != &players[displayplayer]) // only for p1 + if (stplyr != &players[displayplayers[0]]) // only for p1 return; // The only code duplication from the Kart, just to avoid the actual item function from calculating pingame twice @@ -8347,11 +8677,14 @@ static void K_drawDistributionDebugger(void) bestbumper = players[i].kartstuff[k_bumper]; } - useodds = K_FindUseodds(stplyr, 0, pingame, bestbumper, (spbplace != -1 && stplyr->kartstuff[k_position] == spbplace+1), dontforcespb); + 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); + const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush); if (itemodds <= 0) continue; @@ -8393,7 +8726,7 @@ static void K_drawDistributionDebugger(void) static void K_drawCheckpointDebugger(void) { - if (stplyr != &players[displayplayer]) // only for p1 + if (stplyr != &players[displayplayers[0]]) // only for p1 return; if (stplyr->starpostnum >= (numstarposts - (numstarposts/2))) @@ -8407,20 +8740,21 @@ void K_drawKartHUD(void) { boolean isfreeplay = false; boolean battlefullscreen = false; + UINT8 i; // 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(); + for (i = 0; i <= splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]] && !camera[i].chase) + K_drawKartFirstPerson(); + } // Draw full screen stuff that turns off the rest of the HUD - if (mapreset && stplyr == &players[displayplayer]) + if (mapreset && stplyr == &players[displayplayers[0]]) { K_drawChallengerScreen(); return; @@ -8433,10 +8767,10 @@ void K_drawKartHUD(void) && comeback && stplyr->playerstate == PST_LIVE))); - if (!battlefullscreen || splitscreen) + if (!demo.title && (!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) + if (cv_kartcheck.value && !splitscreen && !players[displayplayers[0]].exiting) K_drawKartPlayerCheck(); // Draw WANTED status @@ -8448,7 +8782,7 @@ void K_drawKartHUD(void) K_drawKartWanted(); } - if (cv_kartminimap.value && !titledemo) + if (cv_kartminimap.value) { #ifdef HAVE_BLUA if (LUA_HudEnabled(hud_minimap)) @@ -8470,7 +8804,7 @@ void K_drawKartHUD(void) K_drawKartItem(); // If not splitscreen, draw... - if (!splitscreen && !titledemo) + if (!splitscreen && !demo.title) { // Draw the timestamp #ifdef HAVE_BLUA @@ -8490,25 +8824,44 @@ void K_drawKartHUD(void) if (!stplyr->spectator) // Bottom of the screen elements, don't need in spectate mode { - if (G_RaceGametype()) // Race-only elements + if (demo.title) // Draw title logo instead in demo.titles { - if (!titledemo) - { - // Draw the lap counter -#ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_gametypeinfo)) -#endif - K_drawKartLaps(); + INT32 x = BASEVIDWIDTH - 32, y = 128, offs; - if (!splitscreen) - { - // Draw the speedometer - // TODO: Make a better speedometer. + if (splitscreen == 3) + { + x = BASEVIDWIDTH/2 + 10; + y = BASEVIDHEIGHT/2 - 30; + } + + if (timeinmap < 113) + { + INT32 count = ((INT32)(timeinmap) - 104); + offs = 256; + while (count-- > 0) + offs >>= 1; + x += offs; + } + + V_DrawTinyScaledPatch(x-54, y, 0, W_CachePatchName("TTKBANNR", PU_CACHE)); + V_DrawTinyScaledPatch(x-54, y+25, 0, W_CachePatchName("TTKART", PU_CACHE)); + } + else if (G_RaceGametype()) // Race-only elements + { + // Draw the lap counter #ifdef HAVE_BLUA - if (LUA_HudEnabled(hud_speedometer)) + if (LUA_HudEnabled(hud_gametypeinfo)) #endif - K_drawKartSpeedometer(); - } + 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) @@ -8521,7 +8874,7 @@ void K_drawKartHUD(void) #endif K_DrawKartPositionNum(stplyr->kartstuff[k_position]); } - else //if (!(demoplayback && hu_showscores)) + else //if (!(demo.playback && hu_showscores)) { // Draw the input UI #ifdef HAVE_BLUA @@ -8593,6 +8946,25 @@ void K_drawKartHUD(void) 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 dc37956af..2ba5d1bdc 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -11,7 +11,7 @@ #define KART_FULLTURN 800 -UINT8 colortranslations[MAXSKINCOLORS][16]; +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); @@ -23,6 +23,7 @@ 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); @@ -41,6 +42,7 @@ 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); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 78f972f87..6700d5af8 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -163,6 +163,7 @@ static int lib_pRandomFixed(lua_State *L) { NOHUD lua_pushfixed(L, P_RandomFixed()); + demo_writerng = 2; return 1; } @@ -170,6 +171,7 @@ static int lib_pRandomByte(lua_State *L) { NOHUD lua_pushinteger(L, P_RandomByte()); + demo_writerng = 2; return 1; } @@ -181,6 +183,7 @@ static int lib_pRandomKey(lua_State *L) if (a > 65536) LUA_UsageWarning(L, "P_RandomKey: range > 65536 is undefined behavior"); lua_pushinteger(L, P_RandomKey(a)); + demo_writerng = 2; return 1; } @@ -198,6 +201,7 @@ static int lib_pRandomRange(lua_State *L) if ((b-a+1) > 65536) LUA_UsageWarning(L, "P_RandomRange: range > 65536 is undefined behavior"); lua_pushinteger(L, P_RandomRange(a, b)); + demo_writerng = 2; return 1; } @@ -207,6 +211,7 @@ static int lib_pRandom(lua_State *L) NOHUD LUA_Deprecated(L, "P_Random", "P_RandomByte"); lua_pushinteger(L, P_RandomByte()); + demo_writerng = 2; return 1; } @@ -214,6 +219,7 @@ static int lib_pSignedRandom(lua_State *L) { NOHUD lua_pushinteger(L, P_SignedRandom()); + demo_writerng = 2; return 1; } @@ -222,6 +228,7 @@ static int lib_pRandomChance(lua_State *L) fixed_t p = luaL_checkfixed(L, 1); NOHUD lua_pushboolean(L, P_RandomChance(p)); + demo_writerng = 2; return 1; } @@ -632,19 +639,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)); @@ -782,7 +776,8 @@ static int lib_pRestoreMusic(lua_State *L) NOHUD if (!player) return LUA_ErrInvalid(L, "player_t"); - P_RestoreMusic(player); + if (P_IsLocalPlayer(player)) + P_RestoreMusic(player); return 0; } @@ -959,40 +954,6 @@ static int lib_pHomingAttack(lua_State *L) return 1; }*/ -static int lib_pDoJump(lua_State *L) -{ - player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); - boolean soundandstate = (boolean)lua_opttrueboolean(L, 2); - NOHUD - if (!player) - return LUA_ErrInvalid(L, "player_t"); - P_DoJump(player, soundandstate); - return 0; -} - -static int lib_pSpawnThokMobj(lua_State *L) -{ - player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); - NOHUD - if (!player) - return LUA_ErrInvalid(L, "player_t"); - P_SpawnThokMobj(player); - return 0; -} - -static int lib_pSpawnSpinMobj(lua_State *L) -{ - player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); - mobjtype_t type = luaL_checkinteger(L, 2); - NOHUD - if (!player) - return LUA_ErrInvalid(L, "player_t"); - if (type >= NUMMOBJTYPES) - return luaL_error(L, "mobj type %d out of range (0 - %d)", type, NUMMOBJTYPES-1); - P_SpawnSpinMobj(player, type); - return 0; -} - static int lib_pTelekinesis(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); @@ -1849,7 +1810,7 @@ static int lib_sChangeMusic(lua_State *L) { #ifdef MUSICSLOT_COMPATIBILITY const char *music_name; - UINT32 music_num; + UINT32 music_num, position, prefadems, fadeinms; char music_compat_name[7]; boolean looping; @@ -1877,7 +1838,6 @@ static int lib_sChangeMusic(lua_State *L) music_name = luaL_checkstring(L, 1); } - looping = (boolean)lua_opttrueboolean(L, 2); #else @@ -1902,8 +1862,12 @@ static int lib_sChangeMusic(lua_State *L) #endif music_flags = (UINT16)luaL_optinteger(L, 4, 0); + position = (UINT32)luaL_optinteger(L, 5, 0); + prefadems = (UINT32)luaL_optinteger(L, 6, 0); + fadeinms = (UINT32)luaL_optinteger(L, 7, 0); + if (!player || P_IsLocalPlayer(player)) - S_ChangeMusic(music_name, music_flags, looping); + S_ChangeMusicEx(music_name, music_flags, looping, position, prefadems, fadeinms); return 0; } @@ -1920,10 +1884,8 @@ static int lib_sSpeedMusic(lua_State *L) return LUA_ErrInvalid(L, "player_t"); } if (!player || P_IsLocalPlayer(player)) - lua_pushboolean(L, S_SpeedMusic(speed)); - else - lua_pushboolean(L, false); - return 1; + S_SpeedMusic(speed); + return 0; } static int lib_sStopMusic(lua_State *L) @@ -1941,6 +1903,110 @@ static int lib_sStopMusic(lua_State *L) return 0; } +static int lib_sSetInternalMusicVolume(lua_State *L) +{ + UINT32 volume = (UINT32)luaL_checkinteger(L, 1); + player_t *player = NULL; + NOHUD + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + { + player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + if (!player || P_IsLocalPlayer(player)) + { + S_SetInternalMusicVolume(volume); + lua_pushboolean(L, true); + } + else + lua_pushnil(L); + return 1; +} + +static int lib_sStopFadingMusic(lua_State *L) +{ + player_t *player = NULL; + NOHUD + if (!lua_isnone(L, 1) && lua_isuserdata(L, 1)) + { + player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + if (!player || P_IsLocalPlayer(player)) + { + S_StopFadingMusic(); + lua_pushboolean(L, true); + } + else + lua_pushnil(L); + return 1; +} + +static int lib_sFadeMusic(lua_State *L) +{ + UINT32 target_volume = (UINT32)luaL_checkinteger(L, 1); + UINT32 ms; + INT32 source_volume; + player_t *player = NULL; + NOHUD + if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) + { + player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + ms = (UINT32)luaL_checkinteger(L, 2); + source_volume = -1; + } + else if (!lua_isnone(L, 4) && lua_isuserdata(L, 4)) + { + player = *((player_t **)luaL_checkudata(L, 4, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + source_volume = (INT32)luaL_checkinteger(L, 2); + ms = (UINT32)luaL_checkinteger(L, 3); + } + else if (luaL_optinteger(L, 3, INT32_MAX) == INT32_MAX) + { + ms = (UINT32)luaL_checkinteger(L, 2); + source_volume = -1; + } + else + { + source_volume = (INT32)luaL_checkinteger(L, 2); + ms = (UINT32)luaL_checkinteger(L, 3); + } + + NOHUD + + if (!player || P_IsLocalPlayer(player)) + lua_pushboolean(L, S_FadeMusicFromVolume(target_volume, source_volume, ms)); + else + lua_pushnil(L); + return 1; +} + +static int lib_sFadeOutStopMusic(lua_State *L) +{ + UINT32 ms = (UINT32)luaL_checkinteger(L, 1); + player_t *player = NULL; + NOHUD + if (!lua_isnone(L, 2) && lua_isuserdata(L, 2)) + { + player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + if (!player) + return LUA_ErrInvalid(L, "player_t"); + } + if (!player || P_IsLocalPlayer(player)) + { + lua_pushboolean(L, S_FadeOutStopMusic(ms)); + } + else + lua_pushnil(L); + return 1; +} + static int lib_sOriginPlaying(lua_State *L) { void *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); @@ -2627,7 +2693,6 @@ 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 @@ -2659,9 +2724,6 @@ static luaL_Reg lib[] = { {"P_NukeEnemies",lib_pNukeEnemies}, {"P_HomingAttack",lib_pHomingAttack}, //{"P_SuperReady",lib_pSuperReady}, - {"P_DoJump",lib_pDoJump}, - {"P_SpawnThokMobj",lib_pSpawnThokMobj}, - {"P_SpawnSpinMobj",lib_pSpawnSpinMobj}, {"P_Telekinesis",lib_pTelekinesis}, // p_map @@ -2741,6 +2803,10 @@ static luaL_Reg lib[] = { {"S_ChangeMusic",lib_sChangeMusic}, {"S_SpeedMusic",lib_sSpeedMusic}, {"S_StopMusic",lib_sStopMusic}, + {"S_SetInternalMusicVolume", lib_sSetInternalMusicVolume}, + {"S_StopFadingMusic",lib_sStopFadingMusic}, + {"S_FadeMusic",lib_sFadeMusic}, + {"S_FadeOutStopMusic",lib_sFadeOutStopMusic}, {"S_OriginPlaying",lib_sOriginPlaying}, {"S_IdPlaying",lib_sIdPlaying}, {"S_SoundPlaying",lib_sSoundPlaying}, @@ -2766,7 +2832,7 @@ static luaL_Reg lib[] = { // k_kart {"K_PlayAttackTaunt", lib_kAttackSound}, {"K_PlayBoostTaunt", lib_kBoostSound}, - {"K_PlayPowerGloatSund", lib_kGloatSound}, + {"K_PlayPowerGloatSound", lib_kGloatSound}, {"K_PlayOvertakeSound", lib_kOvertakeSound}, {"K_PlayLossSound", lib_kLossSound}, {"K_PlayHitEmSound", lib_kHitEmSound}, diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c index 0ea0c8097..299870e00 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -118,14 +118,14 @@ void COM_Lua_f(void) flags = (UINT8)lua_tointeger(gL, -1); lua_pop(gL, 1); // pop flags - if (flags & 2) // flag 2: splitscreen player command. + if (flags & 2) // flag 2: splitscreen player command. TODO: support 4P { if (!splitscreen) { lua_pop(gL, 1); // pop command info table return; // can't execute splitscreen command without player 2! } - playernum = secondarydisplayplayer; + playernum = displayplayers[1]; } if (netgame) diff --git a/src/lua_hook.h b/src/lua_hook.h index 126e7e405..e61acdf13 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -51,6 +51,7 @@ enum hook { hook_PlayerExplode, //SRB2KART hook_PlayerSquish, //SRB2KART hook_PlayerCmd, //SRB2KART + hook_IntermissionThinker, //SRB2KART hook_MAX // last hook }; @@ -99,4 +100,6 @@ boolean LUAh_PlayerSquish(player_t *player, mobj_t *inflictor, mobj_t *source); boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Allows to write to player cmd before the game does anything with them. +void LUAh_IntermissionThinker(void); // Hook for Y_Ticker + #endif diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 5a95877e3..c15d13a0c 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -62,6 +62,7 @@ const char *const hookNames[hook_MAX+1] = { "PlayerExplode", "PlayerSquish", "PlayerCmd", + "IntermissionThinker", NULL }; @@ -420,6 +421,28 @@ void LUAh_ThinkFrame(void) } } +// Hook for Y_Ticker +void LUAh_IntermissionThinker(void) +{ + hook_p hookp; + if (!gL || !(hooksAvailable[hook_IntermissionThinker/8] & (1<<(hook_IntermissionThinker%8)))) + return; + + for (hookp = roothook; hookp; hookp = hookp->next) + if (hookp->type == hook_IntermissionThinker) + { + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + if (lua_pcall(gL, 0, 0, 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; + } + } +} + + // Hook for mobj collisions UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) { diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index cd8e03923..c6f2f782b 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -410,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); @@ -425,26 +443,26 @@ static int libd_drawFill(lua_State *L) static int libd_fadeScreen(lua_State *L) { - UINT16 color = luaL_checkinteger(L, 1); - UINT8 strength = luaL_checkinteger(L, 2); - const UINT8 maxstrength = ((color & 0xFF00) ? 32 : 10); + UINT16 color = luaL_checkinteger(L, 1); + UINT8 strength = luaL_checkinteger(L, 2); + const UINT8 maxstrength = ((color & 0xFF00) ? 32 : 10); - HUDONLY + HUDONLY - if (!strength) - return 0; + if (!strength) + return 0; - if (strength > maxstrength) - return luaL_error(L, "%s fade strength %d out of range (0 - %d)", ((color & 0xFF00) ? "COLORMAP" : "TRANSMAP"), strength, maxstrength); + if (strength > maxstrength) + return luaL_error(L, "%s fade strength %d out of range (0 - %d)", ((color & 0xFF00) ? "COLORMAP" : "TRANSMAP"), strength, maxstrength); - if (strength == maxstrength) // Allow as a shortcut for drawfill... - { - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); - return 0; - } + if (strength == maxstrength) // Allow as a shortcut for drawfill... + { + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); + return 0; + } - V_DrawFadeScreen(color, strength); - return 0; + V_DrawFadeScreen(color, strength); + return 0; } static int libd_drawString(lua_State *L) @@ -613,6 +631,7 @@ 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}, @@ -778,24 +797,24 @@ void LUAh_GameHUD(player_t *stplayr) lua_remove(gL, -3); // pop HUD LUA_PushUserdata(gL, stplayr, META_PLAYER); - if (splitscreen > 2 && stplayr == &players[fourthdisplayplayer]) + if (splitscreen > 2 && stplayr == &players[displayplayers[3]]) { - LUA_PushUserdata(gL, &camera4, META_CAMERA); + LUA_PushUserdata(gL, &camera[3], META_CAMERA); camnum = 4; } - else if (splitscreen > 1 && stplayr == &players[thirddisplayplayer]) + else if (splitscreen > 1 && stplayr == &players[displayplayers[2]]) { - LUA_PushUserdata(gL, &camera3, META_CAMERA); + LUA_PushUserdata(gL, &camera[2], META_CAMERA); camnum = 3; } - else if (splitscreen && stplayr == &players[secondarydisplayplayer]) + else if (splitscreen && stplayr == &players[displayplayers[1]]) { - LUA_PushUserdata(gL, &camera2, META_CAMERA); + LUA_PushUserdata(gL, &camera[1], META_CAMERA); camnum = 2; } else { - LUA_PushUserdata(gL, &camera, META_CAMERA); + LUA_PushUserdata(gL, &camera[0], META_CAMERA); camnum = 1; } diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 0bb9a99d6..19292b3d6 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -1477,6 +1477,12 @@ static int mapheaderinfo_get(lua_State *L) lua_pushstring(L, header->musname); else if (fastcmp(field,"mustrack")) lua_pushinteger(L, header->mustrack); + else if (fastcmp(field,"muspos")) + lua_pushinteger(L, header->muspos); + else if (fastcmp(field,"musinterfadeout")) + lua_pushinteger(L, header->musinterfadeout); + else if (fastcmp(field,"musintername")) + lua_pushstring(L, header->musintername); else if (fastcmp(field,"forcecharacter")) lua_pushstring(L, header->forcecharacter); else if (fastcmp(field,"weather")) diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index b56538d0f..dfb344e34 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -421,13 +421,13 @@ static int mobj_set(lua_State *L) case mobj_angle: mo->angle = luaL_checkangle(L, 3); if (mo->player == &players[consoleplayer]) - localangle = mo->angle; - else if (mo->player == &players[secondarydisplayplayer]) - localangle2 = mo->angle; - else if (mo->player == &players[thirddisplayplayer]) - localangle3 = mo->angle; - else if (mo->player == &players[fourthdisplayplayer]) - localangle4 = mo->angle; + localangle[0] = mo->angle; + else if (mo->player == &players[displayplayers[1]]) + localangle[1] = mo->angle; + else if (mo->player == &players[displayplayers[2]]) + localangle[2] = mo->angle; + else if (mo->player == &players[displayplayers[3]]) + localangle[3] = mo->angle; break; case mobj_sprite: mo->sprite = luaL_checkinteger(L, 3); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 5f136fc15..3cca1f91f 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -155,36 +155,8 @@ static int player_get(lua_State *L) else if (fastcmp(field,"kartweight")) lua_pushinteger(L, plr->kartweight); // - else if (fastcmp(field,"normalspeed")) - lua_pushfixed(L, plr->normalspeed); - else if (fastcmp(field,"runspeed")) - lua_pushfixed(L, plr->runspeed); - else if (fastcmp(field,"thrustfactor")) - lua_pushinteger(L, plr->thrustfactor); - else if (fastcmp(field,"accelstart")) - lua_pushinteger(L, plr->accelstart); - else if (fastcmp(field,"acceleration")) - lua_pushinteger(L, plr->acceleration); - else if (fastcmp(field,"charability")) - lua_pushinteger(L, plr->charability); - else if (fastcmp(field,"charability2")) - lua_pushinteger(L, plr->charability2); else if (fastcmp(field,"charflags")) lua_pushinteger(L, plr->charflags); - else if (fastcmp(field,"thokitem")) - lua_pushinteger(L, plr->thokitem); - else if (fastcmp(field,"spinitem")) - lua_pushinteger(L, plr->spinitem); - else if (fastcmp(field,"revitem")) - lua_pushinteger(L, plr->revitem); - else if (fastcmp(field,"actionspd")) - lua_pushfixed(L, plr->actionspd); - else if (fastcmp(field,"mindash")) - lua_pushfixed(L, plr->mindash); - else if (fastcmp(field,"maxdash")) - lua_pushfixed(L, plr->maxdash); - else if (fastcmp(field,"jumpfactor")) - lua_pushfixed(L, plr->jumpfactor); else if (fastcmp(field,"lives")) lua_pushinteger(L, plr->lives); else if (fastcmp(field,"continues")) @@ -325,6 +297,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); @@ -380,13 +354,13 @@ static int player_set(lua_State *L) else if (fastcmp(field,"aiming")) { plr->aiming = luaL_checkangle(L, 3); if (plr == &players[consoleplayer]) - localaiming = plr->aiming; - else if (plr == &players[secondarydisplayplayer]) - localaiming2 = plr->aiming; - else if (plr == &players[thirddisplayplayer]) - localaiming3 = plr->aiming; - else if (plr == &players[fourthdisplayplayer]) - localaiming4 = plr->aiming; + localaiming[0] = plr->aiming; + else if (plr == &players[displayplayers[1]]) + localaiming[1] = plr->aiming; + else if (plr == &players[displayplayers[2]]) + localaiming[2] = plr->aiming; + else if (plr == &players[displayplayers[3]]) + localaiming[3] = plr->aiming; } else if (fastcmp(field,"health")) plr->health = (INT32)luaL_checkinteger(L, 3); @@ -429,36 +403,8 @@ static int player_set(lua_State *L) else if (fastcmp(field,"kartweight")) plr->kartweight = (UINT8)luaL_checkinteger(L, 3); // - else if (fastcmp(field,"normalspeed")) - plr->normalspeed = luaL_checkfixed(L, 3); - else if (fastcmp(field,"runspeed")) - plr->runspeed = luaL_checkfixed(L, 3); - else if (fastcmp(field,"thrustfactor")) - plr->thrustfactor = (UINT8)luaL_checkinteger(L, 3); - else if (fastcmp(field,"accelstart")) - plr->accelstart = (UINT8)luaL_checkinteger(L, 3); - else if (fastcmp(field,"acceleration")) - plr->acceleration = (UINT8)luaL_checkinteger(L, 3); - else if (fastcmp(field,"charability")) - plr->charability = (UINT8)luaL_checkinteger(L, 3); - else if (fastcmp(field,"charability2")) - plr->charability2 = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"charflags")) plr->charflags = (UINT32)luaL_checkinteger(L, 3); - else if (fastcmp(field,"thokitem")) - plr->thokitem = luaL_checkinteger(L, 3); - else if (fastcmp(field,"spinitem")) - plr->spinitem = luaL_checkinteger(L, 3); - else if (fastcmp(field,"revitem")) - plr->revitem = luaL_checkinteger(L, 3); - else if (fastcmp(field,"actionspd")) - plr->actionspd = (INT32)luaL_checkinteger(L, 3); - else if (fastcmp(field,"mindash")) - plr->mindash = (INT32)luaL_checkinteger(L, 3); - else if (fastcmp(field,"maxdash")) - plr->maxdash = (INT32)luaL_checkinteger(L, 3); - else if (fastcmp(field,"jumpfactor")) - plr->jumpfactor = (INT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"lives")) plr->lives = (SINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"continues")) @@ -613,6 +559,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); @@ -739,6 +687,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_skinlib.c b/src/lua_skinlib.c index 8fdd92a52..f44e97afe 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 2fc2e85c9..e7e877ada 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -273,7 +273,7 @@ boolean cht_Responder(event_t *ev) #define REQUIRE_OBJECTPLACE if (!objectplacing)\ { CONS_Printf(M_GetText("OBJECTPLACE must be enabled.\n")); return; } -#define REQUIRE_INLEVEL if (gamestate != GS_LEVEL || demoplayback)\ +#define REQUIRE_INLEVEL if (gamestate != GS_LEVEL || demo.playback)\ { CONS_Printf(M_GetText("You must be in a level to use this.\n")); return; } #define REQUIRE_SINGLEPLAYER if (netgame || multiplayer)\ diff --git a/src/m_fixed.c b/src/m_fixed.c index d45bb70bf..5e7896739 100644 --- a/src/m_fixed.c +++ b/src/m_fixed.c @@ -56,7 +56,7 @@ fixed_t FixedDiv2(fixed_t a, fixed_t b) if (b == 0) I_Error("FixedDiv: divide by zero"); - ret = (((INT64)a * FRACUNIT) ) / b; + ret = (((INT64)a * FRACUNIT)) / b; if ((ret > INT32_MAX) || (ret < INT32_MIN)) I_Error("FixedDiv: divide by zero"); @@ -117,7 +117,7 @@ fixed_t FixedHypot(fixed_t x, fixed_t y) yx = FixedDiv(y, x); // (x/y) } yx2 = FixedMul(yx, yx); // (x/y)^2 - yx1 = FixedSqrt(1*FRACUNIT + yx2); // (1 + (x/y)^2)^1/2 + yx1 = FixedSqrt(1 * FRACUNIT + yx2); // (1 + (x/y)^2)^1/2 return FixedMul(ax, yx1); // |x|*((1 + (x/y)^2)^1/2) } @@ -191,8 +191,8 @@ vector2_t *FV2_Divide(vector2_t *a_i, fixed_t a_c) // Vector Complex Math vector2_t *FV2_Midpoint(const vector2_t *a_1, const vector2_t *a_2, vector2_t *a_o) { - a_o->x = FixedDiv(a_2->x - a_1->x, 2*FRACUNIT); - a_o->y = FixedDiv(a_2->y - a_1->y, 2*FRACUNIT); + a_o->x = FixedDiv(a_2->x - a_1->x, 2 * FRACUNIT); + a_o->y = FixedDiv(a_2->y - a_1->y, 2 * FRACUNIT); a_o->x = a_1->x + a_o->x; a_o->y = a_1->y + a_o->y; return a_o; @@ -200,16 +200,16 @@ vector2_t *FV2_Midpoint(const vector2_t *a_1, const vector2_t *a_2, vector2_t *a fixed_t FV2_Distance(const vector2_t *p1, const vector2_t *p2) { - fixed_t xs = FixedMul(p2->x-p1->x,p2->x-p1->x); - fixed_t ys = FixedMul(p2->y-p1->y,p2->y-p1->y); - return FixedSqrt(xs+ys); + fixed_t xs = FixedMul(p2->x - p1->x, p2->x - p1->x); + fixed_t ys = FixedMul(p2->y - p1->y, p2->y - p1->y); + return FixedSqrt(xs + ys); } fixed_t FV2_Magnitude(const vector2_t *a_normal) { - fixed_t xs = FixedMul(a_normal->x,a_normal->x); - fixed_t ys = FixedMul(a_normal->y,a_normal->y); - return FixedSqrt(xs+ys); + fixed_t xs = FixedMul(a_normal->x, a_normal->x); + fixed_t ys = FixedMul(a_normal->y, a_normal->y); + return FixedSqrt(xs + ys); } // Also returns the magnitude @@ -240,7 +240,7 @@ vector2_t *FV2_Negate(vector2_t *a_1) boolean FV2_Equal(const vector2_t *a_1, const vector2_t *a_2) { - fixed_t Epsilon = FRACUNIT/FRACUNIT; + fixed_t Epsilon = FRACUNIT / FRACUNIT; if ((abs(a_2->x - a_1->x) > Epsilon) || (abs(a_2->y - a_1->y) > Epsilon)) @@ -261,7 +261,7 @@ fixed_t FV2_Dot(const vector2_t *a_1, const vector2_t *a_2) // // Given two points, create a vector between them. // -vector2_t *FV2_Point2Vec (const vector2_t *point1, const vector2_t *point2, vector2_t *a_o) +vector2_t *FV2_Point2Vec(const vector2_t *point1, const vector2_t *point2, vector2_t *a_o) { a_o->x = point1->x - point2->x; a_o->y = point1->y - point2->y; @@ -344,9 +344,9 @@ vector3_t *FV3_Divide(vector3_t *a_i, fixed_t a_c) // Vector Complex Math vector3_t *FV3_Midpoint(const vector3_t *a_1, const vector3_t *a_2, vector3_t *a_o) { - a_o->x = FixedDiv(a_2->x - a_1->x, 2*FRACUNIT); - a_o->y = FixedDiv(a_2->y - a_1->y, 2*FRACUNIT); - a_o->z = FixedDiv(a_2->z - a_1->z, 2*FRACUNIT); + a_o->x = FixedDiv(a_2->x - a_1->x, 2 * FRACUNIT); + a_o->y = FixedDiv(a_2->y - a_1->y, 2 * FRACUNIT); + a_o->z = FixedDiv(a_2->z - a_1->z, 2 * FRACUNIT); a_o->x = a_1->x + a_o->x; a_o->y = a_1->y + a_o->y; a_o->z = a_1->z + a_o->z; @@ -355,18 +355,18 @@ vector3_t *FV3_Midpoint(const vector3_t *a_1, const vector3_t *a_2, vector3_t *a fixed_t FV3_Distance(const vector3_t *p1, const vector3_t *p2) { - fixed_t xs = FixedMul(p2->x-p1->x,p2->x-p1->x); - fixed_t ys = FixedMul(p2->y-p1->y,p2->y-p1->y); - fixed_t zs = FixedMul(p2->z-p1->z,p2->z-p1->z); - return FixedSqrt(xs+ys+zs); + fixed_t xs = FixedMul(p2->x - p1->x, p2->x - p1->x); + fixed_t ys = FixedMul(p2->y - p1->y, p2->y - p1->y); + fixed_t zs = FixedMul(p2->z - p1->z, p2->z - p1->z); + return FixedSqrt(xs + ys + zs); } fixed_t FV3_Magnitude(const vector3_t *a_normal) { - fixed_t xs = FixedMul(a_normal->x,a_normal->x); - fixed_t ys = FixedMul(a_normal->y,a_normal->y); - fixed_t zs = FixedMul(a_normal->z,a_normal->z); - return FixedSqrt(xs+ys+zs); + fixed_t xs = FixedMul(a_normal->x, a_normal->x); + fixed_t ys = FixedMul(a_normal->y, a_normal->y); + fixed_t zs = FixedMul(a_normal->z, a_normal->z); + return FixedSqrt(xs + ys + zs); } // Also returns the magnitude @@ -399,7 +399,7 @@ vector3_t *FV3_Negate(vector3_t *a_1) boolean FV3_Equal(const vector3_t *a_1, const vector3_t *a_2) { - fixed_t Epsilon = FRACUNIT/FRACUNIT; + fixed_t Epsilon = FRACUNIT / FRACUNIT; if ((abs(a_2->x - a_1->x) > Epsilon) || (abs(a_2->y - a_1->y) > Epsilon) || @@ -458,6 +458,20 @@ vector3_t *FV3_ClosestPointOnLine(const vector3_t *Line, const vector3_t *p, vec return FV3_AddEx(&Line[0], &V, out); } +// +// ClosestPointOnVector +// +// Similar to ClosestPointOnLine, but uses a vector instead of two points. +// +void FV3_ClosestPointOnVector(const vector3_t *dir, const vector3_t *p, vector3_t *out) +{ + fixed_t t = FV3_Dot(dir, p); + + // Return the point on the line closest + FV3_MulEx(dir, t, out); + return; +} + // // ClosestPointOnTriangle // @@ -465,7 +479,7 @@ vector3_t *FV3_ClosestPointOnLine(const vector3_t *Line, const vector3_t *p, vec // the closest point on the edge of // the triangle is returned. // -void FV3_ClosestPointOnTriangle (const vector3_t *tri, const vector3_t *point, vector3_t *result) +void FV3_ClosestPointOnTriangle(const vector3_t *tri, const vector3_t *point, vector3_t *result) { UINT8 i; fixed_t dist, closestdist; @@ -506,7 +520,7 @@ void FV3_ClosestPointOnTriangle (const vector3_t *tri, const vector3_t *point, v // // Given two points, create a vector between them. // -vector3_t *FV3_Point2Vec (const vector3_t *point1, const vector3_t *point2, vector3_t *a_o) +vector3_t *FV3_Point2Vec(const vector3_t *point1, const vector3_t *point2, vector3_t *a_o) { a_o->x = point1->x - point2->x; a_o->y = point1->y - point2->y; @@ -519,7 +533,7 @@ vector3_t *FV3_Point2Vec (const vector3_t *point1, const vector3_t *point2, vect // // Calculates the normal of a polygon. // -void FV3_Normal (const vector3_t *a_triangle, vector3_t *a_normal) +fixed_t FV3_Normal(const vector3_t *a_triangle, vector3_t *a_normal) { vector3_t a_1; vector3_t a_2; @@ -529,7 +543,28 @@ void FV3_Normal (const vector3_t *a_triangle, vector3_t *a_normal) FV3_Cross(&a_1, &a_2, a_normal); - FV3_NormalizeEx(a_normal, a_normal); + return FV3_NormalizeEx(a_normal, a_normal); +} + +// +// Strength +// +// Measures the 'strength' of a vector in a particular direction. +// +fixed_t FV3_Strength(const vector3_t *a_1, const vector3_t *dir) +{ + vector3_t normal; + fixed_t dist = FV3_NormalizeEx(a_1, &normal); + fixed_t dot = FV3_Dot(&normal, dir); + + FV3_ClosestPointOnVector(dir, a_1, &normal); + + dist = FV3_Magnitude(&normal); + + if (dot < 0) // Not facing same direction, so negate result. + dist = -dist; + + return dist; } // @@ -550,11 +585,11 @@ boolean FV3_IntersectedPlane(const vector3_t *a_triangle, const vector3_t *a_lin *originDistance = FV3_PlaneDistance(a_normal, &a_triangle[0]); - distance1 = (FixedMul(a_normal->x, a_line[0].x) + FixedMul(a_normal->y, a_line[0].y) - + FixedMul(a_normal->z, a_line[0].z)) + *originDistance; + distance1 = (FixedMul(a_normal->x, a_line[0].x) + FixedMul(a_normal->y, a_line[0].y) + + FixedMul(a_normal->z, a_line[0].z)) + *originDistance; - distance2 = (FixedMul(a_normal->x, a_line[1].x) + FixedMul(a_normal->y, a_line[1].y) - + FixedMul(a_normal->z, a_line[1].z)) + *originDistance; + distance2 = (FixedMul(a_normal->x, a_line[1].x) + FixedMul(a_normal->y, a_line[1].y) + + FixedMul(a_normal->z, a_line[1].z)) + *originDistance; // Positive or zero number means no intersection if (FixedMul(distance1, distance2) >= 0) @@ -575,8 +610,8 @@ boolean FV3_IntersectedPlane(const vector3_t *a_triangle, const vector3_t *a_lin fixed_t FV3_PlaneIntersection(const vector3_t *pOrigin, const vector3_t *pNormal, const vector3_t *rOrigin, const vector3_t *rVector) { fixed_t d = -(FV3_Dot(pNormal, pOrigin)); - fixed_t number = FV3_Dot(pNormal,rOrigin) + d; - fixed_t denom = FV3_Dot(pNormal,rVector); + fixed_t number = FV3_Dot(pNormal, rOrigin) + d; + fixed_t denom = FV3_Dot(pNormal, rVector); return -FixedDiv(number, denom); } @@ -597,11 +632,11 @@ fixed_t FV3_IntersectRaySphere(const vector3_t *rO, const vector3_t *rV, const v c = FV3_Magnitude(&Q); v = FV3_Dot(&Q, rV); - d = FixedMul(sR, sR) - (FixedMul(c,c) - FixedMul(v,v)); + d = FixedMul(sR, sR) - (FixedMul(c, c) - FixedMul(v, v)); // If there was no intersection, return -1 - if (d < 0*FRACUNIT) - return (-1*FRACUNIT); + if (d < 0 * FRACUNIT) + return (-1 * FRACUNIT); // Return the distance to the [first] intersecting point return (v - FixedSqrt(d)); @@ -629,9 +664,9 @@ vector3_t *FV3_IntersectionPoint(const vector3_t *vNormal, const vector3_t *vLin // Here I just chose a arbitrary point as the point to find that distance. You notice we negate that // distance. We negate the distance because we want to eventually go BACKWARDS from our point to the plane. // By doing this is will basically bring us back to the plane to find our intersection point. - Numerator = - (FixedMul(vNormal->x, vLine[0].x) + // Use the plane equation with the normal and the line - FixedMul(vNormal->y, vLine[0].y) + - FixedMul(vNormal->z, vLine[0].z) + distance); + Numerator = -(FixedMul(vNormal->x, vLine[0].x) + // Use the plane equation with the normal and the line + FixedMul(vNormal->y, vLine[0].y) + + FixedMul(vNormal->z, vLine[0].z) + distance); // 3) If we take the dot product between our line vector and the normal of the polygon, // this will give us the cosine of the angle between the 2 (since they are both normalized - length 1). @@ -643,7 +678,7 @@ vector3_t *FV3_IntersectionPoint(const vector3_t *vNormal, const vector3_t *vLin // on the plane (the normal is perpendicular to the line - (Normal.Vector = 0)). // In this case, we should just return any point on the line. - if( Denominator == 0*FRACUNIT) // Check so we don't divide by zero + if (Denominator == 0 * FRACUNIT) // Check so we don't divide by zero { ReturnVec->x = vLine[0].x; ReturnVec->y = vLine[0].y; @@ -686,8 +721,8 @@ vector3_t *FV3_IntersectionPoint(const vector3_t *vNormal, const vector3_t *vLin // UINT8 FV3_PointOnLineSide(const vector3_t *point, const vector3_t *line) { - fixed_t s1 = FixedMul((point->y - line[0].y),(line[1].x - line[0].x)); - fixed_t s2 = FixedMul((point->x - line[0].x),(line[1].y - line[0].y)); + fixed_t s1 = FixedMul((point->y - line[0].y), (line[1].x - line[0].x)); + fixed_t s2 = FixedMul((point->x - line[0].x), (line[1].y - line[0].y)); return (UINT8)(s1 - s2 < 0); } @@ -752,7 +787,7 @@ void FM_CreateObjectMatrix(matrix_t *matrix, fixed_t x, fixed_t y, fixed_t z, fi matrix->m[0] = upcross.x; matrix->m[1] = upcross.y; matrix->m[2] = upcross.z; - matrix->m[3] = 0*FRACUNIT; + matrix->m[3] = 0 * FRACUNIT; matrix->m[4] = upx; matrix->m[5] = upy; @@ -764,9 +799,9 @@ void FM_CreateObjectMatrix(matrix_t *matrix, fixed_t x, fixed_t y, fixed_t z, fi matrix->m[10] = anglez; matrix->m[11] = 0; - matrix->m[12] = x - FixedMul(upx,radius); - matrix->m[13] = y - FixedMul(upy,radius); - matrix->m[14] = z - FixedMul(upz,radius); + matrix->m[12] = x - FixedMul(upx, radius); + matrix->m[13] = y - FixedMul(upy, radius); + matrix->m[14] = z - FixedMul(upz, radius); matrix->m[15] = FRACUNIT; } @@ -778,20 +813,20 @@ void FM_CreateObjectMatrix(matrix_t *matrix, fixed_t x, fixed_t y, fixed_t z, fi void FM_MultMatrixVec3(const matrix_t *matrix, const vector3_t *vec, vector3_t *out) { #define M(row,col) matrix->m[col * 4 + row] - out->x = FixedMul(vec->x,M(0, 0)) - + FixedMul(vec->y,M(0, 1)) - + FixedMul(vec->z,M(0, 2)) - + M(0, 3); + out->x = FixedMul(vec->x, M(0, 0)) + + FixedMul(vec->y, M(0, 1)) + + FixedMul(vec->z, M(0, 2)) + + M(0, 3); - out->y = FixedMul(vec->x,M(1, 0)) - + FixedMul(vec->y,M(1, 1)) - + FixedMul(vec->z,M(1, 2)) - + M(1, 3); + out->y = FixedMul(vec->x, M(1, 0)) + + FixedMul(vec->y, M(1, 1)) + + FixedMul(vec->z, M(1, 2)) + + M(1, 3); - out->z = FixedMul(vec->x,M(2, 0)) - + FixedMul(vec->y,M(2, 1)) - + FixedMul(vec->z,M(2, 2)) - + M(2, 3); + out->z = FixedMul(vec->x, M(2, 0)) + + FixedMul(vec->y, M(2, 1)) + + FixedMul(vec->z, M(2, 2)) + + M(2, 3); #undef M } @@ -811,7 +846,7 @@ void FM_MultMatrix(matrix_t *dest, const matrix_t *multme) for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) - R(i, j) = FixedMul(D(i, 0),M(0, j)) + FixedMul(D(i, 1),M(1, j)) + FixedMul(D(i, 2),M(2, j)) + FixedMul(D(i, 3),M(3, j)); + R(i, j) = FixedMul(D(i, 0), M(0, j)) + FixedMul(D(i, 1), M(1, j)) + FixedMul(D(i, 2), M(2, j)) + FixedMul(D(i, 3), M(3, j)); } M_Memcpy(dest, &result, sizeof(matrix_t)); @@ -869,8 +904,8 @@ void FM_Scale(matrix_t *dest, fixed_t x, fixed_t y, fixed_t z) static inline void M_print(INT64 a) { - const fixed_t w = (a>>FRACBITS); - fixed_t f = a%FRACUNIT; + const fixed_t w = (a >> FRACBITS); + fixed_t f = a % FRACUNIT; fixed_t d = FRACUNIT; if (f == 0) @@ -878,7 +913,7 @@ static inline void M_print(INT64 a) printf("%d", (fixed_t)w); return; } - else while (f != 1 && f/2 == f>>1) + else while (f != 1 && f / 2 == f >> 1) { d /= 2; f /= 2; @@ -892,7 +927,7 @@ static inline void M_print(INT64 a) FUNCMATH FUNCINLINE static inline fixed_t FixedMulC(fixed_t a, fixed_t b) { - return (fixed_t)((((INT64)a * b) ) / FRACUNIT); + return (fixed_t)((((INT64)a * b)) / FRACUNIT); } FUNCMATH FUNCINLINE static inline fixed_t FixedDivC2(fixed_t a, fixed_t b) @@ -902,7 +937,7 @@ FUNCMATH FUNCINLINE static inline fixed_t FixedDivC2(fixed_t a, fixed_t b) if (b == 0) I_Error("FixedDiv: divide by zero"); - ret = (((INT64)a * FRACUNIT) ) / b; + ret = (((INT64)a * FRACUNIT)) / b; if ((ret > INT32_MAX) || (ret < INT32_MIN)) I_Error("FixedDiv: divide by zero"); @@ -911,7 +946,7 @@ FUNCMATH FUNCINLINE static inline fixed_t FixedDivC2(fixed_t a, fixed_t b) FUNCMATH FUNCINLINE static inline fixed_t FixedDivC(fixed_t a, fixed_t b) { - if ((abs(a) >> (FRACBITS-2)) >= abs(b)) + if ((abs(a) >> (FRACBITS - 2)) >= abs(b)) return (a^b) < 0 ? INT32_MIN : INT32_MAX; return FixedDivC2(a, b); @@ -938,43 +973,43 @@ int main(int argc, char** argv) #ifdef MULDIV_TEST for (a = 1; a <= INT32_MAX; a += FRACUNIT) - for (b = 0; b <= INT32_MAX; b += FRACUNIT) - { - c = FixedMul(a, b); - d = FixedMulC(a, b); - if (c != d) + for (b = 0; b <= INT32_MAX; b += FRACUNIT) { - printf("("); - M_print(a); - printf(") * ("); - M_print(b); - printf(") = ("); - M_print(c); - printf(") != ("); - M_print(d); - printf(") \n"); - n--; - printf("%d != %d\n", c, d); + c = FixedMul(a, b); + d = FixedMulC(a, b); + if (c != d) + { + printf("("); + M_print(a); + printf(") * ("); + M_print(b); + printf(") = ("); + M_print(c); + printf(") != ("); + M_print(d); + printf(") \n"); + n--; + printf("%d != %d\n", c, d); + } + c = FixedDiv(a, b); + d = FixedDivC(a, b); + if (c != d) + { + printf("("); + M_print(a); + printf(") / ("); + M_print(b); + printf(") = ("); + M_print(c); + printf(") != ("); + M_print(d); + printf(")\n"); + n--; + printf("%d != %d\n", c, d); + } + if (n <= 0) + exit(-1); } - c = FixedDiv(a, b); - d = FixedDivC(a, b); - if (c != d) - { - printf("("); - M_print(a); - printf(") / ("); - M_print(b); - printf(") = ("); - M_print(c); - printf(") != ("); - M_print(d); - printf(")\n"); - n--; - printf("%d != %d\n", c, d); - } - if (n <= 0) - exit(-1); - } #endif #ifdef SQRT_TEST @@ -982,7 +1017,7 @@ int main(int argc, char** argv) { c = FixedSqrt(a); d = FixedSqrtC(a); - b = abs(c-d); + b = abs(c - d); if (b > 1) { printf("sqrt("); diff --git a/src/m_fixed.h b/src/m_fixed.h index 4609913b7..8145a6917 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -394,9 +394,11 @@ boolean FV3_Equal(const vector3_t *a_1, const vector3_t *a_2); fixed_t FV3_Dot(const vector3_t *a_1, const vector3_t *a_2); vector3_t *FV3_Cross(const vector3_t *a_1, const vector3_t *a_2, vector3_t *a_o); vector3_t *FV3_ClosestPointOnLine(const vector3_t *Line, const vector3_t *p, vector3_t *out); +void FV3_ClosestPointOnVector(const vector3_t *dir, const vector3_t *p, vector3_t *out); void FV3_ClosestPointOnTriangle(const vector3_t *tri, const vector3_t *point, vector3_t *result); vector3_t *FV3_Point2Vec(const vector3_t *point1, const vector3_t *point2, vector3_t *a_o); -void FV3_Normal(const vector3_t *a_triangle, vector3_t *a_normal); +fixed_t FV3_Normal(const vector3_t *a_triangle, vector3_t *a_normal); +fixed_t FV3_Strength(const vector3_t *a_1, const vector3_t *dir); fixed_t FV3_PlaneDistance(const vector3_t *a_normal, const vector3_t *a_point); boolean FV3_IntersectedPlane(const vector3_t *a_triangle, const vector3_t *a_line, vector3_t *a_normal, fixed_t *originDistance); fixed_t FV3_PlaneIntersection(const vector3_t *pOrigin, const vector3_t *pNormal, const vector3_t *rOrigin, const vector3_t *rVector); diff --git a/src/m_menu.c b/src/m_menu.c index 615b8c893..0831a877b 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -121,41 +121,8 @@ typedef enum const char *quitmsg[NUM_QUITMESSAGES]; // Stuff for customizing the player select screen Tails 09-22-2003 -description_t description[32] = -{ - {"\x82Sonic\x80\n\x82Speed:\x80 7\n\x82Weight:\x80 3", "", "sonic"}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""}, - {"???", "", ""} -}; +description_t description[MAXSKINS]; + //static char *char_notes = NULL; //static fixed_t char_scroll = 0; @@ -233,7 +200,9 @@ static char *M_GetConditionString(condition_t cond); menu_t SR_MainDef, SR_UnlockChecklistDef; // Misc. Main Menu +#if 0 // Bring this back when we have actual single-player static void M_SinglePlayerMenu(INT32 choice); +#endif static void M_Options(INT32 choice); static void M_Manual(INT32 choice); static void M_SelectableClearMenus(INT32 choice); @@ -317,7 +286,7 @@ menu_t OP_SoundOptionsDef; //static void M_RestartAudio(void); //Misc -menu_t /*OP_DataOptionsDef,*/ OP_ScreenshotOptionsDef, OP_EraseDataDef; +menu_t OP_DataOptionsDef, OP_ScreenshotOptionsDef, OP_EraseDataDef; menu_t OP_HUDOptionsDef, OP_ChatOptionsDef; menu_t OP_GameOptionsDef, OP_ServerOptionsDef; #ifndef NONET @@ -334,8 +303,29 @@ static patch_t *addonsp[NUM_EXT+5]; #define numaddonsshown 4 +// Replay hut +menu_t MISC_ReplayHutDef; +menu_t MISC_ReplayOptionsDef; +static void M_HandleReplayHutList(INT32 choice); +static void M_DrawReplayHut(void); +static void M_DrawReplayStartMenu(void); +static boolean M_QuitReplayHut(void); +static void M_HutStartReplay(INT32 choice); + +static void M_DrawPlaybackMenu(void); +static void M_PlaybackRewind(INT32 choice); +static void M_PlaybackPause(INT32 choice); +static void M_PlaybackFastForward(INT32 choice); +static void M_PlaybackAdvance(INT32 choice); +static void M_PlaybackSetViews(INT32 choice); +static void M_PlaybackAdjustView(INT32 choice); +static void M_PlaybackQuit(INT32 choice); + +static UINT8 playback_enterheld = 0; // horrid hack to prevent holding the button from being extremely fucked + // Drawing functions static void M_DrawGenericMenu(void); +static void M_DrawGenericBackgroundMenu(void); static void M_DrawCenteredMenu(void); static void M_DrawAddons(void); static void M_DrawSkyRoom(void); @@ -399,6 +389,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"}, @@ -410,27 +402,9 @@ static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; consvar_t cv_chooseskin = {"chooseskin", DEFAULTSKIN, CV_HIDEN|CV_CALL, skins_cons_t, Nextmap_OnChange, 0, NULL, NULL, 0, 0, NULL}; // This gametype list is integral for many different reasons. -// When you add gametypes here, don't forget to update them in CV_AddValue! -CV_PossibleValue_t gametype_cons_t[] = -{ - {GT_RACE, "Race"}, {GT_MATCH, "Battle"}, +// When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h! +CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; - /* // SRB2kart - {GT_COOP, "Co-op"}, - - {GT_COMPETITION, "Competition"}, - {GT_RACE, "Race"}, - - {GT_MATCH, "Match"}, - {GT_TEAMMATCH, "Team Match"}, - - {GT_TAG, "Tag"}, - {GT_HIDEANDSEEK, "Hide and Seek"}, - - {GT_CTF, "CTF"}, - */ - {0, NULL} -}; consvar_t cv_newgametype = {"newgametype", "Race", CV_HIDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t serversort_cons_t[] = { @@ -498,12 +472,13 @@ static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDEN|CV_CALL, dummystaf // --------- static menuitem_t MainMenu[] = { - {IT_SUBMENU|IT_STRING, NULL, "Extras", &SR_UnlockChecklistDef, 76}, - {IT_CALL |IT_STRING, NULL, "1 Player", M_SinglePlayerMenu, 84}, - {IT_SUBMENU|IT_STRING, NULL, "Multiplayer", &MP_MainDef, 92}, - {IT_CALL |IT_STRING, NULL, "Options", M_Options, 100}, - {IT_CALL |IT_STRING, NULL, "Addons", M_Addons, 108}, - {IT_CALL |IT_STRING, NULL, "Quit Game", M_QuitSRB2, 116}, + {IT_SUBMENU|IT_STRING, NULL, "Extras", &SR_MainDef, 76}, + //{IT_CALL |IT_STRING, NULL, "1 Player", M_SinglePlayerMenu, 84}, + {IT_CALL |IT_STRING, NULL, "Time Attack", M_TimeAttack, 84}, + {IT_SUBMENU|IT_STRING, NULL, "Multiplayer", &MP_MainDef, 92}, + {IT_CALL |IT_STRING, NULL, "Options", M_Options, 100}, + {IT_CALL |IT_STRING, NULL, "Addons", M_Addons, 108}, + {IT_CALL |IT_STRING, NULL, "Quit Game", M_QuitSRB2, 116}, }; typedef enum @@ -521,6 +496,65 @@ static menuitem_t MISC_AddonsMenu[] = {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleAddons, 0}, // dummy menuitem for the control func }; +static menuitem_t MISC_ReplayHutMenu[] = +{ + {IT_KEYHANDLER|IT_NOTHING, NULL, "", M_HandleReplayHutList, 0}, // Dummy menuitem for the replay list + {IT_NOTHING, NULL, "", NULL, 0}, // Dummy for handling wrapping to the top of the menu.. +}; + +static menuitem_t MISC_ReplayStartMenu[] = +{ + {IT_CALL |IT_STRING, NULL, "Load Addons and Watch", M_HutStartReplay, 0}, + {IT_CALL |IT_STRING, NULL, "Watch Without Addons", M_HutStartReplay, 10}, + {IT_CALL |IT_STRING, NULL, "Watch Replay", M_HutStartReplay, 10}, + {IT_SUBMENU |IT_STRING, NULL, "Back", &MISC_ReplayHutDef, 30}, +}; + +static menuitem_t MISC_ReplayOptionsMenu[] = +{ + {IT_CVAR|IT_STRING, NULL, "Record Replays", &cv_recordmultiplayerdemos, 0}, + {IT_CVAR|IT_STRING, NULL, "Sync Check Interval", &cv_netdemosyncquality, 10}, +}; + +static menuitem_t PlaybackMenu[] = +{ + {IT_CALL | IT_STRING, "M_PHIDE", "Hide Menu", M_SelectableClearMenus, 0}, + + {IT_CALL | IT_STRING, "M_PREW", "Rewind", M_PlaybackRewind, 20}, + {IT_CALL | IT_STRING, "M_PPAUSE", "Pause", M_PlaybackPause, 36}, + {IT_CALL | IT_STRING, "M_PFFWD", "Fast-Foward", M_PlaybackFastForward, 52}, + {IT_CALL | IT_STRING, "M_PSTEPB", "Backup Frame", M_PlaybackRewind, 20}, + {IT_CALL | IT_STRING, "M_PRESUM", "Resume", M_PlaybackPause, 36}, + {IT_CALL | IT_STRING, "M_PFADV", "Advance Frame", M_PlaybackAdvance, 52}, + + {IT_ARROWS | IT_STRING, "M_PVIEWS", "View Count", M_PlaybackSetViews, 72}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint", M_PlaybackAdjustView, 88}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 2", M_PlaybackAdjustView, 104}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 3", M_PlaybackAdjustView, 120}, + {IT_ARROWS | IT_STRING, "M_PNVIEW", "Viewpoint 4", M_PlaybackAdjustView, 136}, + + //{IT_CALL | IT_STRING, "M_POPTS", "More Options...", M_ReplayHut, 156}, + //{IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 172}, + {IT_CALL | IT_STRING, "M_PEXIT", "Stop Playback", M_PlaybackQuit, 156}, +}; +typedef enum +{ + playback_hide, + playback_rewind, + playback_pause, + playback_fastforward, + playback_backframe, + playback_resume, + playback_advanceframe, + playback_viewcount, + playback_view1, + playback_view2, + playback_view3, + playback_view4, + //playback_moreoptions, + playback_quit +} playback_e; + // --------------------------------- // Pause Menu Mode Attacking Edition // --------------------------------- @@ -693,7 +727,9 @@ static menuitem_t SR_PandorasBox[] = // Sky Room Custom Unlocks static menuitem_t SR_MainMenu[] = { - {IT_STRING|IT_SUBMENU,NULL, "Secrets Checklist", &SR_UnlockChecklistDef, 0}, + {IT_STRING|IT_SUBMENU, NULL, "Unlockables", &SR_UnlockChecklistDef, 100}, + {IT_CALL|IT_STRING|IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 108}, + {IT_CALL|IT_STRING, NULL, "Replay Hut", M_ReplayHut, 116}, {IT_DISABLED, NULL, "", NULL, 0}, // Custom1 {IT_DISABLED, NULL, "", NULL, 0}, // Custom2 {IT_DISABLED, NULL, "", NULL, 0}, // Custom3 @@ -948,14 +984,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 @@ -1041,15 +1078,13 @@ static menuitem_t OP_MainMenu[] = {IT_SUBMENU|IT_STRING, NULL, "Sound Options...", &OP_SoundOptionsDef, 40}, {IT_SUBMENU|IT_STRING, NULL, "HUD Options...", &OP_HUDOptionsDef, 60}, - {IT_STRING|IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 70}, + {IT_SUBMENU|IT_STRING, NULL, "Gameplay Options...", &OP_GameOptionsDef, 70}, + {IT_SUBMENU|IT_STRING, NULL, "Server Options...", &OP_ServerOptionsDef, 80}, - {IT_SUBMENU|IT_STRING, NULL, "Gameplay Options...", &OP_GameOptionsDef, 90}, - {IT_SUBMENU|IT_STRING, NULL, "Server Options...", &OP_ServerOptionsDef, 100}, - {IT_STRING|IT_CALL, NULL, "Add-on Options...", M_AddonsOptions, 110}, + {IT_SUBMENU|IT_STRING, NULL, "Data Options...", &OP_DataOptionsDef, 100}, - {IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", M_Manual, 130}, - {IT_CALL|IT_STRING, NULL, "Play Credits", M_Credits, 140}, - {IT_SUBMENU|IT_STRING, NULL, "Erase Data...", &OP_EraseDataDef, 150}, + {IT_CALL|IT_STRING, NULL, "Tricks & Secrets (F1)", M_Manual, 120}, + {IT_CALL|IT_STRING, NULL, "Play Credits", M_Credits, 130}, }; static menuitem_t OP_ControlsMenu[] = @@ -1214,8 +1249,9 @@ static menuitem_t OP_VideoOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Vertical Sync", &cv_vidwait, 90}, #ifdef HWRENDER - {IT_STRING | IT_CVAR, NULL, "3D models", &cv_grmd2, 105}, - {IT_SUBMENU|IT_STRING, NULL, "OpenGL Options...", &OP_OpenGLOptionsDef, 115}, + {IT_STRING | IT_CVAR, NULL, "3D models", &cv_grmdls, 105}, + {IT_STRING | IT_CVAR, NULL, "Fallback Player 3D Model", &cv_grfallbackplayermodel, 115}, + {IT_SUBMENU|IT_STRING, NULL, "OpenGL Options...", &OP_OpenGLOptionsDef, 125}, #endif }; @@ -1249,7 +1285,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}, @@ -1316,14 +1352,19 @@ 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[] = +static menuitem_t OP_DataOptionsMenu[] = { - {IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 10}, + {IT_STRING | IT_CALL, NULL, "Screenshot Options...", M_ScreenshotOptions, 10}, + {IT_STRING | IT_CALL, NULL, "Add-on Options...", M_AddonsOptions, 20}, + {IT_STRING | IT_SUBMENU, NULL, "Replay Options...", &MISC_ReplayOptionsDef, 30}, - {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 30}, -};*/ + {IT_STRING | IT_SUBMENU, NULL, "Erase Data...", &OP_EraseDataDef, 50}, +}; static menuitem_t OP_ScreenshotOptionsMenu[] = { @@ -1390,7 +1431,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, @@ -1402,8 +1443,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? @@ -1417,6 +1461,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[] = @@ -1469,15 +1515,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 @@ -1558,7 +1605,7 @@ menu_t MISC_AddonsDef = { NULL, sizeof (MISC_AddonsMenu)/sizeof (menuitem_t), - &MainDef, + &OP_DataOptionsDef, MISC_AddonsMenu, M_DrawAddons, 50, 28, @@ -1566,6 +1613,54 @@ menu_t MISC_AddonsDef = NULL }; +menu_t MISC_ReplayHutDef = +{ + NULL, + sizeof (MISC_ReplayHutMenu)/sizeof (menuitem_t), + NULL, + MISC_ReplayHutMenu, + M_DrawReplayHut, + 30, 80, + 0, + M_QuitReplayHut +}; + +menu_t MISC_ReplayOptionsDef = +{ + "M_REPOPT", + sizeof (MISC_ReplayOptionsMenu)/sizeof (menuitem_t), + &OP_DataOptionsDef, + MISC_ReplayOptionsMenu, + M_DrawGenericMenu, + 27, 40, + 0, + NULL +}; + +menu_t MISC_ReplayStartDef = +{ + NULL, + sizeof (MISC_ReplayStartMenu)/sizeof (menuitem_t), + &MISC_ReplayHutDef, + MISC_ReplayStartMenu, + M_DrawReplayStartMenu, + 30, 90, + 0, + NULL +}; + +menu_t PlaybackMenuDef = { + NULL, + sizeof (PlaybackMenu)/sizeof (menuitem_t), + NULL, + PlaybackMenu, + M_DrawPlaybackMenu, + //BASEVIDWIDTH/2 - 94, 2, + BASEVIDWIDTH/2 - 88, 2, + 0, + NULL +}; + menu_t MAPauseDef = PAUSEMENUSTYLE(MAPauseMenu, 40, 72); menu_t SPauseDef = PAUSEMENUSTYLE(SPauseMenu, 40, 72); menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72); @@ -1663,17 +1758,7 @@ menu_t SR_PandoraDef = 0, M_ExitPandorasBox }; -menu_t SR_MainDef = -{ - "M_SECRET", - sizeof (SR_MainMenu)/sizeof (menuitem_t), - &MainDef, - SR_MainMenu, - M_DrawSkyRoom, - 60, 40, - 0, - NULL -}; +menu_t SR_MainDef = CENTERMENUSTYLE(NULL, SR_MainMenu, &MainDef, 72); //menu_t SR_LevelSelectDef = MAPICONMENUSTYLE(NULL, SR_LevelSelectMenu, &SR_MainDef); @@ -1681,7 +1766,7 @@ menu_t SR_UnlockChecklistDef = { NULL, 1, - &MainDef, //&SR_MainDef + &SR_MainDef, SR_UnlockChecklistMenu, M_DrawChecklist, 280, 185, @@ -1719,7 +1804,7 @@ menu_t SP_LevelStatsDef = { "M_STATS", 1, - &SP_MainDef, + &SR_MainDef, SP_LevelStatsMenu, M_DrawLevelStats, 280, 185, @@ -2017,10 +2102,10 @@ menu_t OP_OpenGLColorDef = NULL }; #endif -//menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE("M_DATA", OP_DataOptionsMenu, &OP_MainDef, 60, 30); -menu_t OP_ScreenshotOptionsDef = DEFAULTMENUSTYLE("M_SCSHOT", OP_ScreenshotOptionsMenu, &OP_MainDef, 30, 30); -menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE("M_ADDONS", OP_AddonsOptionsMenu, &OP_MainDef, 30, 30); -menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_MainDef, 30, 30); +menu_t OP_DataOptionsDef = DEFAULTMENUSTYLE("M_DATA", OP_DataOptionsMenu, &OP_MainDef, 60, 30); +menu_t OP_ScreenshotOptionsDef = DEFAULTMENUSTYLE("M_SCSHOT", OP_ScreenshotOptionsMenu, &OP_DataOptionsDef, 30, 30); +menu_t OP_AddonsOptionsDef = DEFAULTMENUSTYLE("M_ADDONS", OP_AddonsOptionsMenu, &OP_DataOptionsDef, 30, 30); +menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_DataOptionsDef, 30, 30); // ========================================================================== // CVAR ONCHANGE EVENTS GO HERE @@ -2319,10 +2404,8 @@ static void M_ChangeCvar(INT32 choice) choice *= (TICRATE/7); else if (cv == &cv_maxsend) choice *= 512; -#ifdef NEWPING else if (cv == &cv_maxping) choice *= 50; -#endif #endif CV_AddValue(cv,choice); } @@ -2379,6 +2462,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) @@ -2392,6 +2478,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) @@ -2427,7 +2516,7 @@ boolean M_Responder(event_t *ev) static INT32 lastx = 0, lasty = 0; void (*routine)(INT32 choice); // for some casting problem - if (dedicated || (demoplayback && titledemo) + if (dedicated || (demo.playback && demo.title) || gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_GAMEEND || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) return false; @@ -2475,7 +2564,7 @@ boolean M_Responder(event_t *ev) { if (ev->type == ev_joystick && ev->data1 == 0 && joywait < I_GetTime()) { - const INT32 jdeadzone = JOYAXISRANGE/4; + const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_deadzone.value) >> FRACBITS; if (ev->data3 != INT32_MAX) { if (Joystick.bGamepadStyle || abs(ev->data3) > jdeadzone) @@ -2547,8 +2636,6 @@ boolean M_Responder(event_t *ev) } } } - else if (ev->type == ev_keydown) // Preserve event for other responders - ch = ev->data1; if (ch == -1) return false; @@ -2678,8 +2765,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)) @@ -2691,6 +2781,19 @@ boolean M_Responder(event_t *ev) routine = M_ChangeCvar; } + if (currentMenu == &PlaybackMenuDef) + { + // Flip left/right with up/down for the playback menu, since it's a horizontal icon row. + switch (ch) + { + case KEY_LEFTARROW: ch = KEY_UPARROW; break; + case KEY_UPARROW: ch = KEY_RIGHTARROW; break; + case KEY_RIGHTARROW: ch = KEY_DOWNARROW; break; + case KEY_DOWNARROW: ch = KEY_LEFTARROW; break; + default: break; + } + } + // Keys usable within menu switch (ch) { @@ -2737,6 +2840,15 @@ boolean M_Responder(event_t *ev) case KEY_ENTER: noFurtherInput = true; currentMenu->lastOn = itemOn; + + if (currentMenu == &PlaybackMenuDef) + { + boolean held = (boolean)playback_enterheld; + playback_enterheld = TICRATE/7; + if (held) + return true; + } + if (routine) { if (((currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_CALL @@ -2849,7 +2961,7 @@ void M_Drawer(void) if (menuactive) { // now that's more readable with a faded background (yeah like Quake...) - if (!WipeInAction) + if (!WipeInAction && currentMenu != &PlaybackMenuDef) // Replay playback has its own background V_DrawFadeScreen(0xFF00, 16); if (currentMenu->drawroutine) @@ -2880,7 +2992,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)) @@ -2895,14 +3007,6 @@ void M_Drawer(void) // void M_StartControlPanel(void) { - // time attack HACK - if (modeattacking && demoplayback) - { - G_CheckDemoStatus(); - S_ChangeMusicInternal("racent", true); - return; - } - // intro might call this repeatedly if (menuactive) { @@ -2912,7 +3016,11 @@ void M_StartControlPanel(void) menuactive = true; - if (!Playing()) + if (demo.playback) + { + currentMenu = &PlaybackMenuDef; + } + else if (!Playing()) { // Secret menu! //MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); @@ -3144,6 +3252,14 @@ void M_Ticker(void) if (--skullAnimCounter <= 0) skullAnimCounter = 8; + if (currentMenu == &PlaybackMenuDef) + { + if (playback_enterheld > 0) + playback_enterheld--; + } + else + playback_enterheld = 0; + //added : 30-01-98 : test mode for five seconds if (vidm_testingmode > 0) { @@ -3229,6 +3345,55 @@ void M_Init(void) CV_RegisterVar(&cv_allcaps); } +void M_InitCharacterTables(void) +{ + UINT8 i; + + // Setup PlayerMenu table + for (i = 0; i < MAXSKINS; i++) + { + PlayerMenu[i].status = (i < 4 ? IT_CALL : IT_DISABLED); + PlayerMenu[i].patch = PlayerMenu[i].text = NULL; + PlayerMenu[i].itemaction = M_ChoosePlayer; + PlayerMenu[i].alphaKey = 0; + } + + // Setup description table + for (i = 0; i < MAXSKINS; i++) + { + if (i == 0) + { + strcpy(description[i].notes, "\x82Sonic\x80 is the fastest of the three, but also the hardest to control. Beginners beware, but experts will find Sonic very powerful.\n\n\x82""Ability:\x80 Speed Thok\nDouble jump to zoom forward with a huge burst of speed.\n\n\x82Tip:\x80 Simply letting go of forward does not slow down in SRB2. To slow down, hold the opposite direction."); + strcpy(description[i].picname, ""); + strcpy(description[i].skinname, "sonic"); + } + else if (i == 1) + { + strcpy(description[i].notes, "\x82Tails\x80 is the most mobile of the three, but has the slowest speed. Because of his mobility, he's well-\nsuited to beginners.\n\n\x82""Ability:\x80 Fly\nDouble jump to start flying for a limited time. Repetitively hit the jump button to ascend.\n\n\x82Tip:\x80 To quickly descend while flying, hit the spin button."); + strcpy(description[i].picname, ""); + strcpy(description[i].skinname, "tails"); + } + else if (i == 2) + { + strcpy(description[i].notes, "\x82Knuckles\x80 is well-\nrounded and can destroy breakable walls simply by touching them, but he can't jump as high as the other two.\n\n\x82""Ability:\x80 Glide & Climb\nDouble jump to glide in the air as long as jump is held. Glide into a wall to climb it.\n\n\x82Tip:\x80 Press spin while climbing to jump off the wall; press jump instead to jump off\nand face away from\nthe wall."); + strcpy(description[i].picname, ""); + strcpy(description[i].skinname, "knuckles"); + } + else if (i == 3) + { + strcpy(description[i].notes, "\x82Sonic & Tails\x80 team up to take on Dr. Eggman!\nControl Sonic while Tails desperately struggles to keep up.\n\nPlayer 2 can control Tails directly by setting the controls in the options menu.\nTails's directional controls are relative to Player 1's camera.\n\nTails can pick up Sonic while flying and carry him around."); + strcpy(description[i].picname, "CHRS&T"); + strcpy(description[i].skinname, "sonic&tails"); + } + else + { + strcpy(description[i].notes, "???"); + strcpy(description[i].picname, ""); + strcpy(description[i].skinname, ""); + } + } +} + // ========================================================================== // SPECIAL MENU OPTION DRAW ROUTINES GO HERE // ========================================================================== @@ -3347,7 +3512,7 @@ static void M_DrawSlider(INT32 x, INT32 y, const consvar_t *cv, boolean ontop) void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines) { // Solid color textbox. - V_DrawFill(x+5, y+5, width*8+6, boxlines*8+6, 239); + V_DrawFill(x+5, y+5, width*8+6, boxlines*8+6, 159); //V_DrawFill(x+8, y+8, width*8, boxlines*8, 31); /* patch_t *p; @@ -3453,7 +3618,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)); @@ -3553,6 +3718,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: @@ -3560,6 +3727,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); @@ -3637,6 +3825,12 @@ static void M_DrawGenericMenu(void) } } +static void M_DrawGenericBackgroundMenu(void) +{ + V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); + M_DrawGenericMenu(); +} + static void M_DrawPauseMenu(void) { #if 0 @@ -3767,7 +3961,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)); @@ -4170,7 +4364,7 @@ void M_StartMessage(const char *string, void *routine, M_StartControlPanel(); // can't put menuactive to true if (currentMenu == &MessageDef) // Prevent recursion - MessageDef.prevMenu = &MainDef; + MessageDef.prevMenu = ((demo.playback) ? &PlaybackMenuDef : &MainDef); else MessageDef.prevMenu = currentMenu; @@ -4421,7 +4615,7 @@ static void M_Addons(INT32 choice) else --menupathindex[menudepthleft]; - if (!preparefilemenu(false)) + if (!preparefilemenu(false, false)) { M_StartMessage(va("No files/folders found.\n\n%s\n\n(Press a key)\n", (recommendedflags == V_SKYMAP ? LOCATIONSTRING2 : LOCATIONSTRING1)),NULL,MM_NOTHING); return; @@ -4477,10 +4671,10 @@ static void M_DrawTemperature(INT32 x, fixed_t t) t = (FixedMul(h<>FRACBITS); // border - V_DrawFill(x - 1, vpadding, 1, h, 120); - V_DrawFill(x + width, vpadding, 1, h, 120); - V_DrawFill(x - 1, vpadding-1, width+2, 1, 120); - V_DrawFill(x - 1, vpadding+h, width+2, 1, 120); + V_DrawFill(x - 1, vpadding, 1, h, 0); + V_DrawFill(x + width, vpadding, 1, h, 0); + V_DrawFill(x - 1, vpadding-1, width+2, 1, 0); + V_DrawFill(x - 1, vpadding+h, width+2, 1, 0); // bar itself y = h; @@ -4545,7 +4739,7 @@ static void M_AddonsClearName(INT32 choice) // returns whether to do message draw static boolean M_AddonsRefresh(void) { - if ((refreshdirmenu & REFRESHDIR_NORMAL) && !preparefilemenu(true)) + if ((refreshdirmenu & REFRESHDIR_NORMAL) && !preparefilemenu(true, false)) { UNEXIST; if (refreshdirname) @@ -4620,10 +4814,7 @@ static void M_DrawAddons(void) y = FRACUNIT; else { - x = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))< y) - y = x; + y = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))< FRACUNIT) // happens because of how we're shrinkin' it a little y = FRACUNIT; } @@ -4634,14 +4825,14 @@ static void M_DrawAddons(void) x = currentMenu->x; y = currentMenu->y + 1; - hilicol = V_GetStringColormap(highlightflags)[120]; + hilicol = V_GetStringColormap(highlightflags)[0]; V_DrawString(x-21, (y - 16) + (lsheadingheight - 12), highlightflags|V_ALLOWLOWERCASE, M_AddonsHeaderPath()); V_DrawFill(x-21, (y - 16) + (lsheadingheight - 3), MAXSTRINGLENGTH*8+6, 1, hilicol); V_DrawFill(x-21, (y - 16) + (lsheadingheight - 2), MAXSTRINGLENGTH*8+6, 1, 30); m = (BASEVIDHEIGHT - currentMenu->y + 2) - (y - 1); - V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, m, 239); + V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, m, 159); // scrollbar! if (sizedirmenu <= (2*numaddonsshown + 1)) @@ -4794,7 +4985,7 @@ static void M_HandleAddons(INT32 choice) if (dirmenu && dirmenu[dir_on[menudepthleft]]) tempname = Z_StrDup(dirmenu[dir_on[menudepthleft]]+DIR_STRING); // don't need to I_Error if can't make - not important, just QoL #if 0 // much slower - if (!preparefilemenu(true)) + if (!preparefilemenu(true, false)) { UNEXIST; return; @@ -4848,13 +5039,13 @@ static void M_HandleAddons(INT32 choice) menupathindex[--menudepthleft] = strlen(menupath); menupath[menupathindex[menudepthleft]] = 0; - if (!preparefilemenu(false)) + if (!preparefilemenu(false, false)) { S_StartSound(NULL, sfx_s224); M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING); menupath[menupathindex[++menudepthleft]] = 0; - if (!preparefilemenu(true)) + if (!preparefilemenu(true, false)) { UNEXIST; return; @@ -4877,7 +5068,7 @@ static void M_HandleAddons(INT32 choice) case EXT_UP: S_StartSound(NULL, sfx_menu1); menupath[menupathindex[++menudepthleft]] = 0; - if (!preparefilemenu(false)) + if (!preparefilemenu(false, false)) { UNEXIST; return; @@ -4934,6 +5125,830 @@ static void M_HandleAddons(INT32 choice) } } +// ---- REPLAY HUT ----- +menudemo_t *demolist; + +#define DF_ENCORE 0x40 +static INT16 replayScrollTitle = 0; +static SINT8 replayScrollDelay = TICRATE, replayScrollDir = 1; + +static void PrepReplayList(void) +{ + size_t i; + + if (demolist) + Z_Free(demolist); + + demolist = Z_Calloc(sizeof(menudemo_t) * sizedirmenu, PU_STATIC, NULL); + + for (i = 0; i < sizedirmenu; i++) + { + if (dirmenu[i][DIR_TYPE] == EXT_UP) + { + demolist[i].type = MD_SUBDIR; + sprintf(demolist[i].title, "UP"); + } + else if (dirmenu[i][DIR_TYPE] == EXT_FOLDER) + { + demolist[i].type = MD_SUBDIR; + strncpy(demolist[i].title, dirmenu[i] + DIR_STRING, 64); + } + else + { + demolist[i].type = MD_NOTLOADED; + snprintf(demolist[i].filepath, 255, "%s%s", menupath, dirmenu[i] + DIR_STRING); + sprintf(demolist[i].title, "....."); + } + } +} + +void M_ReplayHut(INT32 choice) +{ + (void)choice; + + if (!demo.inreplayhut) + { + snprintf(menupath, 1024, "%s"PATHSEP"replay"PATHSEP"online"PATHSEP, srb2home); + menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath); + } + if (!preparefilemenu(false, true)) + { + M_StartMessage("No replays found.\n\n(Press a key)\n", NULL, MM_NOTHING); + return; + } + else if (!demo.inreplayhut) + dir_on[menudepthleft] = 0; + demo.inreplayhut = true; + + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + + PrepReplayList(); + + menuactive = true; + M_SetupNextMenu(&MISC_ReplayHutDef); + G_SetGamestate(GS_TIMEATTACK); + + demo.rewinding = false; + + S_ChangeMusicInternal("replst", true); +} + +static void M_HandleReplayHutList(INT32 choice) +{ + switch (choice) + { + case KEY_UPARROW: + if (dir_on[menudepthleft]) + dir_on[menudepthleft]--; + else + M_PrevOpt(); + + S_StartSound(NULL, sfx_menu1); + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + break; + + case KEY_DOWNARROW: + if (dir_on[menudepthleft] < sizedirmenu-1) + dir_on[menudepthleft]++; + else + itemOn = 0; // Not M_NextOpt because that would take us to the extra dummy item + + S_StartSound(NULL, sfx_menu1); + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + break; + + case KEY_ESCAPE: + M_QuitReplayHut(); + break; + + case KEY_ENTER: + switch (dirmenu[dir_on[menudepthleft]][DIR_TYPE]) + { + case EXT_FOLDER: + strcpy(&menupath[menupathindex[menudepthleft]],dirmenu[dir_on[menudepthleft]]+DIR_STRING); + if (menudepthleft) + { + menupathindex[--menudepthleft] = strlen(menupath); + menupath[menupathindex[menudepthleft]] = 0; + + if (!preparefilemenu(false, true)) + { + S_StartSound(NULL, sfx_s224); + M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING); + menupath[menupathindex[++menudepthleft]] = 0; + + if (!preparefilemenu(true, true)) + { + M_QuitReplayHut(); + return; + } + } + else + { + S_StartSound(NULL, sfx_menu1); + dir_on[menudepthleft] = 1; + PrepReplayList(); + } + } + else + { + S_StartSound(NULL, sfx_s26d); + M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING); + menupath[menupathindex[menudepthleft]] = 0; + } + break; + case EXT_UP: + S_StartSound(NULL, sfx_menu1); + menupath[menupathindex[++menudepthleft]] = 0; + if (!preparefilemenu(false, true)) + { + M_QuitReplayHut(); + return; + } + PrepReplayList(); + break; + default: + // We can't just use M_SetupNextMenu because that'll run ReplayDef's quitroutine and boot us back to the title screen! + currentMenu->lastOn = itemOn; + currentMenu = &MISC_ReplayStartDef; + + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + + switch (demolist[dir_on[menudepthleft]].addonstatus) + { + case DFILE_ERROR_CANNOTLOAD: + // Only show "Watch Replay Without Addons" + MISC_ReplayStartMenu[0].status = IT_DISABLED; + MISC_ReplayStartMenu[1].status = IT_CALL|IT_STRING; + //MISC_ReplayStartMenu[1].alphaKey = 0; + MISC_ReplayStartMenu[2].status = IT_DISABLED; + itemOn = 1; + break; + + case DFILE_ERROR_NOTLOADED: + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + // Show "Load Addons and Watch Replay" and "Watch Replay Without Addons" + MISC_ReplayStartMenu[0].status = IT_CALL|IT_STRING; + MISC_ReplayStartMenu[1].status = IT_CALL|IT_STRING; + //MISC_ReplayStartMenu[1].alphaKey = 10; + MISC_ReplayStartMenu[2].status = IT_DISABLED; + itemOn = 0; + break; + + case DFILE_ERROR_EXTRAFILES: + case DFILE_ERROR_OUTOFORDER: + default: + // Show "Watch Replay" + MISC_ReplayStartMenu[0].status = IT_DISABLED; + MISC_ReplayStartMenu[1].status = IT_DISABLED; + MISC_ReplayStartMenu[2].status = IT_CALL|IT_STRING; + //MISC_ReplayStartMenu[2].alphaKey = 0; + itemOn = 2; + break; + } + } + + break; + } +} + +#define SCALEDVIEWWIDTH (vid.width/vid.dupx) +#define SCALEDVIEWHEIGHT (vid.height/vid.dupy) +static void DrawReplayHutReplayInfo(void) +{ + lumpnum_t lumpnum; + patch_t *patch; + UINT8 *colormap; + INT32 x, y, w, h; + + switch (demolist[dir_on[menudepthleft]].type) + { + case MD_NOTLOADED: + V_DrawCenteredString(160, 40, V_SNAPTOTOP, "Loading replay information..."); + break; + + case MD_INVALID: + V_DrawCenteredString(160, 40, V_SNAPTOTOP|warningflags, "This replay cannot be played."); + break; + + case MD_SUBDIR: + break; // Can't think of anything to draw here right now + + case MD_OUTDATED: + V_DrawThinString(17, 64, V_SNAPTOTOP|V_ALLOWLOWERCASE|V_TRANSLUCENT|highlightflags, "Recorded on an outdated version."); + /*fallthru*/ + default: + // Draw level stuff + x = 15; y = 15; + + // A 160x100 image of the level as entry MAPxxP + //CONS_Printf("%d %s\n", demolist[dir_on[menudepthleft]].map, G_BuildMapName(demolist[dir_on[menudepthleft]].map)); + lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(demolist[dir_on[menudepthleft]].map))); + if (lumpnum != LUMPERROR) + patch = W_CachePatchNum(lumpnum, PU_CACHE); + else + patch = W_CachePatchName("M_NOLVL", PU_CACHE); + + if (!(demolist[dir_on[menudepthleft]].kartspeed & DF_ENCORE)) + V_DrawSmallScaledPatch(x, y, V_SNAPTOTOP, patch); + else + { + w = SHORT(patch->width); + h = SHORT(patch->height); + V_DrawSmallScaledPatch(x+(w>>1), y, V_SNAPTOTOP|V_FLIP, patch); + + { + static angle_t rubyfloattime = 0; + const fixed_t rubyheight = FINESINE(rubyfloattime>>ANGLETOFINESHIFT); + V_DrawFixedPatch((x+(w>>2))<>2))<width), y+20, V_SNAPTOTOP, patch, colormap); + + break; + } +} + +static void M_DrawReplayHut(void) +{ + INT32 x, y, cursory = 0; + INT16 i; + INT16 replaylistitem = currentMenu->numitems-2; + boolean processed_one_this_frame = false; + + static UINT16 replayhutmenuy = 0; + + V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); + + if (cv_vhseffect.value) + V_DrawVhsEffect(false); + + // Draw menu choices + x = currentMenu->x; + y = currentMenu->y; + + if (itemOn > replaylistitem) + { + itemOn = replaylistitem; + dir_on[menudepthleft] = sizedirmenu-1; + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + } + else if (itemOn < replaylistitem) + { + dir_on[menudepthleft] = 0; + replayScrollTitle = 0; replayScrollDelay = TICRATE; replayScrollDir = 1; + } + + if (itemOn == replaylistitem) + { + INT32 maxy; + // Scroll menu items if needed + cursory = y + currentMenu->menuitems[replaylistitem].alphaKey + dir_on[menudepthleft]*10; + maxy = y + currentMenu->menuitems[replaylistitem].alphaKey + sizedirmenu*10; + + if (cursory > maxy - 20) + cursory = maxy - 20; + + if (cursory - replayhutmenuy > SCALEDVIEWHEIGHT-50) + replayhutmenuy += (cursory-SCALEDVIEWHEIGHT-replayhutmenuy + 51)/2; + else if (cursory - replayhutmenuy < 110) + replayhutmenuy += (max(0, cursory-110)-replayhutmenuy - 1)/2; + } + else + replayhutmenuy /= 2; + + y -= replayhutmenuy; + + // Draw static menu items + for (i = 0; i < replaylistitem; i++) + { + INT32 localy = y + currentMenu->menuitems[i].alphaKey; + + if (localy < 65) + continue; + + if (i == itemOn) + cursory = localy; + + if ((currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING) + V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT, currentMenu->menuitems[i].text); + else + V_DrawString(x, localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[i].text); + } + + y += currentMenu->menuitems[replaylistitem].alphaKey; + + for (i = 0; i < (INT16)sizedirmenu; i++) + { + INT32 localy = y+i*10; + INT32 localx = x; + + if (localy < 65) + continue; + if (localy >= SCALEDVIEWHEIGHT) + break; + + if (demolist[i].type == MD_NOTLOADED && !processed_one_this_frame) + { + processed_one_this_frame = true; + G_LoadDemoInfo(&demolist[i]); + } + + if (demolist[i].type == MD_SUBDIR) + { + localx += 8; + V_DrawScaledPatch(x - 4, localy, V_SNAPTOTOP|V_SNAPTOLEFT, W_CachePatchName(dirmenu[i][DIR_TYPE] == EXT_UP ? "M_RBACK" : "M_RFLDR", PU_CACHE)); + } + + if (itemOn == replaylistitem && i == (INT16)dir_on[menudepthleft]) + { + cursory = localy; + + if (replayScrollDelay) + replayScrollDelay--; + else if (replayScrollDir > 0) + { + if (replayScrollTitle < (V_StringWidth(demolist[i].title, 0) - (SCALEDVIEWWIDTH - (x<<1)))<<1) + replayScrollTitle++; + else + { + replayScrollDelay = TICRATE; + replayScrollDir = -1; + } + } + else + { + if (replayScrollTitle > 0) + replayScrollTitle--; + else + { + replayScrollDelay = TICRATE; + replayScrollDir = 1; + } + } + + V_DrawString(localx - (replayScrollTitle>>1), localy, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags|V_ALLOWLOWERCASE, demolist[i].title); + } + else + V_DrawString(localx, localy, V_SNAPTOTOP|V_SNAPTOLEFT|V_ALLOWLOWERCASE, demolist[i].title); + } + + // Draw scrollbar + y = sizedirmenu*10 + currentMenu->menuitems[replaylistitem].alphaKey + 30; + if (y > SCALEDVIEWHEIGHT-80) + { + V_DrawFill(BASEVIDWIDTH-4, 75, 4, SCALEDVIEWHEIGHT-80, V_SNAPTOTOP|V_SNAPTORIGHT|159); + V_DrawFill(BASEVIDWIDTH-3, 76 + (SCALEDVIEWHEIGHT-80) * replayhutmenuy / y, 2, (((SCALEDVIEWHEIGHT-80) * (SCALEDVIEWHEIGHT-80))-1) / y - 1, V_SNAPTOTOP|V_SNAPTORIGHT|149); + } + + // Draw the cursor + V_DrawScaledPatch(currentMenu->x - 24, cursory, V_SNAPTOTOP|V_SNAPTOLEFT, + W_CachePatchName("M_CURSOR", PU_CACHE)); + V_DrawString(currentMenu->x, cursory, V_SNAPTOTOP|V_SNAPTOLEFT|highlightflags, currentMenu->menuitems[itemOn].text); + + // Now draw some replay info! + V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); + + if (itemOn == replaylistitem) + { + DrawReplayHutReplayInfo(); + } +} + +static void M_DrawReplayStartMenu(void) +{ + const char *warning; + UINT8 i; + + M_DrawGenericBackgroundMenu(); + +#define STARTY 62-(replayScrollTitle>>1) + // Draw rankings beyond first + for (i = 1; i < MAXPLAYERS && demolist[dir_on[menudepthleft]].standings[i].ranking; i++) + { + patch_t *patch; + UINT8 *colormap; + + V_DrawRightAlignedString(BASEVIDWIDTH-100, STARTY + i*20, V_SNAPTOTOP|highlightflags, va("%2d", demolist[dir_on[menudepthleft]].standings[i].ranking)); + V_DrawThinString(BASEVIDWIDTH-96, STARTY + i*20, V_SNAPTOTOP|V_ALLOWLOWERCASE, demolist[dir_on[menudepthleft]].standings[i].name); + + if (demolist[dir_on[menudepthleft]].standings[i].timeorscore == UINT32_MAX-1) + V_DrawThinString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, "NO CONTEST"); + else if (demolist[dir_on[menudepthleft]].gametype == GT_RACE) + V_DrawRightAlignedString(BASEVIDWIDTH-40, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d'%02d\"%02d", + G_TicsToMinutes(demolist[dir_on[menudepthleft]].standings[i].timeorscore, true), + G_TicsToSeconds(demolist[dir_on[menudepthleft]].standings[i].timeorscore), + G_TicsToCentiseconds(demolist[dir_on[menudepthleft]].standings[i].timeorscore) + )); + else + V_DrawString(BASEVIDWIDTH-92, STARTY + i*20 + 9, V_SNAPTOTOP, va("%d", demolist[dir_on[menudepthleft]].standings[i].timeorscore)); + + // Character face! + if (W_CheckNumForName(skins[demolist[dir_on[menudepthleft]].standings[i].skin].facerank) != LUMPERROR) + { + patch = facerankprefix[demolist[dir_on[menudepthleft]].standings[i].skin]; + colormap = R_GetTranslationColormap( + demolist[dir_on[menudepthleft]].standings[i].skin, + demolist[dir_on[menudepthleft]].standings[i].color, + GTC_MENUCACHE); + } + else + { + patch = W_CachePatchName("M_NORANK", PU_CACHE); + colormap = R_GetTranslationColormap( + TC_RAINBOW, + demolist[dir_on[menudepthleft]].standings[i].color, + GTC_MENUCACHE); + } + + V_DrawMappedPatch(BASEVIDWIDTH-5 - SHORT(patch->width), STARTY + i*20, V_SNAPTOTOP, patch, colormap); + } +#undef STARTY + + // Handle scrolling rankings + if (replayScrollDelay) + replayScrollDelay--; + else if (replayScrollDir > 0) + { + if (replayScrollTitle < (i*20 - SCALEDVIEWHEIGHT + 100)<<1) + replayScrollTitle++; + else + { + replayScrollDelay = TICRATE; + replayScrollDir = -1; + } + } + else + { + if (replayScrollTitle > 0) + replayScrollTitle--; + else + { + replayScrollDelay = TICRATE; + replayScrollDir = 1; + } + } + + V_DrawFill(10, 10, 300, 60, V_SNAPTOTOP|159); + DrawReplayHutReplayInfo(); + + V_DrawString(10, 72, V_SNAPTOTOP|highlightflags|V_ALLOWLOWERCASE, demolist[dir_on[menudepthleft]].title); + + // Draw a warning prompt if needed + switch (demolist[dir_on[menudepthleft]].addonstatus) + { + case DFILE_ERROR_CANNOTLOAD: + warning = "Some addons in this replay cannot be loaded.\nYou can watch anyway, but desyncs may occur."; + break; + + case DFILE_ERROR_NOTLOADED: + case DFILE_ERROR_INCOMPLETEOUTOFORDER: + warning = "Loading addons will mark your game as modified, and record attack may be unavailable.\nYou can watch without loading addons, but desyncs may occur."; + break; + + case DFILE_ERROR_EXTRAFILES: + warning = "You have addons loaded that were not present in this replay.\nYou can watch anyway, but desyncs may occur."; + break; + + case DFILE_ERROR_OUTOFORDER: + warning = "You have this replay's addons loaded, but they are out of order.\nYou can watch anyway, but desyncs may occur."; + break; + + default: + return; + } + + V_DrawSmallString(4, BASEVIDHEIGHT-14, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, warning); +} + +static boolean M_QuitReplayHut(void) +{ + // D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate. + menuactive = false; + D_StartTitle(); + + if (demolist) + Z_Free(demolist); + demolist = NULL; + + demo.inreplayhut = false; + + return true; +} + +static void M_HutStartReplay(INT32 choice) +{ + (void)choice; + + M_ClearMenus(false); + demo.loadfiles = (itemOn == 0); + demo.ignorefiles = (itemOn != 0); + + G_DoPlayDemo(demolist[dir_on[menudepthleft]].filepath); +} + +void M_SetPlaybackMenuPointer(void) +{ + itemOn = playback_pause; +} + +static void M_DrawPlaybackMenu(void) +{ + INT16 i; + patch_t *icon; + UINT8 *activemap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GOLD, GTC_MENUCACHE); + + // Toggle items + if (paused && !demo.rewinding) + { + PlaybackMenu[playback_pause].status = PlaybackMenu[playback_fastforward].status = PlaybackMenu[playback_rewind].status = IT_DISABLED; + PlaybackMenu[playback_resume].status = PlaybackMenu[playback_advanceframe].status = PlaybackMenu[playback_backframe].status = IT_CALL|IT_STRING; + + if (itemOn >= playback_rewind && itemOn <= playback_fastforward) + itemOn += playback_backframe - playback_rewind; + } + else + { + PlaybackMenu[playback_pause].status = PlaybackMenu[playback_fastforward].status = PlaybackMenu[playback_rewind].status = IT_CALL|IT_STRING; + PlaybackMenu[playback_resume].status = PlaybackMenu[playback_advanceframe].status = PlaybackMenu[playback_backframe].status = IT_DISABLED; + + if (itemOn >= playback_backframe && itemOn <= playback_advanceframe) + itemOn -= playback_backframe - playback_rewind; + } + + if (modeattacking) + { + for (i = playback_viewcount; i <= playback_view4; i++) + PlaybackMenu[i].status = IT_DISABLED; + + //PlaybackMenu[playback_moreoptions].alphaKey = 72; + //PlaybackMenu[playback_quit].alphaKey = 88; + PlaybackMenu[playback_quit].alphaKey = 72; + + //currentMenu->x = BASEVIDWIDTH/2 - 52; + currentMenu->x = BASEVIDWIDTH/2 - 44; + } + else + { + PlaybackMenu[playback_viewcount].status = IT_ARROWS|IT_STRING; + + for (i = 0; i <= splitscreen; i++) + PlaybackMenu[playback_view1+i].status = IT_ARROWS|IT_STRING; + for (i = splitscreen+1; i < 4; i++) + PlaybackMenu[playback_view1+i].status = IT_DISABLED; + + //PlaybackMenu[playback_moreoptions].alphaKey = 156; + //PlaybackMenu[playback_quit].alphaKey = 172; + PlaybackMenu[playback_quit].alphaKey = 156; + + //currentMenu->x = BASEVIDWIDTH/2 - 94; + currentMenu->x = BASEVIDWIDTH/2 - 88; + } + + // wip + //M_DrawTextBox(currentMenu->x-68, currentMenu->y-7, 15, 15); + //M_DrawCenteredMenu(); + + for (i = 0; i < currentMenu->numitems; i++) + { + UINT8 *inactivemap = NULL; + + if (i >= playback_view1 && i <= playback_view4) + { + if (modeattacking) continue; + + if (splitscreen >= i - playback_view1) + { + INT32 ply = displayplayers[i - playback_view1]; + + icon = facerankprefix[players[ply].skin]; + if (i != itemOn) + inactivemap = R_GetTranslationColormap(players[ply].skin, players[ply].skincolor, GTC_MENUCACHE); + } + else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) + icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); + else + icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp + } + else if (currentMenu->menuitems[i].status == IT_DISABLED) + continue; + else if (currentMenu->menuitems[i].patch && W_CheckNumForName(currentMenu->menuitems[i].patch) != LUMPERROR) + icon = W_CachePatchName(currentMenu->menuitems[i].patch, PU_CACHE); + else + icon = W_CachePatchName("PLAYRANK", PU_CACHE); // temp + + if ((i == playback_fastforward && cv_playbackspeed.value > 1) || (i == playback_rewind && demo.rewinding)) + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, V_SNAPTOTOP, icon, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JAWZ, GTC_MENUCACHE)); + else + V_DrawMappedPatch(currentMenu->x + currentMenu->menuitems[i].alphaKey, currentMenu->y, V_SNAPTOTOP, icon, (i == itemOn) ? activemap : inactivemap); + + if (i == itemOn) + { + V_DrawCharacter(currentMenu->x + currentMenu->menuitems[i].alphaKey + 4, currentMenu->y + 14, + '\x1A' | V_SNAPTOTOP|highlightflags, false); + + V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 18, V_SNAPTOTOP|V_ALLOWLOWERCASE, currentMenu->menuitems[i].text); + + if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_ARROWS) + { + char *str; + + if (!(i == playback_viewcount && splitscreen == 3)) + V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 28 - (skullAnimCounter/5), + '\x1A' | V_SNAPTOTOP|highlightflags, false); // up arrow + + if (!(i == playback_viewcount && splitscreen == 0)) + V_DrawCharacter(BASEVIDWIDTH/2 - 4, currentMenu->y + 48 + (skullAnimCounter/5), + '\x1B' | V_SNAPTOTOP|highlightflags, false); // down arrow + + switch (i) + { + case playback_viewcount: + str = va("%d", splitscreen+1); + break; + + case playback_view1: + case playback_view2: + case playback_view3: + case playback_view4: + str = player_names[displayplayers[i - playback_view1]]; // 0 to 3 + break; + + default: // shouldn't ever be reached but whatever + continue; + } + + V_DrawCenteredString(BASEVIDWIDTH/2, currentMenu->y + 38, V_SNAPTOTOP|V_ALLOWLOWERCASE|highlightflags, str); + } + } + } +} + +static void M_PlaybackRewind(INT32 choice) +{ + static tic_t lastconfirmtime; + + (void)choice; + + if (!demo.rewinding) + { + if (paused) + { + G_ConfirmRewind(leveltime-1); + paused = true; + S_PauseAudio(); + } + else + demo.rewinding = paused = true; + } + else if (lastconfirmtime + TICRATE/2 < I_GetTime()) + { + lastconfirmtime = I_GetTime(); + G_ConfirmRewind(leveltime); + } + + CV_SetValue(&cv_playbackspeed, 1); +} + +static void M_PlaybackPause(INT32 choice) +{ + (void)choice; + + paused = !paused; + + if (demo.rewinding) + { + G_ConfirmRewind(leveltime); + paused = true; + S_PauseAudio(); + } + else if (paused) + S_PauseAudio(); + else + S_ResumeAudio(); + + CV_SetValue(&cv_playbackspeed, 1); +} + +static void M_PlaybackFastForward(INT32 choice) +{ + (void)choice; + + if (demo.rewinding) + { + G_ConfirmRewind(leveltime); + paused = false; + S_ResumeAudio(); + } + CV_SetValue(&cv_playbackspeed, cv_playbackspeed.value == 1 ? 4 : 1); +} + +static void M_PlaybackAdvance(INT32 choice) +{ + (void)choice; + + paused = false; + TryRunTics(1); + paused = true; +} + + +static void M_PlaybackSetViews(INT32 choice) +{ + if (choice > 0) + { + if (splitscreen < 3) + G_AdjustView(splitscreen + 2, 0, true); + } + else if (splitscreen) + { + splitscreen--; + R_ExecuteSetViewSize(); + } +} + +static void M_PlaybackAdjustView(INT32 choice) +{ + G_AdjustView(itemOn - playback_viewcount, (choice > 0) ? 1 : -1, true); +} + +static void M_PlaybackQuit(INT32 choice) +{ + (void)choice; + G_StopDemo(); + + if (demo.inreplayhut) + M_ReplayHut(choice); + else if (modeattacking) + { + M_EndModeAttackRun(); + S_ChangeMusicInternal("racent", true); + } + else + D_StartTitle(); +} + static void M_PandorasBox(INT32 choice) { (void)choice; @@ -5066,11 +6081,10 @@ static void M_Options(INT32 choice) (void)choice; // if the player is not admin or server, disable gameplay & server options - OP_MainMenu[5].status = OP_MainMenu[6].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); + OP_MainMenu[4].status = OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); - // if the player is playing _at all_, disable the erase data & credits options - OP_MainMenu[9].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); - OP_MainMenu[10].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); + OP_MainMenu[8].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL); // Play credits + OP_DataOptionsMenu[3].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU); // Erase data OP_GameOptionsMenu[3].status = (M_SecretUnlocked(SECRET_ENCORE)) ? (IT_CVAR|IT_STRING) : IT_SECRET; // cv_kartencore @@ -5315,7 +6329,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 { @@ -5575,6 +6589,7 @@ static void M_Credits(INT32 choice) // SINGLE PLAYER MENU // ================== +#if 0 // Bring this back when we have actual single-player static void M_SinglePlayerMenu(INT32 choice) { (void)choice; @@ -5585,6 +6600,7 @@ static void M_SinglePlayerMenu(INT32 choice) M_SetupNextMenu(&SP_MainDef); } +#endif /*static void M_LoadGameLevelSelect(INT32 choice) { @@ -5658,7 +6674,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); } @@ -6348,7 +7364,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)); @@ -6483,7 +7499,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); } @@ -6563,7 +7579,7 @@ void M_DrawTimeAttackMenu(void) time = mainrecords[cv_nextmap.value-1]->time; } - V_DrawFill((BASEVIDWIDTH - dupadjust)>>1, 78, dupadjust, 36, 239); + V_DrawFill((BASEVIDWIDTH - dupadjust)>>1, 78, dupadjust, 36, 159); V_DrawRightAlignedString(149, 80, highlightflags, "BEST LAP:"); K_drawKartTimestamp(lap, 19, 86, 0, 2); @@ -6608,7 +7624,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)); @@ -6768,7 +7784,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)); @@ -6902,6 +7918,7 @@ static void M_HandleStaffReplay(INT32 choice) break; M_ClearMenus(true); modeattacking = ATTACKING_RECORD; + demo.loadfiles = false; demo.ignorefiles = true; // Just assume that record attack replays have the files needed G_DoPlayDemo(va("%sS%02u",G_BuildMapName(cv_nextmap.value),cv_dummystaff.value)); break; default: @@ -6922,6 +7939,7 @@ static void M_ReplayTimeAttack(INT32 choice) const char *which; M_ClearMenus(true); modeattacking = ATTACKING_RECORD; // set modeattacking before G_DoPlayDemo so the map loader knows + demo.loadfiles = false; demo.ignorefiles = true; // Just assume that record attack replays have the files needed if (currentMenu == &SP_ReplayDef) { @@ -7118,7 +8136,7 @@ static void M_ExitGameResponse(INT32 ch) static void M_EndGame(INT32 choice) { (void)choice; - if (demoplayback || demorecording) + if (demo.playback) return; if (!Playing()) @@ -7235,8 +8253,9 @@ static void M_DrawRoomMenu(void) static void M_DrawConnectMenu(void) { - UINT16 i, j; + UINT16 i; 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++) @@ -7281,16 +8300,23 @@ static void M_DrawConnectMenu(void) va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time))); gt = "Unknown"; - for (j = 0; gametype_cons_t[j].strvalue; j++) - { - if (gametype_cons_t[j].value == serverlist[slindex].info.gametype) - gt = gametype_cons_t[j].strvalue; - } + if (serverlist[slindex].info.gametype < NUMGAMETYPES) + gt = Gametype_Names[serverlist[slindex].info.gametype]; 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; } @@ -7401,7 +8427,10 @@ static void M_ConnectMenu(INT32 choice) // first page of servers serverlistpage = 0; - M_SetupNextMenu(&MP_ConnectDef); + if (ms_RoomId < 0) + M_RoomMenu(0); // Select a room instead of staring at an empty list + else + M_SetupNextMenu(&MP_ConnectDef); itemOn = 0; M_Refresh(0); } @@ -7474,7 +8503,15 @@ static void M_ChooseRoom(INT32 choice) } serverlistpage = 0; - M_SetupNextMenu(currentMenu->prevMenu); + /* + We were on the Multiplayer menu? That means that we must have been trying to + view the server browser, but we hadn't selected a room yet. So we need to go + to the browser next, not back there. + */ + if (currentMenu->prevMenu == &MP_MainDef) + M_SetupNextMenu(&MP_ConnectDef); + else + M_SetupNextMenu(currentMenu->prevMenu); if (currentMenu == &MP_ConnectDef) M_Refresh(0); } @@ -7522,10 +8559,17 @@ static void M_StartServer(INT32 choice) multiplayer = true; + strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); + // Still need to reset devmode cv_debug = 0; - if (demoplayback) + if (strlen(cv_dummyjoinpassword.string) > 0) + D_SetJoinPassword(cv_dummyjoinpassword.string); + else + joinpasswordset = false; + + if (demo.playback) G_StopDemo(); if (metalrecording) G_StopMetalDemo(); @@ -7582,7 +8626,7 @@ static void M_DrawLevelSelectOnly(boolean leftfade, boolean rightfade) y = currentMenu->y + 130 + 8 - i; if (currentMenu->menuitems[itemOn].itemaction == &cv_nextmap && skullAnimCounter < 4) - trans = 120; + trans = 0; else trans = G_GetGametypeColor(cv_newgametype.value); @@ -7774,7 +8818,7 @@ Update the maxplayers label... #ifndef NONET y += MP_MainMenu[8].alphaKey; - V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 239); + V_DrawFill(x+5, y+4+5, /*16*8 + 6,*/ BASEVIDWIDTH - 2*(x+5), 8+6, 159); // draw name string V_DrawString(x+8,y+12, V_ALLOWLOWERCASE, setupm_ip); @@ -7826,7 +8870,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<value; // disable skin changes if we can't actually change skins - if (splitscreen && !CanChangeSkin(secondarydisplayplayer)) + if (splitscreen && !CanChangeSkin(displayplayers[1])) MP_PlayerSetupMenu[2].status = (IT_GRAYEDOUT); else MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER | IT_STRING); @@ -8452,7 +9497,7 @@ static void M_SetupMultiPlayer3(INT32 choice) strcpy(setupm_name, cv_playername3.string); // set for splitscreen third player - setupm_player = &players[thirddisplayplayer]; + setupm_player = &players[displayplayers[2]]; setupm_cvskin = &cv_skin3; setupm_cvcolor = &cv_playercolor3; setupm_cvname = &cv_playername3; @@ -8464,7 +9509,7 @@ static void M_SetupMultiPlayer3(INT32 choice) setupm_fakecolor = setupm_cvcolor->value; // disable skin changes if we can't actually change skins - if (splitscreen > 1 && !CanChangeSkin(thirddisplayplayer)) + if (splitscreen > 1 && !CanChangeSkin(displayplayers[2])) MP_PlayerSetupMenu[2].status = (IT_GRAYEDOUT); else MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER | IT_STRING); @@ -8483,7 +9528,7 @@ static void M_SetupMultiPlayer4(INT32 choice) strcpy(setupm_name, cv_playername4.string); // set for splitscreen fourth player - setupm_player = &players[fourthdisplayplayer]; + setupm_player = &players[displayplayers[3]]; setupm_cvskin = &cv_skin4; setupm_cvcolor = &cv_playercolor4; setupm_cvname = &cv_playername4; @@ -8495,7 +9540,7 @@ static void M_SetupMultiPlayer4(INT32 choice) setupm_fakecolor = setupm_cvcolor->value; // disable skin changes if we can't actually change skins - if (splitscreen > 2 && !CanChangeSkin(fourthdisplayplayer)) + if (splitscreen > 2 && !CanChangeSkin(displayplayers[3])) MP_PlayerSetupMenu[2].status = (IT_GRAYEDOUT); else MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER | IT_STRING); @@ -8852,7 +9897,7 @@ static void M_Setup1PControlsMenu(INT32 choice) OP_AllControlsMenu[15].status = IT_CONTROL; // Chat //OP_AllControlsMenu[16].status = IT_CONTROL; // Team-chat OP_AllControlsMenu[16].status = IT_CONTROL; // Rankings - OP_AllControlsMenu[17].status = IT_CONTROL; // Viewpoint + //OP_AllControlsMenu[17].status = IT_CONTROL; // Viewpoint // 18 is Reset Camera, 19 is Toggle Chasecam OP_AllControlsMenu[20].status = IT_CONTROL; // Pause OP_AllControlsMenu[21].status = IT_CONTROL; // Screenshot @@ -8884,7 +9929,7 @@ static void M_Setup2PControlsMenu(INT32 choice) OP_AllControlsMenu[15].status = IT_GRAYEDOUT2; // Chat //OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Team-chat OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Rankings - OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Viewpoint + //OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Viewpoint // 18 is Reset Camera, 19 is Toggle Chasecam OP_AllControlsMenu[20].status = IT_GRAYEDOUT2; // Pause OP_AllControlsMenu[21].status = IT_GRAYEDOUT2; // Screenshot @@ -8916,7 +9961,7 @@ static void M_Setup3PControlsMenu(INT32 choice) OP_AllControlsMenu[15].status = IT_GRAYEDOUT2; // Chat //OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Team-chat OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Rankings - OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Viewpoint + //OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Viewpoint // 18 is Reset Camera, 19 is Toggle Chasecam OP_AllControlsMenu[20].status = IT_GRAYEDOUT2; // Pause OP_AllControlsMenu[21].status = IT_GRAYEDOUT2; // Screenshot @@ -8948,7 +9993,7 @@ static void M_Setup4PControlsMenu(INT32 choice) OP_AllControlsMenu[15].status = IT_GRAYEDOUT2; // Chat //OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Team-chat OP_AllControlsMenu[16].status = IT_GRAYEDOUT2; // Rankings - OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Viewpoint + //OP_AllControlsMenu[17].status = IT_GRAYEDOUT2; // Viewpoint // 18 is Reset Camera, 19 is Toggle Chasecam OP_AllControlsMenu[20].status = IT_GRAYEDOUT2; // Pause OP_AllControlsMenu[21].status = IT_GRAYEDOUT2; // Screenshot diff --git a/src/m_menu.h b/src/m_menu.h index 864f4cacc..62c852e4d 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -38,6 +38,9 @@ void M_Drawer(void); // Called by D_SRB2Main, loads the config file. void M_Init(void); +// Called by D_SRB2Main also, sets up the playermenu and description tables. +void M_InitCharacterTables(void); + // Called by intro code to force menu up upon a keypress, // does nothing if menu is already up. void M_StartControlPanel(void); @@ -104,6 +107,7 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); #define IT_CV_NOPRINT 1536 #define IT_CV_NOMOD 2048 #define IT_CV_INVISSLIDER 2560 +#define IT_CV_PASSWORD 3072 //call/submenu specific // There used to be a lot more here but ... @@ -209,8 +213,9 @@ typedef struct UINT8 netgame; } saveinfo_t; -extern description_t description[32]; +extern description_t description[MAXSKINS]; +extern consvar_t cv_showfocuslost; extern consvar_t cv_newgametype, cv_nextmap, cv_chooseskin, cv_serversort; extern CV_PossibleValue_t gametype_cons_t[]; @@ -233,6 +238,9 @@ void Screenshot_option_Onchange(void); // Addons menu updating void Addons_option_Onchange(void); +void M_ReplayHut(INT32 choice); +void M_SetPlaybackMenuPointer(void); + INT32 HU_GetHighlightColor(void); // These defines make it a little easier to make menus diff --git a/src/m_misc.c b/src/m_misc.c index c95aa392c..f4a4ec291 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -743,12 +743,12 @@ static void M_PNGText(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png else snprintf(lvlttltext, 48, "Unknown"); - if (gamestate == GS_LEVEL && &players[displayplayer] && players[displayplayer].mo) + if (gamestate == GS_LEVEL && &players[displayplayers[0]] && players[displayplayers[0]].mo) snprintf(locationtxt, 40, "X:%d Y:%d Z:%d A:%d", - players[displayplayer].mo->x>>FRACBITS, - players[displayplayer].mo->y>>FRACBITS, - players[displayplayer].mo->z>>FRACBITS, - FixedInt(AngleFixed(players[displayplayer].mo->angle))); + players[displayplayers[0]].mo->x>>FRACBITS, + players[displayplayers[0]].mo->y>>FRACBITS, + players[displayplayers[0]].mo->z>>FRACBITS, + FixedInt(AngleFixed(players[displayplayers[0]].mo->angle))); else snprintf(locationtxt, 40, "Unknown"); diff --git a/src/md5.h b/src/md5.h index 0fe017f51..eaa85dc19 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/mserv.c b/src/mserv.c index f5c4fa889..29aa99fae 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -661,11 +661,19 @@ FUNCMATH static const char *int2str(INT32 n) #ifndef NONET static INT32 ConnectionFailed(void) { + time(&MSLastPing); con_state = MSCS_FAILED; CONS_Alert(CONS_ERROR, M_GetText("Connection to Master Server failed\n")); CloseConnection(); return MS_CONNECT_ERROR; } + +static INT32 ConnectionFailedwerrno(int no) +{ + CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error: %s\n"), + strerror(no)); + return ConnectionFailed(); +} #endif /** Tries to register the local game server on the master server. @@ -682,44 +690,43 @@ static INT32 AddToMasterServer(boolean firstadd) msg_server_t *info = (msg_server_t *)msg.buffer; INT32 room = -1; fd_set tset; - time_t timestamp = time(NULL); UINT32 signature, tmp; const char *insname; + if (socket_fd == (SOCKET_TYPE)ERRSOCKET)/* Woah, our socket was closed! */ + { + if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) + return ConnectionFailedwerrno(errno); + } + M_Memcpy(&tset, &wset, sizeof (tset)); res = select(255, NULL, &tset, NULL, &select_timeout); - if (res != ERRSOCKET && !res) + if (res == ERRSOCKET) + return ConnectionFailedwerrno(errno); + if (res == 0)/* nothing selected */ { - if (retry++ > 30) // an about 30 second timeout + /* + Timeout next call because SendPingToMasterServer + (our calling function) already calls this once + every two minutes. + */ + if (retry++ == 1) { retry = 0; CONS_Alert(CONS_ERROR, M_GetText("Master Server timed out\n")); - MSLastPing = timestamp; return ConnectionFailed(); } return MS_CONNECT_ERROR; } retry = 0; - if (res == ERRSOCKET) - { - if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0)) - { - CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error #%u: %s\n"), errno, strerror(errno)); - MSLastPing = timestamp; - return ConnectionFailed(); - } - } // so, the socket is writable, but what does that mean, that the connection is // ok, or bad... let see that! j = (socklen_t)sizeof (i); - getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&i, &j); + if (getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&i, &j) == ERRSOCKET) + return ConnectionFailedwerrno(errno); if (i) // it was bad - { - CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error #%u: %s\n"), errno, strerror(errno)); - MSLastPing = timestamp; - return ConnectionFailed(); - } + return ConnectionFailedwerrno(i); #ifdef PARANOIA if (ms_RoomId <= 0) @@ -752,15 +759,12 @@ static INT32 AddToMasterServer(boolean firstadd) msg.length = (UINT32)sizeof (msg_server_t); msg.room = 0; if (MS_Write(&msg) < 0) - { - MSLastPing = timestamp; return ConnectionFailed(); - } if(con_state != MSCS_REGISTERED) CONS_Printf(M_GetText("Master Server update successful.\n")); - MSLastPing = timestamp; + time(&MSLastPing); con_state = MSCS_REGISTERED; CloseConnection(); #endif diff --git a/src/p_enemy.c b/src/p_enemy.c index 9d3aa9519..bf1cacdc7 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3079,7 +3079,7 @@ void A_Invincibility(mobj_t *actor) { S_StopMusic(); if (mariomode) - G_GhostAddColor(GHC_INVINCIBLE); + G_GhostAddColor((INT32) (player - players), GHC_INVINCIBLE); S_ChangeMusicInternal((mariomode) ? "minvnc" : "invinc", false); } } @@ -4174,12 +4174,12 @@ void A_OverlayThink(mobj_t *actor) { angle_t viewingangle; - if (players[displayplayer].awayviewtics) - viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y); - else if (!camera.chase && players[displayplayer].mo) - viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y); + if (players[displayplayers[0]].awayviewtics) + viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y); + else if (!camera[0].chase && players[displayplayers[0]].mo) + viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y); else - viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, camera.x, camera.y); + viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, camera[0].x, camera[0].y); destx = actor->target->x + P_ReturnThrustX(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale)); desty = actor->target->y + P_ReturnThrustY(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale)); @@ -4781,8 +4781,8 @@ void A_DetonChase(mobj_t *actor) actor->reactiontime = -42; exact = actor->movedir>>ANGLETOFINESHIFT; - xyspeed = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINECOSINE(exact)); - actor->momz = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINESINE(exact)); + xyspeed = FixedMul(FixedMul(K_GetKartSpeed(actor->tracer->player, false),3*FRACUNIT/4), FINECOSINE(exact)); + actor->momz = FixedMul(FixedMul(K_GetKartSpeed(actor->tracer->player, false),3*FRACUNIT/4), FINESINE(exact)); exact = actor->angle>>ANGLETOFINESHIFT; actor->momx = FixedMul(xyspeed, FINECOSINE(exact)); @@ -8434,6 +8434,9 @@ void A_SPBChase(mobj_t *actor) wspeed = (3*defspeed)/2; if (wspeed < 20*actor->tracer->scale) wspeed = 20*actor->tracer->scale; + if (actor->tracer->player->pflags & PF_SLIDING) + wspeed = actor->tracer->player->speed/2; + // ^^^^ current section: These are annoying, and grand metropolis in particular needs this. hang = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y); vang = R_PointToAngle2(0, actor->z, dist, actor->tracer->z); @@ -8640,6 +8643,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_floor.c b/src/p_floor.c index e11fe4030..ccbfd6eae 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -2536,9 +2536,9 @@ void T_CameraScanner(elevator_t *elevator) lastleveltime = leveltime; } - if (players[displayplayer].mo) + if (players[displayplayers[0]].mo) { - if (players[displayplayer].mo->subsector->sector == elevator->actionsector) + if (players[displayplayers[0]].mo->subsector->sector == elevator->actionsector) { if (t_cam_dist == -42) t_cam_dist = cv_cam_dist.value; @@ -2564,9 +2564,9 @@ void T_CameraScanner(elevator_t *elevator) } } - if (splitscreen && players[secondarydisplayplayer].mo) + if (splitscreen && players[displayplayers[1]].mo) { - if (players[secondarydisplayplayer].mo->subsector->sector == elevator->actionsector) + if (players[displayplayers[1]].mo->subsector->sector == elevator->actionsector) { if (t_cam2_rotate == -42) t_cam2_dist = cv_cam2_dist.value; @@ -2592,9 +2592,9 @@ void T_CameraScanner(elevator_t *elevator) } } - if (splitscreen > 1 && players[thirddisplayplayer].mo) + if (splitscreen > 1 && players[displayplayers[2]].mo) { - if (players[thirddisplayplayer].mo->subsector->sector == elevator->actionsector) + if (players[displayplayers[2]].mo->subsector->sector == elevator->actionsector) { if (t_cam3_rotate == -42) t_cam3_dist = cv_cam3_dist.value; @@ -2620,9 +2620,9 @@ void T_CameraScanner(elevator_t *elevator) } } - if (splitscreen > 2 && players[fourthdisplayplayer].mo) + if (splitscreen > 2 && players[displayplayers[3]].mo) { - if (players[fourthdisplayplayer].mo->subsector->sector == elevator->actionsector) + if (players[displayplayers[3]].mo->subsector->sector == elevator->actionsector) { if (t_cam4_rotate == -42) t_cam4_dist = cv_cam4_dist.value; diff --git a/src/p_inter.c b/src/p_inter.c index dd27858fc..a910445da 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -62,11 +62,11 @@ void P_ForceConstant(const BasicFF_t *FFInfo) ConstantQuake.Magnitude = FFInfo->Magnitude; if (FFInfo->player == &players[consoleplayer]) I_Tactile(ConstantForce, &ConstantQuake); - else if (splitscreen && FFInfo->player == &players[secondarydisplayplayer]) + else if (splitscreen && FFInfo->player == &players[displayplayers[1]]) I_Tactile2(ConstantForce, &ConstantQuake); - else if (splitscreen > 1 && FFInfo->player == &players[thirddisplayplayer]) + else if (splitscreen > 1 && FFInfo->player == &players[displayplayers[2]]) I_Tactile3(ConstantForce, &ConstantQuake); - else if (splitscreen > 2 && FFInfo->player == &players[fourthdisplayplayer]) + else if (splitscreen > 2 && FFInfo->player == &players[displayplayers[3]]) I_Tactile4(ConstantForce, &ConstantQuake); } void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End) @@ -83,11 +83,11 @@ void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End) RampQuake.End = End; if (FFInfo->player == &players[consoleplayer]) I_Tactile(ConstantForce, &RampQuake); - else if (splitscreen && FFInfo->player == &players[secondarydisplayplayer]) + else if (splitscreen && FFInfo->player == &players[displayplayers[1]]) I_Tactile2(ConstantForce, &RampQuake); - else if (splitscreen > 1 && FFInfo->player == &players[thirddisplayplayer]) + else if (splitscreen > 1 && FFInfo->player == &players[displayplayers[2]]) I_Tactile3(ConstantForce, &RampQuake); - else if (splitscreen > 2 && FFInfo->player == &players[fourthdisplayplayer]) + else if (splitscreen > 2 && FFInfo->player == &players[displayplayers[3]]) I_Tactile4(ConstantForce, &RampQuake); } @@ -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! } } @@ -215,7 +218,7 @@ void P_DoNightsScore(player_t *player) dummymo->fuse = 3*TICRATE; // What?! NO, don't use the camera! Scale up instead! - //P_InstaThrust(dummymo, R_PointToAngle2(dummymo->x, dummymo->y, camera.x, camera.y), 3*FRACUNIT); + //P_InstaThrust(dummymo, R_PointToAngle2(dummymo->x, dummymo->y, camera[0].x, camera[0].y), 3*FRACUNIT); dummymo->scalespeed = FRACUNIT/25; dummymo->destscale = 2*FRACUNIT; } @@ -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); @@ -842,7 +851,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) // Secret emblem thingy case MT_EMBLEM: { - if (demoplayback || player->bot) + if (demo.playback || player->bot) return; emblemlocations[special->health-1].collected = true; @@ -1171,13 +1180,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) toucher->angle = special->angle; if (player == &players[consoleplayer]) - localangle = toucher->angle; - else if (player == &players[secondarydisplayplayer]) - localangle2 = toucher->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = toucher->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = toucher->angle; + localangle[0] = toucher->angle; + else if (player == &players[displayplayers[1]]) + localangle[1] = toucher->angle; + else if (player == &players[displayplayers[2]]) + localangle[2] = toucher->angle; + else if (player == &players[displayplayers[3]]) + localangle[3] = toucher->angle; P_ResetPlayer(player); @@ -1200,7 +1209,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } // CECHO showing you what this item is - if (player == &players[displayplayer] || G_IsSpecialStage(gamemap)) + if (player == &players[displayplayers[0]] || G_IsSpecialStage(gamemap)) { HU_SetCEchoFlags(V_AUTOFADEOUT); HU_SetCEchoDuration(4); @@ -1222,7 +1231,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } // CECHO showing you what this item is - if (player == &players[displayplayer] || G_IsSpecialStage(gamemap)) + if (player == &players[displayplayers[0]] || G_IsSpecialStage(gamemap)) { HU_SetCEchoFlags(V_AUTOFADEOUT); HU_SetCEchoDuration(4); @@ -1254,7 +1263,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } // CECHO showing you what this item is - if (player == &players[displayplayer] || G_IsSpecialStage(gamemap)) + if (player == &players[displayplayers[0]] || G_IsSpecialStage(gamemap)) { HU_SetCEchoFlags(V_AUTOFADEOUT); HU_SetCEchoDuration(4); @@ -1284,7 +1293,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } // CECHO showing you what this item is - if (player == &players[displayplayer] || G_IsSpecialStage(gamemap)) + if (player == &players[displayplayers[0]] || G_IsSpecialStage(gamemap)) { HU_SetCEchoFlags(V_AUTOFADEOUT); HU_SetCEchoDuration(4); @@ -1312,7 +1321,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } // CECHO showing you what this item is - if (player == &players[displayplayer] || G_IsSpecialStage(gamemap)) + if (player == &players[displayplayers[0]] || G_IsSpecialStage(gamemap)) { HU_SetCEchoFlags(V_AUTOFADEOUT); HU_SetCEchoDuration(4); @@ -1424,7 +1433,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; player->powers[pw_shield] |= SH_FIREFLOWER; toucher->color = SKINCOLOR_WHITE; - G_GhostAddColor(GHC_FIREFLOWER); + G_GhostAddColor(player - players, GHC_FIREFLOWER); break; // *************** // @@ -1468,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; @@ -1827,6 +1837,9 @@ void P_CheckTimeLimit(void) } } + if (playercount > MAXPLAYERS) + playercount = MAXPLAYERS; + //Sort 'em. for (i = 1; i < playercount; i++) { @@ -2314,17 +2327,17 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) AM_Stop(); //added : 22-02-98: recenter view for next life... - localaiming = 0; + localaiming[0] = 0; } - if (target->player == &players[secondarydisplayplayer]) + if (target->player == &players[displayplayers[1]]) { // added : 22-02-98: recenter view for next life... - localaiming2 = 0; + localaiming[1] = 0; } - if (target->player == &players[thirddisplayplayer]) - localaiming3 = 0; - if (target->player == &players[fourthdisplayplayer]) - localaiming4 = 0; + if (target->player == &players[displayplayers[2]]) + localaiming[2] = 0; + if (target->player == &players[displayplayers[3]]) + localaiming[3] = 0; //tag deaths handled differently in suicide cases. Don't count spectators! /*if (G_TagGametype() @@ -2952,66 +2965,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) @@ -3028,7 +2981,7 @@ void P_RemoveShield(player_t *player) if (!player->powers[pw_super]) { player->mo->color = player->skincolor; - G_GhostAddColor(GHC_NORMAL); + G_GhostAddColor((INT32) (player - players), GHC_NORMAL); } } else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB) // Give them what's coming to them! @@ -3459,7 +3412,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da target->health -= damage; if (source && source->player && target) - G_GhostAddHit(target); + G_GhostAddHit((INT32) (source->player - players), target); if (target->health <= 0) { diff --git a/src/p_local.h b/src/p_local.h index cf1387fee..0d0ddc89b 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -22,6 +22,7 @@ #include "p_tick.h" #include "r_defs.h" #include "p_maputl.h" +#include "doomstat.h" // MAXSPLITSCREENPLAYERS #define FLOATSPEED (FRACUNIT*4) @@ -108,7 +109,7 @@ typedef struct camera_s fixed_t pan; } camera_t; -extern camera_t camera, camera2, camera3, camera4; +extern camera_t camera[MAXSPLITSCREENPLAYERS]; extern consvar_t cv_cam_dist, cv_cam_still, cv_cam_height; extern consvar_t cv_cam_speed, cv_cam_rotate, cv_cam_rotspeed; @@ -137,6 +138,7 @@ boolean P_PlayerInPain(player_t *player); void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor); void P_ResetPlayer(player_t *player); boolean P_IsLocalPlayer(player_t *player); +boolean P_IsDisplayPlayer(player_t *player); boolean P_SpectatorJoinGame(player_t *player); boolean P_IsObjectInGoop(mobj_t *mo); @@ -178,7 +180,6 @@ boolean P_LookForEnemies(player_t *player); void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius); void P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user //boolean P_SuperReady(player_t *player); -void P_DoJump(player_t *player, boolean soundandstate); boolean P_AnalogMove(player_t *player); /*boolean P_TransferToNextMare(player_t *player); UINT8 P_FindLowestMare(void);*/ @@ -187,8 +188,6 @@ UINT8 P_FindHighestLap(void); void P_FindEmerald(void); //void P_TransferToAxis(player_t *player, INT32 axisnum); boolean P_PlayerMoving(INT32 pnum); -void P_SpawnThokMobj(player_t *player); -void P_SpawnSpinMobj(player_t *player, mobjtype_t type); void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range); void P_PlayLivesJingle(player_t *player); @@ -274,8 +273,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 41e5a455d..2c766349d 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -212,16 +212,16 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) { object->angle = spring->angle; - if (!demoplayback || P_AnalogMove(object->player)) + if (!demo.playback || P_AnalogMove(object->player)) { if (object->player == &players[consoleplayer]) - localangle = spring->angle; - else if (object->player == &players[secondarydisplayplayer]) - localangle2 = spring->angle; - else if (object->player == &players[thirddisplayplayer]) - localangle3 = spring->angle; - else if (object->player == &players[fourthdisplayplayer]) - localangle4 = spring->angle; + localangle[0] = spring->angle; + else if (object->player == &players[displayplayers[1]]) + localangle[1] = spring->angle; + else if (object->player == &players[displayplayers[2]]) + localangle[2] = spring->angle; + else if (object->player == &players[displayplayers[3]]) + localangle[3] = spring->angle; } } @@ -747,6 +747,9 @@ 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 @@ -850,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])); @@ -912,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)); @@ -980,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) @@ -1021,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) { @@ -1056,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)); @@ -1079,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) { @@ -1240,16 +1264,16 @@ static boolean PIT_CheckThing(mobj_t *thing) thing->angle = tmthing->angle; - if (!demoplayback || P_AnalogMove(thing->player)) + if (!demo.playback || P_AnalogMove(thing->player)) { if (thing->player == &players[consoleplayer]) - localangle = thing->angle; - else if (thing->player == &players[secondarydisplayplayer]) - localangle2 = thing->angle; - else if (thing->player == &players[thirddisplayplayer]) - localangle3 = thing->angle; - else if (thing->player == &players[fourthdisplayplayer]) - localangle4 = thing->angle; + localangle[0] = thing->angle; + else if (thing->player == &players[displayplayers[1]]) + localangle[1] = thing->angle; + else if (thing->player == &players[displayplayers[2]]) + localangle[2] = thing->angle; + else if (thing->player == &players[displayplayers[3]]) + localangle[3] = thing->angle; } return true; @@ -1556,12 +1580,12 @@ static boolean PIT_CheckThing(mobj_t *thing) if (G_BattleGametype()) { - if (thing->player->kartstuff[k_sneakertimer] && !(tmthing->player->kartstuff[k_sneakertimer])) + if (thing->player->kartstuff[k_sneakertimer] && !(tmthing->player->kartstuff[k_sneakertimer]) && !(thing->player->powers[pw_flashing])) // Don't steal bumpers while intangible { K_StealBumper(thing->player, tmthing->player, false); K_SpinPlayer(tmthing->player, thing, 0, tmthing, false); } - else if (tmthing->player->kartstuff[k_sneakertimer] && !(thing->player->kartstuff[k_sneakertimer])) + else if (tmthing->player->kartstuff[k_sneakertimer] && !(thing->player->kartstuff[k_sneakertimer]) && !(tmthing->player->powers[pw_flashing])) { K_StealBumper(tmthing->player, thing->player, false); K_SpinPlayer(thing->player, tmthing, 0, thing, false); @@ -2090,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)) ; @@ -2480,41 +2504,46 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam) subsector_t *s = R_PointInSubsector(x, y); boolean retval = true; boolean itsatwodlevel = false; + UINT8 i; floatok = false; - if (twodlevel - || (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD)) - || (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)) - || (thiscam == &camera3 && players[thirddisplayplayer].mo && (players[thirddisplayplayer].mo->flags2 & MF2_TWOD)) - || (thiscam == &camera4 && players[fourthdisplayplayer].mo && (players[fourthdisplayplayer].mo->flags2 & MF2_TWOD))) + if (twodlevel) itsatwodlevel = true; + else + { + for (i = 0; i <= splitscreen; i++) + { + if (thiscam == &camera[i] && players[displayplayers[i]].mo + && (players[displayplayers[i]].mo->flags2 & MF2_TWOD)) + { + itsatwodlevel = true; + break; + } + } + } - if (!itsatwodlevel && players[displayplayer].mo) + if (!itsatwodlevel && players[displayplayers[0]].mo) { fixed_t tryx = thiscam->x; fixed_t tryy = thiscam->y; + for (i = 0; i <= splitscreen; i++) + { #ifndef NOCLIPCAM - if ((thiscam == &camera && (players[displayplayer].pflags & PF_NOCLIP)) - || (thiscam == &camera2 && (players[secondarydisplayplayer].pflags & PF_NOCLIP)) - || (thiscam == &camera3 && (players[thirddisplayplayer].pflags & PF_NOCLIP)) - || (thiscam == &camera4 && (players[fourthdisplayplayer].pflags & PF_NOCLIP)) - || (leveltime < introtime)) + if ((thiscam == &camera[i] && (players[displayplayers[i]].pflags & PF_NOCLIP)) || (leveltime < introtime)) // Noclipping player camera noclips too!! #else - if ((thiscam == &camera && !(players[displayplayer].pflags & PF_TIMEOVER)) - || (thiscam == &camera2 && !(players[secondarydisplayplayer].pflags & PF_TIMEOVER)) - || (thiscam == &camera3 && !(players[thirddisplayplayer].pflags & PF_TIMEOVER)) - || (thiscam == &camera4 && !(players[fourthdisplayplayer].pflags & PF_TIMEOVER))) + if (thiscam == &camera[i] && !(players[displayplayers[i]].pflags & PF_TIMEOVER)) // Time Over should not clip through walls #endif - { // Noclipping player camera noclips too!! - floatok = true; - thiscam->floorz = thiscam->z; - thiscam->ceilingz = thiscam->z + thiscam->height; - thiscam->x = x; - thiscam->y = y; - thiscam->subsector = s; - return true; + { + floatok = true; + thiscam->floorz = thiscam->z; + thiscam->ceilingz = thiscam->z + thiscam->height; + thiscam->x = x; + thiscam->y = y; + thiscam->subsector = s; + return true; + } } do { @@ -2763,8 +2792,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; @@ -2777,8 +2805,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; diff --git a/src/p_maputl.c b/src/p_maputl.c index 1be57399c..355c58db8 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -339,9 +339,9 @@ void P_CameraLineOpening(line_t *linedef) frontceiling = sectors[front->camsec].ceilingheight; #ifdef ESLOPE if (sectors[front->camsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope) - frontfloor = P_GetZAt(sectors[front->camsec].f_slope, camera.x, camera.y); + frontfloor = P_GetZAt(sectors[front->camsec].f_slope, camera[0].x, camera[0].y); if (sectors[front->camsec].c_slope) - frontceiling = P_GetZAt(sectors[front->camsec].c_slope, camera.x, camera.y); + frontceiling = P_GetZAt(sectors[front->camsec].c_slope, camera[0].x, camera[0].y); #endif } @@ -351,9 +351,9 @@ void P_CameraLineOpening(line_t *linedef) frontceiling = sectors[front->heightsec].ceilingheight; #ifdef ESLOPE if (sectors[front->heightsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope) - frontfloor = P_GetZAt(sectors[front->heightsec].f_slope, camera.x, camera.y); + frontfloor = P_GetZAt(sectors[front->heightsec].f_slope, camera[0].x, camera[0].y); if (sectors[front->heightsec].c_slope) - frontceiling = P_GetZAt(sectors[front->heightsec].c_slope, camera.x, camera.y); + frontceiling = P_GetZAt(sectors[front->heightsec].c_slope, camera[0].x, camera[0].y); #endif } else @@ -367,9 +367,9 @@ void P_CameraLineOpening(line_t *linedef) backceiling = sectors[back->camsec].ceilingheight; #ifdef ESLOPE if (sectors[back->camsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope) - frontfloor = P_GetZAt(sectors[back->camsec].f_slope, camera.x, camera.y); + frontfloor = P_GetZAt(sectors[back->camsec].f_slope, camera[0].x, camera[0].y); if (sectors[back->camsec].c_slope) - frontceiling = P_GetZAt(sectors[back->camsec].c_slope, camera.x, camera.y); + frontceiling = P_GetZAt(sectors[back->camsec].c_slope, camera[0].x, camera[0].y); #endif } else if (back->heightsec >= 0) @@ -378,9 +378,9 @@ void P_CameraLineOpening(line_t *linedef) backceiling = sectors[back->heightsec].ceilingheight; #ifdef ESLOPE if (sectors[back->heightsec].f_slope) // SRB2CBTODO: ESLOPE (sectors[front->heightsec].f_slope) - frontfloor = P_GetZAt(sectors[back->heightsec].f_slope, camera.x, camera.y); + frontfloor = P_GetZAt(sectors[back->heightsec].f_slope, camera[0].x, camera[0].y); if (sectors[back->heightsec].c_slope) - frontceiling = P_GetZAt(sectors[back->heightsec].c_slope, camera.x, camera.y); + frontceiling = P_GetZAt(sectors[back->heightsec].c_slope, camera[0].x, camera[0].y); #endif } else @@ -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 3cf27604e..63c55cf5c 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 @@ -1220,7 +1125,7 @@ static void P_PlayerFlip(mobj_t *mo) if (!mo->player) return; - G_GhostAddFlip(); + G_GhostAddFlip((INT32) (mo->player - players)); // Flip aiming to match! if (mo->player->pflags & PF_NIGHTSMODE) // NiGHTS doesn't use flipcam @@ -1230,45 +1135,21 @@ static void P_PlayerFlip(mobj_t *mo) } else if (mo->player->pflags & PF_FLIPCAM) { + UINT8 i; + mo->player->aiming = InvAngle(mo->player->aiming); - if (mo->player-players == displayplayer) + + for (i = 0; i <= splitscreen; i++) { - localaiming = mo->player->aiming; - if (camera.chase) { - camera.aiming = InvAngle(camera.aiming); - camera.z = mo->z - camera.z + mo->z; - if (mo->eflags & MFE_VERTICALFLIP) - camera.z += FixedMul(20*FRACUNIT, mo->scale); - } - } - else if (mo->player-players == secondarydisplayplayer) - { - localaiming2 = mo->player->aiming; - if (camera2.chase) { - camera2.aiming = InvAngle(camera2.aiming); - camera2.z = mo->z - camera2.z + mo->z; - if (mo->eflags & MFE_VERTICALFLIP) - camera2.z += FixedMul(20*FRACUNIT, mo->scale); - } - } - else if (mo->player-players == thirddisplayplayer) - { - localaiming3 = mo->player->aiming; - if (camera3.chase) { - camera3.aiming = InvAngle(camera3.aiming); - camera3.z = mo->z - camera3.z + mo->z; - if (mo->eflags & MFE_VERTICALFLIP) - camera3.z += FixedMul(20*FRACUNIT, mo->scale); - } - } - else if (mo->player-players == fourthdisplayplayer) - { - localaiming4 = mo->player->aiming; - if (camera4.chase) { - camera4.aiming = InvAngle(camera4.aiming); - camera4.z = mo->z - camera4.z + mo->z; - if (mo->eflags & MFE_VERTICALFLIP) - camera4.z += FixedMul(20*FRACUNIT, mo->scale); + if (mo->player-players == displayplayers[i]) + { + localaiming[i] = mo->player->aiming; + if (camera[i].chase) { + camera[i].aiming = InvAngle(camera[i].aiming); + camera[i].z = mo->z - camera[i].z + mo->z; + if (mo->eflags & MFE_VERTICALFLIP) + camera[i].z += FixedMul(20*FRACUNIT, mo->scale); + } } } } @@ -1404,11 +1285,14 @@ fixed_t P_GetMobjGravity(mobj_t *mo) break; case MT_BANANA: case MT_EGGMANITEM: + case MT_SSMINE: + case MT_SINK: + if (mo->extravalue2 > 0) + gravityadd *= mo->extravalue2; + /* FALLTHRU */ case MT_ORBINAUT: case MT_JAWZ: case MT_JAWZ_DUD: - case MT_SSMINE: - case MT_SINK: gravityadd = (5*gravityadd)/2; break; case MT_SIGN: @@ -2024,7 +1908,7 @@ void P_XYMovement(mobj_t *mo) if (mo->type == MT_ORBINAUT || mo->type == MT_JAWZ_DUD || mo->type == MT_JAWZ || mo->type == MT_BALLHOG) //(mo->type == MT_JAWZ && !mo->tracer)) return; - if (mo->player && (mo->player->kartstuff[k_spinouttimer] && !mo->player->kartstuff[k_wipeoutslow]) && mo->player->speed <= mo->player->normalspeed/2) + if (mo->player && (mo->player->kartstuff[k_spinouttimer] && !mo->player->kartstuff[k_wipeoutslow]) && mo->player->speed <= K_GetKartSpeed(mo->player, false)/2) return; //} @@ -2099,7 +1983,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; @@ -3222,27 +3106,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->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 +3262,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))) { @@ -3659,17 +3522,26 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled { boolean itsatwodlevel = false; postimg_t postimg = postimg_none; + UINT8 i; // This can happen when joining if (thiscam->subsector == NULL || thiscam->subsector->sector == NULL) return true; - if (twodlevel - || (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD)) - || (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)) - || (thiscam == &camera3 && players[thirddisplayplayer].mo && (players[thirddisplayplayer].mo->flags2 & MF2_TWOD)) - || (thiscam == &camera4 && players[fourthdisplayplayer].mo && (players[fourthdisplayplayer].mo->flags2 & MF2_TWOD))) + if (twodlevel) itsatwodlevel = true; + else + { + for (i = 0; i <= splitscreen; i++) + { + if (thiscam == &camera[i] && players[displayplayers[i]].mo + && (players[displayplayers[i]].mo->flags2 & MF2_TWOD)) + { + itsatwodlevel = true; + break; + } + } + } if (encoremode) postimg = postimg_mirror; @@ -3701,14 +3573,11 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled if (postimg != postimg_none) { - if (splitscreen && player == &players[secondarydisplayplayer]) - postimgtype2 = postimg; - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - postimgtype3 = postimg; - else if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - postimgtype4 = postimg; - else - postimgtype = postimg; + for (i = 0; i <= splitscreen; i++) + { + if (player == &players[displayplayers[i]]) + postimgtype[i] = postimg; + } } if (thiscam->momx || thiscam->momy) @@ -3754,11 +3623,11 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled fixed_t cam_height = cv_cam_height.value; thiscam->z = thiscam->floorz; - if (player == &players[secondarydisplayplayer]) + if (player == &players[displayplayers[1]]) cam_height = cv_cam2_height.value; - if (player == &players[thirddisplayplayer]) + if (player == &players[displayplayers[2]]) cam_height = cv_cam3_height.value; - if (player == &players[fourthdisplayplayer]) + if (player == &players[displayplayers[3]]) cam_height = cv_cam4_height.value; if (thiscam->z > player->mo->z + player->mo->height + FixedMul(cam_height*FRACUNIT + 16*FRACUNIT, player->mo->scale)) { @@ -6010,7 +5879,7 @@ void P_SetScale(mobj_t *mobj, fixed_t newscale) if (player) { - G_GhostAddScale(newscale); + G_GhostAddScale((INT32) (player - players), newscale); player->viewheight = FixedMul(FixedDiv(player->viewheight, oldscale), newscale); // Nonono don't calculate viewheight elsewhere, this is the best place for it! player->dashspeed = FixedMul(FixedDiv(player->dashspeed, oldscale), newscale); // Prevents the player from having to re-charge up spindash if the player grew in size } @@ -6214,12 +6083,12 @@ void P_RunOverlays(void) { angle_t viewingangle; - if (players[displayplayer].awayviewtics) - viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y); - else if (!camera.chase && players[displayplayer].mo) - viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y); + if (players[displayplayers[0]].awayviewtics) + viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y); + else if (!camera[0].chase && players[displayplayers[0]].mo) + viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y); else - viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, camera.x, camera.y); + viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, camera[0].x, camera[0].y); if (!(mo->state->frame & FF_ANIMATE) && mo->state->var1) viewingangle += ANGLE_180; @@ -6637,7 +6506,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 +6520,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 +6544,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 +6651,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); @@ -6788,7 +6662,7 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->target && mobj->target->health && mobj->target->player && !mobj->target->player->spectator && mobj->target->player->health && mobj->target->player->playerstate != PST_DEAD - /*&& players[displayplayer].mo && !players[displayplayer].spectator*/) + /*&& players[displayplayers[0]].mo && !players[displayplayers[0]].spectator*/) { fixed_t scale = 3*mobj->target->scale; mobj->color = mobj->target->color; @@ -6796,7 +6670,7 @@ void P_MobjThinker(mobj_t *mobj) if ((G_RaceGametype() || mobj->target->player->kartstuff[k_bumper] <= 0) #if 1 // Set to 0 to test without needing to host - || ((mobj->target->player == &players[displayplayer]) || P_IsLocalPlayer(mobj->target->player)) + || ((mobj->target->player == &players[displayplayers[0]]) || P_IsLocalPlayer(mobj->target->player)) #endif ) mobj->flags2 |= MF2_DONTDRAW; @@ -6807,10 +6681,10 @@ void P_MobjThinker(mobj_t *mobj) mobj->angle = R_PointToAngle(mobj->x, mobj->y) + ANGLE_90; // literally only happened because i wanted to ^L^R the SPR_ITEM's - if (!splitscreen && players[displayplayer].mo) + if (!splitscreen && players[displayplayers[0]].mo) { - scale = mobj->target->scale + FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayer].mo->x-mobj->target->x, - players[displayplayer].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale); + scale = mobj->target->scale + FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayers[0]].mo->x-mobj->target->x, + players[displayplayers[0]].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale); if (scale > 16*mobj->target->scale) scale = 16*mobj->target->scale; } @@ -6995,7 +6869,7 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->target && mobj->target->health && mobj->tracer && mobj->target->player && !mobj->target->player->spectator && mobj->target->player->health && mobj->target->player->playerstate != PST_DEAD - && players[displayplayer].mo && !players[displayplayer].spectator) + && players[displayplayers[0]].mo && !players[displayplayers[0]].spectator) { fixed_t scale = 3*mobj->target->scale; @@ -7017,8 +6891,8 @@ void P_MobjThinker(mobj_t *mobj) if (!splitscreen) { - scale = mobj->target->scale + FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayer].mo->x-mobj->target->x, - players[displayplayer].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale); + scale = mobj->target->scale + FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayers[0]].mo->x-mobj->target->x, + players[displayplayers[0]].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale); if (scale > 16*mobj->target->scale) scale = 16*mobj->target->scale; } @@ -7225,6 +7099,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 +7297,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 +7310,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: @@ -7467,7 +7344,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 +7365,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 +8034,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 +8054,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 +8067,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 +8230,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 +8239,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 +8260,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: @@ -8401,12 +8278,12 @@ void P_MobjThinker(mobj_t *mobj) angle_t viewingangle; statenum_t curstate = ((mobj->tics == 1) ? (mobj->state->nextstate) : ((statenum_t)(mobj->state-states))); - if (players[displayplayer].awayviewtics) - viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y); - else if (!camera.chase && players[displayplayer].mo) - viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y); + if (players[displayplayers[0]].awayviewtics) + viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].awayviewmobj->x, players[displayplayers[0]].awayviewmobj->y); + else if (!camera[0].chase && players[displayplayers[0]].mo) + viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y); else - viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, camera.x, camera.y); + viewingangle = R_PointToAngle2(mobj->target->x, mobj->target->y, camera[0].x, camera[0].y); if (curstate > S_THUNDERSHIELD15) viewingangle += ANGLE_180; @@ -8508,6 +8385,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 +8410,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; @@ -9997,7 +9879,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) break; } - mobj->color = SKINCOLOR_AQUA; + mobj->color = SKINCOLOR_CYAN; break; } case MT_MARBLETORCH: @@ -10662,13 +10544,13 @@ void P_PrecipitationEffects(void) // Local effects from here on out! // If we're not in game fully yet, we don't worry about them. - if (!playeringame[displayplayer] || !players[displayplayer].mo) + if (!playeringame[displayplayers[0]] || !players[displayplayers[0]].mo) return; if (sound_disabled) return; // Sound off? D'aw, no fun. - if (players[displayplayer].mo->subsector->sector->ceilingpic == skyflatnum) + if (players[displayplayers[0]].mo->subsector->sector->ceilingpic == skyflatnum) volume = 255; // Sky above? We get it full blast. else { @@ -10676,17 +10558,17 @@ void P_PrecipitationEffects(void) fixed_t closedist, newdist; // Essentially check in a 1024 unit radius of the player for an outdoor area. - yl = players[displayplayer].mo->y - 1024*FRACUNIT; - yh = players[displayplayer].mo->y + 1024*FRACUNIT; - xl = players[displayplayer].mo->x - 1024*FRACUNIT; - xh = players[displayplayer].mo->x + 1024*FRACUNIT; + yl = players[displayplayers[0]].mo->y - 1024*FRACUNIT; + yh = players[displayplayers[0]].mo->y + 1024*FRACUNIT; + xl = players[displayplayers[0]].mo->x - 1024*FRACUNIT; + xh = players[displayplayers[0]].mo->x + 1024*FRACUNIT; closedist = 2048*FRACUNIT; for (y = yl; y <= yh; y += FRACUNIT*64) for (x = xl; x <= xh; x += FRACUNIT*64) { if (R_PointInSubsector(x, y)->sector->ceilingpic == skyflatnum) // Found the outdoors! { - newdist = S_CalculateSoundDistance(players[displayplayer].mo->x, players[displayplayer].mo->y, 0, x, y, 0); + newdist = S_CalculateSoundDistance(players[displayplayers[0]].mo->x, players[displayplayers[0]].mo->y, 0, x, y, 0); if (newdist < closedist) closedist = newdist; } @@ -10701,7 +10583,7 @@ void P_PrecipitationEffects(void) volume = 255; if (sounds_rain && (!leveltime || leveltime % 80 == 1)) - S_StartSoundAtVolume(players[displayplayer].mo, sfx_rainin, volume); + S_StartSoundAtVolume(players[displayplayers[0]].mo, sfx_rainin, volume); if (!sounds_thunder) return; @@ -10709,7 +10591,7 @@ void P_PrecipitationEffects(void) if (effects_lightning && lightningStrike && volume) { // Large, close thunder sounds to go with our lightning. - S_StartSoundAtVolume(players[displayplayer].mo, sfx_litng1 + M_RandomKey(4), volume); + S_StartSoundAtVolume(players[displayplayers[0]].mo, sfx_litng1 + M_RandomKey(4), volume); } else if (thunderchance < 20) { @@ -10717,7 +10599,7 @@ void P_PrecipitationEffects(void) if (volume < 80) volume = 80; - S_StartSoundAtVolume(players[displayplayer].mo, sfx_athun1 + M_RandomKey(2), volume); + S_StartSoundAtVolume(players[displayplayers[0]].mo, sfx_athun1 + M_RandomKey(2), volume); } } @@ -10889,7 +10771,8 @@ void P_SpawnPlayer(INT32 playernum) } // spawn as spectator determination - if (!G_GametypeHasSpectators()) + if (multiplayer && demo.playback); // Don't mess with spectator values since the demo setup handles them already. + else if (!G_GametypeHasSpectators()) p->spectator = false; else if (netgame && p->jointime <= 1 && pcount) { @@ -11023,15 +10906,21 @@ void P_AfterPlayerSpawn(INT32 playernum) { player_t *p = &players[playernum]; mobj_t *mobj = p->mo; + UINT8 i; if (playernum == consoleplayer) - localangle = mobj->angle; - else if (playernum == secondarydisplayplayer) - localangle2 = mobj->angle; - else if (playernum == thirddisplayplayer) - localangle3 = mobj->angle; - else if (playernum == fourthdisplayplayer) - localangle4 = mobj->angle; + localangle[0] = mobj->angle; + else if (splitscreen) + { + for (i = 1; i <= splitscreen; i++) + { + if (playernum == displayplayers[i]) + { + localangle[i] = mobj->angle; + break; + } + } + } p->viewheight = 32<x, mobj->y, mobj->angle); - if (camera.chase) + for (i = 0; i <= splitscreen; i++) { - if (displayplayer == playernum) - P_ResetCamera(p, &camera); - } - if (camera2.chase && splitscreen) - { - if (secondarydisplayplayer == playernum) - P_ResetCamera(p, &camera2); - } - if (camera3.chase && splitscreen > 1) - { - if (thirddisplayplayer == playernum) - P_ResetCamera(p, &camera3); - } - if (camera4.chase && splitscreen > 2) - { - if (fourthdisplayplayer == playernum) - P_ResetCamera(p, &camera4); + if (camera[i].chase) + { + if (displayplayers[i] == playernum) + P_ResetCamera(p, &camera[i]); + } } if (CheckForReverseGravity) @@ -11124,7 +11001,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 +11009,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 +11070,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) @@ -11644,20 +11528,16 @@ void P_SpawnMapThing(mapthing_t *mthing) mobjtype_t macetype = MT_SMALLMACE; boolean firsttime; mobj_t *spawnee; - size_t line; + INT32 line; const size_t mthingi = (size_t)(mthing - mapthings); - // Why does P_FindSpecialLineFromTag not work here?!? - // Monster Iestyn: tag lists haven't been initialised yet for the map, that's why - for (line = 0; line < numlines; line++) - { - if (lines[line].special == 9 && lines[line].tag == mthing->angle) - break; - } + // Find the corresponding linedef special, using angle as tag + // P_FindSpecialLineFromTag works here now =D + line = P_FindSpecialLineFromTag(9, mthing->angle, -1); - if (line == numlines) + if (line == -1) { - CONS_Debug(DBG_GAMELOGIC, "Mace chain (mapthing #%s) needs tagged to a #9 parameter line (trying to find tag %d).\n", sizeu1(mthingi), mthing->angle); + CONS_Debug(DBG_GAMELOGIC, "Mace chain (mapthing #%s) needs to be tagged to a #9 parameter line (trying to find tag %d).\n", sizeu1(mthingi), mthing->angle); return; } /* diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 34402f1ac..03fb10d0f 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -1336,13 +1336,13 @@ static void Polyobj_rotateThings(polyobj_t *po, vertex_t origin, angle_t delta, if (turnthings == 2 || (turnthings == 1 && !mo->player)) { mo->angle += delta; if (mo->player == &players[consoleplayer]) - localangle = mo->angle; - else if (mo->player == &players[secondarydisplayplayer]) - localangle2 = mo->angle; - else if (mo->player == &players[thirddisplayplayer]) - localangle3 = mo->angle; - else if (mo->player == &players[fourthdisplayplayer]) - localangle4 = mo->angle; + localangle[0] += delta; + else if (mo->player == &players[displayplayers[1]]) + localangle[1] += delta; + else if (mo->player == &players[displayplayers[2]]) + localangle[2] += delta; + else if (mo->player == &players[displayplayers[3]]) + localangle[3] += delta; } } } diff --git a/src/p_saveg.c b/src/p_saveg.c index 975a4a5d2..7d2e9a307 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) @@ -263,25 +265,11 @@ static void P_NetArchivePlayers(void) if (flags & AWAYVIEW) WRITEUINT32(save_p, players[i].awayviewmobj->mobjnum); - WRITEUINT8(save_p, players[i].charability); - WRITEUINT8(save_p, players[i].charability2); WRITEUINT32(save_p, players[i].charflags); - WRITEUINT32(save_p, (UINT32)players[i].thokitem); - WRITEUINT32(save_p, (UINT32)players[i].spinitem); - WRITEUINT32(save_p, (UINT32)players[i].revitem); - WRITEFIXED(save_p, players[i].actionspd); - WRITEFIXED(save_p, players[i].mindash); - WRITEFIXED(save_p, players[i].maxdash); // SRB2kart WRITEUINT8(save_p, players[i].kartspeed); WRITEUINT8(save_p, players[i].kartweight); // - WRITEFIXED(save_p, players[i].normalspeed); - WRITEFIXED(save_p, players[i].runspeed); - WRITEUINT8(save_p, players[i].thrustfactor); - WRITEUINT8(save_p, players[i].accelstart); - WRITEUINT8(save_p, players[i].acceleration); - WRITEFIXED(save_p, players[i].jumpfactor); for (j = 0; j < MAXPREDICTTICS; j++) { @@ -426,6 +414,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) @@ -443,25 +433,11 @@ static void P_NetUnArchivePlayers(void) players[i].viewheight = 32<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) @@ -710,13 +684,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; @@ -2104,13 +2078,13 @@ static void LoadMobjThinker(actionf_p1 thinker) mobj->player->mo = mobj; // added for angle prediction if (consoleplayer == i) - localangle = mobj->angle; - if (secondarydisplayplayer == i) - localangle2 = mobj->angle; - if (thirddisplayplayer == i) - localangle3 = mobj->angle; - if (fourthdisplayplayer == i) - localangle4 = mobj->angle; + localangle[0] = mobj->angle; + if (displayplayers[1] == i) + localangle[1] = mobj->angle; + if (displayplayers[2] == i) + localangle[2] = mobj->angle; + if (displayplayers[3] == i) + localangle[3] = mobj->angle; } if (diff & MD_MOVEDIR) mobj->movedir = READANGLE(save_p); @@ -3313,6 +3287,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); @@ -3421,6 +3396,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); @@ -3447,7 +3423,7 @@ void P_SaveNetGame(void) mobj_t *mobj; INT32 i = 1; // don't start from 0, it'd be confused with a blank pointer otherwise - CV_SaveNetVars(&save_p); + CV_SaveNetVars(&save_p, false); P_NetArchiveMisc(); // Assign the mobjnumber for pointer tracking diff --git a/src/p_setup.c b/src/p_setup.c index 9ea8f0344..b812fc92b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -65,6 +65,10 @@ #include "lua_script.h" #include "lua_hook.h" +#if !defined (UNDER_CE) +#include +#endif + #if defined (_WIN32) || defined (_WIN32_WCE) #include #include @@ -194,6 +198,12 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) mapheaderinfo[num]->musname[6] = 0; DEH_WriteUndoline("MUSICTRACK", va("%d", mapheaderinfo[num]->mustrack), UNDO_NONE); mapheaderinfo[num]->mustrack = 0; + DEH_WriteUndoline("MUSICPOS", va("%d", mapheaderinfo[num]->muspos), UNDO_NONE); + mapheaderinfo[num]->muspos = 0; + DEH_WriteUndoline("MUSICINTERFADEOUT", va("%d", mapheaderinfo[num]->musinterfadeout), UNDO_NONE); + mapheaderinfo[num]->musinterfadeout = 0; + DEH_WriteUndoline("MUSICINTER", mapheaderinfo[num]->musintername, UNDO_NONE); + mapheaderinfo[num]->musintername[0] = '\0'; DEH_WriteUndoline("FORCECHARACTER", va("%d", mapheaderinfo[num]->forcecharacter), UNDO_NONE); mapheaderinfo[num]->forcecharacter[0] = '\0'; DEH_WriteUndoline("WEATHER", va("%d", mapheaderinfo[num]->weather), UNDO_NONE); @@ -1545,19 +1555,33 @@ static void P_LoadRawSideDefs2(void *data) { M_Memcpy(process,msd->bottomtexture,8); process[8] = '\0'; - sd->bottomtexture = get_number(process)-1; + sd->bottomtexture = get_number(process); } - M_Memcpy(process,msd->toptexture,8); - process[8] = '\0'; - sd->text = Z_Malloc(7, PU_LEVEL, NULL); - // If they type in O_ or D_ and their music name, just shrug, - // then copy the rest instead. - if ((process[0] == 'O' || process[0] == 'D') && process[7]) - M_Memcpy(sd->text, process+2, 6); - else // Assume it's a proper music name. - M_Memcpy(sd->text, process, 6); - sd->text[6] = 0; + if (!(msd->midtexture[0] == '-' && msd->midtexture[1] == '\0') || msd->midtexture[1] != '\0') + { + M_Memcpy(process,msd->midtexture,8); + process[8] = '\0'; + sd->midtexture = get_number(process); + } + + // always process if back sidedef, because we need that - symbol + sd->text = Z_Malloc(7, PU_LEVEL, NULL); + if (i == 1 || msd->toptexture[0] != '-' || msd->toptexture[1] != '\0') + { + M_Memcpy(process,msd->toptexture,8); + process[8] = '\0'; + + // If they type in O_ or D_ and their music name, just shrug, + // then copy the rest instead. + if ((process[0] == 'O' || process[0] == 'D') && process[7]) + M_Memcpy(sd->text, process+2, 6); + else // Assume it's a proper music name. + M_Memcpy(sd->text, process, 6); + sd->text[6] = 0; + } + else + sd->text[0] = 0; break; } @@ -2266,7 +2290,7 @@ static void P_LevelInitStuff(void) leveltime = 0; - localaiming = localaiming2 = localaiming3 = localaiming4 = 0; + memset(localaiming, 0, sizeof(localaiming)); // map object scale mapobjectscale = mapheaderinfo[gamemap-1]->mobj_scale; @@ -2533,29 +2557,29 @@ static void P_ForceCharacter(const char *forcecharskin) { if (splitscreen) { - SetPlayerSkin(secondarydisplayplayer, forcecharskin); - if ((unsigned)cv_playercolor2.value != skins[players[secondarydisplayplayer].skin].prefcolor && !modeattacking) + SetPlayerSkin(displayplayers[1], forcecharskin); + if ((unsigned)cv_playercolor2.value != skins[players[displayplayers[1]].skin].prefcolor && !modeattacking) { - CV_StealthSetValue(&cv_playercolor2, skins[players[secondarydisplayplayer].skin].prefcolor); - players[secondarydisplayplayer].skincolor = skins[players[secondarydisplayplayer].skin].prefcolor; + CV_StealthSetValue(&cv_playercolor2, skins[players[displayplayers[1]].skin].prefcolor); + players[displayplayers[1]].skincolor = skins[players[displayplayers[1]].skin].prefcolor; } if (splitscreen > 1) { - SetPlayerSkin(thirddisplayplayer, forcecharskin); - if ((unsigned)cv_playercolor3.value != skins[players[thirddisplayplayer].skin].prefcolor && !modeattacking) + SetPlayerSkin(displayplayers[2], forcecharskin); + if ((unsigned)cv_playercolor3.value != skins[players[displayplayers[2]].skin].prefcolor && !modeattacking) { - CV_StealthSetValue(&cv_playercolor3, skins[players[thirddisplayplayer].skin].prefcolor); - players[thirddisplayplayer].skincolor = skins[players[thirddisplayplayer].skin].prefcolor; + CV_StealthSetValue(&cv_playercolor3, skins[players[displayplayers[2]].skin].prefcolor); + players[displayplayers[2]].skincolor = skins[players[displayplayers[2]].skin].prefcolor; } if (splitscreen > 2) { - SetPlayerSkin(fourthdisplayplayer, forcecharskin); - if ((unsigned)cv_playercolor4.value != skins[players[fourthdisplayplayer].skin].prefcolor && !modeattacking) + SetPlayerSkin(displayplayers[3], forcecharskin); + if ((unsigned)cv_playercolor4.value != skins[players[displayplayers[3]].skin].prefcolor && !modeattacking) { - CV_StealthSetValue(&cv_playercolor4, skins[players[fourthdisplayplayer].skin].prefcolor); - players[fourthdisplayplayer].skincolor = skins[players[fourthdisplayplayer].skin].prefcolor; + CV_StealthSetValue(&cv_playercolor4, skins[players[displayplayers[3]].skin].prefcolor); + players[displayplayers[3]].skincolor = skins[players[displayplayers[3]].skin].prefcolor; } } } @@ -2726,7 +2750,7 @@ static boolean P_CanSave(void) if ((cursaveslot < 0) // Playing without saving || (modifiedgame && !savemoddata) // Game is modified || (netgame || multiplayer) // Not in single-player - || (demoplayback || demorecording || metalrecording) // Currently in demo + || (demo.playback || demo.recording || metalrecording) // Currently in demo || (players[consoleplayer].lives <= 0) // Completely dead || (modeattacking || ultimatemode || G_IsSpecialStage(gamemap))) // Specialized instances return false; @@ -2790,10 +2814,10 @@ boolean P_SetupLevel(boolean skipprecip) P_LevelInitStuff(); - postimgtype = postimgtype2 = postimgtype3 = postimgtype4 = postimg_none; + for (i = 0; i <= splitscreen; i++) + postimgtype[i] = postimg_none; - if (mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0' - && atoi(mapheaderinfo[gamemap-1]->forcecharacter) != 255) + if (mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0') P_ForceCharacter(mapheaderinfo[gamemap-1]->forcecharacter); // chasecam on in chaos, race, coop @@ -2826,7 +2850,7 @@ boolean P_SetupLevel(boolean skipprecip) // Encore mode fade to pink to white // This is handled BEFORE sounds are stopped. - if (rendermode != render_none && encoremode && !prevencoremode) + if (rendermode != render_none && encoremode && !prevencoremode && !demo.rewinding) { tic_t locstarttime, endtime, nowtime; @@ -2835,13 +2859,13 @@ boolean P_SetupLevel(boolean skipprecip) S_StartSound(NULL, sfx_ruby1); F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 122); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 209); F_WipeEndScreen(); F_RunWipe(wipedefs[wipe_speclevel_towhite], false); F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 120); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0); F_WipeEndScreen(); F_RunWipe(wipedefs[wipe_level_final], false); @@ -2874,11 +2898,11 @@ boolean P_SetupLevel(boolean skipprecip) // We should be fine starting it here. S_Start(); - levelfadecol = (encoremode && !ranspecialwipe ? 122 : 120); + levelfadecol = (encoremode && !ranspecialwipe ? 209 : 0); // Let's fade to white here // But only if we didn't do the encore startup wipe - if (rendermode != render_none && !ranspecialwipe) + if (rendermode != render_none && !ranspecialwipe && !demo.rewinding) { F_WipeStartScreen(); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); @@ -3042,6 +3066,10 @@ boolean P_SetupLevel(boolean skipprecip) P_PrepareThings(lastloadedmaplumpnum + ML_THINGS); } + // init gravity, tag lists, + // anything that P_ResetDynamicSlopes/P_LoadThings needs to know + P_InitSpecials(); + #ifdef ESLOPE P_ResetDynamicSlopes(); #endif @@ -3060,7 +3088,6 @@ boolean P_SetupLevel(boolean skipprecip) if (loadprecip) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame) P_SpawnPrecipitation(); - globalweather = mapheaderinfo[gamemap-1]->weather; // The waypoint data that's in PU_LEVEL needs to be reset back to 0/NULL now since PU_LEVEL was cleared K_ClearWaypoints(); @@ -3072,7 +3099,6 @@ boolean P_SetupLevel(boolean skipprecip) CONS_Printf("Waypoints were not able to be setup! Player positions will not work correctly."); } } - #ifdef HWRENDER // not win32 only 19990829 by Kin if (rendermode != render_soft && rendermode != render_none) { @@ -3121,9 +3147,9 @@ boolean P_SetupLevel(boolean skipprecip) } } - if (modeattacking == ATTACKING_RECORD && !demoplayback) + if (modeattacking == ATTACKING_RECORD && !demo.playback) P_LoadRecordGhosts(); - /*else if (modeattacking == ATTACKING_NIGHTS && !demoplayback) + /*else if (modeattacking == ATTACKING_NIGHTS && !demo.playback) P_LoadNightsGhosts();*/ if (G_TagGametype()) @@ -3171,25 +3197,25 @@ boolean P_SetupLevel(boolean skipprecip) ? cv_basenumlaps.value : mapheaderinfo[gamemap - 1]->numlaps); + // Start recording replay in multiplayer with a temp filename + //@TODO I'd like to fix dedis crashing when recording replays for the future too... + if (!demo.playback && multiplayer && !dedicated) { + static char buf[256]; + sprintf(buf, "replay"PATHSEP"online"PATHSEP"%d-%s", (int) (time(NULL)), G_BuildMapName(gamemap)); + + I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755); + I_mkdir(va("%s"PATHSEP"replay"PATHSEP"online", srb2home), 0755); + G_RecordDemo(buf); + } + // =========== // landing point for netgames. netgameskip: if (!dedicated) { - P_SetupCamera(displayplayer, &camera); - if (splitscreen) - { - P_SetupCamera(secondarydisplayplayer, &camera2); - if (splitscreen > 1) - { - P_SetupCamera(thirddisplayplayer, &camera3); - if (splitscreen > 2) - { - P_SetupCamera(fourthdisplayplayer, &camera4); - } - } - } + for (i = 0; i <= splitscreen; i++) + P_SetupCamera(displayplayers[i], &camera[i]); // Salt: CV_ClearChangedFlags() messes with your settings :( /*if (!cv_cam_height.changed) @@ -3226,12 +3252,11 @@ 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! + displayplayers[0] = consoleplayer; // Start with your OWN view, please! } /*if (cv_useranalog.value) @@ -3256,6 +3281,7 @@ boolean P_SetupLevel(boolean skipprecip) wantedcalcdelay = wantedfrequency*2; indirectitemcooldown = 0; + hyubgone = 0; mapreset = 0; nospectategrief = 0; thwompsactive = false; @@ -3309,7 +3335,10 @@ boolean P_SetupLevel(boolean skipprecip) savedata.lives = 0; } - skyVisible = skyVisible1 = skyVisible2 = skyVisible3 = skyVisible4 = true; // assume the skybox is visible on level load. + // assume the skybox is visible on level load. + skyVisible = true; + memset(skyVisiblePerPlayer, true, sizeof(skyVisiblePerPlayer)); + if (loadprecip) // uglier hack { // to make a newly loaded level start on the second frame. INT32 buf = gametic % BACKUPTICS; diff --git a/src/p_slopes.c b/src/p_slopes.c index c6416b75a..76af7bfde 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -264,7 +264,7 @@ void P_SpawnSlope_Line(int linenum) if(!line->frontsector || !line->backsector) { - CONS_Printf("P_SpawnSlope_Line used on a line without two sides.\n"); + CONS_Debug(DBG_SETUP, "P_SpawnSlope_Line used on a line without two sides. (line number %i)\n", linenum); return; } diff --git a/src/p_spec.c b/src/p_spec.c index 4077ea692..a37a2b8e5 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2243,7 +2243,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) I_Assert(!mo || !P_MobjWasRemoved(mo)); // If mo is there, mo must be valid! if (mo && mo->player && botingame) - bot = players[secondarydisplayplayer].mo; + bot = players[displayplayers[1]].mo; // note: only commands with linedef types >= 400 && < 500 can be used switch (line->special) @@ -2381,35 +2381,21 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (mo->player) { + UINT8 i; + if (bot) // This might put poor Tails in a wall if he's too far behind! D: But okay, whatever! >:3 P_TeleportMove(bot, bot->x + x, bot->y + y, bot->z + z); - if (splitscreen > 2 && mo->player == &players[fourthdisplayplayer] && camera4.chase) + + for (i = 0; i <= splitscreen; i++) { - camera4.x += x; - camera4.y += y; - camera4.z += z; - camera4.subsector = R_PointInSubsector(camera4.x, camera4.y); - } - else if (splitscreen > 1 && mo->player == &players[thirddisplayplayer] && camera3.chase) - { - camera3.x += x; - camera3.y += y; - camera3.z += z; - camera3.subsector = R_PointInSubsector(camera3.x, camera3.y); - } - else if (splitscreen && mo->player == &players[secondarydisplayplayer] && camera2.chase) - { - camera2.x += x; - camera2.y += y; - camera2.z += z; - camera2.subsector = R_PointInSubsector(camera2.x, camera2.y); - } - else if (camera.chase && mo->player == &players[displayplayer]) - { - camera.x += x; - camera.y += y; - camera.z += z; - camera.subsector = R_PointInSubsector(camera.x, camera.y); + if (mo->player == &players[displayplayers[i]] && camera[i].chase) + { + camera[i].x += x; + camera[i].y += y; + camera[i].z += z; + camera[i].subsector = R_PointInSubsector(camera[i].x, camera[i].y); + break; + } } } } @@ -2440,18 +2426,71 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) // console player only unless NOCLIMB is set if ((line->flags & ML_NOCLIMB) || (mo && mo->player && P_IsLocalPlayer(mo->player))) { - UINT16 tracknum = (UINT16)sides[line->sidenum[0]].bottomtexture; + boolean musicsame = (!sides[line->sidenum[0]].text[0] || !strnicmp(sides[line->sidenum[0]].text, S_MusicName(), 7)); + UINT16 tracknum = (UINT16)max(sides[line->sidenum[0]].bottomtexture, 0); + INT32 position = (INT32)max(sides[line->sidenum[0]].midtexture, 0); + UINT32 prefadems = (UINT32)max(sides[line->sidenum[0]].textureoffset >> FRACBITS, 0); + UINT32 postfadems = (UINT32)max(sides[line->sidenum[0]].rowoffset >> FRACBITS, 0); + UINT8 fadetarget = (UINT8)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].textureoffset >> FRACBITS : 0, 0); + INT16 fadesource = (INT16)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].rowoffset >> FRACBITS : -1, -1); - strncpy(mapmusname, sides[line->sidenum[0]].text, 7); - mapmusname[6] = 0; + // Seek offset from current song position + if (line->flags & ML_EFFECT1) + { + // adjust for loop point if subtracting + if (position < 0 && S_GetMusicLength() && + S_GetMusicPosition() > S_GetMusicLoopPoint() && + S_GetMusicPosition() + position < S_GetMusicLoopPoint()) + position = max(S_GetMusicLength() - (S_GetMusicLoopPoint() - (S_GetMusicPosition() + position)), 0); + else + position = max(S_GetMusicPosition() + position, 0); + } - mapmusflags = tracknum & MUSIC_TRACKMASK; - if (!(line->flags & ML_BLOCKMONSTERS)) - mapmusflags |= MUSIC_RELOADRESET; + // Fade current music to target volume (if music won't be changed) + if ((line->flags & ML_EFFECT2) && fadetarget && musicsame) + { + // 0 fadesource means fade from current volume. + // meaning that we can't specify volume 0 as the source volume -- this starts at 1. + if (!fadesource) + fadesource = -1; - S_ChangeMusic(mapmusname, mapmusflags, !(line->flags & ML_EFFECT4)); - if (!(line->flags & ML_EFFECT3)) - S_ShowMusicCredit(); + if (!postfadems) + S_SetInternalMusicVolume(fadetarget); + else + S_FadeMusicFromVolume(fadetarget, fadesource, postfadems); + + if (!(line->flags & ML_EFFECT3)) + S_ShowMusicCredit(); + + if (position) + S_SetMusicPosition(position); + } + // Change the music and apply position/fade operations + else + { + strncpy(mapmusname, sides[line->sidenum[0]].text, 7); + mapmusname[6] = 0; + + mapmusflags = tracknum & MUSIC_TRACKMASK; + if (!(line->flags & ML_BLOCKMONSTERS)) + mapmusflags |= MUSIC_RELOADRESET; + if (line->flags & ML_BOUNCY) + mapmusflags |= MUSIC_FORCERESET; + + mapmusposition = position; + + S_ChangeMusicEx(mapmusname, mapmusflags, !(line->flags & ML_EFFECT4), position, + !(line->flags & ML_EFFECT2) ? prefadems : 0, + !(line->flags & ML_EFFECT2) ? postfadems : 0); + + if ((line->flags & ML_EFFECT2) && fadetarget) + { + if (!postfadems) + S_SetInternalMusicVolume(fadetarget); + else + S_FadeMusicFromVolume(fadetarget, fadesource, postfadems); + } + } // Except, you can use the ML_BLOCKMONSTERS flag to change this behavior. // if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn. @@ -2515,8 +2554,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (line->flags & ML_NOCLIMB) { // play the sound from nowhere, but only if display player triggered it - if (mo && mo->player && (mo->player == &players[displayplayer] || mo->player == &players[secondarydisplayplayer] - || mo->player == &players[thirddisplayplayer] || mo->player == &players[fourthdisplayplayer])) + if (mo && mo->player && P_IsDisplayPlayer(mo->player)) S_StartSound(NULL, sfxnum); } else if (line->flags & ML_EFFECT4) @@ -3834,16 +3872,16 @@ DoneSection2: if (player->mo->scale > mapobjectscale) linespeed = FixedMul(linespeed, mapobjectscale + (player->mo->scale - mapobjectscale)); - if (!demoplayback || P_AnalogMove(player)) + if (!demo.playback || P_AnalogMove(player)) { if (player == &players[consoleplayer]) - localangle = player->mo->angle; - else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = player->mo->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = player->mo->angle; + localangle[0] = player->mo->angle; + else if (player == &players[displayplayers[1]]) + localangle[1] = player->mo->angle; + else if (player == &players[displayplayers[2]]) + localangle[2] = player->mo->angle; + else if (player == &players[displayplayers[3]]) + localangle[3] = player->mo->angle; } if (!(lines[i].flags & ML_EFFECT4)) @@ -4247,13 +4285,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)) @@ -5620,6 +5659,45 @@ static void P_RunLevelLoadExecutors(void) } } +/** Before things are loaded, initialises certain stuff in case they're needed + * by P_ResetDynamicSlopes or P_LoadThings. This was split off from + * P_SpawnSpecials, in case you couldn't tell. + * + * \sa P_SpawnSpecials, P_InitTagLists + * \author Monster Iestyn + */ +void P_InitSpecials(void) +{ + // Set the default gravity. Custom gravity overrides this setting. + gravity = (FRACUNIT*8)/10; + + // Defaults in case levels don't have them set. + sstimer = 90*TICRATE + 6; + totalrings = 1; + + CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false; + + // Set curWeather + switch (mapheaderinfo[gamemap-1]->weather) + { + case PRECIP_SNOW: // snow + case PRECIP_RAIN: // rain + case PRECIP_STORM: // storm + case PRECIP_STORM_NORAIN: // storm w/o rain + case PRECIP_STORM_NOSTRIKES: // storm w/o lightning + curWeather = mapheaderinfo[gamemap-1]->weather; + break; + default: // blank/none + curWeather = PRECIP_NONE; + break; + } + + // Set globalweather + globalweather = mapheaderinfo[gamemap-1]->weather; + + P_InitTagLists(); // Create xref tables for tags +} + /** After the map has loaded, scans for specials that spawn 3Dfloors and * thinkers. * @@ -5641,15 +5719,6 @@ void P_SpawnSpecials(INT32 fromnetsave) // but currently isn't. (void)fromnetsave; - // Set the default gravity. Custom gravity overrides this setting. - gravity = (FRACUNIT*8)/10; - - // Defaults in case levels don't have them set. - sstimer = 90*TICRATE + 6; - totalrings = 1; - - CheckForBustableBlocks = CheckForBouncySector = CheckForQuicksand = CheckForMarioBlocks = CheckForFloatBob = CheckForReverseGravity = false; - // Init special SECTORs. sector = sectors; for (i = 0; i < numsectors; i++, sector++) @@ -5698,20 +5767,6 @@ void P_SpawnSpecials(INT32 fromnetsave) } } - if (mapheaderinfo[gamemap-1]->weather == 2) // snow - curWeather = PRECIP_SNOW; - else if (mapheaderinfo[gamemap-1]->weather == 3) // rain - curWeather = PRECIP_RAIN; - else if (mapheaderinfo[gamemap-1]->weather == 1) // storm - curWeather = PRECIP_STORM; - else if (mapheaderinfo[gamemap-1]->weather == 5) // storm w/o rain - curWeather = PRECIP_STORM_NORAIN; - else if (mapheaderinfo[gamemap-1]->weather == 6) // storm w/o lightning - curWeather = PRECIP_STORM_NOSTRIKES; - else - curWeather = PRECIP_NONE; - - P_InitTagLists(); // Create xref tables for tags P_SearchForDisableLinedefs(); // Disable linedefs are now allowed to disable *any* line P_SpawnScrollers(); // Add generalized scrollers @@ -7842,44 +7897,44 @@ void T_Pusher(pusher_t *p) thing->player->pflags |= PF_SLIDING; thing->angle = R_PointToAngle2 (0, 0, xspeed<<(FRACBITS-PUSH_FACTOR), yspeed<<(FRACBITS-PUSH_FACTOR)); - if (!demoplayback || P_AnalogMove(thing->player)) + if (!demo.playback || P_AnalogMove(thing->player)) { if (thing->player == &players[consoleplayer]) { - if (thing->angle - localangle > ANGLE_180) - localangle -= (localangle - thing->angle) / 8; + if (thing->angle - localangle[0] > ANGLE_180) + localangle[0] -= (localangle[0] - thing->angle) / 8; else - localangle += (thing->angle - localangle) / 8; + localangle[0] += (thing->angle - localangle[0]) / 8; } - else if (thing->player == &players[secondarydisplayplayer]) + else if (thing->player == &players[displayplayers[1]]) { - if (thing->angle - localangle2 > ANGLE_180) - localangle2 -= (localangle2 - thing->angle) / 8; + if (thing->angle - localangle[1] > ANGLE_180) + localangle[1] -= (localangle[1] - thing->angle) / 8; else - localangle2 += (thing->angle - localangle2) / 8; + localangle[1] += (thing->angle - localangle[1]) / 8; } - else if (thing->player == &players[thirddisplayplayer]) + else if (thing->player == &players[displayplayers[2]]) { - if (thing->angle - localangle3 > ANGLE_180) - localangle3 -= (localangle3 - thing->angle) / 8; + if (thing->angle - localangle[2] > ANGLE_180) + localangle[2] -= (localangle[2] - thing->angle) / 8; else - localangle3 += (thing->angle - localangle3) / 8; + localangle[2] += (thing->angle - localangle[2]) / 8; } - else if (thing->player == &players[fourthdisplayplayer]) + else if (thing->player == &players[displayplayers[3]]) { - if (thing->angle - localangle4 > ANGLE_180) - localangle4 -= (localangle4 - thing->angle) / 8; + if (thing->angle - localangle[3] > ANGLE_180) + localangle[3] -= (localangle[3] - thing->angle) / 8; else - localangle4 += (thing->angle - localangle4) / 8; + localangle[3] += (thing->angle - localangle[3]) / 8; } /*if (thing->player == &players[consoleplayer]) - localangle = thing->angle; - else if (thing->player == &players[secondarydisplayplayer]) - localangle2 = thing->angle; - else if (thing->player == &players[thirddisplayplayer]) - localangle3 = thing->angle; - else if (thing->player == &players[fourthdisplayplayer]) - localangle4 = thing->angle;*/ + localangle[0] = thing->angle; + else if (thing->player == &players[displayplayers[1]]) + localangle[1] = thing->angle; + else if (thing->player == &players[displayplayers[2]]) + localangle[2] = thing->angle; + else if (thing->player == &players[displayplayers[3]]) + localangle[3] = thing->angle;*/ } } diff --git a/src/p_spec.h b/src/p_spec.h index 1231aeda8..b604ac951 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -32,6 +32,7 @@ void P_InitPicAnims(void); void P_SetupLevelFlatAnims(void); // at map load +void P_InitSpecials(void); void P_SpawnSpecials(INT32 fromnetsave); // every tic diff --git a/src/p_telept.c b/src/p_telept.c index 24e201fc1..74f9d462c 100644 --- a/src/p_telept.c +++ b/src/p_telept.c @@ -36,6 +36,7 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, INT32 flags2) { const INT32 takeflags2 = MF2_TWOD|MF2_OBJECTFLIP; + UINT8 i; // the move is ok, // so link the thing into its new position @@ -64,23 +65,25 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, // absolute angle position if (thing == players[consoleplayer].mo) - localangle = angle; - if (thing == players[secondarydisplayplayer].mo) - localangle2 = angle; - if (thing == players[thirddisplayplayer].mo) - localangle3 = angle; - if (thing == players[fourthdisplayplayer].mo) - localangle4 = angle; + localangle[0] = angle; + else if (splitscreen) + { + for (i = 1; i <= splitscreen; i++) + { + if (thing == players[displayplayers[i]].mo) + { + localangle[i] = angle; + break; + } + } + } // move chasecam at new player location - if (splitscreen > 2 && camera4.chase && thing->player == &players[fourthdisplayplayer]) - P_ResetCamera(thing->player, &camera4); - else if (splitscreen > 1 && camera3.chase && thing->player == &players[thirddisplayplayer]) - P_ResetCamera(thing->player, &camera3); - else if (splitscreen && camera2.chase && thing->player == &players[secondarydisplayplayer]) - P_ResetCamera(thing->player, &camera2); - else if (camera.chase && thing->player == &players[displayplayer]) - P_ResetCamera(thing->player, &camera); + for (i = 0; i <= splitscreen; i++) + { + if (thing->player == &players[displayplayers[i]] && camera[i].chase) + P_ResetCamera(thing->player, &camera[i]); + } // don't run in place after a teleport thing->player->cmomx = thing->player->cmomy = 0; @@ -123,6 +126,8 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, */ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle, boolean flash, boolean dontstopmove) { + UINT8 i; + if (!P_TeleportMove(thing, x, y, z)) return false; @@ -144,24 +149,26 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle thing->reactiontime = TICRATE/2; // don't move for about half a second // absolute angle position - if (thing->player == &players[consoleplayer]) - localangle = angle; - if (thing->player == &players[secondarydisplayplayer]) - localangle2 = angle; - if (thing->player == &players[thirddisplayplayer]) - localangle3 = angle; - if (thing->player == &players[fourthdisplayplayer]) - localangle4 = angle; + if (thing == players[consoleplayer].mo) + localangle[0] = angle; + else if (splitscreen) + { + for (i = 1; i <= splitscreen; i++) + { + if (thing == players[displayplayers[i]].mo) + { + localangle[i] = angle; + break; + } + } + } // move chasecam at new player location - if (splitscreen > 2 && camera4.chase && thing->player == &players[fourthdisplayplayer]) - P_ResetCamera(thing->player, &camera4); - else if (splitscreen > 1 && camera3.chase && thing->player == &players[thirddisplayplayer]) - P_ResetCamera(thing->player, &camera3); - else if (splitscreen && camera2.chase && thing->player == &players[secondarydisplayplayer]) - P_ResetCamera(thing->player, &camera2); - else if (camera.chase && thing->player == &players[displayplayer]) - P_ResetCamera(thing->player, &camera); + for (i = 0; i <= splitscreen; i++) + { + if (thing->player == &players[displayplayers[i]] && camera[i].chase) + P_ResetCamera(thing->player, &camera[i]); + } // don't run in place after a teleport if (!dontstopmove) diff --git a/src/p_tick.c b/src/p_tick.c index 8509a7621..0a340d80c 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -13,6 +13,7 @@ #include "doomstat.h" #include "g_game.h" +#include "g_input.h" #include "p_local.h" #include "z_zone.h" #include "s_sound.h" @@ -583,7 +584,7 @@ void P_Ticker(boolean run) { P_MapStart(); OP_ObjectplaceMovement(&players[0]); - P_MoveChaseCamera(&players[0], &camera, false); + P_MoveChaseCamera(&players[0], &camera[0], false); P_MapEnd(); return; } @@ -591,18 +592,60 @@ void P_Ticker(boolean run) // Check for pause or menu up in single player if (paused || P_AutoPause()) - return; + { + if (demo.rewinding && leveltime > 0) + { + leveltime = (leveltime-1) & ~3; + G_PreviewRewind(leveltime); + } - postimgtype = postimgtype2 = postimgtype3 = postimgtype4 = postimg_none; + return; + } + + for (i = 0; i <= splitscreen; i++) + postimgtype[i] = postimg_none; P_MapStart(); if (run) { - if (demorecording) - G_WriteDemoTiccmd(&players[consoleplayer].cmd, 0); - if (demoplayback) - G_ReadDemoTiccmd(&players[consoleplayer].cmd, 0); + if (demo.recording) + { + G_WriteDemoExtraData(); + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + G_WriteDemoTiccmd(&players[i].cmd, i); + } + if (demo.playback) + { + +#ifdef DEMO_COMPAT_100 + if (demo.version == 0x0001) + { + G_ReadDemoTiccmd(&players[consoleplayer].cmd, 0); + } + else + { +#endif + G_ReadDemoExtraData(); + for (i = 0; i < MAXPLAYERS; i++) + if (playeringame[i]) + { + //@TODO all this throwdir stuff shouldn't be here! But it's added to maintain 1.0.4 compat for now... + // Remove for 1.1! + if (players[i].cmd.buttons & BT_FORWARD) + players[i].kartstuff[k_throwdir] = 1; + else if (players[i].cmd.buttons & BT_BACKWARD) + players[i].kartstuff[k_throwdir] = -1; + else + players[i].kartstuff[k_throwdir] = 0; + + G_ReadDemoTiccmd(&players[i].cmd, i); + } +#ifdef DEMO_COMPAT_100 + } +#endif + } for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) @@ -610,7 +653,7 @@ void P_Ticker(boolean run) } // Keep track of how long they've been playing! - if (!demoplayback) // Don't increment if a demo is playing. + if (!demo.playback) // Don't increment if a demo is playing. totalplaytime++; /*if (!useNightsSS && G_IsSpecialStage(gamemap)) @@ -681,6 +724,8 @@ void P_Ticker(boolean run) if (indirectitemcooldown) indirectitemcooldown--; + if (hyubgone) + hyubgone--; if (G_BattleGametype()) { @@ -704,10 +749,25 @@ void P_Ticker(boolean run) G_ReadMetalTic(metalplayback); if (metalrecording) G_WriteMetalTic(players[consoleplayer].mo); - if (demorecording) - G_WriteGhostTic(players[consoleplayer].mo); - if (demoplayback) // Use Ghost data for consistency checks. - G_ConsGhostTic(); + + if (demo.recording) + { + G_WriteAllGhostTics(); + + if (cv_recordmultiplayerdemos.value && (demo.savemode == DSM_NOTSAVING || demo.savemode == DSM_WILLAUTOSAVE)) + if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime && InputDown(gc_lookback, 1)) + demo.savemode = DSM_TITLEENTRY; + } + else if (demo.playback) // Use Ghost data for consistency checks. + { +#ifdef DEMO_COMPAT_100 + if (demo.version == 0x0001) + G_ConsGhostTic(0); + else +#endif + G_ConsAllGhostTics(); + } + if (modeattacking) G_GhostTicker(); @@ -723,17 +783,17 @@ void P_Ticker(boolean run) } // Always move the camera. - if (camera.chase) - P_MoveChaseCamera(&players[displayplayer], &camera, false); - if (splitscreen && camera2.chase) - P_MoveChaseCamera(&players[secondarydisplayplayer], &camera2, false); - if (splitscreen > 1 && camera3.chase) - P_MoveChaseCamera(&players[thirddisplayplayer], &camera3, false); - if (splitscreen > 2 && camera4.chase) - P_MoveChaseCamera(&players[fourthdisplayplayer], &camera4, false); + for (i = 0; i <= splitscreen; i++) + { + if (camera[i].chase) + P_MoveChaseCamera(&players[displayplayers[i]], &camera[i], false); + } P_MapEnd(); + if (demo.playback) + G_StoreRewindInfo(); + // Z_CheckMemCleanup(); } @@ -743,7 +803,8 @@ void P_PreTicker(INT32 frames) INT32 i,framecnt; ticcmd_t temptic; - postimgtype = postimgtype2 = postimgtype3 = postimgtype4 = postimg_none; + for (i = 0; i <= splitscreen; i++) + postimgtype[i] = postimg_none; for (framecnt = 0; framecnt < frames; ++framecnt) { diff --git a/src/p_user.c b/src/p_user.c index 76f57a9ba..61d8f36f3 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -169,10 +169,10 @@ fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move) boolean P_AutoPause(void) { // Don't pause even on menu-up or focus-lost in netgames or record attack - if (netgame || modeattacking) + if (netgame || modeattacking || demo.title) return false; - return (menuactive || window_notinfocus); + return ((menuactive && !demo.playback) || ( window_notinfocus && cv_pauseifunfocused.value )); } // @@ -654,13 +654,13 @@ static void P_DeNightserizePlayer(player_t *player) // Restore aiming angle if (player == &players[consoleplayer]) - localaiming = 0; - else if (player == &players[secondarydisplayplayer]) - localaiming2 = 0; - else if (player == &players[thirddisplayplayer]) - localaiming3 = 0; - else if (player == &players[fourthdisplayplayer]) - localaiming4 = 0; + localaiming[0] = 0; + else if (player == &players[displayplayers[1]]) + localaiming[1] = 0; + else if (player == &players[displayplayers[2]]) + localaiming[2] = 0; + else if (player == &players[displayplayers[3]]) + localaiming[3] = 0; if (player->mo->tracer) P_RemoveMobj(player->mo->tracer); @@ -1147,29 +1147,32 @@ boolean P_EndingMusic(player_t *player) if (!P_IsLocalPlayer(player)) // Only applies to a local player return false; + if (multiplayer && demo.playback) // Don't play this in multiplayer replays + return false; + // Event - Level Finish // Check for if this is valid or not if (splitscreen) { - if (!((players[displayplayer].exiting || (players[displayplayer].pflags & PF_TIMEOVER)) - || (players[secondarydisplayplayer].exiting || (players[secondarydisplayplayer].pflags & PF_TIMEOVER)) - || ((splitscreen < 2) && (players[thirddisplayplayer].exiting || (players[thirddisplayplayer].pflags & PF_TIMEOVER))) - || ((splitscreen < 3) && (players[fourthdisplayplayer].exiting || (players[fourthdisplayplayer].pflags & PF_TIMEOVER))))) + if (!((players[displayplayers[0]].exiting || (players[displayplayers[0]].pflags & PF_TIMEOVER)) + || (players[displayplayers[1]].exiting || (players[displayplayers[1]].pflags & PF_TIMEOVER)) + || ((splitscreen < 2) && (players[displayplayers[2]].exiting || (players[displayplayers[2]].pflags & PF_TIMEOVER))) + || ((splitscreen < 3) && (players[displayplayers[3]].exiting || (players[displayplayers[3]].pflags & PF_TIMEOVER))))) return false; - bestlocalplayer = &players[displayplayer]; - bestlocalpos = ((players[displayplayer].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[displayplayer].kartstuff[k_position]); + bestlocalplayer = &players[displayplayers[0]]; + bestlocalpos = ((players[displayplayers[0]].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[displayplayers[0]].kartstuff[k_position]); #define setbests(p) \ if (((players[p].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[p].kartstuff[k_position]) < bestlocalpos) \ { \ bestlocalplayer = &players[p]; \ bestlocalpos = ((players[p].pflags & PF_TIMEOVER) ? MAXPLAYERS+1 : players[p].kartstuff[k_position]); \ } - setbests(secondarydisplayplayer); + setbests(displayplayers[1]); if (splitscreen > 1) - setbests(thirddisplayplayer); + setbests(displayplayers[2]); if (splitscreen > 2) - setbests(fourthdisplayplayer); + setbests(displayplayers[3]); #undef setbests } else @@ -1250,12 +1253,12 @@ void P_RestoreMusic(player_t *player) else if (players[p].kartstuff[k_invincibilitytimer] > bestlocaltimer) \ { wantedmus = 1; bestlocaltimer = players[p].kartstuff[k_invincibilitytimer]; } \ } - setbests(displayplayer); - setbests(secondarydisplayplayer); + setbests(displayplayers[0]); + setbests(displayplayers[1]); if (splitscreen > 1) - setbests(thirddisplayplayer); + setbests(displayplayers[2]); if (splitscreen > 2) - setbests(fourthdisplayplayer); + setbests(displayplayers[3]); #undef setbests } else @@ -1283,7 +1286,7 @@ void P_RestoreMusic(player_t *player) if (G_RaceGametype() && player->laps >= (UINT8)(cv_numlaps.value - 1)) S_SpeedMusic(1.2f); #endif - S_ChangeMusic(mapmusname, mapmusflags, true); + S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); } } } @@ -1513,10 +1516,39 @@ fixed_t P_GetPlayerSpinHeight(player_t *player) // boolean P_IsLocalPlayer(player_t *player) { - return ((splitscreen > 2 && player == &players[fourthdisplayplayer]) - || (splitscreen > 1 && player == &players[thirddisplayplayer]) - || (splitscreen && player == &players[secondarydisplayplayer]) - || player == &players[consoleplayer]); + UINT8 i; + + if (player == &players[consoleplayer]) + return true; + else if (splitscreen) + { + for (i = 1; i <= splitscreen; i++) // Skip P1 + { + if (player == &players[displayplayers[i]]) + return true; + } + } + + return false; +} + +// +// P_IsDisplayPlayer +// +// Returns true if player is +// currently being watched. +// +boolean P_IsDisplayPlayer(player_t *player) +{ + UINT8 i; + + for (i = 0; i <= splitscreen; i++) // DON'T skip P1 + { + if (player == &players[displayplayers[i]]) + return true; + } + + return false; } // @@ -1653,113 +1685,6 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) return ghost; } -// -// P_SpawnThokMobj -// -// Spawns the appropriate thok object on the player -// -void P_SpawnThokMobj(player_t *player) -{ - mobj_t *mobj; - mobjtype_t type = player->thokitem; - fixed_t zheight; - - if (player->skincolor == 0) - return; - - if (player->spectator) - return; - - if (type == MT_GHOST) - mobj = P_SpawnGhostMobj(player->mo); // virtually does everything here for us - else - { - if (player->mo->eflags & MFE_VERTICALFLIP) - zheight = player->mo->z + player->mo->height + FixedDiv(P_GetPlayerHeight(player) - player->mo->height, 3*FRACUNIT) - FixedMul(mobjinfo[type].height, player->mo->scale); - else - zheight = player->mo->z - FixedDiv(P_GetPlayerHeight(player) - player->mo->height, 3*FRACUNIT); - - if (!(player->mo->eflags & MFE_VERTICALFLIP) && zheight < player->mo->floorz && !(mobjinfo[type].flags & MF_NOCLIPHEIGHT)) - zheight = player->mo->floorz; - else if (player->mo->eflags & MFE_VERTICALFLIP && zheight + FixedMul(mobjinfo[type].height, player->mo->scale) > player->mo->ceilingz && !(mobjinfo[type].flags & MF_NOCLIPHEIGHT)) - zheight = player->mo->ceilingz - FixedMul(mobjinfo[type].height, player->mo->scale); - - mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type); - - // set to player's angle, just in case - mobj->angle = player->mo->angle; - - // color and skin - mobj->color = player->mo->color; - mobj->skin = player->mo->skin; - - // vertical flip - if (player->mo->eflags & MFE_VERTICALFLIP) - mobj->flags2 |= MF2_OBJECTFLIP; - mobj->eflags |= (player->mo->eflags & MFE_VERTICALFLIP); - - // scale - P_SetScale(mobj, player->mo->scale); - mobj->destscale = player->mo->scale; - } - - P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do - if (demorecording) - G_GhostAddThok(); -} - -// -// P_SpawnSpinMobj -// -// Spawns the appropriate spin object on the player -// -void P_SpawnSpinMobj(player_t *player, mobjtype_t type) -{ - mobj_t *mobj; - fixed_t zheight; - - if (player->skincolor == 0) - return; - - if (player->spectator) - return; - - if (type == MT_GHOST) - mobj = P_SpawnGhostMobj(player->mo); // virtually does everything here for us - else - { - if (player->mo->eflags & MFE_VERTICALFLIP) - zheight = player->mo->z + player->mo->height + FixedDiv(P_GetPlayerHeight(player) - player->mo->height, 3*FRACUNIT) - FixedMul(mobjinfo[type].height, player->mo->scale); - else - zheight = player->mo->z - FixedDiv(P_GetPlayerHeight(player) - player->mo->height, 3*FRACUNIT); - - if (!(player->mo->eflags & MFE_VERTICALFLIP) && zheight < player->mo->floorz && !(mobjinfo[type].flags & MF_NOCLIPHEIGHT)) - zheight = player->mo->floorz; - else if (player->mo->eflags & MFE_VERTICALFLIP && zheight + FixedMul(mobjinfo[type].height, player->mo->scale) > player->mo->ceilingz && !(mobjinfo[type].flags & MF_NOCLIPHEIGHT)) - zheight = player->mo->ceilingz - FixedMul(mobjinfo[type].height, player->mo->scale); - - mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type); - - // set to player's angle, just in case - mobj->angle = player->mo->angle; - - // color and skin - mobj->color = player->mo->color; - mobj->skin = player->mo->skin; - - // vertical flip - if (player->mo->eflags & MFE_VERTICALFLIP) - mobj->flags2 |= MF2_OBJECTFLIP; - mobj->eflags |= (player->mo->eflags & MFE_VERTICALFLIP); - - // scale - P_SetScale(mobj, player->mo->scale); - mobj->destscale = player->mo->scale; - } - - P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do -} - // // P_DoPlayerExit // @@ -1769,11 +1694,7 @@ void P_DoPlayerExit(player_t *player) if (player->exiting || mapreset) return; - if ((player == &players[consoleplayer] - || (splitscreen && player == &players[secondarydisplayplayer]) - || (splitscreen > 1 && player == &players[thirddisplayplayer]) - || (splitscreen > 2 && player == &players[fourthdisplayplayer])) - && (!player->spectator && !demoplayback)) + if (P_IsLocalPlayer(player) && (!player->spectator && !demo.playback)) legitimateexit = true; if (G_RaceGametype()) // If in Race Mode, allow @@ -1832,6 +1753,9 @@ void P_DoPlayerExit(player_t *player) player->powers[pw_spacetime] = 0; player->kartstuff[k_cardanimation] = 0; // srb2kart: reset battle animation + if (player == &players[consoleplayer]) + demo.savebutton = leveltime; + /*if (playeringame[player-players] && netgame && !circuitmap) CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);*/ } @@ -1960,13 +1884,13 @@ static void P_CheckBustableBlocks(player_t *player) // ...or are drilling in NiGHTS (or Metal Sonic) if (!(rover->flags & FF_SHATTER) && !(rover->flags & FF_SPINBUST) && !((player->pflags & PF_SPINNING) && !(player->pflags & PF_JUMPED)) - && (player->charability != CA_GLIDEANDCLIMB && !player->powers[pw_super]) + && (/*player->charability != CA_GLIDEANDCLIMB &&*/ !player->powers[pw_super]) && !(player->pflags & PF_DRILLING) && !metalrecording) continue; // Only Knuckles can break this rock... - if (!(rover->flags & FF_SHATTER) && (rover->flags & FF_ONLYKNUX) && !(player->charability == CA_GLIDEANDCLIMB)) - continue; + /*if (!(rover->flags & FF_SHATTER) && (rover->flags & FF_ONLYKNUX) && !(player->charability == CA_GLIDEANDCLIMB)) + 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); @@ -2413,12 +2337,12 @@ static void P_CheckInvincibilityTimer(player_t *player) //if (player->powers[pw_shield] & SH_FIREFLOWER) //{ // player->mo->color = SKINCOLOR_WHITE; - // G_GhostAddColor(GHC_FIREFLOWER); + // G_GhostAddColor((INT32) (player - players), GHC_FIREFLOWER); //} //else { player->mo->color = player->skincolor; - G_GhostAddColor(GHC_NORMAL); + G_GhostAddColor((INT32) (player - players), GHC_NORMAL); } } @@ -2468,7 +2392,7 @@ static void P_DoBubbleBreath(player_t *player) return; // Tails stirs up the water while flying in it - if (player->powers[pw_tailsfly] && (leveltime & 1) && player->charability != CA_SWIM) + /*if (player->powers[pw_tailsfly] && (leveltime & 1) && player->charability != CA_SWIM) { fixed_t radius = (3*player->mo->radius)>>1; angle_t fa = ((leveltime%45)*FINEANGLES/8) & FINEMASK; @@ -2494,7 +2418,7 @@ static void P_DoBubbleBreath(player_t *player) stirwaterz, MT_SMALLBUBBLE); bubble->destscale = player->mo->scale; P_SetScale(bubble,bubble->destscale); - } + }*/ } // @@ -2509,8 +2433,7 @@ static void P_DoPlayerHeadSigns(player_t *player) // If you're "IT", show a big "IT" over your head for others to see. if (player->pflags & PF_TAGIT) { - if (!(player == &players[consoleplayer] || player == &players[displayplayer] || player == &players[secondarydisplayplayer] - || player == &players[thirddisplayplayer] || player == &players[fourthdisplayplayer])) // Don't display it on your own view. + if (!P_IsDisplayPlayer(player)) // Don't display it on your own view. { if (!(player->mo->eflags & MFE_VERTICALFLIP)) P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height, MT_TAG); @@ -2998,13 +2921,13 @@ static void P_DoClimbing(player_t *player) // SRB2kart - unused } if (player == &players[consoleplayer]) - localangle = player->mo->angle; - else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = player->mo->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = player->mo->angle; + localangle[0] = player->mo->angle; + else if (player == &players[displayplayers[1]]) + localangle[1] = player->mo->angle; + else if (player == &players[displayplayers[2]]) + localangle[2] = player->mo->angle; + else if (player == &players[displayplayers[3]]) + localangle[3] = player->mo->angle; if (player->climbing == 0) P_SetPlayerMobjState(player->mo, S_PLAY_ATK1); @@ -3634,354 +3557,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 -// -// Jump routine for the player -// -void P_DoJump(player_t *player, boolean soundandstate) -{ - fixed_t factor; - const fixed_t dist6 = FixedMul(FixedDiv(player->speed, player->mo->scale), player->actionspd)/20; - - return; - - if (player->pflags & PF_JUMPSTASIS) - return; - - if (!player->jumpfactor) - return; - - if (player->kartstuff[k_spinouttimer]) // SRB2kart - return; - - /* // SRB2kart - climbing in a kart? - if (player->climbing) - { - // Jump this high. - if (player->powers[pw_super]) - player->mo->momz = 5*FRACUNIT; - else if (player->mo->eflags & MFE_UNDERWATER) - player->mo->momz = 2*FRACUNIT; - else - player->mo->momz = 15*(FRACUNIT/4); - - player->mo->angle = player->mo->angle - ANGLE_180; // Turn around from the wall you were climbing. - - if (player == &players[consoleplayer]) - localangle = player->mo->angle; // Adjust the local control angle. - else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = player->mo->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = player->mo->angle; - - player->climbing = 0; // Stop climbing, duh! - P_InstaThrust(player->mo, player->mo->angle, FixedMul(6*FRACUNIT, player->mo->scale)); // Jump off the wall. - } - // Quicksand jumping. - else if (P_InQuicksand(player->mo)) - { - if (player->mo->ceilingz-player->mo->floorz <= player->mo->height-1) - return; - player->mo->momz += (39*(FRACUNIT/4))>>1; - if (player->mo->momz >= 6*FRACUNIT) - player->mo->momz = 6*FRACUNIT; //max momz in quicksand - else if (player->mo->momz < 0) // still descending? - player->mo->momz = (39*(FRACUNIT/4))>>1; // just default to the jump height. - } - else*/ if (!(player->pflags & PF_JUMPED)) // Spin Attack - { - if (player->mo->ceilingz-player->mo->floorz <= player->mo->height-1) - return; - - // Jump this high. - if (player->pflags & PF_CARRIED) - { - player->mo->momz = 9*FRACUNIT; - player->pflags &= ~PF_CARRIED; - /*if (player-players == consoleplayer && botingame) - CV_SetValue(&cv_analog2, true);*/ - } - else if (player->pflags & PF_ITEMHANG) - { - player->mo->momz = 9*FRACUNIT; - player->pflags &= ~PF_ITEMHANG; - } - else if (player->pflags & PF_ROPEHANG) - { - player->mo->momz = 12*FRACUNIT; - player->pflags &= ~PF_ROPEHANG; - P_SetTarget(&player->mo->tracer, NULL); - } - else if (player->mo->eflags & MFE_GOOWATER) - { - player->mo->momz = 7*FRACUNIT; - if (player->charability == CA_JUMPBOOST && onground) - { - if (player->charability2 == CA2_MULTIABILITY) - player->mo->momz += FixedMul(FRACUNIT/4, dist6); - else - player->mo->momz += FixedMul(FRACUNIT/8, dist6); - } - } - else if (maptol & TOL_NIGHTS) - player->mo->momz = 24*FRACUNIT; - else - player->mo->momz = 3*FRACUNIT; // Kart jump momentum. - /* // SRB2kart - Okay enough of that. - else if (player->powers[pw_super]) - { - if (player->charability == CA_FLOAT) - player->mo->momz = 28*FRACUNIT; //Obscene jump height anyone? - else if (player->charability == CA_SLOWFALL) - player->mo->momz = 37*(FRACUNIT/2); //Less obscene because during super, floating propells oneself upward. - else // Default super jump momentum. - player->mo->momz = 13*FRACUNIT; - - // Add a boost for super characters with float/slowfall and multiability. - if (player->charability2 == CA2_MULTIABILITY && - (player->charability == CA_FLOAT || player->charability == CA_SLOWFALL)) - player->mo->momz += 2*FRACUNIT; - else if (player->charability == CA_JUMPBOOST) - { - if (player->charability2 == CA2_MULTIABILITY) - player->mo->momz += FixedMul(FRACUNIT/4, dist6); - else - player->mo->momz += FixedMul(FRACUNIT/8, dist6); - } - } - else if (player->charability2 == CA2_MULTIABILITY && - (player->charability == CA_DOUBLEJUMP || player->charability == CA_FLOAT || player->charability == CA_SLOWFALL)) - { - // Multiability exceptions, since some abilities cannot effectively use it and need a boost. - if (player->charability == CA_DOUBLEJUMP) - player->mo->momz = 23*(FRACUNIT/2); // Increased jump height instead of infinite jumps. - else if (player->charability == CA_FLOAT || player->charability == CA_SLOWFALL) - player->mo->momz = 12*FRACUNIT; // Increased jump height due to ineffective repeat. - } - else - { - player->mo->momz = 39*(FRACUNIT/4); // Default jump momentum. - if (player->charability == CA_JUMPBOOST && onground) - { - if (player->charability2 == CA2_MULTIABILITY) - player->mo->momz += FixedMul(FRACUNIT/4, dist6); - else - player->mo->momz += FixedMul(FRACUNIT/8, dist6); - } - } - */ - - // Reduce player momz by 58.5% when underwater. - if (player->mo->eflags & MFE_UNDERWATER) - player->mo->momz = FixedMul(player->mo->momz, FixedDiv(117*FRACUNIT, 200*FRACUNIT)); - - player->jumping = 1; - } - - factor = player->jumpfactor; - - if (twodlevel || (player->mo->flags2 & MF2_TWOD)) - factor += player->jumpfactor / 10; - - P_SetObjectMomZ(player->mo, FixedMul(factor, player->mo->momz), false); // Custom height - - // set just an eensy above the ground - if (player->mo->eflags & MFE_VERTICALFLIP) - { - player->mo->z--; - if (player->mo->pmomz < 0) - player->mo->momz += player->mo->pmomz; // Add the platform's momentum to your jump. - else - player->mo->pmomz = 0; - } - else - { - player->mo->z++; - if (player->mo->pmomz > 0) - player->mo->momz += player->mo->pmomz; // Add the platform's momentum to your jump. - else - player->mo->pmomz = 0; - } - player->mo->eflags &= ~MFE_APPLYPMOMZ; - - player->pflags |= PF_JUMPED; - - if (soundandstate) - { - if (!player->spectator) - S_StartSound(player->mo, sfx_jump); // Play jump sound! - - /* // SRB2kart - don't need jump frames - if (!(player->charability2 == CA2_SPINDASH)) - P_SetPlayerMobjState(player->mo, S_PLAY_SPRING); - else - P_SetPlayerMobjState(player->mo, S_PLAY_ATK1); - */ - } -} - // // P_DoSpinDash // @@ -4031,8 +3606,8 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd) // SRB2kart - unused. // Now spawn the color thok circle. P_SpawnSpinMobj(player, player->revitem); - if (demorecording) - G_GhostAddRev(); + if (demo.recording) + G_GhostAddRev((INT32) (player - players)); } } // If not moving up or down, and travelling faster than a speed of four while not holding @@ -4105,7 +3680,7 @@ void P_DoJumpShield(player_t *player) return; player->pflags &= ~PF_JUMPED; - P_DoJump(player, false); + //P_DoJump(player, false); player->pflags &= ~PF_JUMPED; player->secondjump = 0; player->jumping = 0; @@ -4163,338 +3738,10 @@ void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range) } } - P_SpawnThokMobj(player); + //P_SpawnThokMobj(player); player->pflags |= PF_THOKKED; } -// -// P_DoJumpStuff -// -// Handles player jumping -// -static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) -{ - if (player->pflags & PF_JUMPSTASIS) - return; - - if (cmd->buttons & BT_BRAKE && !(player->pflags & PF_JUMPDOWN) && !player->exiting && !P_PlayerInPain(player)) - { - if (onground || player->climbing || player->pflags & (PF_CARRIED|PF_ITEMHANG|PF_ROPEHANG)) - {} - else if (player->pflags & PF_MACESPIN && player->mo->tracer) - {} - else if (!(player->pflags & PF_SLIDING) && ((gametype != GT_CTF) || (!player->gotflag))) - { -#ifdef HAVE_BLUA - if (!LUAh_JumpSpinSpecial(player)) -#endif - switch (player->charability) - { - case CA_TELEKINESIS: - if (player->pflags & PF_JUMPED) - { - if (!(player->pflags & PF_THOKKED) || (player->charability2 == CA2_MULTIABILITY)) - { - P_Telekinesis(player, - -FixedMul(player->actionspd, player->mo->scale), // -ve thrust (pulling towards player) - FixedMul(384*FRACUNIT, player->mo->scale)); - } - } - break; - case CA_AIRDRILL: - if (player->pflags & PF_JUMPED) - { - if (player->pflags & PF_THOKKED) // speed up falling down - { - if (player->secondjump < 42) - player->secondjump ++; - } - } - break; - default: - break; - } - } - } - - if (player->charability == CA_AIRDRILL) - { - if (player->pflags & PF_JUMPED) - { - if (player->flyangle > 0 && player->pflags & PF_THOKKED) - { - player->flyangle--; - - P_SetObjectMomZ(player->mo, ((player->flyangle-24 - player->secondjump*3)*((player->actionspd>>FRACBITS)/12 + 1)<mo->eflags & MFE_UNDERWATER)) - P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->normalspeed, player->mo->scale)*(80-player->flyangle - (player->actionspd>>FRACBITS)/2)/80); - else - P_InstaThrust(player->mo, player->mo->angle, ((FixedMul(player->normalspeed - player->actionspd/4, player->mo->scale))*2)/3); - } - } - } - - if (cmd->buttons & BT_DRIFT && !player->exiting && !P_PlayerInPain(player)) - { -#ifdef HAVE_BLUA - if (LUAh_JumpSpecial(player)) - ; - else -#endif - if (player->pflags & PF_JUMPDOWN) // all situations below this require jump button not to be pressed already - ; - else - // Jump S3&K style while in quicksand. - if (P_InQuicksand(player->mo)) - { - P_DoJump(player, true); - player->secondjump = 0; - player->pflags &= ~PF_THOKKED; - } - else - // can't jump while in air, can't jump while jumping - if (onground || player->climbing || player->pflags & (PF_CARRIED|PF_ITEMHANG|PF_ROPEHANG)) - { - P_DoJump(player, true); - player->secondjump = 0; - player->pflags &= ~PF_THOKKED; - } - /* // SRB2kart - no jumpy power things - else if (player->pflags & PF_MACESPIN && player->mo->tracer) - { - player->pflags &= ~PF_MACESPIN; - player->powers[pw_flashing] = TICRATE/4; - } - else if (player->pflags & PF_SLIDING || (gametype == GT_CTF && player->gotflag)) - ; - else if (P_SuperReady(player)) - { - // If you can turn super and aren't already, - // and you don't have a shield, do it! - P_DoSuperTransformation(player, false); - } - else if (player->pflags & PF_JUMPED) - { -#ifdef HAVE_BLUA - if (!LUAh_AbilitySpecial(player)) -#endif - switch (player->charability) - { - case CA_THOK: - case CA_HOMINGTHOK: - case CA_JUMPTHOK: // Credit goes to CZ64 and Sryder13 for the original - // Now it's Sonic's abilities turn! - // THOK! - if (!(player->pflags & PF_THOKKED) || (player->charability2 == CA2_MULTIABILITY)) - { - // Catapult the player - fixed_t actionspd = player->actionspd; - if (player->mo->eflags & MFE_UNDERWATER) - actionspd >>= 1; - if ((player->charability == CA_JUMPTHOK) && !(player->pflags & PF_THOKKED)) - { - player->pflags &= ~PF_JUMPED; - P_DoJump(player, false); - } - P_InstaThrust(player->mo, player->mo->angle, FixedMul(actionspd, player->mo->scale)); - - if (maptol & TOL_2D) - { - player->mo->momx /= 2; - player->mo->momy /= 2; - } - else if (player->charability == CA_HOMINGTHOK) - { - player->mo->momx /= 3; - player->mo->momy /= 3; - } - - if (player->mo->info->attacksound && !player->spectator) - S_StartSound(player->mo, player->mo->info->attacksound); // Play the THOK sound - - P_SpawnThokMobj(player); - - if (player->charability == CA_HOMINGTHOK && !player->homing) - { - if (P_LookForEnemies(player)) - { - if (player->mo->tracer) - player->homing = 3*TICRATE; - } - } - - player->pflags &= ~(PF_SPINNING|PF_STARTDASH); - player->pflags |= PF_THOKKED; - } - break; - - case CA_FLY: - case CA_SWIM: // Swim - // If currently in the air from a jump, and you pressed the - // button again and have the ability to fly, do so! - if (player->charability == CA_SWIM && !(player->mo->eflags & MFE_UNDERWATER)) - ; // Can't do anything if you're a fish out of water! - else if (!(player->pflags & PF_THOKKED) && !(player->powers[pw_tailsfly])) - { - //P_SetPlayerMobjState(player->mo, S_PLAY_ABL1); // Change to the flying animation - - player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer - - player->pflags &= ~(PF_JUMPED|PF_SPINNING|PF_STARTDASH); - player->pflags |= PF_THOKKED; - } - break; - case CA_GLIDEANDCLIMB: - // Now Knuckles-type abilities are checked. - // If you can turn super and aren't already, - // and you don't have a shield, do it! - if (!(player->pflags & PF_THOKKED) || player->charability2 == CA2_MULTIABILITY) - { - INT32 glidespeed = player->actionspd; - - player->pflags |= PF_GLIDING|PF_THOKKED; - player->glidetime = 0; - - if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])) - { - // Glide at double speed while super. - glidespeed *= 2; - player->pflags &= ~PF_THOKKED; - } - - //P_SetPlayerMobjState(player->mo, S_PLAY_ABL1); - P_InstaThrust(player->mo, player->mo->angle, FixedMul(glidespeed, player->mo->scale)); - player->pflags &= ~(PF_SPINNING|PF_STARTDASH); - } - break; - case CA_DOUBLEJUMP: // Double-Jump - if (!(player->pflags & PF_THOKKED)) - { - player->pflags &= ~PF_JUMPED; - P_DoJump(player, true); - - // Allow infinite double jumping if super. - if (!player->powers[pw_super]) - player->pflags |= PF_THOKKED; - } - break; - case CA_FLOAT: // Float - case CA_SLOWFALL: // Slow descent hover - if (!player->secondjump) - player->secondjump = 1; - break; - case CA_TELEKINESIS: - if (!(player->pflags & PF_THOKKED) || player->charability2 == CA2_MULTIABILITY) - { - P_Telekinesis(player, - FixedMul(player->actionspd, player->mo->scale), // +ve thrust (pushing away from player) - FixedMul(384*FRACUNIT, player->mo->scale)); - } - break; - case CA_FALLSWITCH: - if (!(player->pflags & PF_THOKKED) || player->charability2 == CA2_MULTIABILITY) - { - player->mo->momz = -player->mo->momz; - P_SpawnThokMobj(player); - player->pflags |= PF_THOKKED; - } - break; - - case CA_AIRDRILL: - if (!(player->pflags & PF_THOKKED) || player->charability2 == CA2_MULTIABILITY) - { - player->flyangle = 56 + (60-(player->actionspd>>FRACBITS))/3; - player->pflags |= PF_THOKKED; - S_StartSound(player->mo, sfx_spndsh); - } - break; - default: - break; - } - } - else if (player->pflags & PF_THOKKED) - { -#ifdef HAVE_BLUA - if (!LUAh_AbilitySpecial(player)) -#endif - switch (player->charability) - { - case CA_FLY: - case CA_SWIM: // Swim - if (player->charability == CA_SWIM && !(player->mo->eflags & MFE_UNDERWATER)) - ; // Can't do anything if you're a fish out of water! - else if (player->powers[pw_tailsfly]) // If currently flying, give an ascend boost. - { - if (!player->fly1) - player->fly1 = 20; - else - player->fly1 = 2; - - if (player->charability == CA_SWIM) - player->fly1 /= 2; - - // Slow down! - if (player->speed > FixedMul(8*FRACUNIT, player->mo->scale) && player->speed > FixedMul(player->normalspeed>>1, player->mo->scale)) - P_Thrust(player->mo, R_PointToAngle2(0,0,player->mo->momx,player->mo->momy), FixedMul(-4*FRACUNIT, player->mo->scale)); - } - break; - default: - break; - } - } - else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_JUMP && !player->powers[pw_super]) - P_DoJumpShield(player); - */ - } - - if (cmd->buttons & BT_DRIFT) - { - player->pflags |= PF_JUMPDOWN; - - if ((gametype != GT_CTF || !player->gotflag) && !player->exiting) - { - if (player->secondjump == 1) - { - if (player->charability == CA_FLOAT) - player->mo->momz = 0; - else if (player->charability == CA_SLOWFALL) - { - if (player->powers[pw_super]) - { - if (P_MobjFlip(player->mo)*player->mo->momz < gravity*16) - player->mo->momz = P_MobjFlip(player->mo)*gravity*16; //Float upward 4x as fast while super. - } - else if (P_MobjFlip(player->mo)*player->mo->momz < -gravity*4) - player->mo->momz = P_MobjFlip(player->mo)*-gravity*4; - } - player->pflags &= ~PF_SPINNING; - } - } - } - else // If not pressing the jump button - { - player->pflags &= ~PF_JUMPDOWN; - - // Repeat abilities, but not double jump! - if ((player->charability2 == CA2_MULTIABILITY && player->charability != CA_DOUBLEJUMP) - || (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))) - player->secondjump = 0; - else if (player->charability == CA_FLOAT && player->secondjump == 1) - player->secondjump = 2; - - - // If letting go of the jump button while still on ascent, cut the jump height. - if (player->pflags & PF_JUMPED && P_MobjFlip(player->mo)*player->mo->momz > 0 && player->jumping == 1) - { - player->mo->momz >>= 1; - player->jumping = 0; - } - } -} - boolean P_AnalogMove(player_t *player) { return player->pflags & PF_ANALOGMODE; @@ -4518,14 +3765,14 @@ boolean P_AnalogMove(player_t *player) fixed_t tempx = 0, tempy = 0; angle_t tempangle, origtempangle; - if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - thiscam = &camera4; - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - thiscam = &camera3; - else if (splitscreen && player == &players[secondarydisplayplayer]) - thiscam = &camera2; + if (splitscreen > 2 && player == &players[displayplayers[3]]) + thiscam = &camera[3]; + else if (splitscreen > 1 && player == &players[displayplayers[2]]) + thiscam = &camera[2]; + else if (splitscreen && player == &players[displayplayers[1]]) + thiscam = &camera[1]; else - thiscam = &camera; + thiscam = &camera[0]; if (!cmd->forwardmove && !cmd->sidemove) return 0; @@ -4666,13 +3913,13 @@ static void P_2dMovement(player_t *player) } if (player == &players[consoleplayer]) - localangle = player->mo->angle; - else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = player->mo->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = player->mo->angle; + localangle[0] = player->mo->angle; + else if (player == &players[displayplayers[1]]) + localangle[1] = player->mo->angle; + else if (player == &players[displayplayers[2]]) + localangle[2] = player->mo->angle; + else if (player == &players[displayplayers[3]]) + localangle[3] = player->mo->angle; if (player->pflags & PF_GLIDING) movepushangle = player->mo->angle; @@ -6202,13 +5449,13 @@ static void P_NiGHTSMovement(player_t *player) P_SetMobjStateNF(player->mo->tracer, leveltime & 1 ? flystate : flystate+1); if (player == &players[consoleplayer]) - localangle = player->mo->angle; - else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = player->mo->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = player->mo->angle; + localangle[0] = player->mo->angle; + else if (player == &players[displayplayers[1]]) + localangle[1] = player->mo->angle; + else if (player == &players[displayplayers[2]]) + localangle[2] = player->mo->angle; + else if (player == &players[displayplayers[3]]) + localangle[3] = player->mo->angle; if (still) { @@ -6235,13 +5482,13 @@ static void P_NiGHTSMovement(player_t *player) movingangle = InvAngle(movingangle); if (player == &players[consoleplayer]) - localaiming = movingangle; - else if (player == &players[secondarydisplayplayer]) - localaiming2 = movingangle; - else if (player == &players[thirddisplayplayer]) - localaiming3 = movingangle; - else if (player == &players[fourthdisplayplayer]) - localaiming4 = movingangle; + localaiming[0] = movingangle; + else if (player == &players[displayplayers[1]]) + localaiming[1] = movingangle; + else if (player == &players[displayplayers[2]]) + localaiming[2] = movingangle; + else if (player == &players[displayplayers[3]]) + localaiming[3] = movingangle; player->mo->tracer->angle = player->mo->angle; @@ -6382,99 +5629,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) @@ -6598,7 +5752,7 @@ static void P_MovePlayer(player_t *player) { if (G_IsSpecialStage(gamemap)) { - if (player == &players[displayplayer]) // only play the sound for yourself landing + if (player == &players[displayplayers[0]]) // only play the sound for yourself landing S_StartSound(NULL, sfx_s3k6a); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) @@ -6662,7 +5816,7 @@ static void P_MovePlayer(player_t *player) //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 ?: 1)); + // (INT16) (cmd->angleturn - (player->mo->angle>>16)) * 100 / (angle_diff ? angle_diff : 1)); } } @@ -6790,14 +5944,7 @@ static void P_MovePlayer(player_t *player) P_SetPlayerMobjState(player->mo, S_KART_STND1); // SRB2kart - was S_PLAY_STND } - // Cap the speed limit on a spindash - // Up the 60*FRACUNIT number to boost faster, you speed demon you! - if (player->dashspeed > FixedMul(player->maxdash, player->mo->scale)) - player->dashspeed = FixedMul(player->maxdash, player->mo->scale); - else if (player->dashspeed > 0 && player->dashspeed < FixedMul(player->mindash, player->mo->scale)) - player->dashspeed = FixedMul(player->mindash, player->mo->scale); - - if (!(player->charability == CA_GLIDEANDCLIMB) || player->gotflag) // If you can't glide, then why the heck would you be gliding? + if (/*!(player->charability == CA_GLIDEANDCLIMB) ||*/ player->gotflag) // If you can't glide, then why the heck would you be gliding? { /* // SRB2kart - ??? if (player->pflags & PF_GLIDING || player->climbing) @@ -7013,8 +6160,8 @@ static void P_MovePlayer(player_t *player) if (player->pflags & PF_SPINNING && player->speed > FixedMul(15<mo->scale) && !(player->pflags & PF_JUMPED)) { P_SpawnSpinMobj(player, player->spinitem); - if (demorecording) - G_GhostAddSpin(); + if (demo.recording) + G_GhostAddSpin((INT32) (player - players)); } */ @@ -7050,7 +6197,7 @@ static void P_MovePlayer(player_t *player) P_DoSpinDash(player, cmd); */ // jumping - P_DoJumpStuff(player, cmd); + //P_DoJumpStuff(player, cmd); /* // If you're not spinning, you'd better not be spindashing! @@ -7106,18 +6253,18 @@ static void P_MovePlayer(player_t *player) } // Otherwise, face the direction you're travelling. else if (player->panim == PA_WALK || player->panim == PA_RUN || player->panim == PA_ROLL - || (/*(player->mo->state >= &states[S_PLAY_ABL1] && player->mo->state <= &states[S_PLAY_SPC4]) && */player->charability == CA_FLY)) // SRB2kart - idk + /*|| ((player->mo->state >= &states[S_PLAY_ABL1] && player->mo->state <= &states[S_PLAY_SPC4]) && player->charability == CA_FLY)*/) // SRB2kart - idk player->mo->angle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy); // Update the local angle control. if (player == &players[consoleplayer]) - localangle = player->mo->angle; - else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = player->mo->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = player->mo->angle; + localangle[0] = player->mo->angle; + else if (player == &players[displayplayers[1]]) + localangle[1] = player->mo->angle; + else if (player == &players[displayplayers[2]]) + localangle[2] = player->mo->angle; + else if (player == &players[displayplayers[3]]) + localangle[3] = player->mo->angle; } #endif @@ -7296,8 +6443,8 @@ static void P_MovePlayer(player_t *player) speed = R_PointToDist2(player->rmomx, player->rmomy, 0, 0); - if (speed > player->normalspeed-5*FRACUNIT) - speed = player->normalspeed-5*FRACUNIT; + if (speed > K_GetKartSpeed(player, false)-(5<= runnyspeed) player->fovadd = speed-runnyspeed; @@ -7439,13 +6586,13 @@ static void P_DoZoomTube(player_t *player) player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y); if (player == &players[consoleplayer]) - localangle = player->mo->angle; - else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = player->mo->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = player->mo->angle; + localangle[0] = player->mo->angle; + else if (player == &players[displayplayers[1]]) + localangle[1] = player->mo->angle; + else if (player == &players[displayplayers[2]]) + localangle[2] = player->mo->angle; + else if (player == &players[displayplayers[3]]) + localangle[3] = player->mo->angle; } #if 0 if (player->mo->state != &states[S_KART_SPIN]) @@ -7654,12 +6801,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)); @@ -7833,13 +6982,13 @@ void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target if (source->player) { if (source->player == &players[consoleplayer]) - localangle = source->angle; - else if (source->player == &players[secondarydisplayplayer]) - localangle2 = source->angle; - else if (source->player == &players[thirddisplayplayer]) - localangle3 = source->angle; - else if (source->player == &players[fourthdisplayplayer]) - localangle4 = source->angle; + localangle[0] = source->angle; + else if (source->player == &players[displayplayers[1]]) + localangle[1] = source->angle; + else if (source->player == &players[displayplayers[2]]) + localangle[2] = source->angle; + else if (source->player == &players[displayplayers[3]]) + localangle[3] = source->angle; } // change slope @@ -7850,7 +6999,7 @@ void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target dist = 1; if (source->type == MT_DETON && enemy->player) // For Deton Chase (Unused) - ns = FixedDiv(FixedMul(enemy->player->normalspeed, enemy->scale), FixedDiv(20*FRACUNIT,17*FRACUNIT)); + ns = FixedDiv(FixedMul(K_GetKartSpeed(enemy->player, false), enemy->scale), FixedDiv(20*FRACUNIT,17*FRACUNIT)); else if (source->type != MT_PLAYER) { if (source->threshold == 32000) @@ -7859,7 +7008,7 @@ void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target ns = FixedMul(source->info->speed, source->scale); } else if (source->player) - ns = FixedDiv(FixedMul(source->player->actionspd, source->scale), 3*FRACUNIT/2); + ns = FixedDiv(FixedMul(K_GetKartSpeed(source->player, false), source->scale), 3*FRACUNIT/2); source->momx = FixedMul(FixedDiv(enemy->x - source->x, dist), ns); source->momy = FixedMul(FixedDiv(enemy->y - source->y, dist), ns); @@ -7972,7 +7121,7 @@ notrealplayer: // P_MoveCamera: make sure the camera is not outside the world and looks at the player avatar // -camera_t camera, camera2, camera3, camera4; // Four cameras, three for splitscreen +camera_t camera[MAXSPLITSCREENPLAYERS]; // Four cameras, three for splitscreen static void CV_CamRotate_OnChange(void) { @@ -8077,10 +7226,10 @@ void P_ResetCamera(player_t *player, camera_t *thiscam) thiscam->y = y; thiscam->z = z; - if (!(thiscam == &camera && (cv_cam_still.value || cv_analog.value)) - && !(thiscam == &camera2 && (cv_cam2_still.value || cv_analog2.value)) - && !(thiscam == &camera3 && (cv_cam3_still.value || cv_analog3.value)) - && !(thiscam == &camera4 && (cv_cam4_still.value || cv_analog4.value))) + if (!(thiscam == &camera[0] && (cv_cam_still.value || cv_analog.value)) + && !(thiscam == &camera[1] && (cv_cam2_still.value || cv_analog2.value)) + && !(thiscam == &camera[2] && (cv_cam3_still.value || cv_analog3.value)) + && !(thiscam == &camera[3] && (cv_cam4_still.value || cv_analog4.value))) { thiscam->angle = player->mo->angle; thiscam->aiming = 0; @@ -8138,46 +7287,49 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (player->spectator) // force cam off for spectators return true; - if (!cv_chasecam.value && thiscam == &camera) + if (!cv_chasecam.value && thiscam == &camera[0]) return true; - if (!cv_chasecam2.value && thiscam == &camera2) + if (!cv_chasecam2.value && thiscam == &camera[1]) return true; - if (!cv_chasecam3.value && thiscam == &camera3) + if (!cv_chasecam3.value && thiscam == &camera[2]) return true; - if (!cv_chasecam4.value && thiscam == &camera4) + if (!cv_chasecam4.value && thiscam == &camera[3]) return true; } if (!thiscam->chase && !resetcalled) { if (player == &players[consoleplayer]) - focusangle = localangle; - else if (player == &players[secondarydisplayplayer]) - focusangle = localangle2; - else if (player == &players[thirddisplayplayer]) - focusangle = localangle3; - else if (player == &players[fourthdisplayplayer]) - focusangle = localangle4; + focusangle = localangle[0]; + else if (player == &players[displayplayers[1]]) + focusangle = localangle[1]; + else if (player == &players[displayplayers[2]]) + focusangle = localangle[2]; + else if (player == &players[displayplayers[3]]) + focusangle = localangle[3]; else focusangle = mo->angle; - if (thiscam == &camera) + + if (thiscam == &camera[0]) camrotate = cv_cam_rotate.value; - else if (thiscam == &camera2) + else if (thiscam == &camera[1]) camrotate = cv_cam2_rotate.value; - else if (thiscam == &camera3) + else if (thiscam == &camera[2]) camrotate = cv_cam3_rotate.value; - else if (thiscam == &camera4) + else if (thiscam == &camera[3]) camrotate = cv_cam4_rotate.value; else camrotate = 0; + if (leveltime < introtime) // Whoooshy camera! { const INT32 introcam = (introtime - leveltime); camrotate += introcam*5; } + thiscam->angle = focusangle + FixedAngle(camrotate*FRACUNIT); P_ResetCamera(player, thiscam); return true; @@ -8191,30 +7343,30 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall // if (leveltime > 0 && timeinmap <= 0) // return true; - if (demoplayback) + if (demo.playback) { focusangle = mo->angle; focusaiming = 0; } else if (player == &players[consoleplayer]) { - focusangle = localangle; - focusaiming = localaiming; + focusangle = localangle[0]; + focusaiming = localaiming[0]; } - else if (player == &players[secondarydisplayplayer]) + else if (player == &players[displayplayers[1]]) { - focusangle = localangle2; - focusaiming = localaiming2; + focusangle = localangle[1]; + focusaiming = localaiming[1]; } - else if (player == &players[thirddisplayplayer]) + else if (player == &players[displayplayers[2]]) { - focusangle = localangle3; - focusaiming = localaiming3; + focusangle = localangle[2]; + focusaiming = localaiming[2]; } - else if (player == &players[fourthdisplayplayer]) + else if (player == &players[displayplayers[3]]) { - focusangle = localangle4; - focusaiming = localaiming4; + focusangle = localangle[3]; + focusaiming = localaiming[3]; } else { @@ -8225,17 +7377,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (P_CameraThinker(player, thiscam, resetcalled)) return true; - if (thiscam == &camera) - { - num = 0; - camspeed = cv_cam_speed.value; - camstill = cv_cam_still.value; - camrotate = cv_cam_rotate.value; - camdist = FixedMul(cv_cam_dist.value, mapobjectscale); - camheight = FixedMul(cv_cam_height.value, mapobjectscale); - lookback = camspin; - } - else if (thiscam == &camera2) // Camera 2 + + if (thiscam == &camera[1]) // Camera 2 { num = 1; camspeed = cv_cam2_speed.value; @@ -8243,9 +7386,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camrotate = cv_cam2_rotate.value; camdist = FixedMul(cv_cam2_dist.value, mapobjectscale); camheight = FixedMul(cv_cam2_height.value, mapobjectscale); - lookback = camspin2; + lookback = camspin[1]; } - else if (thiscam == &camera3) // Camera 3 + else if (thiscam == &camera[2]) // Camera 3 { num = 2; camspeed = cv_cam3_speed.value; @@ -8253,9 +7396,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camrotate = cv_cam3_rotate.value; camdist = FixedMul(cv_cam3_dist.value, mapobjectscale); camheight = FixedMul(cv_cam3_height.value, mapobjectscale); - lookback = camspin3; + lookback = camspin[2]; } - else // Camera 4 + else if (thiscam == &camera[3]) // Camera 4 { num = 3; camspeed = cv_cam4_speed.value; @@ -8263,7 +7406,17 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camrotate = cv_cam4_rotate.value; camdist = FixedMul(cv_cam4_dist.value, mapobjectscale); camheight = FixedMul(cv_cam4_height.value, mapobjectscale); - lookback = camspin4; + lookback = camspin[3]; + } + else // Camera 1 + { + num = 0; + camspeed = cv_cam_speed.value; + camstill = cv_cam_still.value; + camrotate = cv_cam_rotate.value; + camdist = FixedMul(cv_cam_dist.value, mapobjectscale); + camheight = FixedMul(cv_cam_height.value, mapobjectscale); + lookback = camspin[0]; } if (timeover) @@ -8295,9 +7448,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (mo->eflags & MFE_VERTICALFLIP) camheight += thiscam->height; - if (splitscreen == 1) - camspeed = (3*camspeed)/4; - if (camspeed > FRACUNIT) camspeed = FRACUNIT; @@ -8327,10 +7477,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } if (!resetcalled && (leveltime > starttime && timeover != 2) - && ((thiscam == &camera && t_cam_rotate != -42) - || (thiscam == &camera2 && t_cam2_rotate != -42) - || (thiscam == &camera3 && t_cam3_rotate != -42) - || (thiscam == &camera4 && t_cam4_rotate != -42))) + && ((thiscam == &camera[0] && t_cam_rotate != -42) + || (thiscam == &camera[1] && t_cam2_rotate != -42) + || (thiscam == &camera[2] && t_cam3_rotate != -42) + || (thiscam == &camera[3] && t_cam4_rotate != -42))) { angle = FixedAngle(camrotate*FRACUNIT); thiscam->angle = angle; @@ -8351,13 +7501,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); @@ -8623,10 +7766,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; @@ -8735,8 +7875,8 @@ boolean P_SpectatorJoinGame(player_t *player) player->playerstate = PST_REBORN; //Reset away view - if (P_IsLocalPlayer(player) && displayplayer != consoleplayer) - displayplayer = consoleplayer; + if (P_IsLocalPlayer(player) && displayplayers[0] != consoleplayer) + displayplayers[0] = consoleplayer; if (changeto == 1) CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x85', M_GetText("Red team"), '\x80'); @@ -8759,8 +7899,8 @@ boolean P_SpectatorJoinGame(player_t *player) player->playerstate = PST_REBORN; //Reset away view - if (P_IsLocalPlayer(player) && displayplayer != consoleplayer) - displayplayer = consoleplayer; + if (P_IsLocalPlayer(player) && displayplayers[0] != consoleplayer) + displayplayers[0] = consoleplayer; HU_AddChatText(va(M_GetText("\x82*%s entered the game."), player_names[player-players]), false); return true; // no more player->mo, cannot continue. @@ -8771,9 +7911,10 @@ boolean P_SpectatorJoinGame(player_t *player) static void P_CalcPostImg(player_t *player) { sector_t *sector = player->mo->subsector->sector; - postimg_t *type; + postimg_t *type = postimg_none; INT32 *param; fixed_t pviewheight; + UINT8 i; if (player->mo->eflags & MFE_VERTICALFLIP) pviewheight = player->mo->z + player->mo->height - player->viewheight; @@ -8786,25 +7927,14 @@ static void P_CalcPostImg(player_t *player) pviewheight = player->awayviewmobj->z + 20*FRACUNIT; } - if (splitscreen > 2 && player == &players[fourthdisplayplayer]) + for (i = 0; i <= splitscreen; i++) { - type = &postimgtype4; - param = &postimgparam4; - } - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - { - type = &postimgtype3; - param = &postimgparam3; - } - else if (splitscreen && player == &players[secondarydisplayplayer]) - { - type = &postimgtype2; - param = &postimgparam2; - } - else - { - type = &postimgtype; - param = &postimgparam; + if (player == &players[displayplayers[i]]) + { + type = &postimgtype[i]; + param = &postimgparam[i]; + break; + } } // see if we are in heat (no, not THAT kind of heat...) @@ -8912,11 +8042,7 @@ void P_DoTimeOver(player_t *player) player->pflags |= PF_TIMEOVER; - if ((player == &players[consoleplayer] - || (splitscreen && player == &players[secondarydisplayplayer]) - || (splitscreen > 1 && player == &players[thirddisplayplayer]) - || (splitscreen > 2 && player == &players[fourthdisplayplayer])) - && !demoplayback) + if (P_IsLocalPlayer(player) && !demo.playback) legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p if (player->mo) @@ -8956,14 +8082,17 @@ void P_PlayerThink(player_t *player) if (player->bot) { - if (player->playerstate == PST_LIVE && B_CheckRespawn(player)) - player->playerstate = PST_REBORN; + if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD) + { + if (B_CheckRespawn(player)) + player->playerstate = PST_REBORN; + } if (player->playerstate == PST_REBORN) return; } #ifdef SEENAMES - if (netgame && player == &players[displayplayer] && !(leveltime % (TICRATE/5)) && !splitscreen) + if (netgame && player == &players[displayplayers[0]] && !(leveltime % (TICRATE/5)) && !splitscreen) { seenplayer = NULL; @@ -9025,6 +8154,19 @@ void P_PlayerThink(player_t *player) cmd = &player->cmd; + //@TODO This fixes a one-tic latency on direction handling, AND makes behavior consistent while paused, but is not BC with 1.0.4. Do this for 1.1! +#if 0 + // SRB2kart + // Save the dir the player is holding + // to allow items to be thrown forward or backward. + if (cmd->buttons & BT_FORWARD) + player->kartstuff[k_throwdir] = 1; + else if (cmd->buttons & BT_BACKWARD) + player->kartstuff[k_throwdir] = -1; + else + player->kartstuff[k_throwdir] = 0; +#endif + // Add some extra randomization. if (cmd->forwardmove) P_RandomFixed(); @@ -9297,7 +8439,9 @@ void P_PlayerThink(player_t *player) || player->kartstuff[k_driftboost] || player->kartstuff[k_sneakertimer] || player->kartstuff[k_startboost]) && !player->kartstuff[k_invincibilitytimer] // SRB2kart && (player->speed + abs(player->mo->momz)) > FixedMul(20*FRACUNIT,player->mo->scale)) { + UINT8 i; mobj_t *gmobj = P_SpawnGhostMobj(player->mo); + gmobj->fuse = 2; if (leveltime & 1) { @@ -9305,15 +8449,17 @@ void P_PlayerThink(player_t *player) gmobj->frame |= tr_trans70< 1 && player == &players[thirddisplayplayer] && !camera3.chase) - || (splitscreen > 2 && player == &players[fourthdisplayplayer] && !camera4.chase)) - gmobj->flags2 |= MF2_DONTDRAW; + for (i = 0; i <= splitscreen; i++) + { + if (player == &players[displayplayers[i]] && !camera[i].chase) + { + gmobj->flags2 |= MF2_DONTDRAW; + break; + } + } } #endif @@ -9353,7 +8499,7 @@ void P_PlayerThink(player_t *player) || (player->spectator || player->powers[pw_flashing] < K_GetKartFlashing(player)))) player->powers[pw_flashing]--; - if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX && player->charability != CA_SWIM && !(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))) // tails fly counter + if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX /*&& player->charability != CA_SWIM*/ && !(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))) // tails fly counter player->powers[pw_tailsfly]--; /* // SRB2kart - Can't drown. @@ -9513,6 +8659,7 @@ void P_PlayerAfterThink(player_t *player) ticcmd_t *cmd; //INT32 oldweapon = player->currentweapon; // SRB2kart - unused camera_t *thiscam = NULL; // if not one of the displayed players, just don't bother + UINT8 i; #ifdef PARANOIA if (!player->mo) @@ -9535,14 +8682,14 @@ void P_PlayerAfterThink(player_t *player) P_PlayerInSpecialSector(player); #endif - if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - thiscam = &camera4; - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - thiscam = &camera3; - else if (splitscreen && player == &players[secondarydisplayplayer]) - thiscam = &camera2; - else if (player == &players[displayplayer]) - thiscam = &camera; + for (i = 0; i <= splitscreen; i++) + { + if (player == &players[displayplayers[i]]) + { + thiscam = &camera[i]; + break; + } + } if (player->playerstate == PST_DEAD) { @@ -9727,13 +8874,13 @@ void P_PlayerAfterThink(player_t *player) player->mo->angle = player->mo->tracer->angle; if (player == &players[consoleplayer]) - localangle = player->mo->angle; - else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = player->mo->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = player->mo->angle; + localangle[0] = player->mo->angle; + else if (player == &players[displayplayers[1]]) + localangle[1] = player->mo->angle; + else if (player == &players[displayplayers[2]]) + localangle[2] = player->mo->angle; + else if (player == &players[displayplayers[3]]) + localangle[3] = player->mo->angle; } if (P_AproxDistance(player->mo->x - player->mo->tracer->x, player->mo->y - player->mo->tracer->y) > player->mo->radius) @@ -9801,13 +8948,13 @@ void P_PlayerAfterThink(player_t *player) player->mo->angle += cmd->sidemove< ANGLE_MAX if (player == &players[consoleplayer]) - localangle = player->mo->angle; // Adjust the local control angle. - else if (player == &players[secondarydisplayplayer]) - localangle2 = player->mo->angle; - else if (player == &players[thirddisplayplayer]) - localangle3 = player->mo->angle; - else if (player == &players[fourthdisplayplayer]) - localangle4 = player->mo->angle; + localangle[0] = player->mo->angle; // Adjust the local control angle. + else if (player == &players[displayplayers[1]]) + localangle[1] = player->mo->angle; + else if (player == &players[displayplayers[2]]) + localangle[2] = player->mo->angle; + else if (player == &players[displayplayers[3]]) + localangle[3] = player->mo->angle; } } diff --git a/src/r_bsp.c b/src/r_bsp.c index b819735e1..296cbbe87 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -252,20 +252,23 @@ sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, INT32 *floorlightlevel, mobj_t *viewmobj = viewplayer->mo; INT32 heightsec; boolean underwater; + UINT8 i; - if (splitscreen > 2 && viewplayer == &players[fourthdisplayplayer] && camera4.chase) - heightsec = R_PointInSubsector(camera4.x, camera4.y)->sector->heightsec; - else if (splitscreen > 1 && viewplayer == &players[thirddisplayplayer] && camera3.chase) - heightsec = R_PointInSubsector(camera3.x, camera3.y)->sector->heightsec; - else if (splitscreen && viewplayer == &players[secondarydisplayplayer] && camera2.chase) - heightsec = R_PointInSubsector(camera2.x, camera2.y)->sector->heightsec; - else if (camera.chase && viewplayer == &players[displayplayer]) - heightsec = R_PointInSubsector(camera.x, camera.y)->sector->heightsec; - else if (viewmobj) + for (i = 0; i <= splitscreen; i++) + { + if (viewplayer == &players[displayplayers[i]] && camera[i].chase) + { + heightsec = R_PointInSubsector(camera[i].x, camera[i].y)->sector->heightsec; + break; + } + } + + if (i > splitscreen && viewmobj) heightsec = R_PointInSubsector(viewmobj->x, viewmobj->y)->sector->heightsec; else return sec; - underwater = heightsec != -1 && viewz <= sectors[heightsec].floorheight; + + underwater = (heightsec != -1 && viewz <= sectors[heightsec].floorheight); // Replace sector being drawn, with a copy to be hacked *tempsec = *sec; @@ -827,7 +830,7 @@ static void R_AddPolyObjects(subsector_t *sub) drawseg_t *firstseg; -static void R_Subsector(size_t num, UINT8 viewnumber) +static void R_Subsector(size_t num) { INT32 count, floorlightlevel, ceilinglightlevel, light; seg_t *line; @@ -1149,7 +1152,7 @@ static void R_Subsector(size_t num, UINT8 viewnumber) // Either you must pass the fake sector and handle validcount here, on the // real sector, or you must account for the lighting in some other way, // like passing it as an argument. - R_AddSprites(sub->sector, (floorlightlevel+ceilinglightlevel)/2, viewnumber); + R_AddSprites(sub->sector, (floorlightlevel+ceilinglightlevel)/2); firstseg = NULL; @@ -1355,7 +1358,7 @@ INT32 R_GetPlaneLight(sector_t *sector, fixed_t planeheight, boolean underside) // // killough 5/2/98: reformatted, removed tail recursion -void R_RenderBSPNode(INT32 bspnum, UINT8 viewnumber) +void R_RenderBSPNode(INT32 bspnum) { node_t *bsp; INT32 side; @@ -1366,7 +1369,7 @@ void R_RenderBSPNode(INT32 bspnum, UINT8 viewnumber) // Decide which side the view point is on. side = R_PointOnSide(viewx, viewy, bsp); // Recursively divide front space. - R_RenderBSPNode(bsp->children[side], viewnumber); + R_RenderBSPNode(bsp->children[side]); // Possibly divide back space. @@ -1384,5 +1387,5 @@ void R_RenderBSPNode(INT32 bspnum, UINT8 viewnumber) portalcullsector = NULL; } - R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR, viewnumber); + R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); } diff --git a/src/r_bsp.h b/src/r_bsp.h index 7810c9b5c..e3662e2e6 100644 --- a/src/r_bsp.h +++ b/src/r_bsp.h @@ -37,7 +37,7 @@ extern INT32 doorclosed; void R_ClearClipSegs(void); void R_PortalClearClipSegs(INT32 start, INT32 end); void R_ClearDrawSegs(void); -void R_RenderBSPNode(INT32 bspnum, UINT8 viewnumber); +void R_RenderBSPNode(INT32 bspnum); void R_AddPortal(INT32 line1, INT32 line2, INT32 x1, INT32 x2); #ifdef POLYOBJECTS diff --git a/src/r_data.c b/src/r_data.c index 1a74f7336..7fb11855f 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -1600,7 +1600,7 @@ void R_PrecacheLevel(void) thinker_t *th; spriteframe_t *sf; - if (demoplayback) + if (demo.playback) return; // do not flush the memory, Z_Malloc twice with same user will cause error in Z_CheckHeap() diff --git a/src/r_draw.c b/src/r_draw.c index 02a912155..1931ce6ee 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -139,75 +139,12 @@ UINT32 nflatxshift, nflatyshift, nflatshiftup, nflatmask; #define BLINK_TT_CACHE_INDEX (MAXSKINS + 5) #define TT_CACHE_SIZE (MAXSKINS + 6) #define SKIN_RAMP_LENGTH 16 -#define DEFAULT_STARTTRANSCOLOR 160 +#define DEFAULT_STARTTRANSCOLOR 96 #define NUM_PALETTE_ENTRIES 256 static UINT8** translationtablecache[TT_CACHE_SIZE] = {NULL}; - -// See also the enum skincolors_t -// TODO Callum: Can this be translated? -/* -const char *Color_Names[MAXSKINCOLORS] = -{ - "None", // SKINCOLOR_NONE - "White", // SKINCOLOR_WHITE - "Silver", // SKINCOLOR_SILVER - "Grey", // SKINCOLOR_GREY - "Black", // SKINCOLOR_BLACK - "Cyan", // SKINCOLOR_CYAN - "Teal", // SKINCOLOR_TEAL - "Steel_Blue",// SKINCOLOR_STEEL - "Blue", // SKINCOLOR_BLUE - "Peach", // SKINCOLOR_PEACH - "Tan", // SKINCOLOR_TAN - "Pink", // SKINCOLOR_PINK - "Lavender", // SKINCOLOR_LAVENDER - "Purple", // SKINCOLOR_PURPLE - "Orange", // SKINCOLOR_ORANGE - "Rosewood", // SKINCOLOR_ROSEWOOD - "Beige", // SKINCOLOR_BEIGE - "Brown", // SKINCOLOR_BROWN - "Red", // SKINCOLOR_RED - "Dark_Red", // SKINCOLOR_DARKRED - "Neon_Green",// SKINCOLOR_NEONGREEN - "Green", // SKINCOLOR_GREEN - "Zim", // SKINCOLOR_ZIM - "Olive", // SKINCOLOR_OLIVE - "Yellow", // SKINCOLOR_YELLOW - "Gold" // SKINCOLOR_GOLD -}; - -const UINT8 Color_Opposite[MAXSKINCOLORS*2] = -{ - SKINCOLOR_NONE,8, // SKINCOLOR_NONE - SKINCOLOR_BLACK,10, // SKINCOLOR_WHITE - SKINCOLOR_GREY,4, // SKINCOLOR_SILVER - SKINCOLOR_SILVER,12,// SKINCOLOR_GREY - SKINCOLOR_WHITE,8, // SKINCOLOR_BLACK - SKINCOLOR_NONE,8, // SKINCOLOR_CYAN - SKINCOLOR_NONE,8, // SKINCOLOR_TEAL - SKINCOLOR_NONE,8, // SKINCOLOR_STEEL - SKINCOLOR_ORANGE,9, // SKINCOLOR_BLUE - SKINCOLOR_NONE,8, // SKINCOLOR_PEACH - SKINCOLOR_NONE,8, // SKINCOLOR_TAN - SKINCOLOR_NONE,8, // SKINCOLOR_PINK - SKINCOLOR_NONE,8, // SKINCOLOR_LAVENDER - SKINCOLOR_NONE,8, // SKINCOLOR_PURPLE - SKINCOLOR_BLUE,12, // SKINCOLOR_ORANGE - SKINCOLOR_NONE,8, // SKINCOLOR_ROSEWOOD - SKINCOLOR_NONE,8, // SKINCOLOR_BEIGE - SKINCOLOR_NONE,8, // SKINCOLOR_BROWN - SKINCOLOR_GREEN,5, // SKINCOLOR_RED - SKINCOLOR_NONE,8, // SKINCOLOR_DARKRED - SKINCOLOR_NONE,8, // SKINCOLOR_NEONGREEN - SKINCOLOR_RED,11, // SKINCOLOR_GREEN - SKINCOLOR_PURPLE,3, // SKINCOLOR_ZIM - SKINCOLOR_NONE,8, // SKINCOLOR_OLIVE - SKINCOLOR_NONE,8, // SKINCOLOR_YELLOW - SKINCOLOR_NONE,8 // SKINCOLOR_GOLD -}; -*/ +// SKINCOLOR DEFINITIONS HAVE BEEN MOVED TO K_KART.C CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1]; @@ -239,280 +176,6 @@ void R_InitTranslationTables(void) #endif } - -/** \brief Generates a translation colormap. - - \param dest_colormap colormap to populate - \param skinnum number of skin, TC_DEFAULT or TC_BOSS - \param color translation color - - \return void -*/ -/* -static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color) -{ - // Table of indices into the palette of the first entries of each translated ramp - const UINT8 skinbasecolors[] = { - 0x00, // SKINCOLOR_WHITE - 0x03, // SKINCOLOR_SILVER - 0x08, // SKINCOLOR_GREY - 0x18, // SKINCOLOR_BLACK - 0xd0, // SKINCOLOR_CYAN - 0xdc, // SKINCOLOR_TEAL - 0xc8, // SKINCOLOR_STEEL - 0xe2, // SKINCOLOR_BLUE - 0x40, // SKINCOLOR_PEACH - 0x48, // SKINCOLOR_TAN - 0x90, // SKINCOLOR_PINK - 0xf8, // SKINCOLOR_LAVENDER - 0xc0, // SKINCOLOR_PURPLE - 0x52, // SKINCOLOR_ORANGE - 0x5c, // SKINCOLOR_ROSEWOOD - 0x20, // SKINCOLOR_BEIGE - 0x30, // SKINCOLOR_BROWN - 0x7d, // SKINCOLOR_RED - 0x85, // SKINCOLOR_DARKRED - 0xb8, // SKINCOLOR_NEONGREEN - 0xa0, // SKINCOLOR_GREEN - 0xb0, // SKINCOLOR_ZIM - 0x69, // SKINCOLOR_OLIVE - 0x67, // SKINCOLOR_YELLOW - 0x70, // SKINCOLOR_GOLD - }; - INT32 i; - INT32 starttranscolor; - - // Handle a couple of simple special cases - if (skinnum == TC_BOSS || skinnum == TC_ALLWHITE || skinnum == TC_METALSONIC || color == SKINCOLOR_NONE) - { - for (i = 0; i < NUM_PALETTE_ENTRIES; i++) - { - if (skinnum == TC_ALLWHITE) dest_colormap[i] = 0; - 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; - } - - 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 - switch (color) - { - case SKINCOLOR_SILVER: - case SKINCOLOR_GREY: - case SKINCOLOR_PEACH: - case SKINCOLOR_BEIGE: - case SKINCOLOR_BROWN: - case SKINCOLOR_RED: - case SKINCOLOR_GREEN: - case SKINCOLOR_BLUE: - // 16 color ramp - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + i); - break; - - case SKINCOLOR_ORANGE: - // 14 colors of orange + brown - for (i = 0; i < SKIN_RAMP_LENGTH-2; i++) - dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + i); - for (i = 0; i < 2; i++) - dest_colormap[starttranscolor + (i+SKIN_RAMP_LENGTH-2)] = (UINT8)(152 + i); - break; - - case SKINCOLOR_CYAN: - // 12 color ramp - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + (12*i/SKIN_RAMP_LENGTH)); - break; - - case SKINCOLOR_WHITE: - case SKINCOLOR_BLACK: - case SKINCOLOR_STEEL: - case SKINCOLOR_PINK: - case SKINCOLOR_LAVENDER: - case SKINCOLOR_PURPLE: - case SKINCOLOR_DARKRED: - case SKINCOLOR_ZIM: - case SKINCOLOR_YELLOW: - case SKINCOLOR_GOLD: - // 8 color ramp - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + (i >> 1)); - break; - - case SKINCOLOR_TEAL: - // 5 color ramp - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - { - if (5*i/16 == 0) - dest_colormap[starttranscolor + i] = 0xf7; - else - dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + (5*i/SKIN_RAMP_LENGTH) - 1); - } - break; - - case SKINCOLOR_OLIVE: - // 7 color ramp - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + (7*i/SKIN_RAMP_LENGTH)); - break; - - case SKINCOLOR_TAN: - // 16 color ramp, from two color ranges - for (i = 0; i < SKIN_RAMP_LENGTH/2; i++) // Peach half - dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + i); - for (i = 0; i < SKIN_RAMP_LENGTH/2; i++) // Brown half - dest_colormap[starttranscolor + (i+8)] = (UINT8)(48 + i); - break; - - case SKINCOLOR_ROSEWOOD: - // 12 color ramp, from two color ranges! - for (i = 0; i < 6; i++) // Orange ...third? - dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + (12*i/SKIN_RAMP_LENGTH)); - for (i = 0; i < 10; i++) // Rosewood two-thirds-ish - dest_colormap[starttranscolor + (i+6)] = (UINT8)(152 + (12*i/SKIN_RAMP_LENGTH)); - break; - - case SKINCOLOR_NEONGREEN: - // Multi-color ramp - dest_colormap[starttranscolor] = 0xA0; // Brighter green - for (i = 0; i < SKIN_RAMP_LENGTH-1; i++) // Neon Green - dest_colormap[starttranscolor + (i+1)] = (UINT8)(skinbasecolors[color - 1] + (6*i/(SKIN_RAMP_LENGTH-1))); - break; - - // Super colors, from lightest to darkest! - case SKINCOLOR_SUPER1: - // Super White - for (i = 0; i < 10; i++) - dest_colormap[starttranscolor + i] = 120; // True white - for (; i < SKIN_RAMP_LENGTH; i++) // White-yellow fade - dest_colormap[starttranscolor + i] = (UINT8)(96 + (i-10)); - break; - - case SKINCOLOR_SUPER2: - // Super Bright - for (i = 0; i < 5; i++) // White-yellow fade - dest_colormap[starttranscolor + i] = (UINT8)(96 + i); - dest_colormap[starttranscolor + 5] = 112; // Golden shine - for (i = 0; i < 8; i++) // Yellow - dest_colormap[starttranscolor + (i+6)] = (UINT8)(101 + (i>>1)); - for (i = 0; i < 2; i++) // With a fine golden finish! :3 - dest_colormap[starttranscolor + (i+14)] = (UINT8)(113 + i); - break; - - case SKINCOLOR_SUPER3: - // Super Yellow - for (i = 0; i < 3; i++) // White-yellow fade - dest_colormap[starttranscolor + i] = (UINT8)(98 + i); - dest_colormap[starttranscolor + 3] = 112; // Golden shine - for (i = 0; i < 8; i++) // Yellow - dest_colormap[starttranscolor + (i+4)] = (UINT8)(101 + (i>>1)); - for (i = 0; i < 4; i++) // With a fine golden finish! :3 - dest_colormap[starttranscolor + (i+12)] = (UINT8)(113 + i); - break; - - case SKINCOLOR_SUPER4: - // "The SSNTails" - dest_colormap[starttranscolor] = 112; // Golden shine - for (i = 0; i < 8; i++) // Yellow - dest_colormap[starttranscolor + (i+1)] = (UINT8)(101 + (i>>1)); - for (i = 0; i < 7; i++) // With a fine golden finish! :3 - dest_colormap[starttranscolor + (i+9)] = (UINT8)(113 + i); - break; - - case SKINCOLOR_SUPER5: - // Golden Delicious - for (i = 0; i < 8; i++) // Yellow - dest_colormap[starttranscolor + i] = (UINT8)(101 + (i>>1)); - for (i = 0; i < 7; i++) // With a fine golden finish! :3 - dest_colormap[starttranscolor + (i+8)] = (UINT8)(113 + i); - dest_colormap[starttranscolor + 15] = 155; - break; - - // Super Tails - case SKINCOLOR_TSUPER1: - for (i = 0; i < 10; i++) // white - dest_colormap[starttranscolor + i] = 120; - for (; i < SKIN_RAMP_LENGTH; i++) // orange - dest_colormap[starttranscolor + i] = (UINT8)(80 + (i-10)); - break; - - case SKINCOLOR_TSUPER2: - for (i = 0; i < 4; i++) // white - dest_colormap[starttranscolor + i] = 120; - for (; i < SKIN_RAMP_LENGTH; i++) // orange - dest_colormap[starttranscolor + i] = (UINT8)(80 + ((i-4)>>1)); - break; - - case SKINCOLOR_TSUPER3: - dest_colormap[starttranscolor] = 120; // pure white - dest_colormap[starttranscolor+1] = 120; - for (i = 2; i < SKIN_RAMP_LENGTH; i++) // orange - dest_colormap[starttranscolor + i] = (UINT8)(80 + ((i-2)>>1)); - break; - - case SKINCOLOR_TSUPER4: - dest_colormap[starttranscolor] = 120; // pure white - for (i = 1; i < 9; i++) // orange - dest_colormap[starttranscolor + i] = (UINT8)(80 + (i-1)); - for (; i < SKIN_RAMP_LENGTH; i++) // gold - dest_colormap[starttranscolor + i] = (UINT8)(115 + (5*(i-9)/7)); - break; - - case SKINCOLOR_TSUPER5: - for (i = 0; i < 8; i++) // orange - dest_colormap[starttranscolor + i] = (UINT8)(80 + i); - for (; i < SKIN_RAMP_LENGTH; i++) // gold - dest_colormap[starttranscolor + i] = (UINT8)(115 + (5*(i-8)/8)); - break; - - // Super Knuckles - case SKINCOLOR_KSUPER1: - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - dest_colormap[starttranscolor + i] = (UINT8)(120 + (i >> 2)); - break; - - case SKINCOLOR_KSUPER2: - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - dest_colormap[starttranscolor + i] = (UINT8)(120 + (6*i/SKIN_RAMP_LENGTH)); - break; - - case SKINCOLOR_KSUPER3: - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - dest_colormap[starttranscolor + i] = (UINT8)(120 + (i >> 1)); - break; - - case SKINCOLOR_KSUPER4: - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - dest_colormap[starttranscolor + i] = (UINT8)(121 + (i >> 1)); - break; - - case SKINCOLOR_KSUPER5: - for (i = 0; i < SKIN_RAMP_LENGTH; i++) - dest_colormap[starttranscolor + i] = (UINT8)(122 + (i >> 1)); - break; - - default: - I_Error("Invalid skin color #%hu.", (UINT16)color); - break; - } -} -*/ - /** \brief Retrieves a translation colormap from the cache. \param skinnum number of skin, TC_DEFAULT or TC_BOSS diff --git a/src/r_draw.h b/src/r_draw.h index 9a81a7f57..900802cea 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_draw8.c b/src/r_draw8.c index 634ae7075..ef67dbaf7 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -105,7 +105,7 @@ void R_DrawColumn_8(void) } } -#define TRANSPARENTPIXEL 247 +#define TRANSPARENTPIXEL 255 void R_Draw2sMultiPatchColumn_8(void) { diff --git a/src/r_main.c b/src/r_main.c index 8b070e9ea..0d14bed73 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -30,6 +30,7 @@ #include "p_spec.h" // skyboxmo #include "z_zone.h" #include "m_random.h" // quake camera shake +#include "doomstat.h" // MAXSPLITSCREENPLAYERS #ifdef HWRENDER #include "hardware/hw_main.h" @@ -56,6 +57,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; @@ -64,9 +66,10 @@ size_t loopcount; fixed_t viewx, viewy, viewz; angle_t viewangle, aimingangle; +UINT8 viewssnum; fixed_t viewcos, viewsin; boolean viewsky, skyVisible; -boolean skyVisible1, skyVisible2, skyVisible3, skyVisible4; // saved values of skyVisible for P1/P2/P3/P4, for splitscreen +boolean skyVisiblePerPlayer[MAXSPLITSCREENPLAYERS]; // saved values of skyVisible for each splitscreen player sector_t *viewsector; player_t *viewplayer; @@ -134,11 +137,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 +181,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}; @@ -188,19 +195,12 @@ void SplitScreen_OnChange(void) // recompute screen size R_ExecuteSetViewSize(); - if (!demoplayback && !botingame) + if (!demo.playback && !botingame) { - for (i = 1; i < 3; i++) + for (i = 1; i < MAXSPLITSCREENPLAYERS; i++) { if (i > splitscreen) - { - if (i == 1) - CL_RemoveSplitscreenPlayer(secondarydisplayplayer); - else if (i == 2) - CL_RemoveSplitscreenPlayer(thirddisplayplayer); - else if (i == 3) - CL_RemoveSplitscreenPlayer(fourthdisplayplayer); - } + CL_RemoveSplitscreenPlayer(displayplayers[i]); else CL_AddSplitscreenPlayer(); } @@ -210,23 +210,37 @@ void SplitScreen_OnChange(void) } else { - secondarydisplayplayer = consoleplayer; - thirddisplayplayer = consoleplayer; - fourthdisplayplayer = consoleplayer; + for (i = 1; i < MAXSPLITSCREENPLAYERS; i++) + displayplayers[i] = consoleplayer; + for (i = 0; i < MAXPLAYERS; i++) + { if (playeringame[i] && i != consoleplayer) { - if (secondarydisplayplayer == consoleplayer) - secondarydisplayplayer = i; - else if (thirddisplayplayer == consoleplayer) - thirddisplayplayer = i; - else if (fourthdisplayplayer == consoleplayer) - fourthdisplayplayer = i; - else + UINT8 j; + for (j = 1; j < MAXSPLITSCREENPLAYERS; j++) + { + if (displayplayers[j] == consoleplayer) + { + displayplayers[j] = i; + break; + } + } + + if (j == MAXSPLITSCREENPLAYERS) break; } + } } } +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 +531,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 +646,7 @@ void R_ExecuteSetViewSize(void) INT32 j; INT32 level; INT32 startmapl; + angle_t fov; setsizeneeded = false; @@ -660,9 +675,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 +706,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). @@ -827,16 +845,20 @@ static void R_SetupFreelook(void) void R_SkyboxFrame(player_t *player) { - camera_t *thiscam; + camera_t *thiscam = &camera[0]; + UINT8 i; - if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - thiscam = &camera4; - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - thiscam = &camera3; - else if (splitscreen && player == &players[secondarydisplayplayer]) - thiscam = &camera2; - else - thiscam = &camera; + if (splitscreen) + { + for (i = 1; i <= splitscreen; i++) + { + if (player == &players[displayplayers[i]]) + { + thiscam = &camera[i]; + break; + } + } + } // cut-away view stuff viewsky = true; @@ -862,27 +884,24 @@ void R_SkyboxFrame(player_t *player) { aimingangle = player->aiming; viewangle = player->mo->angle; - if (/*!demoplayback && */player->playerstate != PST_DEAD) + if (/*!demo.playback && */player->playerstate != PST_DEAD) { if (player == &players[consoleplayer]) { - viewangle = localangle; // WARNING: camera uses this - aimingangle = localaiming; + viewangle = localangle[0]; // WARNING: camera uses this + aimingangle = localaiming[0]; } - else if (player == &players[secondarydisplayplayer]) + else if (splitscreen) { - viewangle = localangle2; - aimingangle = localaiming2; - } - else if (player == &players[thirddisplayplayer]) - { - viewangle = localangle3; - aimingangle = localaiming3; - } - else if (player == &players[fourthdisplayplayer]) - { - viewangle = localangle4; - aimingangle = localaiming4; + for (i = 1; i <= splitscreen; i++) + { + if (player == &players[displayplayers[i]]) + { + viewangle = localangle[i]; + aimingangle = localaiming[i]; + break; + } + } } } } @@ -1061,24 +1080,24 @@ void R_SetupFrame(player_t *player, boolean skybox) camera_t *thiscam; boolean chasecam = false; - if (splitscreen > 2 && player == &players[fourthdisplayplayer]) + if (splitscreen > 2 && player == &players[displayplayers[3]]) { - thiscam = &camera4; + thiscam = &camera[3]; chasecam = (cv_chasecam4.value != 0); } - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) + else if (splitscreen > 1 && player == &players[displayplayers[2]]) { - thiscam = &camera3; + thiscam = &camera[2]; chasecam = (cv_chasecam3.value != 0); } - else if (splitscreen && player == &players[secondarydisplayplayer]) + else if (splitscreen && player == &players[displayplayers[1]]) { - thiscam = &camera2; + thiscam = &camera[1]; chasecam = (cv_chasecam2.value != 0); } else { - thiscam = &camera; + thiscam = &camera[0]; chasecam = (cv_chasecam.value != 0); } @@ -1124,27 +1143,25 @@ void R_SetupFrame(player_t *player, boolean skybox) aimingangle = player->aiming; viewangle = viewmobj->angle; - if (/*!demoplayback && */player->playerstate != PST_DEAD) + if (/*!demo.playback && */player->playerstate != PST_DEAD) { if (player == &players[consoleplayer]) { - viewangle = localangle; // WARNING: camera uses this - aimingangle = localaiming; + viewangle = localangle[0]; // WARNING: camera uses this + aimingangle = localaiming[0]; } - else if (player == &players[secondarydisplayplayer]) + else if (splitscreen) { - viewangle = localangle2; - aimingangle = localaiming2; - } - else if (player == &players[thirddisplayplayer]) - { - viewangle = localangle3; - aimingangle = localaiming3; - } - else if (player == &players[fourthdisplayplayer]) - { - viewangle = localangle4; - aimingangle = localaiming4; + UINT8 i; + for (i = 1; i <= splitscreen; i++) + { + if (player == &players[displayplayers[i]]) + { + viewangle = localangle[i]; + aimingangle = localaiming[i]; + break; + } + } } } } @@ -1306,27 +1323,18 @@ void R_RenderPlayerView(player_t *player) { portal_pair *portal; const boolean skybox = (skyboxmo[0] && cv_skybox.value); - UINT8 viewnumber; - - if (player == &players[secondarydisplayplayer] && splitscreen) - viewnumber = 1; - else if (player == &players[thirddisplayplayer] && splitscreen > 1) - viewnumber = 2; - else if (player == &players[fourthdisplayplayer] && splitscreen > 2) - viewnumber = 3; - else - viewnumber = 0; + UINT8 i; // if this is display player 1 - if (cv_homremoval.value && player == &players[displayplayer]) + if (cv_homremoval.value && player == &players[displayplayers[0]]) { if (cv_homremoval.value == 1) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); // No HOM effect! else //'development' HOM removal -- makes it blindingly obvious if HOM is spotted. - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 128+(timeinmap&15)); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 32+(timeinmap&15)); } // Draw over the fourth screen so you don't have to stare at a HOM :V - else if (splitscreen == 2 && player == &players[thirddisplayplayer]) + else if (splitscreen == 2 && player == &players[displayplayers[2]]) #if 1 { // V_DrawPatchFill, but for the fourth screen only @@ -1345,14 +1353,14 @@ void R_RenderPlayerView(player_t *player) #endif // load previous saved value of skyVisible for the player - if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - skyVisible = skyVisible4; - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - skyVisible = skyVisible3; - else if (splitscreen && player == &players[secondarydisplayplayer]) - skyVisible = skyVisible2; - else - skyVisible = skyVisible1; + for (i = 0; i <= splitscreen; i++) + { + if (player == &players[displayplayers[i]]) + { + skyVisible = skyVisiblePerPlayer[i]; + break; + } + } portalrender = 0; portal_base = portal_cap = NULL; @@ -1369,7 +1377,7 @@ void R_RenderPlayerView(player_t *player) R_ClearVisibleFloorSplats(); #endif - R_RenderBSPNode((INT32)numnodes - 1, viewnumber); + R_RenderBSPNode((INT32)numnodes - 1); R_ClipSprites(); R_DrawPlanes(); #ifdef FLOORSPLATS @@ -1402,7 +1410,7 @@ void R_RenderPlayerView(player_t *player) mytotal = 0; ProfZeroTimer(); #endif - R_RenderBSPNode((INT32)numnodes - 1, viewnumber); + R_RenderBSPNode((INT32)numnodes - 1); R_ClipSprites(); #ifdef TIMING RDMSR(0x10, &mycount); @@ -1427,7 +1435,7 @@ void R_RenderPlayerView(player_t *player) validcount++; - R_RenderBSPNode((INT32)numnodes - 1, viewnumber); + R_RenderBSPNode((INT32)numnodes - 1); R_ClipSprites(); //R_DrawPlanes(); //R_DrawMasked(); @@ -1453,16 +1461,16 @@ void R_RenderPlayerView(player_t *player) // Check for new console commands. NetUpdate(); - // save value to skyVisible1 or skyVisible2 + // save value to skyVisiblePerPlayer // this is so that P1 can't affect whether P2 can see a skybox or not, or vice versa - if (splitscreen > 2 && player == &players[fourthdisplayplayer]) - skyVisible4 = skyVisible; - else if (splitscreen > 1 && player == &players[thirddisplayplayer]) - skyVisible3 = skyVisible; - else if (splitscreen && player == &players[secondarydisplayplayer]) - skyVisible2 = skyVisible; - else - skyVisible1 = skyVisible; + for (i = 0; i <= splitscreen; i++) + { + if (player == &players[displayplayers[i]]) + { + skyVisiblePerPlayer[i] = skyVisible; + break; + } + } } // ========================================================================= @@ -1490,6 +1498,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); @@ -1543,7 +1552,6 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_grgammared); CV_RegisterVar(&cv_grfovchange); CV_RegisterVar(&cv_grfog); - CV_RegisterVar(&cv_voodoocompatibility); CV_RegisterVar(&cv_grfogcolor); CV_RegisterVar(&cv_grsoftwarefog); #ifdef ALAM_LIGHTING @@ -1552,7 +1560,9 @@ void R_RegisterEngineStuff(void) CV_RegisterVar(&cv_grcoronas); CV_RegisterVar(&cv_grcoronasize); #endif - CV_RegisterVar(&cv_grmd2); + CV_RegisterVar(&cv_grmdls); + CV_RegisterVar(&cv_grfallbackplayermodel); + CV_RegisterVar(&cv_grspritebillboarding); #endif #ifdef HWRENDER diff --git a/src/r_main.h b/src/r_main.h index 7d3e26a89..38a589682 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 3c1fc30e8..db5bfbda2 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -883,12 +883,12 @@ void R_DrawSinglePlane(visplane_t *pl) if (bottom > vid.height) bottom = vid.height; - if (splitscreen > 2 && viewplayer == &players[fourthdisplayplayer]) // Only copy the part of the screen we need + if (splitscreen > 2 && viewplayer == &players[displayplayers[3]]) // Only copy the part of the screen we need scr = (screens[0] + (top+(viewheight))*vid.width + viewwidth); - else if ((splitscreen == 1 && viewplayer == &players[secondarydisplayplayer]) - || (splitscreen > 1 && viewplayer == &players[thirddisplayplayer])) + else if ((splitscreen == 1 && viewplayer == &players[displayplayers[1]]) + || (splitscreen > 1 && viewplayer == &players[displayplayers[2]])) scr = (screens[0] + (top+(viewheight))*vid.width); - else if (splitscreen > 1 && viewplayer == &players[secondarydisplayplayer]) + else if (splitscreen > 1 && viewplayer == &players[displayplayers[1]]) scr = (screens[0] + ((top)*vid.width) + viewwidth); else scr = (screens[0] + ((top)*vid.width)); @@ -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 62c0523d0..399f514bc 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -862,24 +862,26 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) if (leftheight > pfloorleft && rightheight > pfloorright && i+1 < dc_numlights) { lightlist_t *nextlight = &frontsector->lightlist[i+1]; - if (nextlight->slope ? P_GetZAt(nextlight->slope, ds->leftpos.x, ds->leftpos.y) : nextlight->height > pfloorleft - && nextlight->slope ? P_GetZAt(nextlight->slope, ds->rightpos.x, ds->rightpos.y) : nextlight->height > pfloorright) + if ((nextlight->slope ? P_GetZAt(nextlight->slope, ds->leftpos.x, ds->leftpos.y) : nextlight->height) > pfloorleft + && (nextlight->slope ? P_GetZAt(nextlight->slope, ds->rightpos.x, ds->rightpos.y) : nextlight->height) > pfloorright) continue; } leftheight -= viewz; rightheight -= viewz; -#define OVERFLOWTEST(height, scale) \ - overflow_test = (INT64)centeryfrac - (((INT64)height*scale)>>FRACBITS); \ - if (overflow_test < 0) overflow_test = -overflow_test; \ - if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) continue; +#define CLAMPMAX INT32_MAX +#define CLAMPMIN (-INT32_MAX) // This is not INT32_MIN on purpose! INT32_MIN makes the drawers freak out. + // Monster Iestyn (25/03/18): do not skip these lights if they fail overflow test, just clamp them instead so they behave. + overflow_test = (INT64)centeryfrac - (((INT64)leftheight*ds->scale1)>>FRACBITS); + if (overflow_test > (INT64)CLAMPMAX) rlight->height = CLAMPMAX; + else if (overflow_test > (INT64)CLAMPMIN) rlight->height = (fixed_t)overflow_test; + else rlight->height = CLAMPMIN; - OVERFLOWTEST(leftheight, ds->scale1) - OVERFLOWTEST(rightheight, ds->scale2) - - rlight->height = (centeryfrac) - FixedMul(leftheight, ds->scale1); - rlight->heightstep = (centeryfrac) - FixedMul(rightheight, ds->scale2); + overflow_test = (INT64)centeryfrac - (((INT64)rightheight*ds->scale2)>>FRACBITS); + if (overflow_test > (INT64)CLAMPMAX) rlight->heightstep = CLAMPMAX; + else if (overflow_test > (INT64)CLAMPMIN) rlight->heightstep = (fixed_t)overflow_test; + else rlight->heightstep = CLAMPMIN; rlight->heightstep = (rlight->heightstep-rlight->height)/(range); #else if (light->height < *pfloor->bottomheight) @@ -901,12 +903,16 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) leftheight -= viewz; rightheight -= viewz; - OVERFLOWTEST(leftheight, ds->scale1) - OVERFLOWTEST(rightheight, ds->scale2) -#undef OVERFLOWTEST + // Monster Iestyn (25/03/18): do not skip these lights if they fail overflow test, just clamp them instead so they behave. + overflow_test = (INT64)centeryfrac - (((INT64)leftheight*ds->scale1)>>FRACBITS); + if (overflow_test > (INT64)CLAMPMAX) rlight->botheight = CLAMPMAX; + else if (overflow_test > (INT64)CLAMPMIN) rlight->botheight = (fixed_t)overflow_test; + else rlight->botheight = CLAMPMIN; - rlight->botheight = (centeryfrac) - FixedMul(leftheight, ds->scale1); - rlight->botheightstep = (centeryfrac) - FixedMul(rightheight, ds->scale2); + overflow_test = (INT64)centeryfrac - (((INT64)rightheight*ds->scale2)>>FRACBITS); + if (overflow_test > (INT64)CLAMPMAX) rlight->botheightstep = CLAMPMAX; + else if (overflow_test > (INT64)CLAMPMIN) rlight->botheightstep = (fixed_t)overflow_test; + else rlight->botheightstep = CLAMPMIN; rlight->botheightstep = (rlight->botheightstep-rlight->botheight)/(range); #else lheight = *light->caster->bottomheight;// > *pfloor->topheight ? *pfloor->topheight + FRACUNIT : *light->caster->bottomheight; @@ -1079,9 +1085,6 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor) } #endif -#define CLAMPMAX INT32_MAX -#define CLAMPMIN (-INT32_MAX) // This is not INT32_MIN on purpose! INT32_MIN makes the drawers freak out. - // draw the columns for (dc_x = x1; dc_x <= x2; dc_x++) { diff --git a/src/r_state.h b/src/r_state.h index d6d123e99..e37bdf52e 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -17,6 +17,7 @@ // Need data structure definitions. #include "d_player.h" #include "r_data.h" +#include "doomstat.h" // MAXSPLITSCREENPLAYERS #ifdef __GNUG__ #pragma interface @@ -88,8 +89,9 @@ extern side_t *sides; // extern fixed_t viewx, viewy, viewz; extern angle_t viewangle, aimingangle; +extern UINT8 viewssnum; // splitscreen view number extern boolean viewsky, skyVisible; -extern boolean skyVisible1, skyVisible2, skyVisible3, skyVisible4; // saved values of skyVisible for P1 and P2, for splitscreen +extern boolean skyVisiblePerPlayer[MAXSPLITSCREENPLAYERS]; // saved values of skyVisible of each splitscreen player extern sector_t *viewsector; extern player_t *viewplayer; extern UINT8 portalrender; diff --git a/src/r_things.c b/src/r_things.c index 135ae6a29..a11b61669 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -40,7 +40,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); //int vsnprintf(char *str, size_t n, const char *fmt, va_list ap); #endif -static void R_InitSkins(void); +CV_PossibleValue_t Forceskin_cons_t[MAXSKINS+2]; #define MINZ (FRACUNIT*4) #define BASEYCENTER (BASEVIDHEIGHT/2) @@ -583,7 +583,17 @@ void R_InitSprites(void) // // it can be is do before loading config for skin cvar possible value - R_InitSkins(); + // (... what the fuck did you just say to me? "it can be is do"?) +#ifdef SKINVALUES + for (i = 0; i <= MAXSKINS; i++) + { + skin_cons_t[i].value = 0; + skin_cons_t[i].strvalue = NULL; + } +#endif + + numskins = 0; + for (i = 0; i < numwadfiles; i++) R_AddSkins((UINT16)i); @@ -925,6 +935,13 @@ static void R_DrawVisSprite(vissprite_t *vis) if (vis->x2 >= vid.width) vis->x2 = vid.width-1; +#if 1 + // Something is occasionally setting 1px-wide sprites whose frac is exactly the width of the sprite, causing crashes due to + // accessing invalid column info. Until the cause is found, let's try to correct those manually... + while (frac + vis->xiscale*(vis->x2-vis->x1) > SHORT(patch->width)<x2 >= vis->x1) + vis->x2--; +#endif + for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale) { if (vis->scalestep) // currently papersprites only @@ -1315,6 +1332,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); @@ -1694,7 +1712,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) // R_AddSprites // During BSP traversal, this adds sprites by sector. // -void R_AddSprites(sector_t *sec, INT32 lightlevel, UINT8 viewnumber) +void R_AddSprites(sector_t *sec, INT32 lightlevel) { mobj_t *thing; precipmobj_t *precipthing; // Tails 08-25-2002 @@ -1740,19 +1758,19 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel, UINT8 viewnumber) if (splitscreen) { if (thing->eflags & MFE_DRAWONLYFORP1) - if (viewnumber != 0) + if (viewssnum != 0) continue; if (thing->eflags & MFE_DRAWONLYFORP2) - if (viewnumber != 1) + if (viewssnum != 1) continue; if (thing->eflags & MFE_DRAWONLYFORP3 && splitscreen > 1) - if (viewnumber != 2) + if (viewssnum != 2) continue; if (thing->eflags & MFE_DRAWONLYFORP4 && splitscreen > 2) - if (viewnumber != 3) + if (viewssnum != 3) continue; } @@ -1775,19 +1793,19 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel, UINT8 viewnumber) if (splitscreen) { if (thing->eflags & MFE_DRAWONLYFORP1) - if (viewnumber != 0) + if (viewssnum != 0) continue; if (thing->eflags & MFE_DRAWONLYFORP2) - if (viewnumber != 1) + if (viewssnum != 1) continue; if (thing->eflags & MFE_DRAWONLYFORP3 && splitscreen > 1) - if (viewnumber != 2) + if (viewssnum != 2) continue; if (thing->eflags & MFE_DRAWONLYFORP4 && splitscreen > 2) - if (viewnumber != 3) + if (viewssnum != 3) continue; } @@ -1795,7 +1813,7 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel, UINT8 viewnumber) } } - // Someone seriously wants infinite draw distance for precipitation? + // no, no infinite draw distance for precipitation. this option at zero is supposed to turn it off if ((limit_dist = (fixed_t)cv_drawdist_precip.value << FRACBITS)) { for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) @@ -1811,13 +1829,6 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel, UINT8 viewnumber) R_ProjectPrecipitationSprite(precipthing); } } - else - { - // Draw everything in sector, no checks - for (precipthing = sec->preciplist; precipthing; precipthing = precipthing->snext) - if (!(precipthing->precipflags & PCF_INVISIBLE)) - R_ProjectPrecipitationSprite(precipthing); - } } // @@ -2528,7 +2539,7 @@ static void Sk_SetDefaultValue(skin_t *skin) strncpy(skin->facewant, "PLAYWANT", 9); strncpy(skin->facemmap, "PLAYMMAP", 9); - skin->starttranscolor = 160; + skin->starttranscolor = 96; skin->prefcolor = SKINCOLOR_GREEN; // SRB2kart @@ -2536,23 +2547,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++) @@ -2560,66 +2554,6 @@ static void Sk_SetDefaultValue(skin_t *skin) skin->soundsid[S_sfx[i].skinsound] = i; } -// -// Initialize the basic skins -// -void R_InitSkins(void) -{ - skin_t *skin; -#ifdef SKINVALUES - INT32 i; - - for (i = 0; i <= MAXSKINS; i++) - { - skin_cons_t[i].value = 0; - skin_cons_t[i].strvalue = NULL; - } -#endif - - // skin[0] = Sonic skin - skin = &skins[0]; - numskins = 1; - Sk_SetDefaultValue(skin); - - // Hardcoded S_SKIN customizations for Sonic. - strcpy(skin->name, DEFAULTSKIN); -#ifdef SKINVALUES - skin_cons_t[0].strvalue = skins[0].name; -#endif - skin->flags = SF_SUPER|SF_SUPERANIMS|SF_SUPERSPIN; - strcpy(skin->realname, "Sonic"); - strcpy(skin->hudname, "SONIC"); - - strncpy(skin->facerank, "PLAYRANK", 9); - strncpy(skin->facewant, "PLAYWANT", 9); - 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); - - //MD2 for sonic doesn't want to load in Linux. -#ifdef HWRENDER - if (rendermode == render_opengl) - HWR_AddPlayerMD2(0); -#endif -} - // returns true if the skin name is found (loaded from pwad) // warning return -1 if not found INT32 R_SkinAvailable(const char *name) @@ -2672,40 +2606,21 @@ 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 (!(cv_debug || devparm) && !(netgame || multiplayer || demo.playback || modeattacking)) { if (playernum == consoleplayer) CV_StealthSetValue(&cv_playercolor, skin->prefcolor); - else if (playernum == secondarydisplayplayer) + else if (playernum == displayplayers[1]) CV_StealthSetValue(&cv_playercolor2, skin->prefcolor); - else if (playernum == thirddisplayplayer) + else if (playernum == displayplayers[2]) CV_StealthSetValue(&cv_playercolor3, skin->prefcolor); - else if (playernum == fourthdisplayplayer) + else if (playernum == displayplayers[3]) CV_StealthSetValue(&cv_playercolor4, skin->prefcolor); player->skincolor = skin->prefcolor; if (player->mo) @@ -2714,6 +2629,9 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) if (player->mo) P_SetScale(player->mo, player->mo->scale); + + demo_extradata[playernum] |= DXD_SKIN; + return; } @@ -2895,28 +2813,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)) \ { \ @@ -2934,8 +2832,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 @@ -3037,6 +2933,10 @@ next_token: skin_cons_t[numskins].strvalue = skin->name; #endif + // Update the forceskin possiblevalues + Forceskin_cons_t[numskins+1].value = numskins; + Forceskin_cons_t[numskins+1].strvalue = skins[numskins].name; + // add face graphics ST_LoadFaceGraphics(skin->facerank, skin->facewant, skin->facemmap, numskins); @@ -3045,9 +2945,6 @@ next_token: HWR_AddPlayerMD2(numskins); #endif - if (skin->flags & SF_RUNONWATER) // this is literally the only way a skin can be a major mod... this might be a bit heavy handed - G_SetGameModified(multiplayer, true); - numskins++; } return; diff --git a/src/r_things.h b/src/r_things.h index 01d8fc071..697cde256 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -55,7 +55,7 @@ void R_DelSpriteDefs(UINT16 wadnum); #endif //SoM: 6/5/2000: Light sprites correctly! -void R_AddSprites(sector_t *sec, INT32 lightlevel, UINT8 viewnumber); +void R_AddSprites(sector_t *sec, INT32 lightlevel); void R_InitSprites(void); void R_ClearSprites(void); void R_ClipSprites(void); @@ -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; @@ -115,6 +97,8 @@ typedef struct sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table } skin_t; +extern CV_PossibleValue_t Forceskin_cons_t[]; + // ----------- // NOT SKINS STUFF ! // ----------- diff --git a/src/s_sound.c b/src/s_sound.c index 856aa0459..21b668f28 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -38,6 +38,10 @@ extern INT32 msg_id; #include "p_local.h" // camera info #include "m_misc.h" // for tunes command +#if defined(HAVE_BLUA) && defined(HAVE_LUA_MUSICPLUS) +#include "lua_hook.h" // MusicChange hook +#endif + #ifdef HW3SOUND // 3D Sound Interface #include "hardware/hw3sound.h" @@ -57,6 +61,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 +117,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 +280,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); @@ -429,7 +442,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) listener_t listener3 = {0,0,0,0}; listener_t listener4 = {0,0,0,0}; - mobj_t *listenmobj = players[displayplayer].mo; + mobj_t *listenmobj = players[displayplayers[0]].mo; mobj_t *listenmobj2 = NULL; mobj_t *listenmobj3 = NULL; mobj_t *listenmobj4 = NULL; @@ -441,26 +454,26 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) if (sfx_id == sfx_None) return; - if (players[displayplayer].awayviewtics) - listenmobj = players[displayplayer].awayviewmobj; + if (players[displayplayers[0]].awayviewtics) + listenmobj = players[displayplayers[0]].awayviewmobj; if (splitscreen) { - listenmobj2 = players[secondarydisplayplayer].mo; - if (players[secondarydisplayplayer].awayviewtics) - listenmobj2 = players[secondarydisplayplayer].awayviewmobj; + listenmobj2 = players[displayplayers[1]].mo; + if (players[displayplayers[1]].awayviewtics) + listenmobj2 = players[displayplayers[1]].awayviewmobj; if (splitscreen > 1) { - listenmobj3 = players[thirddisplayplayer].mo; - if (players[thirddisplayplayer].awayviewtics) - listenmobj3 = players[thirddisplayplayer].awayviewmobj; + listenmobj3 = players[displayplayers[2]].mo; + if (players[displayplayers[2]].awayviewtics) + listenmobj3 = players[displayplayers[2]].awayviewmobj; if (splitscreen > 2) { - listenmobj4 = players[fourthdisplayplayer].mo; - if (players[fourthdisplayplayer].awayviewtics) - listenmobj4 = players[fourthdisplayplayer].awayviewmobj; + listenmobj4 = players[displayplayers[3]].mo; + if (players[displayplayers[3]].awayviewtics) + listenmobj4 = players[displayplayers[3]].awayviewmobj; } } } @@ -473,12 +486,12 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) }; #endif - if (camera.chase && !players[displayplayer].awayviewtics) + if (camera[0].chase && !players[displayplayers[0]].awayviewtics) { - listener.x = camera.x; - listener.y = camera.y; - listener.z = camera.z; - listener.angle = camera.angle; + listener.x = camera[0].x; + listener.y = camera[0].y; + listener.z = camera[0].z; + listener.angle = camera[0].angle; } else if (listenmobj) { @@ -492,12 +505,12 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) if (listenmobj2) { - if (camera2.chase && !players[secondarydisplayplayer].awayviewtics) + if (camera[1].chase && !players[displayplayers[1]].awayviewtics) { - listener2.x = camera2.x; - listener2.y = camera2.y; - listener2.z = camera2.z; - listener2.angle = camera2.angle; + listener2.x = camera[1].x; + listener2.y = camera[1].y; + listener2.z = camera[1].z; + listener2.angle = camera[1].angle; } else { @@ -510,12 +523,12 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) if (listenmobj3) { - if (camera3.chase && !players[thirddisplayplayer].awayviewtics) + if (camera[2].chase && !players[displayplayers[2]].awayviewtics) { - listener3.x = camera3.x; - listener3.y = camera3.y; - listener3.z = camera3.z; - listener3.angle = camera3.angle; + listener3.x = camera[2].x; + listener3.y = camera[2].y; + listener3.z = camera[2].z; + listener3.angle = camera[2].angle; } else { @@ -528,12 +541,12 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) if (listenmobj4) { - if (camera4.chase && !players[fourthdisplayplayer].awayviewtics) + if (camera[3].chase && !players[displayplayers[3]].awayviewtics) { - listener4.x = camera4.x; - listener4.y = camera4.y; - listener4.z = camera4.z; - listener4.angle = camera4.angle; + listener4.x = camera[3].x; + listener4.y = camera[3].y; + listener4.z = camera[3].z; + listener4.angle = camera[3].angle; } else { @@ -890,7 +903,7 @@ void S_UpdateSounds(void) listener_t listener3; listener_t listener4; - mobj_t *listenmobj = players[displayplayer].mo; + mobj_t *listenmobj = players[displayplayers[0]].mo; mobj_t *listenmobj2 = NULL; mobj_t *listenmobj3 = NULL; mobj_t *listenmobj4 = NULL; @@ -926,36 +939,36 @@ void S_UpdateSounds(void) if (dedicated || sound_disabled) return; - if (players[displayplayer].awayviewtics) - listenmobj = players[displayplayer].awayviewmobj; + if (players[displayplayers[0]].awayviewtics) + listenmobj = players[displayplayers[0]].awayviewmobj; if (splitscreen) { - listenmobj2 = players[secondarydisplayplayer].mo; - if (players[secondarydisplayplayer].awayviewtics) - listenmobj2 = players[secondarydisplayplayer].awayviewmobj; + listenmobj2 = players[displayplayers[1]].mo; + if (players[displayplayers[1]].awayviewtics) + listenmobj2 = players[displayplayers[1]].awayviewmobj; if (splitscreen > 1) { - listenmobj3 = players[thirddisplayplayer].mo; - if (players[thirddisplayplayer].awayviewtics) - listenmobj3 = players[thirddisplayplayer].awayviewmobj; + listenmobj3 = players[displayplayers[2]].mo; + if (players[displayplayers[2]].awayviewtics) + listenmobj3 = players[displayplayers[2]].awayviewmobj; if (splitscreen > 2) { - listenmobj4 = players[fourthdisplayplayer].mo; - if (players[fourthdisplayplayer].awayviewtics) - listenmobj4 = players[fourthdisplayplayer].awayviewmobj; + listenmobj4 = players[displayplayers[3]].mo; + if (players[displayplayers[3]].awayviewtics) + listenmobj4 = players[displayplayers[3]].awayviewmobj; } } } - if (camera.chase && !players[displayplayer].awayviewtics) + if (camera[0].chase && !players[displayplayers[0]].awayviewtics) { - listener.x = camera.x; - listener.y = camera.y; - listener.z = camera.z; - listener.angle = camera.angle; + listener.x = camera[0].x; + listener.y = camera[0].y; + listener.z = camera[0].z; + listener.angle = camera[0].angle; } else if (listenmobj) { @@ -980,12 +993,12 @@ void S_UpdateSounds(void) if (listenmobj2) { - if (camera2.chase && !players[secondarydisplayplayer].awayviewtics) + if (camera[1].chase && !players[displayplayers[1]].awayviewtics) { - listener2.x = camera2.x; - listener2.y = camera2.y; - listener2.z = camera2.z; - listener2.angle = camera2.angle; + listener2.x = camera[1].x; + listener2.y = camera[1].y; + listener2.z = camera[1].z; + listener2.angle = camera[1].angle; } else { @@ -998,12 +1011,12 @@ void S_UpdateSounds(void) if (listenmobj3) { - if (camera3.chase && !players[thirddisplayplayer].awayviewtics) + if (camera[2].chase && !players[displayplayers[2]].awayviewtics) { - listener3.x = camera3.x; - listener3.y = camera3.y; - listener3.z = camera3.z; - listener3.angle = camera3.angle; + listener3.x = camera[2].x; + listener3.y = camera[2].y; + listener3.z = camera[2].z; + listener3.angle = camera[2].angle; } else { @@ -1016,12 +1029,12 @@ void S_UpdateSounds(void) if (listenmobj4) { - if (camera4.chase && !players[fourthdisplayplayer].awayviewtics) + if (camera[3].chase && !players[displayplayers[3]].awayviewtics) { - listener4.x = camera4.x; - listener4.y = camera4.y; - listener4.z = camera4.z; - listener4.angle = camera4.angle; + listener4.x = camera[3].x; + listener4.y = camera[3].y; + listener4.z = camera[3].z; + listener4.angle = camera[3].angle; } else { @@ -1051,9 +1064,9 @@ void S_UpdateSounds(void) // check non-local sounds for distance clipping // or modify their params if (c->origin && ((c->origin != players[consoleplayer].mo) - || (splitscreen && c->origin != players[secondarydisplayplayer].mo) - || (splitscreen > 1 && c->origin != players[thirddisplayplayer].mo) - || (splitscreen > 2 && c->origin != players[fourthdisplayplayer].mo))) + || (splitscreen && c->origin != players[displayplayers[1]].mo) + || (splitscreen > 1 && c->origin != players[displayplayers[2]].mo) + || (splitscreen > 2 && c->origin != players[displayplayers[3]].mo))) { // Whomever is closer gets the sound, but only in splitscreen. if (splitscreen) @@ -1062,13 +1075,10 @@ void S_UpdateSounds(void) fixed_t recdist = -1; INT32 i, p = -1; - for (i = 0; i < 4; i++) + for (i = 0; i <= splitscreen; i++) { fixed_t thisdist = -1; - if (i > splitscreen) - break; - if (i == 0 && listenmobj) thisdist = P_AproxDistance(listener.x-soundmobj->x, listener.y-soundmobj->y); else if (i == 1 && listenmobj2) @@ -1241,33 +1251,33 @@ INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 *v if (!listener) return false; - if (listener == players[displayplayer].mo && camera.chase) + if (listener == players[displayplayers[0]].mo && camera[0].chase) { - listensource.x = camera.x; - listensource.y = camera.y; - listensource.z = camera.z; - listensource.angle = camera.angle; + listensource.x = camera[0].x; + listensource.y = camera[0].y; + listensource.z = camera[0].z; + listensource.angle = camera[0].angle; } - else if (splitscreen && listener == players[secondarydisplayplayer].mo && camera2.chase) + else if (splitscreen && listener == players[displayplayers[1]].mo && camera[1].chase) { - listensource.x = camera2.x; - listensource.y = camera2.y; - listensource.z = camera2.z; - listensource.angle = camera2.angle; + listensource.x = camera[1].x; + listensource.y = camera[1].y; + listensource.z = camera[1].z; + listensource.angle = camera[1].angle; } - else if (splitscreen > 1 && listener == players[thirddisplayplayer].mo && camera3.chase) + else if (splitscreen > 1 && listener == players[displayplayers[2]].mo && camera[2].chase) { - listensource.x = camera3.x; - listensource.y = camera3.y; - listensource.z = camera3.z; - listensource.angle = camera3.angle; + listensource.x = camera[2].x; + listensource.y = camera[2].y; + listensource.z = camera[2].z; + listensource.angle = camera[2].angle; } - else if (splitscreen > 2 && listener == players[fourthdisplayplayer].mo && camera4.chase) + else if (splitscreen > 2 && listener == players[displayplayers[3]].mo && camera[3].chase) { - listensource.x = camera4.x; - listensource.y = camera4.y; - listensource.z = camera4.z; - listensource.angle = camera4.angle; + listensource.x = camera[3].x; + listensource.y = camera[3].y; + listensource.z = camera[3].z; + listensource.angle = camera[3].angle; } else { @@ -1541,6 +1551,12 @@ static void *music_data; static UINT16 music_flags; static boolean music_looping; +static char queue_name[7]; +static UINT16 queue_flags; +static boolean queue_looping; +static UINT32 queue_position; +static UINT32 queue_fadeinms; + /// ------------------------ /// Music Definitions /// ------------------------ @@ -1724,7 +1740,7 @@ void S_ShowMusicCredit(void) { musicdef_t *def = musicdefstart; - if (!cv_songcredits.value) + if (!cv_songcredits.value || demo.rewinding) return; if (!def) // No definitions @@ -1779,6 +1795,11 @@ musictype_t S_MusicType(void) return I_SongType(); } +const char *S_MusicName(void) +{ + return music_name; +} + boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping) { if (!I_SongPlaying()) @@ -1809,6 +1830,35 @@ boolean S_SpeedMusic(float speed) return I_SetSongSpeed(speed); } +/// ------------------------ +/// Music Seeking +/// ------------------------ + +UINT32 S_GetMusicLength(void) +{ + return I_GetSongLength(); +} + +boolean S_SetMusicLoopPoint(UINT32 looppoint) +{ + return I_SetSongLoopPoint(looppoint); +} + +UINT32 S_GetMusicLoopPoint(void) +{ + return I_GetSongLoopPoint(); +} + +boolean S_SetMusicPosition(UINT32 position) +{ + return I_SetSongPosition(position); +} + +UINT32 S_GetMusicPosition(void) +{ + return I_GetSongPosition(); +} + /// ------------------------ /// Music Playback /// ------------------------ @@ -1885,64 +1935,126 @@ static void S_UnloadMusic(void) music_looping = false; } -static boolean S_PlayMusic(boolean looping) +static boolean S_PlayMusic(boolean looping, UINT32 fadeinms) { if (S_MusicDisabled()) return false; - if (!I_PlaySong(looping)) + if ((!fadeinms && !I_PlaySong(looping)) || + (fadeinms && !I_FadeInPlaySong(fadeinms, looping))) { S_UnloadMusic(); return false; } S_InitMusicVolume(); // switch between digi and sequence volume + + if (window_notinfocus && !cv_playmusicifunfocused.value) + I_PauseSong(); + return true; } -void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping) +static void S_QueueMusic(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 fadeinms) { + strncpy(queue_name, mmusic, 7); + queue_flags = mflags; + queue_looping = looping; + queue_position = position; + queue_fadeinms = fadeinms; +} + +static void S_ClearQueue(void) +{ + queue_name[0] = queue_flags = queue_looping = queue_position = queue_fadeinms = 0; +} + +static void S_ChangeMusicToQueue(void) +{ + S_ChangeMusicEx(queue_name, queue_flags, queue_looping, queue_position, 0, queue_fadeinms); + S_ClearQueue(); +} + +void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms) +{ + char newmusic[7]; + #if defined (DC) || defined (_WIN32_WCE) || defined (PSP) || defined(GP2X) S_ClearSfx(); #endif if (S_MusicDisabled() - || titledemo) // SRB2Kart: Demos don't interrupt title screen music + || demo.rewinding // Don't mess with music while rewinding! + || demo.title) // SRB2Kart: Demos don't interrupt title screen music return; - // No Music (empty string) - if (mmusic[0] == 0) - { - S_StopMusic(); + strncpy(newmusic, mmusic, 7); +#if defined(HAVE_BLUA) && defined(HAVE_LUA_MUSICPLUS) + if(LUAh_MusicChange(music_name, newmusic, &mflags, &looping, &position, &prefadems, &fadeinms)) + return; +#endif + newmusic[6] = 0; + + // No Music (empty string) + if (newmusic[0] == 0) + { + if (prefadems) + I_FadeSong(0, prefadems, &S_StopMusic); + else + S_StopMusic(); return; } - if (strnicmp(music_name, mmusic, 6)) + if (prefadems && S_MusicPlaying()) // queue music change for after fade // allow even if the music is the same { - S_StopMusic(); // shutdown old music + CONS_Debug(DBG_DETAILED, "Now fading out song %s\n", music_name); + S_QueueMusic(newmusic, mflags, looping, position, fadeinms); + I_FadeSong(0, prefadems, S_ChangeMusicToQueue); + return; + } + else if (strnicmp(music_name, newmusic, 6) || (mflags & MUSIC_FORCERESET)) + { + CONS_Debug(DBG_DETAILED, "Now playing song %s\n", newmusic); - if (!S_LoadMusic(mmusic)) + S_StopMusic(); + + if (!S_LoadMusic(newmusic)) { - CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded!\n", mmusic); + CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded!\n", newmusic); return; } music_flags = mflags; music_looping = looping; - if (!S_PlayMusic(looping)) - { - CONS_Alert(CONS_ERROR, "Music %.6s could not be played!\n", mmusic); + if (!S_PlayMusic(looping, fadeinms)) + { + CONS_Alert(CONS_ERROR, "Music %.6s could not be played!\n", newmusic); return; } + + if (position) + I_SetSongPosition(position); + + I_SetSongTrack(mflags & MUSIC_TRACKMASK); + } + else if (fadeinms) // let fades happen with same music + { + I_SetSongPosition(position); + I_FadeSong(100, fadeinms, NULL); + } + else // reset volume to 100 with same music + { + I_StopFadingSong(); + I_FadeSong(100, 500, NULL); } - I_SetSongTrack(mflags & MUSIC_TRACKMASK); } void S_StopMusic(void) { if (!I_SongPlaying() - || titledemo) // SRB2Kart: Demos don't interrupt title screen music + || demo.rewinding // Don't mess with music while rewinding! + || demo.title) // SRB2Kart: Demos don't interrupt title screen music return; if (I_SongPaused()) @@ -1978,6 +2090,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) @@ -2024,6 +2154,32 @@ void S_SetMusicVolume(INT32 digvolume, INT32 seqvolume) } } +/// ------------------------ +/// Music Fading +/// ------------------------ + +void S_SetInternalMusicVolume(INT32 volume) +{ + I_SetInternalMusicVolume(min(max(volume, 0), 100)); +} + +void S_StopFadingMusic(void) +{ + I_StopFadingSong(); +} + +boolean S_FadeMusicFromVolume(UINT8 target_volume, INT16 source_volume, UINT32 ms) +{ + if (source_volume < 0) + return I_FadeSong(target_volume, ms, NULL); + else + return I_FadeSongFromVolume(target_volume, source_volume, ms, NULL); +} + +boolean S_FadeOutStopMusic(UINT32 ms) +{ + return I_FadeSong(0, ms, &S_StopMusic); +} /// ------------------------ /// Init & Others @@ -2041,26 +2197,28 @@ void S_Start(void) strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname, 7); mapmusname[6] = 0; mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK); + mapmusposition = mapheaderinfo[gamemap-1]->muspos; } //if (cv_resetmusic.value) // Starting ambience should always be restarted S_StopMusic(); if (leveltime < (starttime + (TICRATE/2))) // SRB2Kart - S_ChangeMusic((encoremode ? "estart" : "kstart"), 0, false); + S_ChangeMusicEx((encoremode ? "estart" : "kstart"), 0, false, mapmusposition, 0, 0); else - S_ChangeMusic(mapmusname, mapmusflags, true); + S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); } static void Command_Tunes_f(void) { const char *tunearg; UINT16 tunenum, track = 0; + UINT32 position = 0; const size_t argc = COM_Argc(); if (argc < 2) //tunes slot ... { - CONS_Printf("tunes [track] [speed] / <-show> / <-default> / <-none>:\n"); + CONS_Printf("tunes [track] [speed] [position] / <-show> / <-default> / <-none>:\n"); CONS_Printf(M_GetText("Play an arbitrary music lump. If a map number is used, 'MAP##M' is played.\n")); CONS_Printf(M_GetText("If the format supports multiple songs, you can specify which one to play.\n\n")); CONS_Printf(M_GetText("* With \"-show\", shows the currently playing tune and track.\n")); @@ -2107,10 +2265,15 @@ static void Command_Tunes_f(void) snprintf(mapmusname, 7, "%sM", G_BuildMapName(tunenum)); else strncpy(mapmusname, tunearg, 7); + + if (argc > 4) + position = (UINT32)atoi(COM_Argv(4)); + mapmusname[6] = 0; mapmusflags = (track & MUSIC_TRACKMASK); + mapmusposition = position; - S_ChangeMusic(mapmusname, mapmusflags, true); + S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); if (argc > 3) { @@ -2151,25 +2314,21 @@ static void Command_RestartAudio_f(void) void GameSounds_OnChange(void) { - if (M_CheckParm("-nosound")) + if (M_CheckParm("-nosound") || M_CheckParm("-noaudio")) return; 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) { - if (M_CheckParm("-nomusic")) + if (M_CheckParm("-nomusic") || M_CheckParm("-noaudio")) return; else if (M_CheckParm("-nodigmusic")) return; @@ -2212,7 +2371,7 @@ void GameDigiMusic_OnChange(void) #ifndef NO_MIDI void GameMIDIMusic_OnChange(void) { - if (M_CheckParm("-nomusic")) + if (M_CheckParm("-nomusic") || M_CheckParm("-noaudio")) return; else if (M_CheckParm("-nomidimusic")) return; @@ -2251,3 +2410,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 1ad519c20..2a904faff 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; @@ -115,14 +117,14 @@ boolean S_MusicDisabled(void); boolean S_MusicPlaying(void); boolean S_MusicPaused(void); musictype_t S_MusicType(void); +const char *S_MusicName(void); boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping); boolean S_MusicExists(const char *mname, boolean checkMIDI, boolean checkDigi); #define S_DigExists(a) S_MusicExists(a, false, true) #define S_MIDIExists(a) S_MusicExists(a, true, false) - // -// Music Properties +// Music Effects // // Set Speed of Music @@ -152,15 +154,35 @@ void S_InitMusicDefs(void); void S_ShowMusicCredit(void); // -// Music Routines +// Music Seeking +// + +// Get Length of Music +UINT32 S_GetMusicLength(void); + +// Set LoopPoint of Music +boolean S_SetMusicLoopPoint(UINT32 looppoint); + +// Get LoopPoint of Music +UINT32 S_GetMusicLoopPoint(void); + +// Set Position of Music +boolean S_SetMusicPosition(UINT32 position); + +// Get Position of Music +UINT32 S_GetMusicPosition(void); + +// +// Music Playback // // Start music track, arbitrary, given its name, and set whether looping // note: music flags 12 bits for tracknum (gme, other formats with more than one track) // 13-15 aren't used yet // and the last bit we ignore (internal game flag for resetting music on reload) -#define S_ChangeMusicInternal(a,b) S_ChangeMusic(a,0,b) -void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping); +void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms); +#define S_ChangeMusicInternal(a,b) S_ChangeMusicEx(a,0,b,0,0,0) +#define S_ChangeMusic(a,b,c) S_ChangeMusicEx(a,b,c,0,0,0) // Stops the music. void S_StopMusic(void); @@ -169,6 +191,21 @@ 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); + +// +// Music Fading +// + +void S_SetInternalMusicVolume(INT32 volume); +void S_StopFadingMusic(void); +boolean S_FadeMusicFromVolume(UINT8 target_volume, INT16 source_volume, UINT32 ms); +#define S_FadeMusic(a, b) S_FadeMusicFromVolume(a, -1, b) +#define S_FadeInChangeMusic(a,b,c,d) S_ChangeMusicEx(a,b,c,0,0,d) +boolean S_FadeOutStopMusic(UINT32 ms); + // // Updates music & sounds // diff --git a/src/screen.c b/src/screen.c index af6aed03c..4cb8bac5d 100644 --- a/src/screen.c +++ b/src/screen.c @@ -59,6 +59,8 @@ INT32 setmodeneeded; //video mode change needed if > 0 (the mode number to set + static CV_PossibleValue_t scr_depth_cons_t[] = {{8, "8 bits"}, {16, "16 bits"}, {24, "24 bits"}, {32, "32 bits"}, {0, NULL}}; +static CV_PossibleValue_t shittyscreen_cons_t[] = {{0, "Okay"}, {1, "Shitty"}, {2, "Extra Shitty"}, {0, NULL}}; + //added : 03-02-98: default screen mode, as loaded/saved in config #ifdef WII consvar_t cv_scr_width = {"scr_width", "640", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -70,6 +72,8 @@ consvar_t cv_scr_height = {"scr_height", "800", CV_SAVE, CV_Unsigned, NULL, 0, N consvar_t cv_scr_depth = {"scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; #endif consvar_t cv_renderview = {"renderview", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_vhseffect = {"vhspause", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_shittyscreen = {"televisionsignal", "Okay", CV_NOSHOWHELP, shittyscreen_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; static void SCR_ChangeFullscreen (void); @@ -403,7 +407,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 +418,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 9ad254d3f..2e4d29b95 100644 --- a/src/screen.h +++ b/src/screen.h @@ -158,7 +158,7 @@ extern INT32 setmodeneeded; // mode number to set if needed, or 0 extern INT32 scr_bpp; extern UINT8 *scr_borderpatch; // patch used to fill the view borders -extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_fullscreen; +extern consvar_t cv_scr_width, cv_scr_height, cv_scr_depth, cv_renderview, cv_fullscreen, cv_vhseffect, cv_shittyscreen; // wait for page flipping to end or not extern consvar_t cv_vidwait; @@ -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/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index d2466bd5b..45b1faab7 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -227,6 +227,10 @@ + + + + @@ -366,8 +370,12 @@ + + + + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index daa13189f..8556627b8 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -246,6 +246,18 @@ Hw_Hardware + + Hw_Hardware + + + Hw_Hardware + + + Hw_Hardware + + + Hw_Hardware + I_Interface @@ -627,9 +639,21 @@ Hw_Hardware + + Hw_Hardware + + + Hw_Hardware + + + Hw_Hardware + Hw_Hardware + + Hw_Hardware + I_Interface diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c index 05ac6450e..4e083b4c2 100644 --- a/src/sdl/hwsym_sdl.c +++ b/src/sdl/hwsym_sdl.c @@ -87,13 +87,11 @@ void *hwSym(const char *funcName,void *handle) GETFUNC(ClearMipMapCache); GETFUNC(SetSpecialState); GETFUNC(GetTextureUsed); - GETFUNC(DrawMD2); - GETFUNC(DrawMD2i); + GETFUNC(DrawModel); + GETFUNC(CreateModelVBOs); GETFUNC(SetTransform); GETFUNC(GetRenderVersion); -#ifdef SHUFFLE GETFUNC(PostImgRedraw); -#endif //SHUFFLE GETFUNC(FlushScreenTextures); GETFUNC(StartScreenWipe); GETFUNC(EndScreenWipe); diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index f360072a0..d24bd5ade 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -131,7 +131,7 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); #include #endif -// Locations for searching the srb2.srb +// Locations for searching for main.kart #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) #define DEFAULTWADLOCATION1 "/usr/local/share/games/SRB2Kart" #define DEFAULTWADLOCATION2 "/usr/local/games/SRB2Kart" @@ -149,8 +149,7 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); /** \brief WAD file to look for */ -#define WADKEYWORD1 "srb2.srb" -#define WADKEYWORD2 "srb2.wad" +#define WADKEYWORD "main.kart" /** \brief holds wad path */ static char returnWadPath[256]; @@ -2974,8 +2973,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; @@ -3061,7 +3060,7 @@ void I_Quit(void) //added:16-02-98: when recording a demo, should exit using 'q' key, // but sometimes we forget and use 'F10'.. so save here too. - if (demorecording) + if (demo.recording) G_CheckDemoStatus(); if (metalrecording) G_StopMetalRecording(); @@ -3179,7 +3178,7 @@ void I_Error(const char *error, ...) G_SaveGameData(false); // Tails 12-08-2002 // Shutdown. Here might be other errors. - if (demorecording) + if (demo.recording) G_CheckDemoStatus(); if (metalrecording) G_StopMetalRecording(); @@ -3454,15 +3453,7 @@ static boolean isWadPathOk(const char *path) if (!wad3path) return false; - sprintf(wad3path, pandf, path, WADKEYWORD1); - - if (FIL_ReadFileOK(wad3path)) - { - free(wad3path); - return true; - } - - sprintf(wad3path, pandf, path, WADKEYWORD2); + sprintf(wad3path, pandf, path, WADKEYWORD); if (FIL_ReadFileOK(wad3path)) { @@ -3487,7 +3478,7 @@ static void pathonly(char *s) } } -/** \brief search for srb2.srb in the given path +/** \brief search for main.kart in the given path \param searchDir starting path @@ -3500,7 +3491,7 @@ static const char *searchWad(const char *searchDir) static char tempsw[256] = ""; filestatus_t fstemp; - strcpy(tempsw, WADKEYWORD1); + strcpy(tempsw, WADKEYWORD); fstemp = filesearch(tempsw,searchDir,NULL,true,20); if (fstemp == FS_FOUND) { @@ -3508,19 +3499,12 @@ static const char *searchWad(const char *searchDir) return tempsw; } - strcpy(tempsw, WADKEYWORD2); - fstemp = filesearch(tempsw, searchDir, NULL, true, 20); - if (fstemp == FS_FOUND) - { - pathonly(tempsw); - return tempsw; - } return NULL; } -/** \brief go through all possible paths and look for srb2.srb +/** \brief go through all possible paths and look for main.kart - \return path to srb2.srb if any + \return path to main.kart if any */ static const char *locateWad(void) { @@ -3649,7 +3633,7 @@ const char *I_LocateWad(void) if (waddir) { - // change to the directory where we found srb2.srb + // change to the directory where we found main.kart #if defined (_WIN32) SetCurrentDirectoryA(waddir); #else diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 2a77604d2..42e0a917f 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" @@ -359,6 +359,14 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code) return 0; } +static void SDLdoGrabMouse(void) +{ + SDL_ShowCursor(SDL_DISABLE); + SDL_SetWindowGrab(window, SDL_TRUE); + if (SDL_SetRelativeMouseMode(SDL_TRUE) == 0) // already warps mouse if successful + wrapmouseok = SDL_TRUE; // TODO: is wrapmouseok or HalfWarpMouse needed anymore? +} + static void SDLdoUngrabMouse(void) { SDL_ShowCursor(SDL_ENABLE); @@ -615,9 +623,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(); @@ -625,12 +637,18 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt) //else firsttimeonmouse = SDL_FALSE; capslock = !!( SDL_GetModState() & KMOD_CAPS );// in case CL changes + + if (USE_MOUSEINPUT) + SDLdoGrabMouse(); } else if (!mousefocus && !kbfocus) { // 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) { @@ -701,9 +719,7 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt) // -- Monster Iestyn if (SDL_GetMouseFocus() == window && SDL_GetKeyboardFocus() == window) { - SDL_SetWindowGrab(window, SDL_TRUE); - if (SDL_SetRelativeMouseMode(SDL_TRUE) == 0) // already warps mouse if successful - wrapmouseok = SDL_TRUE; // TODO: is wrapmouseok or HalfWarpMouse needed anymore? + SDLdoGrabMouse(); } } } @@ -1270,7 +1286,7 @@ void I_StartupMouse(void) else firsttimeonmouse = SDL_FALSE; if (cv_usemouse.value) - return; + SDLdoGrabMouse(); else SDLdoUngrabMouse(); } @@ -1327,6 +1343,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 +1362,8 @@ static inline boolean I_SkipFrame(void) default: return false; } +#endif + return false; } // @@ -1361,6 +1380,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; @@ -1832,13 +1854,11 @@ void I_StartupGraphics(void) HWD.pfnSetSpecialState = hwSym("SetSpecialState",NULL); HWD.pfnSetPalette = hwSym("SetPalette",NULL); HWD.pfnGetTextureUsed = hwSym("GetTextureUsed",NULL); - HWD.pfnDrawMD2 = hwSym("DrawMD2",NULL); - HWD.pfnDrawMD2i = hwSym("DrawMD2i",NULL); + HWD.pfnDrawModel = hwSym("DrawModel",NULL); + HWD.pfnCreateModelVBOs = hwSym("CreateModelVBOs",NULL); HWD.pfnSetTransform = hwSym("SetTransform",NULL); HWD.pfnGetRenderVersion = hwSym("GetRenderVersion",NULL); -#ifdef SHUFFLE HWD.pfnPostImgRedraw = hwSym("PostImgRedraw",NULL); -#endif HWD.pfnFlushScreenTextures=hwSym("FlushScreenTextures",NULL); HWD.pfnStartScreenWipe = hwSym("StartScreenWipe",NULL); HWD.pfnEndScreenWipe = hwSym("EndScreenWipe",NULL); diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 954ef5ee9..1617da2a3 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -75,15 +75,41 @@ UINT8 sound_started = false; static Mix_Music *music; -static UINT8 music_volume, sfx_volume; +static UINT8 music_volume, sfx_volume, internal_volume; static float loop_point; +static float song_length; // length in seconds static boolean songpaused; +static UINT32 music_bytes; +static boolean is_looping; + +// fading +static boolean is_fading; +static UINT8 fading_source; +static UINT8 fading_target; +static UINT32 fading_timer; +static UINT32 fading_duration; +static INT32 fading_id; +static void (*fading_callback)(void); #ifdef HAVE_LIBGME static Music_Emu *gme; static INT32 current_track; #endif +static void var_cleanup(void) +{ + loop_point = song_length =\ + music_bytes = fading_source = fading_target =\ + fading_timer = fading_duration = 0; + + songpaused = is_looping =\ + is_fading = false; + + fading_callback = NULL; + + internal_volume = 100; +} + /// ------------------------ /// Audio System /// ------------------------ @@ -111,6 +137,8 @@ void I_StartupSound(void) return; } + var_cleanup(); + music = NULL; music_volume = sfx_volume = 0; @@ -336,6 +364,7 @@ void *I_GetSfx(sfxinfo_t *sfx) len = (info->play_length * 441 / 10) << 2; mem = malloc(len); gme_play(emu, len >> 1, mem); + gme_free_info(info); gme_delete(emu); return Mix_QuickLoad_RAW((Uint8 *)mem, len); @@ -408,6 +437,7 @@ void *I_GetSfx(sfxinfo_t *sfx) len = (info->play_length * 441 / 10) << 2; mem = malloc(len); gme_play(emu, len >> 1, mem); + gme_free_info(info); gme_delete(emu); return Mix_QuickLoad_RAW((Uint8 *)mem, len); @@ -482,14 +512,102 @@ void I_SetSfxVolume(UINT8 volume) sfx_volume = volume; } +/// ------------------------ +/// Music Utilities +/// ------------------------ + +static UINT32 get_real_volume(UINT8 volume) +{ +#ifdef _WIN32 + if (I_SongType() == MU_MID) + // HACK: Until we stop using native MIDI, + // disable volume changes + return ((UINT32)31*128/31); // volume = 31 + else +#endif + // convert volume to mixer's 128 scale + // then apply internal_volume as a percentage + return ((UINT32)volume*128/31) * (UINT32)internal_volume / 100; +} + +static UINT32 get_adjusted_position(UINT32 position) +{ + // all in milliseconds + UINT32 length = I_GetSongLength(); + UINT32 looppoint = I_GetSongLoopPoint(); + if (length) + return position >= length ? (position % (length-looppoint)) : position; + else + return position; +} + +static void do_fading_callback(void) +{ + if (fading_callback) + (*fading_callback)(); + fading_callback = NULL; +} + /// ------------------------ /// Music Hooks /// ------------------------ +static void count_music_bytes(int chan, void *stream, int len, void *udata) +{ + (void)chan; + (void)stream; + (void)udata; + + if (!music || I_SongType() == MU_GME || I_SongType() == MU_MOD || I_SongType() == MU_MID) + return; + music_bytes += len; +} + static void music_loop(void) { - Mix_PlayMusic(music, 0); - Mix_SetMusicPosition(loop_point); + if (is_looping) + { + Mix_PlayMusic(music, 0); + Mix_SetMusicPosition(loop_point); + music_bytes = loop_point*44100.0L*4; //assume 44.1khz, 4-byte length (see I_GetSongPosition) + } + else + I_StopSong(); +} + +static UINT32 music_fade(UINT32 interval, void *param) +{ + (void)param; + + if (!is_fading || + internal_volume == fading_target || + fading_duration == 0) + { + I_StopFadingSong(); + do_fading_callback(); + return 0; + } + else if (songpaused) // don't decrement timer + return interval; + else if ((fading_timer -= 10) <= 0) + { + internal_volume = fading_target; + Mix_VolumeMusic(get_real_volume(music_volume)); + I_StopFadingSong(); + do_fading_callback(); + return 0; + } + else + { + UINT8 delta = abs(fading_target - fading_source); + fixed_t factor = FixedDiv(fading_duration - fading_timer, fading_duration); + if (fading_target < fading_source) + internal_volume = max(min(internal_volume, fading_source - FixedMul(delta, factor)), fading_target); + else if (fading_target > fading_source) + internal_volume = min(max(internal_volume, fading_source + FixedMul(delta, factor)), fading_target); + Mix_VolumeMusic(get_real_volume(music_volume)); + return interval; + } } #ifdef HAVE_LIBGME @@ -509,7 +627,7 @@ static void mix_gme(void *udata, Uint8 *stream, int len) // apply volume to stream for (i = 0, p = (short *)stream; i < len/2; i++, p++) - *p = ((INT32)*p) * music_volume*2 / 42; + *p = ((INT32)*p) * (music_volume*internal_volume/100)*2 / 42; } #endif @@ -586,6 +704,194 @@ boolean I_SetSongSpeed(float speed) return false; } +/// ------------------------ +/// MUSIC SEEKING +/// ------------------------ + +UINT32 I_GetSongLength(void) +{ + INT32 length; + +#ifdef HAVE_LIBGME + if (gme) + { + gme_info_t *info; + gme_err_t gme_e = gme_track_info(gme, &info, current_track); + + if (gme_e != NULL) + { + CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e); + length = 0; + } + else + { + // reconstruct info->play_length, from GME source + // we only want intro + 1 loop, not 2 + length = info->length; + if (length <= 0) + { + length = info->intro_length + info->loop_length; // intro + 1 loop + if (length <= 0) + length = 150 * 1000; // 2.5 minutes + } + } + + gme_free_info(info); + return max(length, 0); + } + else +#endif + if (!music || I_SongType() == MU_MOD || I_SongType() == MU_MID) + return 0; + else + { + // VERY IMPORTANT to set your LENGTHMS= in your song files, folks! + // SDL mixer can't read music length itself. + length = (UINT32)(song_length*1000); + if (!length) + CONS_Debug(DBG_DETAILED, "Getting music length: music is missing LENGTHMS= tag. Needed for seeking.\n"); + return length; + } +} + +boolean I_SetSongLoopPoint(UINT32 looppoint) +{ + if (!music || I_SongType() == MU_GME || I_SongType() == MU_MOD || I_SongType() == MU_MID || !is_looping) + return false; + else + { + UINT32 length = I_GetSongLength(); + + if (length > 0) + looppoint %= length; + + loop_point = max((float)(looppoint / 1000.0L), 0); + return true; + } +} + +UINT32 I_GetSongLoopPoint(void) +{ +#ifdef HAVE_LIBGME + if (gme) + { + INT32 looppoint; + gme_info_t *info; + gme_err_t gme_e = gme_track_info(gme, &info, current_track); + + if (gme_e != NULL) + { + CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e); + looppoint = 0; + } + else + looppoint = info->intro_length > 0 ? info->intro_length : 0; + + gme_free_info(info); + return max(looppoint, 0); + } + else +#endif + if (!music || I_SongType() == MU_MOD || I_SongType() == MU_MID) + return 0; + else + return (UINT32)(loop_point * 1000); +} + +boolean I_SetSongPosition(UINT32 position) +{ + UINT32 length; +#ifdef HAVE_LIBGME + if (gme) + { + // this is unstable, so fail silently + return true; + // this isn't required technically, but GME thread-locks for a second + // if you seek too high from the counter + // length = I_GetSongLength(); + // if (length) + // position = get_adjusted_position(position); + + // SDL_LockAudio(); + // gme_err_t gme_e = gme_seek(gme, position); + // SDL_UnlockAudio(); + + // if (gme_e != NULL) + // { + // CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e); + // return false; + // } + // else + // return true; + } + else +#endif + if (!music || I_SongType() == MU_MID) + return false; + else if (I_SongType() == MU_MOD) + return Mix_SetMusicPosition(position); // Goes by channels + else + { + // Because SDL mixer can't identify song length, if you have + // a position input greater than the real length, then + // music_bytes becomes inaccurate. + + length = I_GetSongLength(); // get it in MS + if (length) + position = get_adjusted_position(position); + + Mix_RewindMusic(); // needed for mp3 + if(Mix_SetMusicPosition((float)(position/1000.0L)) == 0) + music_bytes = position/1000.0L*44100.0L*4; //assume 44.1khz, 4-byte length (see I_GetSongPosition) + else + // NOTE: This block fires on incorrect song format, + // NOT if position input is greater than song length. + music_bytes = 0; + + return true; + } +} + +UINT32 I_GetSongPosition(void) +{ +#ifdef HAVE_LIBGME + if (gme) + { + INT32 position = gme_tell(gme); + + gme_info_t *info; + gme_err_t gme_e = gme_track_info(gme, &info, current_track); + + if (gme_e != NULL) + { + CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e); + return position; + } + else + { + // adjust position, since GME's counter keeps going past loop + if (info->length > 0) + position %= info->length; + else if (info->intro_length + info->loop_length > 0) + position = position >= (info->intro_length + info->loop_length) ? (position % info->loop_length) : position; + else + position %= 150 * 1000; // 2.5 minutes + } + + gme_free_info(info); + return max(position, 0); + } + else +#endif + if (!music || I_SongType() == MU_MID) + return 0; + else + return music_bytes/44100.0L*1000.0L/4; //assume 44.1khz + // 4 = byte length for 16-bit samples (AUDIO_S16SYS), stereo (2-channel) + // This is hardcoded in I_StartupSound. Other formats for factor: + // 8M: 1 | 8S: 2 | 16M: 2 | 16S: 4 +} + /// ------------------------ /// Music Playback /// ------------------------ @@ -598,6 +904,7 @@ boolean I_LoadSong(char *data, size_t len) const size_t key1len = strlen(key1); const size_t key2len = strlen(key2); const size_t key3len = strlen(key3); + char *p = data; SDL_RWops *rw; @@ -608,6 +915,9 @@ boolean I_LoadSong(char *data, size_t len) ) I_UnloadSong(); + // always do this whether or not a music already exists + var_cleanup(); + #ifdef HAVE_LIBGME if ((UINT8)data[0] == 0x1F && (UINT8)data[1] == 0x8B) @@ -717,30 +1027,35 @@ boolean I_LoadSong(char *data, size_t len) // Find the OGG loop point. loop_point = 0.0f; + song_length = 0.0f; while ((UINT32)(p - data) < len) { - if (strncmp(p++, key1, key1len)) - continue; - p += key1len-1; // skip OOP (the L was skipped in strncmp) - if (!strncmp(p, key2, key2len)) // is it LOOPPOINT=? + if (fpclassify(loop_point) == FP_ZERO && !strncmp(p, key1, key1len)) { - p += key2len; // skip POINT= - loop_point = (float)((44.1L+atoi(p)) / 44100.0L); // LOOPPOINT works by sample count. - // because SDL_Mixer is USELESS and can't even tell us - // something simple like the frequency of the streaming music, - // we are unfortunately forced to assume that ALL MUSIC is 44100hz. - // This means a lot of tracks that are only 22050hz for a reasonable downloadable file size will loop VERY badly. + p += key1len; // skip LOOP + if (!strncmp(p, key2, key2len)) // is it LOOPPOINT=? + { + p += key2len; // skip POINT= + loop_point = (float)((44.1L+atoi(p)) / 44100.0L); // LOOPPOINT works by sample count. + // because SDL_Mixer is USELESS and can't even tell us + // something simple like the frequency of the streaming music, + // we are unfortunately forced to assume that ALL MUSIC is 44100hz. + // This means a lot of tracks that are only 22050hz for a reasonable downloadable file size will loop VERY badly. + } + else if (!strncmp(p, key3, key3len)) // is it LOOPMS=? + { + p += key3len; // skip MS= + loop_point = (float)(atoi(p) / 1000.0L); // LOOPMS works by real time, as miliseconds. + // Everything that uses LOOPMS will work perfectly with SDL_Mixer. + } } - else if (!strncmp(p, key3, key3len)) // is it LOOPMS=? - { - p += key3len; // skip MS= - loop_point = (float)(atoi(p) / 1000.0L); // LOOPMS works by real time, as miliseconds. - // Everything that uses LOOPMS will work perfectly with SDL_Mixer. - } - // Neither?! Continue searching. - } + if (fpclassify(loop_point) != FP_ZERO) // Got what we needed + break; + else // continue searching + p++; + } return true; } @@ -764,7 +1079,6 @@ void I_UnloadSong(void) boolean I_PlaySong(boolean looping) { - boolean lpz = fpclassify(loop_point) == FP_ZERO; #ifdef HAVE_LIBGME if (gme) { @@ -778,21 +1092,37 @@ boolean I_PlaySong(boolean looping) if (!music) return false; + if (fpclassify(song_length) == FP_ZERO && (I_SongType() == MU_OGG || I_SongType() == MU_MP3 || I_SongType() == MU_FLAC)) + CONS_Debug(DBG_DETAILED, "This song is missing a LENGTHMS= tag! Required to make seeking work properly.\n"); - if (Mix_PlayMusic(music, looping && lpz ? -1 : 0) == -1) + if (I_SongType() != MU_MOD && I_SongType() != MU_MID && Mix_PlayMusic(music, 0) == -1) + { + CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError()); + return false; + } + else if ((I_SongType() == MU_MOD || I_SongType() == MU_MID) && Mix_PlayMusic(music, looping ? -1 : 0) == -1) // if MOD, loop forever { CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError()); return false; } - Mix_VolumeMusic((UINT32)music_volume*128/31); - if (!lpz) - Mix_HookMusicFinished(music_loop); + is_looping = looping; + + I_SetMusicVolume(music_volume); + + if (I_SongType() != MU_MOD && I_SongType() != MU_MID) + Mix_HookMusicFinished(music_loop); // don't bother counting if MOD + + if(I_SongType() != MU_MOD && I_SongType() != MU_MID && !Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL)) + CONS_Alert(CONS_WARNING, "Error registering SDL music position counter: %s\n", Mix_GetError()); + return true; } void I_StopSong(void) { + I_StopFadingSong(); + #ifdef HAVE_LIBGME if (gme) { @@ -802,19 +1132,40 @@ void I_StopSong(void) #endif if (music) { + Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes); Mix_HookMusicFinished(NULL); Mix_HaltMusic(); } + + var_cleanup(); } void I_PauseSong(void) { + if(I_SongType() == MU_MID) // really, SDL Mixer? why can't you pause MIDI??? + return; + + if(I_SongType() != MU_GME && I_SongType() != MU_MOD && I_SongType() != MU_MID) + Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes); + Mix_PauseMusic(); songpaused = true; } void I_ResumeSong(void) { + if (I_SongType() == MU_MID) + return; + + if (I_SongType() != MU_GME && I_SongType() != MU_MOD && I_SongType() != MU_MID) + { + while(Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes) != 0) { } + // HACK: fixes issue of multiple effect callbacks being registered + + if(music && I_SongType() != MU_MOD && I_SongType() != MU_MID && !Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL)) + CONS_Alert(CONS_WARNING, "Error registering SDL music position counter: %s\n", Mix_GetError()); + } + Mix_ResumeMusic(); songpaused = false; } @@ -833,7 +1184,7 @@ void I_SetMusicVolume(UINT8 volume) #endif music_volume = volume; - Mix_VolumeMusic((UINT32)music_volume*128/31); + Mix_VolumeMusic(get_real_volume(music_volume)); } boolean I_SetSongTrack(int track) @@ -862,9 +1213,100 @@ boolean I_SetSongTrack(int track) SDL_UnlockAudio(); return false; } + else #endif + if (I_SongType() == MU_MOD) + return !Mix_SetMusicPosition(track); (void)track; return false; } +/// ------------------------ +/// MUSIC FADING +/// ------------------------ + +void I_SetInternalMusicVolume(UINT8 volume) +{ + internal_volume = volume; + if (!I_SongPlaying()) + return; + Mix_VolumeMusic(get_real_volume(music_volume)); +} + +void I_StopFadingSong(void) +{ + if (fading_id) + SDL_RemoveTimer(fading_id); + is_fading = false; + fading_source = fading_target = fading_timer = fading_duration = fading_id = 0; +} + +boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void)) +{ + INT16 volume_delta; + + source_volume = min(source_volume, 100); + volume_delta = (INT16)(target_volume - source_volume); + + I_StopFadingSong(); + + if (!ms && volume_delta) + { + I_SetInternalMusicVolume(target_volume); + if (callback) + (*callback)(); + return true; + + } + else if (!volume_delta) + { + if (callback) + (*callback)(); + return true; + } + + // Round MS to nearest 10 + // If n - lower > higher - n, then round up + ms = (ms - ((ms / 10) * 10) > (((ms / 10) * 10) + 10) - ms) ? + (((ms / 10) * 10) + 10) // higher + : ((ms / 10) * 10); // lower + + if (!ms) + I_SetInternalMusicVolume(target_volume); + else if (source_volume != target_volume) + { + fading_id = SDL_AddTimer(10, music_fade, NULL); + if (fading_id) + { + is_fading = true; + fading_timer = fading_duration = ms; + fading_source = source_volume; + fading_target = target_volume; + fading_callback = callback; + + if (internal_volume != source_volume) + I_SetInternalMusicVolume(source_volume); + } + } + + return is_fading; +} + +boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void)) +{ + return I_FadeSongFromVolume(target_volume, internal_volume, ms, callback); +} + +boolean I_FadeOutStopSong(UINT32 ms) +{ + return I_FadeSongFromVolume(0, internal_volume, ms, &I_StopSong); +} + +boolean I_FadeInPlaySong(UINT32 ms, boolean looping) +{ + if (I_PlaySong(looping)) + return I_FadeSongFromVolume(100, 0, ms, NULL); + else + return false; +} #endif diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c index 9ff1dd0b2..d9967ae03 100644 --- a/src/sdl/sdl_sound.c +++ b/src/sdl/sdl_sound.c @@ -1375,6 +1375,37 @@ boolean I_SetSongSpeed(float speed) return false; } +/// ------------------------ +// MUSIC SEEKING +/// ------------------------ + +UINT32 I_GetSongLength(void) +{ + return 0; +} + +boolean I_SetSongLoopPoint(UINT32 looppoint) +{ + (void)looppoint; + return false; +} + +UINT32 I_GetSongLoopPoint(void) +{ + return 0; +} + +boolean I_SetSongPosition(UINT32 position) +{ + (void)position; + return false; +} + +UINT32 I_GetSongPosition(void) +{ + return 0; +} + /// ------------------------ // MUSIC PLAYBACK /// ------------------------ @@ -1443,6 +1474,47 @@ boolean I_SetSongTrack(int track) return false; } +/// ------------------------ +/// MUSIC FADING +/// ------------------------ + +void I_SetInternalMusicVolume(UINT8 volume) +{ + (void)volume; +} + +void I_StopFadingSong(void) +{ +} + +boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void)) +{ + (void)target_volume; + (void)source_volume; + (void)ms; + return false; +} + +boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void)) +{ + (void)target_volume; + (void)ms; + return false; +} + +boolean I_FadeOutStopSong(UINT32 ms) +{ + (void)ms; + return false; +} + +boolean I_FadeInPlaySong(UINT32 ms, boolean looping) +{ + (void)ms; + (void)looping; + return false; +} + /// ------------------------ // MUSIC LOADING AND CLEANUP // \todo Split logic between loading and playing, diff --git a/src/sdl/srb2icon.png b/src/sdl/srb2icon.png new file mode 100644 index 000000000..2aca4b204 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 99916f58d..0ac7e9e55 100644 --- a/src/sdl12/Srb2SDL-vc10.vcxproj +++ b/src/sdl12/Srb2SDL-vc10.vcxproj @@ -755,6 +755,36 @@ %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) @@ -765,6 +795,16 @@ %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) @@ -1340,7 +1380,11 @@ + + + + diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c index d055a4ca5..62256f3a5 100644 --- a/src/sdl12/i_system.c +++ b/src/sdl12/i_system.c @@ -145,7 +145,7 @@ void __set_fpscr(long); // in libgcc / kernel's startup.s? #define O_BINARY 0 #endif -// Locations for searching the srb2.srb +// Locations for searching the main.kart #ifdef _arch_dreamcast #define DEFAULTWADLOCATION1 "/cd" #define DEFAULTWADLOCATION2 "/pc" @@ -217,8 +217,7 @@ void __set_fpscr(long); // in libgcc / kernel's startup.s? /** \brief WAD file to look for */ -#define WADKEYWORD1 "srb2.srb" -#define WADKEYWORD2 "srb2.wad" +#define WADKEYWORD "main.kart" /** \brief holds wad path */ static char returnWadPath[256]; @@ -3398,15 +3397,7 @@ static boolean isWadPathOk(const char *path) if (!wad3path) return false; - sprintf(wad3path, pandf, path, WADKEYWORD1); - - if (FIL_ReadFileOK(wad3path)) - { - free(wad3path); - return true; - } - - sprintf(wad3path, pandf, path, WADKEYWORD2); + sprintf(wad3path, pandf, path, WADKEYWORD); if (FIL_ReadFileOK(wad3path)) { @@ -3431,7 +3422,7 @@ static void pathonly(char *s) } } -/** \brief search for srb2.srb in the given path +/** \brief search for main.kart in the given path \param searchDir starting path @@ -3444,7 +3435,7 @@ static const char *searchWad(const char *searchDir) static char tempsw[256] = ""; filestatus_t fstemp; - strcpy(tempsw, WADKEYWORD1); + strcpy(tempsw, WADKEYWORD); fstemp = filesearch(tempsw,searchDir,NULL,true,20); if (fstemp == FS_FOUND) { @@ -3452,19 +3443,12 @@ static const char *searchWad(const char *searchDir) return tempsw; } - strcpy(tempsw, WADKEYWORD2); - fstemp = filesearch(tempsw, searchDir, NULL, true, 20); - if (fstemp == FS_FOUND) - { - pathonly(tempsw); - return tempsw; - } return NULL; } -/** \brief go through all possible paths and look for srb2.srb +/** \brief go through all possible paths and look for main.kart - \return path to srb2.srb if any + \return path to main.kart if any */ static const char *locateWad(void) { @@ -3581,7 +3565,7 @@ const char *I_LocateWad(void) if (waddir) { - // change to the directory where we found srb2.srb + // change to the directory where we found main.kart #if (defined (_WIN32) && !defined (_WIN32_WCE)) && !defined (_XBOX) SetCurrentDirectoryA(waddir); #elif !defined (_WIN32_WCE) && !defined (_PS3) diff --git a/src/sdl12/i_video.c b/src/sdl12/i_video.c index 69cf5ca9c..a2a20c611 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/sounds.c b/src/sounds.c index a3bc8bf41..61fddb76f 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 4c341d49d..bb46ea9d8 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 36a658aec..e59846aed 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -372,7 +372,7 @@ static inline void ST_InitData(void) // 'link' the statusbar display to a player, which could be // another player than consoleplayer, for example, when you // change the view in a multiplayer demo with F12. - stplyr = &players[displayplayer]; + stplyr = &players[displayplayers[0]]; st_palette = -1; } @@ -442,7 +442,7 @@ static INT32 SCY(INT32 y) if (splitscreen) { y >>= 1; - if (stplyr != &players[displayplayer]) + if (stplyr != &players[displayplayers[0]]) y += vid.height / 2; } return y; @@ -458,7 +458,7 @@ static INT32 STRINGY(INT32 y) if (splitscreen) { y >>= 1; - if (stplyr != &players[displayplayer]) + if (stplyr != &players[displayplayers[0]]) y += BASEVIDHEIGHT / 2; } return y; @@ -471,7 +471,7 @@ static INT32 SPLITFLAGS(INT32 f) // Pass this V_SNAPTO(TOP|BOTTOM) and it'll trim them to account for splitscreen! -Red if (splitscreen) { - if (stplyr != &players[displayplayer]) + if (stplyr != &players[displayplayers[0]]) f &= ~V_SNAPTOTOP; else f &= ~V_SNAPTOBOTTOM; @@ -498,7 +498,7 @@ static INT32 SCR(INT32 r) if (splitscreen) { y >>= 1; - if (stplyr != &players[displayplayer]) + if (stplyr != &players[displayplayers[0]]) y += vid.height / 2; } return FixedInt(FixedDiv(y, vid.fdupy)); @@ -573,17 +573,17 @@ static void ST_drawDebugInfo(void) if (cv_debug & DBG_DETAILED) { - V_DrawRightAlignedString(320, height - 104, V_MONOSPACE, va("SHIELD: %5x", stplyr->powers[pw_shield])); + //V_DrawRightAlignedString(320, height - 104, V_MONOSPACE, va("SHIELD: %5x", stplyr->powers[pw_shield])); V_DrawRightAlignedString(320, height - 96, V_MONOSPACE, va("SCALE: %5d%%", (stplyr->mo->scale*100)/FRACUNIT)); - V_DrawRightAlignedString(320, height - 88, V_MONOSPACE, va("DASH: %3d/%3d", stplyr->dashspeed>>FRACBITS, FixedMul(stplyr->maxdash,stplyr->mo->scale)>>FRACBITS)); - V_DrawRightAlignedString(320, height - 80, V_MONOSPACE, va("AIR: %4d, %3d", stplyr->powers[pw_underwater], stplyr->powers[pw_spacetime])); + //V_DrawRightAlignedString(320, height - 88, V_MONOSPACE, va("DASH: %3d/%3d", stplyr->dashspeed>>FRACBITS, FixedMul(stplyr->maxdash,stplyr->mo->scale)>>FRACBITS)); + //V_DrawRightAlignedString(320, height - 80, V_MONOSPACE, va("AIR: %4d, %3d", stplyr->powers[pw_underwater], stplyr->powers[pw_spacetime])); // Flags - V_DrawRightAlignedString(304-64, height - 72, V_MONOSPACE, "Flags:"); - V_DrawString(304-60, height - 72, (stplyr->jumping) ? V_GREENMAP : V_REDMAP, "JM"); - V_DrawString(304-40, height - 72, (stplyr->pflags & PF_JUMPED) ? V_GREENMAP : V_REDMAP, "JD"); - V_DrawString(304-20, height - 72, (stplyr->pflags & PF_SPINNING) ? V_GREENMAP : V_REDMAP, "SP"); - V_DrawString(304, height - 72, (stplyr->pflags & PF_STARTDASH) ? V_GREENMAP : V_REDMAP, "ST"); + //V_DrawRightAlignedString(304-64, height - 72, V_MONOSPACE, "Flags:"); + //V_DrawString(304-60, height - 72, (stplyr->jumping) ? V_GREENMAP : V_REDMAP, "JM"); + //V_DrawString(304-40, height - 72, (stplyr->pflags & PF_JUMPED) ? V_GREENMAP : V_REDMAP, "JD"); + //V_DrawString(304-20, height - 72, (stplyr->pflags & PF_SPINNING) ? V_GREENMAP : V_REDMAP, "SP"); + //V_DrawString(304, height - 72, (stplyr->pflags & PF_STARTDASH) ? V_GREENMAP : V_REDMAP, "ST"); V_DrawRightAlignedString(320, height - 64, V_MONOSPACE, va("CEILZ: %6d", stplyr->mo->ceilingz>>FRACBITS)); V_DrawRightAlignedString(320, height - 56, V_MONOSPACE, va("FLOORZ: %6d", stplyr->mo->floorz>>FRACBITS)); @@ -701,7 +701,7 @@ static inline void ST_drawRings(void) // SRB2kart - unused. /* static void ST_drawLives(void) // SRB2kart - unused. { - const INT32 v_splitflag = (splitscreen && stplyr == &players[displayplayer] ? V_SPLITSCREEN : 0); + const INT32 v_splitflag = (splitscreen && stplyr == &players[displayplayers[0]] ? V_SPLITSCREEN : 0); if (!stplyr->skincolor) return; // Just joined a server, skin isn't loaded yet! @@ -1019,7 +1019,7 @@ static void ST_drawNiGHTSHUD(void) // SRB2kart - unused. if (G_IsSpecialStage(gamemap)) { // Since special stages share score, time, rings, etc. // disable splitscreen mode for its HUD. - if (stplyr != &players[displayplayer]) + if (stplyr != &players[displayplayers[0]]) return; nosshack = splitscreen; splitscreen = 0; @@ -1124,7 +1124,7 @@ static void ST_drawNiGHTSHUD(void) // SRB2kart - unused. V_DrawScaledPatch(locx, STRINGY(locy)-3, V_HUDTRANS, drillbar); for (dfill = 0; dfill < stplyr->drillmeter/20 && dfill < 96; ++dfill) V_DrawScaledPatch(locx + 2 + dfill, STRINGY(locy + 3), V_HUDTRANS, drillfill[fillpatch]); - stplyr = &players[secondarydisplayplayer]; + stplyr = &players[displayplayers[1]]; if (stplyr->pflags & PF_DRILLING) fillpatch = (stplyr->drillmeter & 1) + 1; else @@ -1132,7 +1132,7 @@ static void ST_drawNiGHTSHUD(void) // SRB2kart - unused. V_DrawScaledPatch(locx, STRINGY(locy-3), V_SNAPTOBOTTOM|V_HUDTRANS, drillbar); for (dfill = 0; dfill < stplyr->drillmeter/20 && dfill < 96; ++dfill) V_DrawScaledPatch(locx + 2 + dfill, STRINGY(locy + 3), V_SNAPTOBOTTOM|V_HUDTRANS, drillfill[fillpatch]); - stplyr = &players[displayplayer]; + stplyr = &players[displayplayers[0]]; splitscreen = 0; } else @@ -1881,7 +1881,7 @@ static void ST_overlayDrawer(void) ST_drawTeamName(); // Special Stage HUD - if (!useNightsSS && G_IsSpecialStage(gamemap) && stplyr == &players[displayplayer]) + if (!useNightsSS && G_IsSpecialStage(gamemap) && stplyr == &players[displayplayers[0]]) ST_drawSpecialStageHUD(); // Emerald Hunt Indicators @@ -1894,22 +1894,46 @@ static void ST_overlayDrawer(void) V_DrawScaledPatch(hudinfo[HUD_GRAVBOOTSICO].x, STRINGY(hudinfo[HUD_GRAVBOOTSICO].y), V_SNAPTORIGHT, gravboots); */ - if(!P_IsLocalPlayer(stplyr)) + if (!(multiplayer && demo.playback)) { - /*char name[MAXPLAYERNAME+1]; - // shorten the name if its more than twelve characters. - strlcpy(name, player_names[stplyr-players], 13);*/ + if(!P_IsLocalPlayer(stplyr)) + { + /*char name[MAXPLAYERNAME+1]; + // shorten the name if its more than twelve characters. + strlcpy(name, player_names[stplyr-players], 13);*/ - // Show name of player being displayed - V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-40, 0, M_GetText("Viewpoint:")); - V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_ALLOWLOWERCASE, player_names[stplyr-players]); + // Show name of player being displayed + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-40, 0, M_GetText("Viewpoint:")); + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_ALLOWLOWERCASE, player_names[stplyr-players]); + } + } + else if (!demo.title) + { + + if (!splitscreen) + { + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-40, V_HUDTRANSHALF, M_GetText("Viewpoint:")); + V_DrawCenteredString((BASEVIDWIDTH/2), BASEVIDHEIGHT-32, V_HUDTRANSHALF|V_ALLOWLOWERCASE, player_names[stplyr-players]); + } + else if (splitscreen == 1) + { + char name[MAXPLAYERNAME+12]; + + INT32 y = (stplyr == &players[displayplayers[0]]) ? 4 : BASEVIDHEIGHT/2-12; + sprintf(name, "VIEWPOINT: %s", player_names[stplyr-players]); + V_DrawRightAlignedThinString(BASEVIDWIDTH-40, y, V_HUDTRANSHALF|V_ALLOWLOWERCASE|K_calcSplitFlags(V_SNAPTOTOP|V_SNAPTOBOTTOM|V_SNAPTORIGHT), name); + } + else if (splitscreen) + { + V_DrawCenteredThinString((vid.width/vid.dupx)/4, BASEVIDHEIGHT/2 - 12, V_HUDTRANSHALF|V_ALLOWLOWERCASE|K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT), player_names[stplyr-players]); + } } // This is where we draw all the fun cheese if you have the chasecam off! - /*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)) + /*if ((stplyr == &players[displayplayers[0]] && !camera[0].chase) + || ((splitscreen && stplyr == &players[displayplayers[1]]) && !camera[1].chase) + || ((splitscreen > 1 && stplyr == &players[displayplayers[2]]) && !camera[2].chase) + || ((splitscreen > 2 && stplyr == &players[displayplayers[3]]) && !camera[3].chase)) { ST_drawFirstPersonHUD(); }*/ @@ -1990,9 +2014,65 @@ static void ST_overlayDrawer(void) } } + // Replay manual-save stuff + if (demo.recording && multiplayer && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime) + { + switch (demo.savemode) + { + case DSM_NOTSAVING: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|(G_BattleGametype() ? V_REDMAP : V_SKYMAP), "Look Backward: Save replay"); + break; + + case DSM_WILLAUTOSAVE: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|(G_BattleGametype() ? V_REDMAP : V_SKYMAP), "Replay will be saved. (Look Backward: Change title)"); + break; + + case DSM_WILLSAVE: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_HUDTRANS|V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|(G_BattleGametype() ? V_REDMAP : V_SKYMAP), "Replay will be saved."); + break; + + case DSM_TITLEENTRY: + ST_DrawDemoTitleEntry(); + break; + + default: // Don't render anything + break; + } + } + ST_drawDebugInfo(); } +void ST_DrawDemoTitleEntry(void) +{ + static UINT8 skullAnimCounter = 0; + char *nametodraw; + + skullAnimCounter++; + skullAnimCounter %= 8; + + nametodraw = demo.titlename; + while (V_StringWidth(nametodraw, 0) > MAXSTRINGLENGTH*8 - 8) + nametodraw++; + +#define x (BASEVIDWIDTH/2 - 139) +#define y (BASEVIDHEIGHT/2) + M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1); + V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, nametodraw); + if (skullAnimCounter < 4) + V_DrawCharacter(x + 8 + V_StringWidth(nametodraw, 0), y + 12, + '_' | 0x80, false); + + M_DrawTextBox(x + 30, y - 24, 26, 1); + V_DrawString(x + 38, y - 16, V_ALLOWLOWERCASE, "Enter the name of the replay."); + + M_DrawTextBox(x + 50, y + 20, 20, 1); + V_DrawThinString(x + 58, y + 28, V_ALLOWLOWERCASE, "Escape - Cancel"); + V_DrawRightAlignedThinString(x + 220, y + 28, V_ALLOWLOWERCASE, "Enter - Confirm"); +#undef x +#undef y +} + // MayonakaStatic: draw Midnight Channel's TV-like borders static void ST_MayonakaStatic(void) { @@ -2006,8 +2086,10 @@ static void ST_MayonakaStatic(void) void ST_Drawer(void) { + UINT8 i; + #ifdef SEENAMES - if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo && !mapreset) + if (cv_seenames.value && cv_allowseenames.value && displayplayers[0] == consoleplayer && seenplayer && seenplayer->mo && !mapreset) { if (cv_seenames.value == 1) V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF, player_names[seenplayer-players]); @@ -2041,26 +2123,12 @@ void ST_Drawer(void) if (st_overlay) { // No deadview! - stplyr = &players[displayplayer]; - ST_overlayDrawer(); - - if (splitscreen) + for (i = 0; i <= splitscreen; i++) { - stplyr = &players[secondarydisplayplayer]; + stplyr = &players[displayplayers[i]]; ST_overlayDrawer(); - - if (splitscreen > 1) - { - stplyr = &players[thirddisplayplayer]; - ST_overlayDrawer(); - - if (splitscreen > 2) - { - stplyr = &players[fourthdisplayplayer]; - ST_overlayDrawer(); - } - } } + // draw Midnight Channel's overlay ontop if (mapheaderinfo[gamemap-1]->typeoflevel & TOL_TV) // Very specific Midnight Channel stuff. ST_MayonakaStatic(); @@ -2070,8 +2138,8 @@ void ST_Drawer(void) if (timeinmap < 15) { if (timeinmap <= 5) - V_DrawFill(0,0,BASEVIDWIDTH,BASEVIDHEIGHT,120); // Pure white on first few frames, to hide SRB2's awful level load artifacts + V_DrawFill(0,0,BASEVIDWIDTH,BASEVIDHEIGHT,0); // Pure white on first few frames, to hide SRB2's awful level load artifacts else - V_DrawFadeScreen(120, 15-timeinmap); // Then gradually fade out from there + V_DrawFadeScreen(0, 15-timeinmap); // Then gradually fade out from there } } diff --git a/src/st_stuff.h b/src/st_stuff.h index f96aee938..16f7b8811 100644 --- a/src/st_stuff.h +++ b/src/st_stuff.h @@ -26,6 +26,9 @@ // Called by main loop. void ST_Ticker(void); +// Called when naming a replay. +void ST_DrawDemoTitleEntry(void); + // Called by main loop. void ST_Drawer(void); diff --git a/src/tmap.nas b/src/tmap.nas index 78840106f..c72c1890a 100644 --- a/src/tmap.nas +++ b/src/tmap.nas @@ -17,7 +17,7 @@ [BITS 32] %define FRACBITS 16 -%define TRANSPARENTPIXEL 247 +%define TRANSPARENTPIXEL 255 %ifdef LINUX %macro cextern 1 diff --git a/src/tmap_mmx.nas b/src/tmap_mmx.nas index 39380a065..c512de8e9 100644 --- a/src/tmap_mmx.nas +++ b/src/tmap_mmx.nas @@ -18,7 +18,7 @@ [BITS 32] %define FRACBITS 16 -%define TRANSPARENTPIXEL 247 +%define TRANSPARENTPIXEL 255 %ifdef LINUX %macro cextern 1 diff --git a/src/v_video.c b/src/v_video.c index 115e081e0..4cc2ec06c 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -60,7 +60,6 @@ static void CV_Gammaxxx_ONChange(void); static CV_PossibleValue_t grgamma_cons_t[] = {{1, "MIN"}, {255, "MAX"}, {0, NULL}}; static CV_PossibleValue_t grsoftwarefog_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "LightPlanes"}, {0, NULL}}; -consvar_t cv_voodoocompatibility = {"gr_voodoocompatibility", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grfovchange = {"gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grfog = {"gr_fog", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grfogcolor = {"gr_fogcolor", "AAAAAA", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -80,7 +79,9 @@ consvar_t cv_grcoronasize = {"gr_coronasize", "1", CV_SAVE| CV_FLOAT, 0, NULL, 0 //static CV_PossibleValue_t CV_MD2[] = {{0, "Off"}, {1, "On"}, {2, "Old"}, {0, NULL}}; // console variables in development -consvar_t cv_grmd2 = {"gr_md2", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_grmdls = {"gr_mdls", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_grfallbackplayermodel = {"gr_fallbackplayermodel", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_grspritebillboarding = {"gr_spritebillboarding", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; #endif const UINT8 gammatable[5][256] = @@ -1214,6 +1215,66 @@ void V_DrawPatchFill(patch_t *pat) } } +void V_DrawVhsEffect(boolean rewind) +{ + static fixed_t upbary = 100, downbary = 150; + + UINT8 *buf = screens[0], *tmp = screens[4]; + UINT16 y; + UINT32 x, pos = 0; + + UINT8 *normalmapstart = ((UINT8 *)transtables + (8<>1; + + if (rewind) + V_DrawVhsEffect(false); // experimentation + + upbary -= vid.dupy * (rewind ? 3 : 1.8f); + downbary += vid.dupy * (rewind ? 2 : 1); + if (upbary < -barsize) upbary = vid.height; + if (downbary > vid.height) downbary = -barsize; + + for (y = 0; y < vid.height; y+=2) + { + thismapstart = normalmapstart; + offs = 0; + + if (y >= upbary && y < upbary+barsize) + { + thismapstart -= (2<= downbary && y < downbary+barsize) + { + thismapstart -= (2<= vid.height-2 && offs > 0) offs = 0; + + for (x = pos+vid.rowbytes*2; pos < x; pos++) + { + tmp[pos] = thismapstart[buf[pos+offs]]; +#ifdef HQ_VHS + tmp[pos] = tmapstart[buf[pos]<<8 | tmp[pos]]; +#endif + } + } + + memcpy(buf, tmp, vid.rowbytes*vid.height); +} + // // Fade all the screen buffer, so that the menu is more readable, // especially now that we use the small hufont in the menus... @@ -1234,9 +1295,12 @@ void V_DrawFadeScreen(UINT16 color, UINT8 strength) #endif { - const UINT8 *fadetable = ((color & 0xFF00) // Color is not palette index? - ? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade. - : ((UINT8 *)transtables + ((9-strength)< 0xFFF0) // Grab a specific colormap palette? + ? R_GetTranslationColormap(color | 0xFFFF0000, strength, GTC_CACHE) + : ((color & 0xFF00) // Color is not palette index? + ? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade. + : ((UINT8 *)transtables + ((9-strength)<= 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. @@ -1462,8 +1524,11 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) { dupx = dupy = 1; scrwidth = vid.width/vid.dupx; - left = (scrwidth - BASEVIDWIDTH)/2; - scrwidth -= left; + if (!(option & V_SNAPTOLEFT)) + { + left = (scrwidth - BASEVIDWIDTH)/2; + scrwidth -= left; + } } charflags = (option & V_CHARCOLORMASK); @@ -1872,6 +1937,12 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) } } +void V_DrawCenteredThinString(INT32 x, INT32 y, INT32 option, const char *string) +{ + x -= V_ThinStringWidth(string, option)/2; + V_DrawThinString(x, y, option, string); +} + void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *string) { x -= V_ThinStringWidth(string, option); @@ -2015,6 +2086,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< MAXFILENEEDED*sizeof(UINT8)) - { - CONS_Alert(CONS_ERROR, M_GetText("Maximum wad files reached\n")); - refreshdirmenu |= REFRESHDIR_MAX; - if (handle) - fclose(handle); - return INT16_MAX; - } - - packetsizetally = packetsize; - } + important = !W_VerifyNMUSlumps(filename); #ifndef NOMD5 // @@ -795,11 +777,11 @@ UINT16 W_InitFile(const char *filename) CONS_Printf(M_GetText("Loading SOC from %s\n"), wadfile->filename); DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0); break; - case RET_LUA: #ifdef HAVE_BLUA + case RET_LUA: LUA_LoadLump(numwadfiles - 1, 0); -#endif break; +#endif default: break; } @@ -855,16 +837,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; } @@ -1187,23 +1169,17 @@ void zerr(int ret) #define NO_PNG_LUMPS #ifdef NO_PNG_LUMPS -static void ErrorIfPNG(void *d, size_t s, char *f, char *l) +static void ErrorIfPNG(UINT8 *d, size_t s, char *f, char *l) { if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/ return; -#define sigcheck ((UINT8 *)d) - if (sigcheck[0] == 0x89 - && sigcheck[1] == 0x50 - && sigcheck[2] == 0x4e - && sigcheck[3] == 0x47 - && sigcheck[4] == 0x0d - && sigcheck[5] == 0x0a - && sigcheck[6] == 0x1a - && sigcheck[7] == 0x0a) + // Check for PNG file signature using memcmp + // As it may be faster on CPUs with slow unaligned memory access + // Ref: http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature + if (memcmp(&d[0], "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) == 0) { I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .PNG - please convert to either Doom or Flat (raw) image format.", l, f); } -#undef sigcheck } #endif @@ -1297,6 +1273,7 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si //I_Error("ZWAD files not supported on this platform."); return 0; #endif + } #ifdef HAVE_ZLIB case CM_DEFLATE: // Is it compressed via DEFLATE? Very common in ZIPs/PK3s, also what most doom-related editors support. @@ -1556,7 +1533,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 e2e17740f..762d37082 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/Makefile.cfg b/src/win32/Makefile.cfg index 157d97446..e6675421e 100644 --- a/src/win32/Makefile.cfg +++ b/src/win32/Makefile.cfg @@ -24,8 +24,10 @@ ifndef NOASM USEASM=1 endif +ifndef NONET ifndef MINGW64 #miniupnc is broken with MINGW64 HAVE_MINIUPNPC=1 +endif endif OPTS=-DSTDC_HEADERS diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj index 774ce5cbe..ced3d128e 100644 --- a/src/win32/Srb2win-vc10.vcxproj +++ b/src/win32/Srb2win-vc10.vcxproj @@ -230,7 +230,11 @@ + + + + @@ -394,6 +398,10 @@ + + + + diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters index d20dd672b..8f6077969 100644 --- a/src/win32/Srb2win-vc10.vcxproj.filters +++ b/src/win32/Srb2win-vc10.vcxproj.filters @@ -453,6 +453,10 @@ M_Misc + + + + @@ -506,6 +510,15 @@ Hw_Hardware + + Hw_Hardware + + + Hw_Hardware + + + Hw_Hardware + Hw_Hardware @@ -515,6 +528,9 @@ Hw_Hardware + + Hw_Hardware + BLUA diff --git a/src/win32/win_dll.c b/src/win32/win_dll.c index 71eda0437..bc67f04a5 100644 --- a/src/win32/win_dll.c +++ b/src/win32/win_dll.c @@ -109,8 +109,7 @@ static loadfunc_t hwdFuncTable[] = { {"GClipRect@20", &hwdriver.pfnGClipRect}, {"ClearMipMapCache@0", &hwdriver.pfnClearMipMapCache}, {"SetSpecialState@8", &hwdriver.pfnSetSpecialState}, - {"DrawMD2@16", &hwdriver.pfnDrawMD2}, - {"DrawMD2i@36", &hwdriver.pfnDrawMD2i}, + {"DrawModel@16", &hwdriver.pfnDrawModel}, {"SetTransform@4", &hwdriver.pfnSetTransform}, {"GetTextureUsed@0", &hwdriver.pfnGetTextureUsed}, {"GetRenderVersion@0", &hwdriver.pfnGetRenderVersion}, @@ -140,8 +139,7 @@ static loadfunc_t hwdFuncTable[] = { {"GClipRect", &hwdriver.pfnGClipRect}, {"ClearMipMapCache", &hwdriver.pfnClearMipMapCache}, {"SetSpecialState", &hwdriver.pfnSetSpecialState}, - {"DrawMD2", &hwdriver.pfnDrawMD2}, - {"DrawMD2i", &hwdriver.pfnDrawMD2i}, + {"DrawModel", &hwdriver.pfnDrawModel}, {"SetTransform", &hwdriver.pfnSetTransform}, {"GetTextureUsed", &hwdriver.pfnGetTextureUsed}, {"GetRenderVersion", &hwdriver.pfnGetRenderVersion}, diff --git a/src/win32/win_snd.c b/src/win32/win_snd.c index 454c53e37..f3e3bbed4 100644 --- a/src/win32/win_snd.c +++ b/src/win32/win_snd.c @@ -815,6 +815,60 @@ void I_SetMusicVolume(UINT8 volume) FMR_MUSIC(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0)); } +UINT32 I_GetSongLength(void) +{ + UINT32 length; + if (I_SongType() == MU_MID) + return 0; + FMR_MUSIC(FMOD_Sound_GetLength(music_stream, &length, FMOD_TIMEUNIT_MS)); + return length; +} + +boolean I_SetSongLoopPoint(UINT32 looppoint) +{ + (void)looppoint; + return false; +} + +UINT32 I_GetSongLoopPoint(void) +{ + return 0; +} + +boolean I_SetSongPosition(UINT32 position) +{ + FMOD_RESULT e; + if(I_SongType() == MU_MID) + // Dummy out; this works for some MIDI, but not others. + // SDL does not support this for any MIDI. + return false; + e = FMOD_Channel_SetPosition(music_channel, position, FMOD_TIMEUNIT_MS); + if (e == FMOD_OK) + return true; + else if (e == FMOD_ERR_UNSUPPORTED // Only music modules, numbnuts! + || e == FMOD_ERR_INVALID_POSITION) // Out-of-bounds! + return false; + else // Congrats, you horribly broke it somehow + { + FMR_MUSIC(e); + return false; + } +} + +UINT32 I_GetSongPosition(void) +{ + FMOD_RESULT e; + unsigned int fmposition = 0; + if(I_SongType() == MU_MID) + // Dummy out because unsupported, even though FMOD does this correctly. + return 0; + e = FMOD_Channel_GetPosition(music_channel, &fmposition, FMOD_TIMEUNIT_MS); + if (e == FMOD_OK) + return (UINT32)fmposition; + else + return 0; +} + boolean I_SetSongTrack(INT32 track) { if (track != current_track) // If the track's already playing, then why bother? @@ -859,3 +913,46 @@ boolean I_SetSongTrack(INT32 track) } return false; } + +/// ------------------------ +/// MUSIC FADING +/// ------------------------ + +void I_SetInternalMusicVolume(UINT8 volume) +{ + (void)volume; +} + +void I_StopFadingSong(void) +{ +} + +boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void)) +{ + (void)target_volume; + (void)source_volume; + (void)ms; + (void)callback; + return false; +} + +boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void)) +{ + (void)target_volume; + (void)ms; + (void)callback; + return false; +} + +boolean I_FadeOutStopSong(UINT32 ms) +{ + (void)ms; + return false; +} + +boolean I_FadeInPlaySong(UINT32 ms, boolean looping) +{ + (void)ms; + (void)looping; + return false; +} diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c index fa9d6d644..a98aa8615 100644 --- a/src/win32/win_sys.c +++ b/src/win32/win_sys.c @@ -639,15 +639,12 @@ 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(); } // save demo, could be useful for debug // NOTE: demos are normally not saved here. - if (demorecording) + if (demo.recording) G_CheckDemoStatus(); if (metalrecording) G_StopMetalRecording(); @@ -733,7 +730,7 @@ void I_Quit(void) DWORD mode; // when recording a demo, should exit using 'q', // but sometimes we forget and use Alt+F4, so save here too. - if (demorecording) + if (demo.recording) G_CheckDemoStatus(); if (metalrecording) G_StopMetalRecording(); diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c index 9f3d13f8f..45f96e9d4 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_vid.c b/src/win32ce/win_vid.c index 5e8e7e1fb..244dfaf3e 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 095b4ad36..c270f04ab 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -40,6 +40,7 @@ #include "g_input.h" // PLAYER1INPUTDOWN #include "k_kart.h" // colortranslations #include "console.h" // cons_menuhighlight +#include "lua_hook.h" // IntermissionThinker hook #ifdef HWRENDER #include "hardware/hw_main.h" @@ -304,6 +305,15 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) players[i].score += data.match.increase[i]; } + if (demo.recording && !rankingsmode) + G_WriteStanding( + data.match.pos[data.match.numplayers], + data.match.name[data.match.numplayers], + *data.match.character[data.match.numplayers], + *data.match.color[data.match.numplayers], + data.match.val[data.match.numplayers] + ); + data.match.numplayers++; } } @@ -351,7 +361,7 @@ void Y_IntermissionDrawer(void) V_DrawFadeScreen(0xFF00, 22); if (!splitscreen) - whiteplayer = demoplayback ? displayplayer : consoleplayer; + whiteplayer = demo.playback ? displayplayers[0] : consoleplayer; if (cons_menuhighlight.value) hilicol = cons_menuhighlight.value; @@ -360,7 +370,7 @@ void Y_IntermissionDrawer(void) else hilicol = ((intertype == int_race) ? V_SKYMAP : V_REDMAP); - if (sorttic != -1 && intertic > sorttic) + if (sorttic != -1 && intertic > sorttic && !demo.playback) { INT32 count = (intertic - sorttic); @@ -550,11 +560,37 @@ void Y_IntermissionDrawer(void) dotimer: if (timer) { + char *string; INT32 tickdown = (timer+1)/TICRATE; + + if (multiplayer && demo.playback) + string = va("Replay ends in %d", tickdown); + else + string = va("%s starts in %d", cv_advancemap.string, tickdown); + V_DrawCenteredString(BASEVIDWIDTH/2, 188, hilicol, - va("%s starts in %d", cv_advancemap.string, tickdown)); + string); } + if ((demo.recording || demo.savemode == DSM_SAVED) && !demo.playback) + switch (demo.savemode) + { + case DSM_NOTSAVING: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Look Backward: Save replay"); + break; + + case DSM_SAVED: + V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, V_SNAPTOTOP|V_SNAPTORIGHT|V_ALLOWLOWERCASE|hilicol, "Replay saved!"); + break; + + case DSM_TITLEENTRY: + ST_DrawDemoTitleEntry(); + break; + + default: // Don't render any text here + break; + } + // Make it obvious that scrambling is happening next round. if (cv_scrambleonchange.value && cv_teamscramble.value && (intertic/TICRATE % 2 == 0)) V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, hilicol, M_GetText("Teams will be scrambled next round!")); @@ -570,10 +606,23 @@ void Y_Ticker(void) if (intertype == int_none) return; + if (demo.recording) + { + if (demo.savemode == DSM_NOTSAVING && InputDown(gc_lookback, 1)) + demo.savemode = DSM_TITLEENTRY; + + if (demo.savemode == DSM_WILLSAVE || demo.savemode == DSM_WILLAUTOSAVE) + G_SaveDemo(); + } + // Check for pause or menu up in single player if (paused || P_AutoPause()) return; +#ifdef HAVE_BLUA + LUAh_IntermissionThinker(); +#endif + intertic++; // Team scramble code for team match and CTF. @@ -613,7 +662,7 @@ void Y_Ticker(void) { if (sorttic == -1) sorttic = intertic + max((cv_inttime.value/2)-2, 2)*TICRATE; // 8 second pause after match results - else + else if (!(multiplayer && demo.playback)) // Don't advance to rankings in replays { if (!data.match.rankingsmode && (intertic >= sorttic + 8)) Y_CalculateMatchData(1, Y_CompareRank); @@ -762,6 +811,8 @@ void Y_StartIntermission(void) { if (cv_inttime.value == 0 && gametype == GT_COOP) timer = 0; + else if (demo.playback) // Override inttime (which is pulled from the replay anyway + timer = 10*TICRATE; else { timer = cv_inttime.value*TICRATE; @@ -796,7 +847,7 @@ void Y_StartIntermission(void) } case int_race: // (time-only race) { - if (!majormods && !multiplayer && !demoplayback) // remove this once we have a proper time attack screen + if (!majormods && !multiplayer && !demo.playback) // remove this once we have a proper time attack screen { // Update visitation flags mapvisited[gamemap-1] |= MV_BEATEN; @@ -1005,19 +1056,19 @@ void Y_VoteDrawer(void) { case 1: thiscurs = cursor2; - p = secondarydisplayplayer; + p = displayplayers[1]; break; case 2: thiscurs = cursor3; - p = thirddisplayplayer; + p = displayplayers[2]; break; case 3: thiscurs = cursor4; - p = fourthdisplayplayer; + p = displayplayers[3]; break; default: thiscurs = cursor1; - p = displayplayer; + p = displayplayers[0]; break; } @@ -1032,7 +1083,7 @@ void Y_VoteDrawer(void) V_DrawMappedPatch(BASEVIDWIDTH-124, handy, V_SNAPTORIGHT, thiscurs, colormap); if (votetic % 10 < 4) - V_DrawFill(BASEVIDWIDTH-100-sizeadd, y-sizeadd, 80+(sizeadd*2), 50+(sizeadd*2), 120|V_SNAPTORIGHT); + V_DrawFill(BASEVIDWIDTH-100-sizeadd, y-sizeadd, 80+(sizeadd*2), 50+(sizeadd*2), 0|V_SNAPTORIGHT); else V_DrawFill(BASEVIDWIDTH-100-sizeadd, y-sizeadd, 80+(sizeadd*2), 50+(sizeadd*2), color|V_SNAPTORIGHT); @@ -1103,7 +1154,7 @@ void Y_VoteDrawer(void) { V_DrawScaledPatch(x-18, y+9, V_SNAPTOLEFT, cursor); if (voteendtic != -1 && !(votetic % 4)) - V_DrawFill(x-1, y-1, 42, 27, 120|V_SNAPTOLEFT); + V_DrawFill(x-1, y-1, 42, 27, 0|V_SNAPTOLEFT); else V_DrawFill(x-1, y-1, 42, 27, levelinfo[votes[i]].gtc|V_SNAPTOLEFT); } @@ -1172,10 +1223,7 @@ static void Y_VoteStops(SINT8 pick, SINT8 level) S_StartSound(NULL, sfx_noooo2); // gasp else if (mapheaderinfo[nextmap] && (mapheaderinfo[nextmap]->menuflags & LF2_HIDEINMENU)) S_StartSound(NULL, sfx_noooo1); // this is bad - else if (netgame && (pick == consoleplayer - || pick == secondarydisplayplayer - || pick == thirddisplayplayer - || pick == fourthdisplayplayer)) + else if (netgame && P_IsLocalPlayer(&players[pick])) S_StartSound(NULL, sfx_yeeeah); // yeeeah! else S_StartSound(NULL, sfx_kc48); // just a cool sound @@ -1308,13 +1356,13 @@ void Y_VoteTicker(void) switch (i) { case 1: - p = secondarydisplayplayer; + p = displayplayers[1]; break; case 2: - p = thirddisplayplayer; + p = displayplayers[2]; break; case 3: - p = fourthdisplayplayer; + p = displayplayers[3]; break; default: p = consoleplayer; @@ -1505,11 +1553,11 @@ void Y_EndVote(void) // static void Y_UnloadVoteData(void) { + voteclient.loaded = false; + if (rendermode != render_soft) return; - voteclient.loaded = false; - UNLOAD(widebgpatch); UNLOAD(bgpatch); UNLOAD(cursor);