diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..45b6856453 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,12 @@ +Reporting a bug? Please make sure you've given the following information - thanks! + +**Operating system and version:** + + +**Is this for single player or multiplayer?** + + +**Description of the bug (and if possible, steps to reproduce the bug):** + + +**What did you expect to happen instead?** diff --git a/.gitignore b/.gitignore index d776da2678..8ec032c4db 100644 --- a/.gitignore +++ b/.gitignore @@ -172,4 +172,6 @@ pip-log.txt ## Unix ############# *.o -*.so \ No newline at end of file +*.so + +xcode diff --git a/.travis.yml b/.travis.yml index 5d442cd50d..0fe6ec6c03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,22 +1,69 @@ language: cpp -sudo: true -install: - - ./tools/travis-install.sh "${host:-native}" "${flavour:-Release}" $opts +addons: + apt: + packages: + - libsdl2-dev + - libjpeg-turbo8-dev + - zlib1g-dev + - libpng12-dev + +before_install: + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install sdl2 --universal; fi script: - ./tools/travis-build.sh "${host:-native}" "${flavour:-Release}" $opts -compiler: gcc -env: - - "" - - flavour=Debug - #- host=i686-linux-gnu - - host=i686-w64-mingw32 - - host=x86_64-w64-mingw32 - -# One extra build with clang, its warnings are often better matrix: include: - - compiler: clang - env: "" + - os: linux + dist: trusty + sudo: required + compiler: gcc + - os: linux + dist: trusty + sudo: required + compiler: clang + - os: osx + osx_image: xcode7.3 + compiler: clang + +before_deploy: + - export DEPLOY_FILE_NAME=jaenhanced_$TRAVIS_OS_NAME-$CC.zip + - echo "zipping into $DEPLOY_FILE_NAME" + - cd $(pwd)/build/install + - zip -r $DEPLOY_FILE_NAME . -i \* + - echo "deploying $DEPLOY_FILE_NAME to GitHub" + +deploy: + provider: releases + api_key: $GITHUB_KEY + overwrite: true + file: "$DEPLOY_FILE_NAME" + skip_cleanup: true + on: + tags: true + + +#dist: trusty + +#install: +# - ./tools/travis-install.sh "${host:-native}" "${flavour:-Release}" $opts + +#script: +# - ./tools/travis-build.sh "${host:-native}" "${flavour:-Release}" $opts + +#compiler: gcc +#env: +# - "" +# - flavour=Debug +# - host=i686-linux-gnu +# - host=i686-w64-mingw32 +# - host=x86_64-w64-mingw32 + +# One extra build with clang, its warnings are often better +#matrix: +# include: +# - compiler: clang +# env: "" diff --git a/CMakeCache.txt b/CMakeCache.txt new file mode 100644 index 0000000000..9c71f784b3 --- /dev/null +++ b/CMakeCache.txt @@ -0,0 +1,483 @@ +# This is the CMakeCache file. +# For build in directory: c:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK +# It was generated by CMake: C:/Program Files/CMake/bin/cmake.exe +# You can edit this file to change values found and used by cmake. +# If you do not want to change any of the values, simply exit the editor. +# If you do want to change a value, simply edit, save, and exit the editor. +# The syntax for the file is as follows: +# KEY:TYPE=VALUE +# KEY is the name of a variable in the cache. +# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. +# VALUE is the current value for the KEY. + +######################## +# EXTERNAL cache entries +######################## + +//The directory containing a CMake configuration file for Boost. +Boost_DIR:PATH=Boost_DIR-NOTFOUND + +//Path to a file. +Boost_INCLUDE_DIR:PATH=Boost_INCLUDE_DIR-NOTFOUND + +//Boost unit_test_framework library (debug) +Boost_UNIT_TEST_FRAMEWORK_LIBRARY_DEBUG:FILEPATH=Boost_UNIT_TEST_FRAMEWORK_LIBRARY_DEBUG-NOTFOUND + +//Boost unit_test_framework library (release) +Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE:FILEPATH=Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE-NOTFOUND + +//Whether to create projects for the jk2 SP engine (openjo_sp.exe) +BuildJK2SPEngine:BOOL=OFF + +//Whether to create projects for the jk2 sp gamecode mod (jk2gamex86.dll) +BuildJK2SPGame:BOOL=OFF + +//Whether to create projects for the jk2 sp renderer (rdjosp-vanilla_x86.dll) +BuildJK2SPRdVanilla:BOOL=OFF + +//Whether to create projects for the MP clientside gamecode (cgamex86.dll) +BuildMPCGame:BOOL=OFF + +//Whether to create projects for the MP dedicated server (openjkded.exe) +BuildMPDed:BOOL=OFF + +//Whether to create projects for the MP client (openjk.exe) +BuildMPEngine:BOOL=OFF + +//Whether to create projects for the MP server-side gamecode (jampgamex86.dll) +BuildMPGame:BOOL=OFF + +//Whether to create projects for the MP default renderer (rd-vanilla_x86.dll) +BuildMPRdVanilla:BOOL=OFF + +//Whether to create projects for the MP UI code (uix86.dll) +BuildMPUI:BOOL=OFF + +//Build portable version (does not read or write files from your +// user/home directory +BuildPortableVersion:BOOL=ON + +//Whether to create projects for the SP engine (openjk_sp.exe) +BuildSPEngine:BOOL=ON + +//Whether to create projects for the SP gamecode (jagamex86.dll) +BuildSPGame:BOOL=ON + +//Whether to create projects for the SP default renderer (rdsp-vanilla_x86.dll) +BuildSPRdVanilla:BOOL=ON + +//Whether to build automatic unit tests (requires Boost) +BuildTests:BOOL=ON + +//Semicolon separated list of supported configuration types, only +// supports Debug, Release, MinSizeRel, and RelWithDebInfo, anything +// else will be ignored. +CMAKE_CONFIGURATION_TYPES:STRING=Debug;Release;MinSizeRel;RelWithDebInfo + +//Flags used by the compiler during all build types. +CMAKE_CXX_FLAGS:STRING=/DWIN32 /D_WINDOWS /W3 /GR /EHsc + +//Flags used by the compiler during debug builds. +CMAKE_CXX_FLAGS_DEBUG:STRING=/MDd /Zi /Ob0 /Od /RTC1 + +//Flags used by the compiler during release builds for minimum +// size. +CMAKE_CXX_FLAGS_MINSIZEREL:STRING=/MD /O1 /Ob1 /DNDEBUG + +//Flags used by the compiler during release builds. +CMAKE_CXX_FLAGS_RELEASE:STRING=/MD /O2 /Ob2 /DNDEBUG + +//Flags used by the compiler during release builds with debug info. +CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=/MD /Zi /O2 /Ob1 /DNDEBUG + +//Libraries linked by default with all C++ applications. +CMAKE_CXX_STANDARD_LIBRARIES:STRING=kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib + +//Flags used by the compiler during all build types. +CMAKE_C_FLAGS:STRING=/DWIN32 /D_WINDOWS /W3 + +//Flags used by the compiler during debug builds. +CMAKE_C_FLAGS_DEBUG:STRING=/MDd /Zi /Ob0 /Od /RTC1 + +//Flags used by the compiler during release builds for minimum +// size. +CMAKE_C_FLAGS_MINSIZEREL:STRING=/MD /O1 /Ob1 /DNDEBUG + +//Flags used by the compiler during release builds. +CMAKE_C_FLAGS_RELEASE:STRING=/MD /O2 /Ob2 /DNDEBUG + +//Flags used by the compiler during release builds with debug info. +CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=/MD /Zi /O2 /Ob1 /DNDEBUG + +//Libraries linked by default with all C applications. +CMAKE_C_STANDARD_LIBRARIES:STRING=kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib + +//Flags used by the linker. +CMAKE_EXE_LINKER_FLAGS:STRING=/machine:X86 + +//Flags used by the linker during debug builds. +CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=/debug /INCREMENTAL + +//Flags used by the linker during release minsize builds. +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=/INCREMENTAL:NO + +//Flags used by the linker during release builds. +CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=/INCREMENTAL:NO + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=/debug /INCREMENTAL + +//Install path prefix, prepended onto install directories. +CMAKE_INSTALL_PREFIX:PATH=C:/Program Files (x86)/OpenJK + +//Path to a program. +CMAKE_LINKER:FILEPATH=C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.12.25827/bin/Hostx86/x86/link.exe + +//Flags used by the linker during the creation of modules. +CMAKE_MODULE_LINKER_FLAGS:STRING=/machine:X86 + +//Flags used by the linker during debug builds. +CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=/debug /INCREMENTAL + +//Flags used by the linker during release minsize builds. +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=/INCREMENTAL:NO + +//Flags used by the linker during release builds. +CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=/INCREMENTAL:NO + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=/debug /INCREMENTAL + +//Value Computed by CMake +CMAKE_PROJECT_NAME:STATIC=OpenJK + +//RC compiler +CMAKE_RC_COMPILER:FILEPATH=rc + +//Flags for Windows Resource Compiler. +CMAKE_RC_FLAGS:STRING=/DWIN32 + +//Flags for Windows Resource Compiler during debug builds. +CMAKE_RC_FLAGS_DEBUG:STRING=/D_DEBUG + +//Flags for Windows Resource Compiler during release builds for +// minimum size. +CMAKE_RC_FLAGS_MINSIZEREL:STRING= + +//Flags for Windows Resource Compiler during release builds. +CMAKE_RC_FLAGS_RELEASE:STRING= + +//Flags for Windows Resource Compiler during release builds with +// debug info. +CMAKE_RC_FLAGS_RELWITHDEBINFO:STRING= + +//Flags used by the linker during the creation of dll's. +CMAKE_SHARED_LINKER_FLAGS:STRING=/machine:X86 + +//Flags used by the linker during debug builds. +CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=/debug /INCREMENTAL + +//Flags used by the linker during release minsize builds. +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=/INCREMENTAL:NO + +//Flags used by the linker during release builds. +CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=/INCREMENTAL:NO + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=/debug /INCREMENTAL + +//If set, runtime paths are not added when installing shared libraries, +// but are added when building. +CMAKE_SKIP_INSTALL_RPATH:BOOL=OFF + +//If set, runtime paths are not added when using shared libraries. +CMAKE_SKIP_RPATH:BOOL=OFF + +//Flags used by the linker during the creation of static libraries. +CMAKE_STATIC_LINKER_FLAGS:STRING=/machine:X86 + +//Flags used by the linker during debug builds. +CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//If this value is on, makefiles will be generated without the +// .SILENT directive, and all commands will be echoed to the console +// during the make. This is useful for debugging only. With Visual +// Studio IDE projects all commands are done without /nologo. +CMAKE_VERBOSE_MAKEFILE:BOOL=OFF + +//Enable to build 7-Zip packages +CPACK_BINARY_7Z:BOOL=OFF + +//Enable to build IFW packages +CPACK_BINARY_IFW:BOOL=OFF + +//Enable to build NSIS packages +CPACK_BINARY_NSIS:BOOL=ON + +//Enable to build WiX packages +CPACK_BINARY_WIX:BOOL=OFF + +//Enable to build ZIP packages +CPACK_BINARY_ZIP:BOOL=OFF + +//Enable to build 7-Zip source packages +CPACK_SOURCE_7Z:BOOL=ON + +//Enable to build ZIP source packages +CPACK_SOURCE_ZIP:BOOL=ON + +//Path to a file. +MSVC_REDIST_DIR:PATH=C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Redist/MSVC/14.12.25810 + +//OpenGL library for win32 +OPENGL_gl_LIBRARY:STRING=opengl32 + +//GLU library for win32 +OPENGL_glu_LIBRARY:STRING=glu32 + +//Path to a library. +OpenALLibrary:FILEPATH=C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/lib/OpenAL32.lib + +//Value Computed by CMake +OpenJK_BINARY_DIR:STATIC=C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK + +//Value Computed by CMake +OpenJK_SOURCE_DIR:STATIC=C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK + +//Project Name +ProjectName:STRING=OpenJK + +//If set, use bundled libjpeg. +UseInternalJPEG:BOOL=ON + +//If set, use bundled OpenAL. +UseInternalOpenAL:BOOL=ON + +//If set, use bundled libpng. +UseInternalPNG:BOOL=ON + +//If set, use bundled SDL2. +UseInternalSDL2:BOOL=ON + +//If set, use bundled zlib. +UseInternalZlib:BOOL=ON + +//Path to a program. +ZIP_EXECUTABLE:FILEPATH=C:/Program Files/7-Zip/7z.exe + +//Dependencies for target +botlib_LIB_DEPENDS:STATIC= + +//Dependencies for target +bundled_libjpeg_LIB_DEPENDS:STATIC= + +//Dependencies for target +bundled_libpng_LIB_DEPENDS:STATIC= + +//Dependencies for target +bundled_minizip_LIB_DEPENDS:STATIC= + +//Dependencies for target +bundled_zlib_LIB_DEPENDS:STATIC= + +//Dependencies for the target +cgamex86_LIB_DEPENDS:STATIC=general;odbc32;general;odbccp32; + +//Dependencies for the target +jagamex86_LIB_DEPENDS:STATIC=general;winmm; + +//Dependencies for the target +jampgamex86_LIB_DEPENDS:STATIC=general;winmm; + +//Dependencies for the target +rd-vanilla_x86_LIB_DEPENDS:STATIC=general;bundled_libjpeg;general;bundled_libpng;general;bundled_zlib;general;bundled_minizip;general;opengl32;general;glu32; + +//Dependencies for the target +rdsp-vanilla_x86_LIB_DEPENDS:STATIC=general;bundled_libjpeg;general;bundled_libpng;general;bundled_zlib;general;bundled_minizip;general;opengl32;general;glu32; + +//Dependencies for the target +uix86_LIB_DEPENDS:STATIC=general;odbc32;general;odbccp32; + + +######################## +# INTERNAL cache entries +######################## + +//ADVANCED property for variable: Boost_DIR +Boost_DIR-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: Boost_INCLUDE_DIR +Boost_INCLUDE_DIR-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: Boost_UNIT_TEST_FRAMEWORK_LIBRARY_DEBUG +Boost_UNIT_TEST_FRAMEWORK_LIBRARY_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE +Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE-ADVANCED:INTERNAL=1 +//This is the directory where this CMakeCache.txt was created +CMAKE_CACHEFILE_DIR:INTERNAL=c:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK +//Major version of cmake used to create the current loaded cache +CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 +//Minor version of cmake used to create the current loaded cache +CMAKE_CACHE_MINOR_VERSION:INTERNAL=10 +//Patch version of cmake used to create the current loaded cache +CMAKE_CACHE_PATCH_VERSION:INTERNAL=1 +//Path to CMake executable. +CMAKE_COMMAND:INTERNAL=C:/Program Files/CMake/bin/cmake.exe +//Path to cpack program executable. +CMAKE_CPACK_COMMAND:INTERNAL=C:/Program Files/CMake/bin/cpack.exe +//Path to ctest program executable. +CMAKE_CTEST_COMMAND:INTERNAL=C:/Program Files/CMake/bin/ctest.exe +//ADVANCED property for variable: CMAKE_CXX_FLAGS +CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG +CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL +CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE +CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO +CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_STANDARD_LIBRARIES +CMAKE_CXX_STANDARD_LIBRARIES-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS +CMAKE_C_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG +CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL +CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE +CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO +CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_STANDARD_LIBRARIES +CMAKE_C_STANDARD_LIBRARIES-ADVANCED:INTERNAL=1 +//Executable file format +CMAKE_EXECUTABLE_FORMAT:INTERNAL=Unknown +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS +CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG +CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE +CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//Name of external makefile project generator. +CMAKE_EXTRA_GENERATOR:INTERNAL= +//Name of generator. +CMAKE_GENERATOR:INTERNAL=Visual Studio 15 2017 +//Name of generator platform. +CMAKE_GENERATOR_PLATFORM:INTERNAL= +//Name of generator toolset. +CMAKE_GENERATOR_TOOLSET:INTERNAL= +//Source directory with the top level CMakeLists.txt file for this +// project +CMAKE_HOME_DIRECTORY:INTERNAL=C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK +//ADVANCED property for variable: CMAKE_LINKER +CMAKE_LINKER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS +CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG +CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE +CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//number of local generators +CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=10 +//Platform information initialized +CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RC_COMPILER +CMAKE_RC_COMPILER-ADVANCED:INTERNAL=1 +CMAKE_RC_COMPILER_WORKS:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RC_FLAGS +CMAKE_RC_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RC_FLAGS_DEBUG +CMAKE_RC_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RC_FLAGS_MINSIZEREL +CMAKE_RC_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RC_FLAGS_RELEASE +CMAKE_RC_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RC_FLAGS_RELWITHDEBINFO +CMAKE_RC_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//Path to CMake installation. +CMAKE_ROOT:INTERNAL=C:/Program Files/CMake/share/cmake-3.10 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS +CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG +CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE +CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//CHECK_TYPE_SIZE: sizeof(void*) +CMAKE_SIZEOF_VOID_P:INTERNAL=4 +//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH +CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_RPATH +CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS +CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG +CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE +CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE +CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CPACK_BINARY_7Z +CPACK_BINARY_7Z-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CPACK_BINARY_IFW +CPACK_BINARY_IFW-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CPACK_BINARY_NSIS +CPACK_BINARY_NSIS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CPACK_BINARY_WIX +CPACK_BINARY_WIX-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CPACK_BINARY_ZIP +CPACK_BINARY_ZIP-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CPACK_SOURCE_7Z +CPACK_SOURCE_7Z-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CPACK_SOURCE_ZIP +CPACK_SOURCE_ZIP-ADVANCED:INTERNAL=1 +//Details about finding OpenGL +FIND_PACKAGE_MESSAGE_DETAILS_OpenGL:INTERNAL=[opengl32][c ][v()] +//Result of TRY_COMPILE +HAVE_CMAKE_SIZEOF_VOID_P:INTERNAL=TRUE +//Have include stddef.h +HAVE_STDDEF_H:INTERNAL=1 +//Have include stdint.h +HAVE_STDINT_H:INTERNAL=1 +//Have include sys/types.h +HAVE_SYS_TYPES_H:INTERNAL=1 +//ADVANCED property for variable: MSVC_REDIST_DIR +MSVC_REDIST_DIR-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: OPENGL_gl_LIBRARY +OPENGL_gl_LIBRARY-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: OPENGL_glu_LIBRARY +OPENGL_glu_LIBRARY-ADVANCED:INTERNAL=1 +//Components requested for this build tree. +_Boost_COMPONENTS_SEARCHED:INTERNAL=unit_test_framework +//Last used Boost_INCLUDE_DIR value. +_Boost_INCLUDE_DIR_LAST:INTERNAL=Boost_INCLUDE_DIR-NOTFOUND +//Last used Boost_NAMESPACE value. +_Boost_NAMESPACE_LAST:INTERNAL=boost +//Last used Boost_USE_MULTITHREADED value. +_Boost_USE_MULTITHREADED_LAST:INTERNAL=TRUE +//Last used Boost_USE_STATIC_LIBS value. +_Boost_USE_STATIC_LIBS_LAST:INTERNAL=ON + diff --git a/CMakeLists.txt b/CMakeLists.txt index abe9930ce8..b653de8369 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,21 +21,25 @@ cmake_minimum_required(VERSION 2.8.9) # For checks in subdirectories set(InOpenJK TRUE) + + # Project name set(ProjectName "OpenJK" CACHE STRING "Project Name") project(${ProjectName}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + # Customizable options option(BuildPortableVersion "Build portable version (does not read or write files from your user/home directory" OFF) -option(BuildMPEngine "Whether to create projects for the MP client (openjk.exe)" ON) -option(BuildMPRdVanilla "Whether to create projects for the MP default renderer (rd-vanilla_x86.dll)" ON) -option(BuildMPDed "Whether to create projects for the MP dedicated server (openjkded.exe)" ON) -option(BuildMPGame "Whether to create projects for the MP server-side gamecode (jampgamex86.dll)" ON) -option(BuildMPCGame "Whether to create projects for the MP clientside gamecode (cgamex86.dll)" ON) -option(BuildMPUI "Whether to create projects for the MP UI code (uix86.dll)" ON) +option(BuildMPEngine "Whether to create projects for the MP client (openjk.exe)" OFF) +option(BuildMPRdVanilla "Whether to create projects for the MP default renderer (rd-vanilla_x86.dll)" OFF) +option(BuildMPDed "Whether to create projects for the MP dedicated server (openjkded.exe)" OFF) +option(BuildMPGame "Whether to create projects for the MP server-side gamecode (jampgamex86.dll)" OFF) +option(BuildMPCGame "Whether to create projects for the MP clientside gamecode (cgamex86.dll)" OFF) +option(BuildMPUI "Whether to create projects for the MP UI code (uix86.dll)" OFF) option(BuildSPEngine "Whether to create projects for the SP engine (openjk_sp.exe)" ON) option(BuildSPGame "Whether to create projects for the SP gamecode (jagamex86.dll)" ON) option(BuildSPRdVanilla "Whether to create projects for the SP default renderer (rdsp-vanilla_x86.dll)" ON) @@ -44,6 +48,11 @@ option(BuildJK2SPEngine "Whether to create projects for the jk2 SP engine (openj option(BuildJK2SPGame "Whether to create projects for the jk2 sp gamecode mod (jk2gamex86.dll)" OFF) option(BuildJK2SPRdVanilla "Whether to create projects for the jk2 sp renderer (rdjosp-vanilla_x86.dll)" OFF) +option(BuildTests "Whether to build automatic unit tests (requires Boost)" OFF) + +Include(CMakeDependentOption) +CMAKE_DEPENDENT_OPTION(BuildSymbolServer "Build WIP Windows Symbol Server (experimental and unused)" OFF "NOT WIN32 OR NOT MSVC" OFF) + # Configure the use of bundled libraries. By default, we assume the user is on # a platform that does not require any bundling. # @@ -85,6 +94,8 @@ list(INSERT CMAKE_MODULE_PATH 0 "${CMAKE_SOURCE_DIR}/CMakeModules") Include(CheckTypeSize) check_type_size("void*" CMAKE_SIZEOF_VOID_P) + + # ${Architecture} must match ARCH_STRING in q_platform.h, # and is used in DLL names (jagamex86.dll, jagamex86.dylib, jagamei386.so). if(WIN32) @@ -122,15 +133,19 @@ endif() message("Architecture is ${Architecture}") + + # Current Git SHA1 hash include(GetGitRevisionDescription) get_git_head_revision(GIT_REFSPEC GIT_SHA1) message("Git revision is ${GIT_SHA1}") + + # Binary names -set(SPEngine "openjk_sp.${Architecture}") -set(SPGame "jagame${Architecture}") -set(SPRDVanillaRenderer "rdsp-vanilla_${Architecture}") +set(SPEngine "ja_enhanced.${Architecture}") +set(SPGame "jaenhancedgame${Architecture}") +set(SPRDVanillaRenderer "rdcustomsp-vanilla_${Architecture}") set(MPEngine "openjk.${Architecture}") set(MPVanillaRenderer "rd-vanilla_${Architecture}") set(MPDed "openjkded.${Architecture}") @@ -145,15 +160,20 @@ set(AssetsPk3 "openjk-${Architecture}.pk3") set(MPBotLib "botlib") set(SharedLib "shared") + + # Paths set(SPDir "${CMAKE_SOURCE_DIR}/code") set(MPDir "${CMAKE_SOURCE_DIR}/codemp") set(JK2SPDir "${CMAKE_SOURCE_DIR}/codeJK2") set(SharedDir ${CMAKE_SOURCE_DIR}/shared) set(OpenJKLibDir "${CMAKE_SOURCE_DIR}/lib") +set(GSLIncludeDirectory "${OpenJKLibDir}/gsl-lite/include") include(InstallConfig) + + # Operating settings if(WIN64) set(SharedDefines ${SharedDefines} "WIN64") @@ -167,6 +187,8 @@ if (NOT WIN32 AND NOT APPLE) set(SharedDefines "ARCH_STRING=\"${Architecture}\"") endif() + + # Compiler settings if(MSVC) @@ -211,6 +233,9 @@ elseif (("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") OR ("${CMAKE_C_COMPILER_ID}" S set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") + # enable somewhat modern C++ + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-comment") @@ -247,7 +272,8 @@ elseif (("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") OR ("${CMAKE_C_COMPILER_ID}" S endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-writable-strings") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-write-strings") + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated-writable-strings") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-comment") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-offsetof") endif() @@ -269,11 +295,35 @@ else() set(SharedDefines ${SharedDefines} "FINAL_BUILD") endif() + + # Settings if(BuildPortableVersion) set(SharedDefines ${SharedDefines} "_PORTABLE_VERSION") endif() + + + +# Files shared across all projects +set(SharedCommonFiles + "${SharedDir}/qcommon/q_color.h" + "${SharedDir}/qcommon/q_color.c" + "${SharedDir}/qcommon/q_math.h" + "${SharedDir}/qcommon/q_math.c" + "${SharedDir}/qcommon/q_string.h" + "${SharedDir}/qcommon/q_string.c" + "${SharedDir}/qcommon/q_platform.h" + ) +set(SharedCommonSafeFiles + "${SharedDir}/qcommon/safe/gsl.h" + "${SharedDir}/qcommon/safe/string.cpp" + "${SharedDir}/qcommon/safe/string.h" + "${SharedDir}/qcommon/safe/sscanf.h" + "${SharedDir}/qcommon/safe/limited_vector.h" + ) + + if(UseInternalJPEG) add_subdirectory(lib/jpeg-9a) else() @@ -302,6 +352,10 @@ if(BuildJK2SPGame) endif() add_subdirectory(${MPDir}) # Windows Symbol Server tools -if(WIN32 AND MSVC) +if(BuildSymbolServer) add_subdirectory("tools/WinSymbol") endif() +if(BuildTests) + enable_testing() + add_subdirectory("tests") +endif() diff --git a/CMakeModules/FindSDL2.cmake b/CMakeModules/FindSDL2.cmake deleted file mode 100644 index 5be23d6e13..0000000000 --- a/CMakeModules/FindSDL2.cmake +++ /dev/null @@ -1,182 +0,0 @@ -# Locate SDL2 library -# This module defines -# SDL2_LIBRARY, the name of the library to link against -# SDL2_FOUND, if false, do not try to link to SDL2 -# SDL2_INCLUDE_DIR, where to find SDL.h -# -# This module responds to the the flag: -# SDL2_BUILDING_LIBRARY -# If this is defined, then no SDL2main will be linked in because -# only applications need main(). -# Otherwise, it is assumed you are building an application and this -# module will attempt to locate and set the the proper link flags -# as part of the returned SDL2_LIBRARY variable. -# -# Don't forget to include SDLmain.h and SDLmain.m your project for the -# OS X framework based version. (Other versions link to -lSDL2main which -# this module will try to find on your behalf.) Also for OS X, this -# module will automatically add the -framework Cocoa on your behalf. -# -# -# Additional Note: If you see an empty SDL2_LIBRARY_TEMP in your configuration -# and no SDL2_LIBRARY, it means CMake did not find your SDL2 library -# (SDL2.dll, libsdl2.so, SDL2.framework, etc). -# Set SDL2_LIBRARY_TEMP to point to your SDL2 library, and configure again. -# Similarly, if you see an empty SDL2MAIN_LIBRARY, you should set this value -# as appropriate. These values are used to generate the final SDL2_LIBRARY -# variable, but when these values are unset, SDL2_LIBRARY does not get created. -# -# -# $SDL2DIR is an environment variable that would -# correspond to the ./configure --prefix=$SDL2DIR -# used in building SDL2. -# l.e.galup 9-20-02 -# -# Modified by Eric Wing. -# Added code to assist with automated building by using environmental variables -# and providing a more controlled/consistent search behavior. -# Added new modifications to recognize OS X frameworks and -# additional Unix paths (FreeBSD, etc). -# Also corrected the header search path to follow "proper" SDL guidelines. -# Added a search for SDL2main which is needed by some platforms. -# Added a search for threads which is needed by some platforms. -# Added needed compile switches for MinGW. -# -# On OSX, this will prefer the Framework version (if found) over others. -# People will have to manually change the cache values of -# SDL2_LIBRARY to override this selection or set the CMake environment -# CMAKE_INCLUDE_PATH to modify the search paths. -# -# Note that the header path has changed from SDL2/SDL.h to just SDL.h -# This needed to change because "proper" SDL convention -# is #include "SDL.h", not . This is done for portability -# reasons because not all systems place things in SDL2/ (see FreeBSD). - -#============================================================================= -# Copyright 2003-2009 Kitware, Inc. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the names of Kitware, Inc., the Insight Software Consortium, -# nor the names of their contributors may be used to endorse or promote -# products derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -SET(SDL2_SEARCH_PATHS - ~/Library/Frameworks - /Library/Frameworks - /usr/local - /usr - /sw # Fink - /opt/local # DarwinPorts - /opt/csw # Blastwave - /opt -) - -FIND_PATH(SDL2_INCLUDE_DIR SDL.h - NAMES SDL2 - HINTS - $ENV{SDL2DIR} - PATH_SUFFIXES include/SDL2 include - PATHS ${SDL2_SEARCH_PATHS} -) - -FIND_LIBRARY(SDL2_LIBRARY_TEMP - NAMES SDL2 - HINTS - $ENV{SDL2DIR} - PATH_SUFFIXES lib64 lib - PATHS ${SDL2_SEARCH_PATHS} -) - -IF(NOT SDL2_BUILDING_LIBRARY) - IF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") - # Non-OS X framework versions expect you to also dynamically link to - # SDL2main. This is mainly for Windows and OS X. Other (Unix) platforms - # seem to provide SDL2main for compatibility even though they don't - # necessarily need it. - FIND_LIBRARY(SDL2MAIN_LIBRARY - NAMES SDL2main - HINTS - $ENV{SDL2DIR} - PATH_SUFFIXES lib64 lib - PATHS ${SDL2_SEARCH_PATHS} - ) - ENDIF(NOT ${SDL2_INCLUDE_DIR} MATCHES ".framework") -ENDIF(NOT SDL2_BUILDING_LIBRARY) - -# SDL2 may require threads on your system. -# The Apple build may not need an explicit flag because one of the -# frameworks may already provide it. -# But for non-OSX systems, I will use the CMake Threads package. -IF(NOT APPLE) - FIND_PACKAGE(Threads) -ENDIF(NOT APPLE) - -# MinGW needs an additional library, mwindows -# It's total link flags should look like -lmingw32 -lSDL2main -lSDL2 -lmwindows -# (Actually on second look, I think it only needs one of the m* libraries.) -IF(MINGW) - SET(MINGW32_LIBRARY mingw32 CACHE STRING "mwindows for MinGW") -ENDIF(MINGW) - -IF(SDL2_LIBRARY_TEMP) - # For SDL2main - IF(NOT SDL2_BUILDING_LIBRARY) - IF(SDL2MAIN_LIBRARY) - SET(SDL2_LIBRARY_TEMP ${SDL2MAIN_LIBRARY} ${SDL2_LIBRARY_TEMP}) - ENDIF(SDL2MAIN_LIBRARY) - ENDIF(NOT SDL2_BUILDING_LIBRARY) - - # For OS X, SDL2 uses Cocoa as a backend so it must link to Cocoa. - # CMake doesn't display the -framework Cocoa string in the UI even - # though it actually is there if I modify a pre-used variable. - # I think it has something to do with the CACHE STRING. - # So I use a temporary variable until the end so I can set the - # "real" variable in one-shot. - IF(APPLE) - SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} "-framework Cocoa") - ENDIF(APPLE) - - # For threads, as mentioned Apple doesn't need this. - # In fact, there seems to be a problem if I used the Threads package - # and try using this line, so I'm just skipping it entirely for OS X. - IF(NOT APPLE) - SET(SDL2_LIBRARY_TEMP ${SDL2_LIBRARY_TEMP} ${CMAKE_THREAD_LIBS_INIT}) - ENDIF(NOT APPLE) - - # For MinGW library - IF(MINGW) - SET(SDL2_LIBRARY_TEMP ${MINGW32_LIBRARY} ${SDL2_LIBRARY_TEMP}) - ENDIF(MINGW) - - # Set the final string here so the GUI reflects the final state. - SET(SDL2_LIBRARY ${SDL2_LIBRARY_TEMP} CACHE STRING "Where the SDL2 Library can be found") - # Set the temp variable to INTERNAL so it is not seen in the CMake GUI - SET(SDL2_LIBRARY_TEMP "${SDL2_LIBRARY_TEMP}" CACHE INTERNAL "") -ENDIF(SDL2_LIBRARY_TEMP) - -INCLUDE(FindPackageHandleStandardArgs) - -FIND_PACKAGE_HANDLE_STANDARD_ARGS(SDL2 REQUIRED_VARS SDL2_LIBRARY SDL2_INCLUDE_DIR) diff --git a/CMakeModules/GetGitRevisionDescription.cmake b/CMakeModules/GetGitRevisionDescription.cmake deleted file mode 100644 index 1bf0230088..0000000000 --- a/CMakeModules/GetGitRevisionDescription.cmake +++ /dev/null @@ -1,123 +0,0 @@ -# - Returns a version string from Git -# -# These functions force a re-configure on each git commit so that you can -# trust the values of the variables in your build system. -# -# get_git_head_revision( [ ...]) -# -# Returns the refspec and sha hash of the current head revision -# -# git_describe( [ ...]) -# -# Returns the results of git describe on the source tree, and adjusting -# the output so that it tests false if an error occurs. -# -# git_get_exact_tag( [ ...]) -# -# Returns the results of git describe --exact-match on the source tree, -# and adjusting the output so that it tests false if there was no exact -# matching tag. -# -# Requires CMake 2.6 or newer (uses the 'function' command) -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright Iowa State University 2009-2010. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -if(__get_git_revision_description) - return() -endif() -set(__get_git_revision_description YES) - -# We must run the following at "include" time, not at function call time, -# to find the path to this module rather than the path to a calling list file -get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) - -function(get_git_head_revision _refspecvar _hashvar) - set(GIT_PARENT_DIR "${CMAKE_SOURCE_DIR}") - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories - set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") - get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) - if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) - # We have reached the root directory, we are not in git - set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - return() - endif() - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - endwhile() - set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") - if(NOT EXISTS "${GIT_DATA}") - file(MAKE_DIRECTORY "${GIT_DATA}") - endif() - - if(NOT EXISTS "${GIT_DIR}/HEAD") - return() - endif() - set(HEAD_FILE "${GIT_DATA}/HEAD") - configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) - - configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" - "${GIT_DATA}/grabRef.cmake" - @ONLY) - include("${GIT_DATA}/grabRef.cmake") - - set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) - set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) -endfunction() - -function(git_describe _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) - return() - endif() - - # TODO sanitize - #if((${ARGN}" MATCHES "&&") OR - # (ARGN MATCHES "||") OR - # (ARGN MATCHES "\\;")) - # message("Please report the following error to the project!") - # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") - #endif() - - #message(STATUS "Arguments to execute_process: ${ARGN}") - - execute_process(COMMAND - "${GIT_EXECUTABLE}" - describe - ${hash} - ${ARGN} - WORKING_DIRECTORY - "${CMAKE_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() - - set(${_var} "${out}" PARENT_SCOPE) -endfunction() - -function(git_get_exact_tag _var) - git_describe(out --exact-match ${ARGN}) - set(${_var} "${out}" PARENT_SCOPE) -endfunction() diff --git a/CMakeModules/GetGitRevisionDescription.cmake.in b/CMakeModules/GetGitRevisionDescription.cmake.in deleted file mode 100644 index 888ce13aab..0000000000 --- a/CMakeModules/GetGitRevisionDescription.cmake.in +++ /dev/null @@ -1,38 +0,0 @@ -# -# Internal file for GetGitRevisionDescription.cmake -# -# Requires CMake 2.6 or newer (uses the 'function' command) -# -# Original Author: -# 2009-2010 Ryan Pavlik -# http://academic.cleardefinition.com -# Iowa State University HCI Graduate Program/VRAC -# -# Copyright Iowa State University 2009-2010. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -set(HEAD_HASH) - -file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) - -string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) -if(HEAD_CONTENTS MATCHES "ref") - # named branch - string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") - if(EXISTS "@GIT_DIR@/${HEAD_REF}") - configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) - elseif(EXISTS "@GIT_DIR@/logs/${HEAD_REF}") - configure_file("@GIT_DIR@/logs/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) - set(HEAD_HASH "${HEAD_REF}") - endif() -else() - # detached HEAD - configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) -endif() - -if(NOT HEAD_HASH) - file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) - string(STRIP "${HEAD_HASH}" HEAD_HASH) -endif() diff --git a/CMakeModules/InstallConfig.cmake b/CMakeModules/InstallConfig.cmake deleted file mode 100644 index 8e0094a6e9..0000000000 --- a/CMakeModules/InstallConfig.cmake +++ /dev/null @@ -1,163 +0,0 @@ -#============================================================================ -# Copyright (C) 2015, OpenJK contributors -# -# This file is part of the OpenJK source code. -# -# OpenJK is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 as -# published by the Free Software Foundation. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, see . -#============================================================================ - -# Subdirectories to package JK2 and JKA into -set(JKAInstallDir "JediAcademy") -set(JK2InstallDir "JediOutcast") - -# Install components -set(JKAMPCoreComponent "JKAMPCore") -set(JKAMPServerComponent "JKAMPServer") -set(JKAMPClientComponent "JKAMPClient") -set(JKASPClientComponent "JKASPClient") -set(JK2SPClientComponent "JK2SPClient") - -# Component display names -include(CPackComponent) - -set(CPACK_COMPONENT_JKAMPCORE_DISPLAY_NAME "Core") -set(CPACK_COMPONENT_JKAMPCORE_REQUIRED TRUE) -set(CPACK_COMPONENT_JKAMPCORE_DESCRIPTION "Core files shared by the multiplayer client and server executables.") -set(CPACK_COMPONENT_JKAMPCLIENT_DISPLAY_NAME "Client") -set(CPACK_COMPONENT_JKAMPCLIENT_DESCRIPTION "Files required to play the multiplayer game.") -set(CPACK_COMPONENT_JKAMPSERVER_DISPLAY_NAME "Server") -set(CPACK_COMPONENT_JKAMPSERVER_DESCRIPTION "Files required to run a Jedi Academy server.") -set(CPACK_COMPONENT_JKASPCLIENT_DISPLAY_NAME "Core") -set(CPACK_COMPONENT_JKASPCLIENT_DESCRIPTION "Files required to play the Jedi Academy single player game.") -set(CPACK_COMPONENT_JK2SPCLIENT_DISPLAY_NAME "Core") -set(CPACK_COMPONENT_JK2SPCLIENT_DESCRIPTION "Files required to play the Jedi Outcast single player game.") -set(CPACK_COMPONENTS_ALL - ${JKAMPCoreComponent} - ${JKAMPClientComponent} - ${JKAMPServerComponent} - ${JKASPClientComponent}) - -set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) - -# Component groups -set(CPACK_COMPONENT_JKAMPCORE_GROUP "JKAMP") -set(CPACK_COMPONENT_JKAMPCLIENT_GROUP "JKAMP") -set(CPACK_COMPONENT_JKAMPSERVER_GROUP "JKAMP") -set(CPACK_COMPONENT_JKASPCLIENT_GROUP "JKASP") -set(CPACK_COMPONENT_JK2SPCLIENT_GROUP "JK2SP") - -cpack_add_component_group(JKAMP - DISPLAY_NAME "Jedi Academy Multiplayer" - DESCRIPTION "Jedi Academy multiplayer game") -cpack_add_component_group(JKASP - DISPLAY_NAME "Jedi Academy Single Player" - DESCRIPTION "Jedi Academy single player game") -cpack_add_component_group(JK2SP - DISPLAY_NAME "Jedi Outcast Single Player" - DESCRIPTION "Jedi Outcast single player game") - -if(WIN32) - include(CPackNSIS) - set(CPACK_NSIS_DISPLAY_NAME "OpenJK") - set(CPACK_NSIS_PACKAGE_NAME "OpenJK") - set(CPACK_NSIS_MUI_ICON "${SharedDir}/icons/icon.ico") - set(CPACK_NSIS_MUI_UNIICON "${SharedDir}/icons/icon.ico") - set(CPACK_NSIS_URL_INFO_ABOUT "http://openjk.org") - - set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) - include(InstallRequiredSystemLibraries) - - if(BuildMPEngine) - string(REPLACE "/" "\\\\" ICON "${MPDir}/win32/icon.ico") - set(CPACK_NSIS_CREATE_ICONS_EXTRA - "${CPACK_NSIS_CREATE_ICONS_EXTRA} - CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Jedi Academy MP.lnk' \\\\ - '$INSTDIR\\\\${MPEngine}.exe' \\\\ - '' \\\\ - '${ICON}'") - - set(CPACK_NSIS_DELETE_ICONS_EXTRA - "${CPACK_NSIS_DELETE_ICONS_EXTRA} - Delete '$SMPROGRAMS\\\\$MUI_TEMP\\\\Jedi Academy MP.lnk'") - - install(FILES ${MPDir}/OpenAL32.dll ${MPDir}/EaxMan.dll - DESTINATION ${JKAInstallDir} - COMPONENT ${JKAMPClientComponent}) - - install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} - DESTINATION ${JKAInstallDir} - COMPONENT ${JKAMPClientComponent}) - endif() - - if(BuildSPEngine) - string(REPLACE "/" "\\\\" ICON "${SPDir}/win32/starwars.ico") - set(CPACK_NSIS_CREATE_ICONS_EXTRA - "${CPACK_NSIS_CREATE_ICONS_EXTRA} - CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Jedi Academy SP.lnk' \\\\ - '$INSTDIR\\\\${SPEngine}.exe' \\\\ - '' \\\\ - '${ICON}'") - - set(CPACK_NSIS_DELETE_ICONS_EXTRA - "${CPACK_NSIS_DELETE_ICONS_EXTRA} - Delete '$SMPROGRAMS\\\\$MUI_TEMP\\\\Jedi Academy SP.lnk'") - - install(FILES ${MPDir}/OpenAL32.dll ${MPDir}/EaxMan.dll - DESTINATION ${JKAInstallDir} - COMPONENT ${JKASPClientComponent}) - - install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} - DESTINATION ${JKAInstallDir} - COMPONENT ${JKASPClientComponent}) - endif() - - # Don't run this for now until we have JK2 SP working - if(FALSE AND BuildJK2SPEngine) - string(REPLACE "/" "\\\\" ICON "${SPDir}/win32/starwars.ico") - set(CPACK_NSIS_CREATE_ICONS_EXTRA - "${CPACK_NSIS_CREATE_ICONS_EXTRA} - CreateShortCut '$SMPROGRAMS\\\\$STARTMENU_FOLDER\\\\Jedi Outcast SP.lnk' \\\\ - '$INSTDIR\\\\${JK2SPEngine}.exe' \\\\ - '' \\\\ - '${ICON}'") - - set(CPACK_NSIS_DELETE_ICONS_EXTRA - "${CPACK_NSIS_DELETE_ICONS_EXTRA} - Delete '$SMPROGRAMS\\\\$MUI_TEMP\\\\Jedi Outcast SP.lnk'") - - install(FILES ${MPDir}/OpenAL32.dll ${MPDir}/EaxMan.dll - DESTINATION ${JK2InstallDir} - COMPONENT ${JK2SPClientComponent}) - - install(PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} - DESTINATION ${JK2InstallDir} - COMPONENT ${JK2SPClientComponent}) - endif() -endif() - -# CPack for installer creation -set(CPACK_PACKAGE_VERSION_MAJOR "1") -set(CPACK_PACKAGE_VERSION_MINOR "0") -set(CPACK_PACKAGE_VERSION_PATCH "0") -set(CPACK_PACKAGE_FILE_NAME "OpenJK-${CMAKE_SYSTEM_NAME}-${Architecture}") - -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "An improved Jedi Academy") -set(CPACK_PACKAGE_VENDOR "JACoders") -set(CPACK_PACKAGE_INSTALL_DIRECTORY "OpenJK") -set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") -set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE.txt") -set(CPACK_PACKAGE_DIRECTORY ${PACKAGE_DIR}) -set(CPACK_BINARY_ZIP ON) # always create at least a zip file -set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) # prevent additional directory in zip - -include(CPack) \ No newline at end of file diff --git a/CMakeModules/InstallZIP.cmake b/CMakeModules/InstallZIP.cmake deleted file mode 100644 index 090b946d03..0000000000 --- a/CMakeModules/InstallZIP.cmake +++ /dev/null @@ -1,104 +0,0 @@ -#============================================================================= -# Copyright 2007-2009 Kitware, Inc. -# Copyright 2015 OpenJK contributors -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# * Neither the names of Kitware, Inc., the Insight Software Consortium, -# nor the names of their contributors may be used to endorse or promote -# products derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#============================================================================= - -include(CMakeParseArguments) - -unset(ZIP_EXECUTABLE CACHE) -if(WIN32) - if(NOT ZIP_EXECUTABLE) - find_program(ZIP_EXECUTABLE wzzip PATHS "$ENV{ProgramFiles}/WinZip") - if(ZIP_EXECUTABLE) - set(ZIP_COMMAND "${ZIP_EXECUTABLE}" -P "" ) - endif() - endif() - - if(NOT ZIP_EXECUTABLE) - find_program(ZIP_EXECUTABLE wzzip PATHS "$ENV{ProgramW6432}/WinZip") - if(ZIP_EXECUTABLE) - set(ZIP_COMMAND "${ZIP_EXECUTABLE}" -P "" ) - endif() - endif() - - if(NOT ZIP_EXECUTABLE) - find_program(ZIP_EXECUTABLE 7z PATHS "$ENV{ProgramFiles}/7-Zip") - if(ZIP_EXECUTABLE) - set(ZIP_COMMAND "${ZIP_EXECUTABLE}" a -tzip "" ) - endif() - endif() - - if(NOT ZIP_EXECUTABLE) - find_program(ZIP_EXECUTABLE 7z PATHS "$ENV{ProgramW6432}/7-Zip") - if(ZIP_EXECUTABLE) - set(ZIP_COMMAND "${ZIP_EXECUTABLE}" a -tzip "" ) - endif() - endif() - - if(NOT ZIP_EXECUTABLE) - find_program(ZIP_EXECUTABLE winrar PATHS "$ENV{ProgramFiles}/WinRAR") - if(ZIP_EXECUTABLE) - set(ZIP_COMMAND "${ZIP_EXECUTABLE}" a "" ) - endif() - endif() - - if(NOT ZIP_EXECUTABLE) - find_program(ZIP_EXECUTABLE winrar PATHS "$ENV{ProgramW6432}/WinRAR") - if(ZIP_EXECUTABLE) - set(ZIP_COMMAND "${ZIP_EXECUTABLE}" a "" ) - endif() - endif() -endif() - -if(NOT ZIP_EXECUTABLE) - if(WIN32) - find_package(Cygwin) - find_program(ZIP_EXECUTABLE zip PATHS "${CYGWIN_INSTALL_PATH}/bin") - else() - find_program(ZIP_EXECUTABLE zip) - endif() - - if(ZIP_EXECUTABLE) - set(ZIP_COMMAND "${ZIP_EXECUTABLE}" -r "" . -i) - endif() -endif() - -function(add_zip_command output) - set(MultiValueArgs FILES DEPENDS) - cmake_parse_arguments(ARGS "" "" "${MultiValueArgs}" ${ARGN}) - - set(ZipCommand ${ZIP_COMMAND}) - string(REPLACE "${output}" ZipCommand "${ZipCommand}") - string(REPLACE "${ARGS_FILES}" ZipCommand "${ZipCommand}") - add_custom_command(OUTPUT ${output} - COMMAND ${ZipCommand} - DEPENDS ${ARGS_DEPENDS}) -endfunction(add_zip_command) diff --git a/CMakeModules/LICENSE_1_0.txt b/CMakeModules/LICENSE_1_0.txt deleted file mode 100644 index d928544d42..0000000000 --- a/CMakeModules/LICENSE_1_0.txt +++ /dev/null @@ -1,26 +0,0 @@ -This license applies to files in this directory that refer to -the Boost Software License, version 1.0 by name: - -Boost Software License - Version 1.0 - August 17th, 2003 - -Permission is hereby granted, free of charge, to any person or organization -obtaining a copy of the software and accompanying documentation covered by -this license (the "Software") to use, reproduce, display, distribute, -execute, and transmit the Software, and to prepare derivative works of the -Software, and to permit third-parties to whom the Software is furnished to -do so, all subject to the following: - -The copyright notices in the Software and this entire statement, including -the above license grant, this restriction and the following disclaimer, -must be included in all copies of the Software, in whole or in part, and -all derivative works of the Software, unless such copies or derivative -works are solely in the form of machine-executable object code generated by -a source language processor. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT -SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/CMakeModules/Toolchains/i686-w64-mingw32.cmake b/CMakeModules/Toolchains/i686-w64-mingw32.cmake deleted file mode 100644 index fcdcdaeaed..0000000000 --- a/CMakeModules/Toolchains/i686-w64-mingw32.cmake +++ /dev/null @@ -1,18 +0,0 @@ -set(GNU_HOST i686-w64-mingw32) -set(CMAKE_SYSTEM_PROCESSOR "i686") - -set(COMPILER_PREFIX "${GNU_HOST}-") - -set(CMAKE_SYSTEM_NAME "Windows") -set(CMAKE_CROSSCOMPILING TRUE) -set(WIN32 TRUE) -set(MINGW TRUE) - -include(CMakeForceCompiler) -cmake_force_c_compiler(${COMPILER_PREFIX}gcc GNU) -cmake_force_cxx_compiler(${COMPILER_PREFIX}g++ GNU) -set(CMAKE_RC_COMPILER ${COMPILER_PREFIX}windres) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/CMakeModules/Toolchains/linux-i686.cmake b/CMakeModules/Toolchains/linux-i686.cmake deleted file mode 100644 index 8df4cadcee..0000000000 --- a/CMakeModules/Toolchains/linux-i686.cmake +++ /dev/null @@ -1,5 +0,0 @@ -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR i686) - -set(CMAKE_C_COMPILER_ARG1 -m32) -set(CMAKE_CXX_COMPILER_ARG1 -m32) diff --git a/CMakeModules/Toolchains/x86_64-w64-mingw32.cmake b/CMakeModules/Toolchains/x86_64-w64-mingw32.cmake deleted file mode 100644 index 7fb132a5b8..0000000000 --- a/CMakeModules/Toolchains/x86_64-w64-mingw32.cmake +++ /dev/null @@ -1,18 +0,0 @@ -set(GNU_HOST x86_64-w64-mingw32) -set(CMAKE_SYSTEM_PROCESSOR "x86_64") - -set(COMPILER_PREFIX "${GNU_HOST}-") - -set(CMAKE_SYSTEM_NAME "Windows") -set(CMAKE_CROSSCOMPILING TRUE) -set(WIN32 TRUE) -set(MINGW TRUE) - -include(CMakeForceCompiler) -cmake_force_c_compiler(${COMPILER_PREFIX}gcc GNU) -cmake_force_cxx_compiler(${COMPILER_PREFIX}g++ GNU) -set(CMAKE_RC_COMPILER ${COMPILER_PREFIX}windres) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/CPackConfig.cmake b/CPackConfig.cmake new file mode 100644 index 0000000000..50a072c6fa --- /dev/null +++ b/CPackConfig.cmake @@ -0,0 +1,128 @@ +# This file will be configured to contain variables for CPack. These variables +# should be set in the CMake list file of the project before CPack module is +# included. The list of available CPACK_xxx variables and their associated +# documentation may be obtained using +# cpack --help-variable-list +# +# Some variables are common to all generators (e.g. CPACK_PACKAGE_NAME) +# and some are specific to a generator +# (e.g. CPACK_NSIS_EXTRA_INSTALL_COMMANDS). The generator specific variables +# usually begin with CPACK__xxxx. + + +SET(CPACK_APP_VALUE_LEN "1") +SET(CPACK_ARCHIVE_COMPONENT_INSTALL "ON") +SET(CPACK_BINARY_7Z "OFF") +SET(CPACK_BINARY_BUNDLE "") +SET(CPACK_BINARY_CYGWIN "") +SET(CPACK_BINARY_DEB "") +SET(CPACK_BINARY_DRAGNDROP "") +SET(CPACK_BINARY_FREEBSD "") +SET(CPACK_BINARY_IFW "OFF") +SET(CPACK_BINARY_NSIS "ON") +SET(CPACK_BINARY_OSXX11 "") +SET(CPACK_BINARY_PACKAGEMAKER "") +SET(CPACK_BINARY_PRODUCTBUILD "") +SET(CPACK_BINARY_RPM "") +SET(CPACK_BINARY_STGZ "") +SET(CPACK_BINARY_TBZ2 "") +SET(CPACK_BINARY_TGZ "") +SET(CPACK_BINARY_TXZ "") +SET(CPACK_BINARY_TZ "") +SET(CPACK_BINARY_WIX "OFF") +SET(CPACK_BINARY_ZIP "ON") +SET(CPACK_BINARY_ZIP "ON") +SET(CPACK_BUILD_SOURCE_DIRS "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK;C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK") +SET(CPACK_CMAKE_GENERATOR "Visual Studio 15 2017") +SET(CPACK_COMPONENTS_ALL "JKAMPCore;JKAMPClient;JKAMPServer;JKASPClient") +SET(CPACK_COMPONENTS_ALL_SET_BY_USER "TRUE") +SET(CPACK_COMPONENT_GROUP_JK2SP_BOLD_TITLE "FALSE") +SET(CPACK_COMPONENT_GROUP_JK2SP_DESCRIPTION "Jedi Outcast single player game") +SET(CPACK_COMPONENT_GROUP_JK2SP_DISPLAY_NAME "Jedi Outcast Single Player") +SET(CPACK_COMPONENT_GROUP_JK2SP_EXPANDED "FALSE") +SET(CPACK_COMPONENT_GROUP_JKAMP_BOLD_TITLE "FALSE") +SET(CPACK_COMPONENT_GROUP_JKAMP_DESCRIPTION "Jedi Academy multiplayer game") +SET(CPACK_COMPONENT_GROUP_JKAMP_DISPLAY_NAME "Jedi Academy Multiplayer") +SET(CPACK_COMPONENT_GROUP_JKAMP_EXPANDED "FALSE") +SET(CPACK_COMPONENT_GROUP_JKASP_BOLD_TITLE "FALSE") +SET(CPACK_COMPONENT_GROUP_JKASP_DESCRIPTION "Jedi Academy single player game") +SET(CPACK_COMPONENT_GROUP_JKASP_DISPLAY_NAME "Jedi Academy Single Player") +SET(CPACK_COMPONENT_GROUP_JKASP_EXPANDED "FALSE") +SET(CPACK_COMPONENT_JK2SPCLIENT_DESCRIPTION "Files required to play the Jedi Outcast single player game.") +SET(CPACK_COMPONENT_JK2SPCLIENT_DISPLAY_NAME "Core") +SET(CPACK_COMPONENT_JK2SPCLIENT_GROUP "JK2SP") +SET(CPACK_COMPONENT_JKAMPCLIENT_DESCRIPTION "Files required to play the multiplayer game.") +SET(CPACK_COMPONENT_JKAMPCLIENT_DISPLAY_NAME "Client") +SET(CPACK_COMPONENT_JKAMPCLIENT_GROUP "JKAMP") +SET(CPACK_COMPONENT_JKAMPCORE_DESCRIPTION "Core files shared by the multiplayer client and server executables.") +SET(CPACK_COMPONENT_JKAMPCORE_DISPLAY_NAME "Core") +SET(CPACK_COMPONENT_JKAMPCORE_GROUP "JKAMP") +SET(CPACK_COMPONENT_JKAMPCORE_REQUIRED "TRUE") +SET(CPACK_COMPONENT_JKAMPSERVER_DESCRIPTION "Files required to run a Jedi Academy server.") +SET(CPACK_COMPONENT_JKAMPSERVER_DISPLAY_NAME "Server") +SET(CPACK_COMPONENT_JKAMPSERVER_GROUP "JKAMP") +SET(CPACK_COMPONENT_JKASPCLIENT_DESCRIPTION "Files required to play the Jedi Academy single player game.") +SET(CPACK_COMPONENT_JKASPCLIENT_DISPLAY_NAME "Core") +SET(CPACK_COMPONENT_JKASPCLIENT_GROUP "JKASP") +SET(CPACK_COMPONENT_UNSPECIFIED_HIDDEN "TRUE") +SET(CPACK_COMPONENT_UNSPECIFIED_REQUIRED "TRUE") +SET(CPACK_GENERATOR "NSIS;ZIP") +SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY "0") +SET(CPACK_INSTALL_CMAKE_PROJECTS "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK;OpenJK;ALL;/") +SET(CPACK_INSTALL_PREFIX "C:/Program Files (x86)/OpenJK") +SET(CPACK_MODULE_PATH "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/CMakeModules") +SET(CPACK_NSIS_CREATE_ICONS_EXTRA " + CreateShortCut '$SMPROGRAMS\\$STARTMENU_FOLDER\\Jedi Academy SP.lnk' \\ + '$INSTDIR\\openjk_sp.x86.exe' \\ + '' \\ + 'C:\\Users\\Chris\\OneDrive\\Mod Tools and Stuff\\Jedi Academy\\DP Code\\OpenJK\\code\\win32\\starwars.ico'") +SET(CPACK_NSIS_DELETE_ICONS_EXTRA " + Delete '$SMPROGRAMS\\$MUI_TEMP\\Jedi Academy SP.lnk'") +SET(CPACK_NSIS_DISPLAY_NAME "OpenJK") +SET(CPACK_NSIS_DISPLAY_NAME_SET "TRUE") +SET(CPACK_NSIS_INSTALLER_ICON_CODE "") +SET(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "") +SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES") +SET(CPACK_NSIS_MUI_ICON "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/shared/icons/icon.ico") +SET(CPACK_NSIS_MUI_UNIICON "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/shared/icons/icon.ico") +SET(CPACK_NSIS_PACKAGE_NAME "OpenJK") +SET(CPACK_NSIS_URL_INFO_ABOUT "http://openjk.org") +SET(CPACK_OUTPUT_CONFIG_FILE "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/CPackConfig.cmake") +SET(CPACK_PACKAGE_DEFAULT_LOCATION "/") +SET(CPACK_PACKAGE_DESCRIPTION_FILE "C:/Program Files/CMake/share/cmake-3.10/Templates/CPack.GenericDescription.txt") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "An improved Jedi Academy") +SET(CPACK_PACKAGE_FILE_NAME "OpenJK-Windows-x86") +SET(CPACK_PACKAGE_INSTALL_DIRECTORY "OpenJK") +SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "OpenJK") +SET(CPACK_PACKAGE_NAME "OpenJK") +SET(CPACK_PACKAGE_RELOCATABLE "true") +SET(CPACK_PACKAGE_VENDOR "JACoders") +SET(CPACK_PACKAGE_VERSION "1.0.0") +SET(CPACK_PACKAGE_VERSION_MAJOR "1") +SET(CPACK_PACKAGE_VERSION_MINOR "0") +SET(CPACK_PACKAGE_VERSION_PATCH "0") +SET(CPACK_RESOURCE_FILE_LICENSE "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/LICENSE.txt") +SET(CPACK_RESOURCE_FILE_README "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/README.md") +SET(CPACK_RESOURCE_FILE_WELCOME "C:/Program Files/CMake/share/cmake-3.10/Templates/CPack.GenericWelcome.txt") +SET(CPACK_SET_DESTDIR "OFF") +SET(CPACK_SOURCE_7Z "ON") +SET(CPACK_SOURCE_CYGWIN "") +SET(CPACK_SOURCE_GENERATOR "7Z;ZIP") +SET(CPACK_SOURCE_OUTPUT_CONFIG_FILE "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/CPackSourceConfig.cmake") +SET(CPACK_SOURCE_RPM "") +SET(CPACK_SOURCE_TBZ2 "") +SET(CPACK_SOURCE_TGZ "") +SET(CPACK_SOURCE_TXZ "") +SET(CPACK_SOURCE_TZ "") +SET(CPACK_SOURCE_ZIP "ON") +SET(CPACK_SYSTEM_NAME "win32") +SET(CPACK_TOPLEVEL_TAG "win32") +SET(CPACK_WIX_SIZEOF_VOID_P "4") + +if(NOT CPACK_PROPERTIES_FILE) + set(CPACK_PROPERTIES_FILE "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/CPackProperties.cmake") +endif() + +if(EXISTS ${CPACK_PROPERTIES_FILE}) + include(${CPACK_PROPERTIES_FILE}) +endif() diff --git a/CPackSourceConfig.cmake b/CPackSourceConfig.cmake new file mode 100644 index 0000000000..e5db7814cb --- /dev/null +++ b/CPackSourceConfig.cmake @@ -0,0 +1,136 @@ +# This file will be configured to contain variables for CPack. These variables +# should be set in the CMake list file of the project before CPack module is +# included. The list of available CPACK_xxx variables and their associated +# documentation may be obtained using +# cpack --help-variable-list +# +# Some variables are common to all generators (e.g. CPACK_PACKAGE_NAME) +# and some are specific to a generator +# (e.g. CPACK_NSIS_EXTRA_INSTALL_COMMANDS). The generator specific variables +# usually begin with CPACK__xxxx. + + +SET(CPACK_APP_VALUE_LEN "1") +SET(CPACK_ARCHIVE_COMPONENT_INSTALL "ON") +SET(CPACK_BINARY_7Z "OFF") +SET(CPACK_BINARY_BUNDLE "") +SET(CPACK_BINARY_CYGWIN "") +SET(CPACK_BINARY_DEB "") +SET(CPACK_BINARY_DRAGNDROP "") +SET(CPACK_BINARY_FREEBSD "") +SET(CPACK_BINARY_IFW "OFF") +SET(CPACK_BINARY_NSIS "ON") +SET(CPACK_BINARY_OSXX11 "") +SET(CPACK_BINARY_PACKAGEMAKER "") +SET(CPACK_BINARY_PRODUCTBUILD "") +SET(CPACK_BINARY_RPM "") +SET(CPACK_BINARY_STGZ "") +SET(CPACK_BINARY_TBZ2 "") +SET(CPACK_BINARY_TGZ "") +SET(CPACK_BINARY_TXZ "") +SET(CPACK_BINARY_TZ "") +SET(CPACK_BINARY_WIX "OFF") +SET(CPACK_BINARY_ZIP "ON") +SET(CPACK_BINARY_ZIP "ON") +SET(CPACK_BUILD_SOURCE_DIRS "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK;C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK") +SET(CPACK_CMAKE_GENERATOR "Visual Studio 15 2017") +SET(CPACK_COMPONENTS_ALL "JKAMPCore;JKAMPClient;JKAMPServer;JKASPClient") +SET(CPACK_COMPONENTS_ALL_SET_BY_USER "TRUE") +SET(CPACK_COMPONENT_GROUP_JK2SP_BOLD_TITLE "FALSE") +SET(CPACK_COMPONENT_GROUP_JK2SP_DESCRIPTION "Jedi Outcast single player game") +SET(CPACK_COMPONENT_GROUP_JK2SP_DISPLAY_NAME "Jedi Outcast Single Player") +SET(CPACK_COMPONENT_GROUP_JK2SP_EXPANDED "FALSE") +SET(CPACK_COMPONENT_GROUP_JKAMP_BOLD_TITLE "FALSE") +SET(CPACK_COMPONENT_GROUP_JKAMP_DESCRIPTION "Jedi Academy multiplayer game") +SET(CPACK_COMPONENT_GROUP_JKAMP_DISPLAY_NAME "Jedi Academy Multiplayer") +SET(CPACK_COMPONENT_GROUP_JKAMP_EXPANDED "FALSE") +SET(CPACK_COMPONENT_GROUP_JKASP_BOLD_TITLE "FALSE") +SET(CPACK_COMPONENT_GROUP_JKASP_DESCRIPTION "Jedi Academy single player game") +SET(CPACK_COMPONENT_GROUP_JKASP_DISPLAY_NAME "Jedi Academy Single Player") +SET(CPACK_COMPONENT_GROUP_JKASP_EXPANDED "FALSE") +SET(CPACK_COMPONENT_JK2SPCLIENT_DESCRIPTION "Files required to play the Jedi Outcast single player game.") +SET(CPACK_COMPONENT_JK2SPCLIENT_DISPLAY_NAME "Core") +SET(CPACK_COMPONENT_JK2SPCLIENT_GROUP "JK2SP") +SET(CPACK_COMPONENT_JKAMPCLIENT_DESCRIPTION "Files required to play the multiplayer game.") +SET(CPACK_COMPONENT_JKAMPCLIENT_DISPLAY_NAME "Client") +SET(CPACK_COMPONENT_JKAMPCLIENT_GROUP "JKAMP") +SET(CPACK_COMPONENT_JKAMPCORE_DESCRIPTION "Core files shared by the multiplayer client and server executables.") +SET(CPACK_COMPONENT_JKAMPCORE_DISPLAY_NAME "Core") +SET(CPACK_COMPONENT_JKAMPCORE_GROUP "JKAMP") +SET(CPACK_COMPONENT_JKAMPCORE_REQUIRED "TRUE") +SET(CPACK_COMPONENT_JKAMPSERVER_DESCRIPTION "Files required to run a Jedi Academy server.") +SET(CPACK_COMPONENT_JKAMPSERVER_DISPLAY_NAME "Server") +SET(CPACK_COMPONENT_JKAMPSERVER_GROUP "JKAMP") +SET(CPACK_COMPONENT_JKASPCLIENT_DESCRIPTION "Files required to play the Jedi Academy single player game.") +SET(CPACK_COMPONENT_JKASPCLIENT_DISPLAY_NAME "Core") +SET(CPACK_COMPONENT_JKASPCLIENT_GROUP "JKASP") +SET(CPACK_COMPONENT_UNSPECIFIED_HIDDEN "TRUE") +SET(CPACK_COMPONENT_UNSPECIFIED_REQUIRED "TRUE") +SET(CPACK_GENERATOR "7Z;ZIP") +SET(CPACK_IGNORE_FILES "/CVS/;/\\.svn/;/\\.bzr/;/\\.hg/;/\\.git/;\\.swp\$;\\.#;/#") +SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY "0") +SET(CPACK_INSTALLED_DIRECTORIES "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK;/") +SET(CPACK_INSTALL_CMAKE_PROJECTS "") +SET(CPACK_INSTALL_PREFIX "C:/Program Files (x86)/OpenJK") +SET(CPACK_MODULE_PATH "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/CMakeModules") +SET(CPACK_NSIS_CREATE_ICONS_EXTRA " + CreateShortCut '$SMPROGRAMS\\$STARTMENU_FOLDER\\Jedi Academy SP.lnk' \\ + '$INSTDIR\\openjk_sp.x86.exe' \\ + '' \\ + 'C:\\Users\\Chris\\OneDrive\\Mod Tools and Stuff\\Jedi Academy\\DP Code\\OpenJK\\code\\win32\\starwars.ico'") +SET(CPACK_NSIS_DELETE_ICONS_EXTRA " + Delete '$SMPROGRAMS\\$MUI_TEMP\\Jedi Academy SP.lnk'") +SET(CPACK_NSIS_DISPLAY_NAME "OpenJK") +SET(CPACK_NSIS_DISPLAY_NAME_SET "TRUE") +SET(CPACK_NSIS_INSTALLER_ICON_CODE "") +SET(CPACK_NSIS_INSTALLER_MUI_ICON_CODE "") +SET(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES") +SET(CPACK_NSIS_MUI_ICON "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/shared/icons/icon.ico") +SET(CPACK_NSIS_MUI_UNIICON "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/shared/icons/icon.ico") +SET(CPACK_NSIS_PACKAGE_NAME "OpenJK") +SET(CPACK_NSIS_URL_INFO_ABOUT "http://openjk.org") +SET(CPACK_OUTPUT_CONFIG_FILE "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/CPackConfig.cmake") +SET(CPACK_PACKAGE_DEFAULT_LOCATION "/") +SET(CPACK_PACKAGE_DESCRIPTION_FILE "C:/Program Files/CMake/share/cmake-3.10/Templates/CPack.GenericDescription.txt") +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "An improved Jedi Academy") +SET(CPACK_PACKAGE_FILE_NAME "OpenJK-1.0.0-Source") +SET(CPACK_PACKAGE_INSTALL_DIRECTORY "OpenJK") +SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "OpenJK") +SET(CPACK_PACKAGE_NAME "OpenJK") +SET(CPACK_PACKAGE_RELOCATABLE "true") +SET(CPACK_PACKAGE_VENDOR "JACoders") +SET(CPACK_PACKAGE_VERSION "1.0.0") +SET(CPACK_PACKAGE_VERSION_MAJOR "1") +SET(CPACK_PACKAGE_VERSION_MINOR "0") +SET(CPACK_PACKAGE_VERSION_PATCH "0") +SET(CPACK_RESOURCE_FILE_LICENSE "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/LICENSE.txt") +SET(CPACK_RESOURCE_FILE_README "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/README.md") +SET(CPACK_RESOURCE_FILE_WELCOME "C:/Program Files/CMake/share/cmake-3.10/Templates/CPack.GenericWelcome.txt") +SET(CPACK_RPM_PACKAGE_SOURCES "ON") +SET(CPACK_SET_DESTDIR "OFF") +SET(CPACK_SOURCE_7Z "ON") +SET(CPACK_SOURCE_CYGWIN "") +SET(CPACK_SOURCE_GENERATOR "7Z;ZIP") +SET(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\.svn/;/\\.bzr/;/\\.hg/;/\\.git/;\\.swp\$;\\.#;/#") +SET(CPACK_SOURCE_INSTALLED_DIRECTORIES "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK;/") +SET(CPACK_SOURCE_OUTPUT_CONFIG_FILE "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/CPackSourceConfig.cmake") +SET(CPACK_SOURCE_PACKAGE_FILE_NAME "OpenJK-1.0.0-Source") +SET(CPACK_SOURCE_RPM "") +SET(CPACK_SOURCE_TBZ2 "") +SET(CPACK_SOURCE_TGZ "") +SET(CPACK_SOURCE_TOPLEVEL_TAG "win32-Source") +SET(CPACK_SOURCE_TXZ "") +SET(CPACK_SOURCE_TZ "") +SET(CPACK_SOURCE_ZIP "ON") +SET(CPACK_STRIP_FILES "") +SET(CPACK_SYSTEM_NAME "win32") +SET(CPACK_TOPLEVEL_TAG "win32-Source") +SET(CPACK_WIX_SIZEOF_VOID_P "4") + +if(NOT CPACK_PROPERTIES_FILE) + set(CPACK_PROPERTIES_FILE "C:/Users/Chris/OneDrive/Mod Tools and Stuff/Jedi Academy/DP Code/OpenJK/CPackProperties.cmake") +endif() + +if(EXISTS ${CPACK_PROPERTIES_FILE}) + include(${CPACK_PROPERTIES_FILE}) +endif() diff --git a/CreateVisualStudio2010Projects.bat b/CreateVisualStudio2010Projects.bat deleted file mode 100644 index 724cf54db0..0000000000 --- a/CreateVisualStudio2010Projects.bat +++ /dev/null @@ -1,18 +0,0 @@ -@REM Create OpenJK projects for Visual Studio 2010 using CMake -@echo off -for %%X in (cmake.exe) do (set FOUND=%%~$PATH:X) -if not defined FOUND ( - echo CMake was not found on your system. Please make sure you have installed CMake - echo from http://www.cmake.org/ and cmake.exe is installed to your system's PATH - echo environment variable. - echo. - pause - exit /b 1 -) else ( - echo Found CMake! -) -if not exist build\nul (mkdir build) -pushd build -cmake -G "Visual Studio 10" -D CMAKE_INSTALL_PREFIX=../install .. -popd -pause \ No newline at end of file diff --git a/CreateVisualStudio2012Projects.bat b/CreateVisualStudio2012Projects.bat deleted file mode 100644 index 23cf8b0bb4..0000000000 --- a/CreateVisualStudio2012Projects.bat +++ /dev/null @@ -1,18 +0,0 @@ -@REM Create OpenJK projects for Visual Studio 2012 using CMake -@echo off -for %%X in (cmake.exe) do (set FOUND=%%~$PATH:X) -if not defined FOUND ( - echo CMake was not found on your system. Please make sure you have installed CMake - echo from http://www.cmake.org/ and cmake.exe is installed to your system's PATH - echo environment variable. - echo. - pause - exit /b 1 -) else ( - echo Found CMake! -) -if not exist build\nul (mkdir build) -pushd build -cmake -G "Visual Studio 11" -D CMAKE_INSTALL_PREFIX=../install .. -popd -pause \ No newline at end of file diff --git a/README.md b/README.md index 4990ce715e..0460110607 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,10 @@ -# OpenJK +# Dusty's Patch Readme + +Dusty's Patch is a single player modification for Jedi Academy based off the coding framework of OpenJK. It attempts to polish/improve certain gameplay mechanics such as combat, NPC AI, and balance in Jedi Academy's single player and to make small, usually optional, additions. + + +------------------------------------------------------------------------------------------------------------------------- +# OpenJK's Readme OpenJK is an effort by the JACoders group to maintain and improve the game engines on which the Jedi Academy (JA) and Jedi Outcast (JO) games run on, while maintaining *full backwards compatibility* with the existing games. *This project does not attempt to rebalance or otherwise modify core gameplay*. @@ -37,6 +43,29 @@ Installing and running OpenJK: 1. [Download the latest build](http://builds.openjk.org) for your operating system. 2. Extract the contents of the file into the Jedi Academy `GameData/` folder. For Steam users, this will be in `/steamapps/common/Jedi Academy/GameData`. 3. Run `openjk.x86.exe` (Windows), `openjk.i386` (Linux 32-bit), `openjk.x86_64` (Linux 64-bit) or the `OpenJK` application (OS X), depending on your operating system. + +**Linux Instructions** + +If you do not have a windows partition and need to download the game base. + +1. Download and Install SteamCMD [SteamCMD](https://developer.valvesoftware.com/wiki/SteamCMD#Linux) . +2. Set the download path using steamCMD, force_install_dir . +3. Using SteamCMD Set the platform to windows to download any windows game on steam. @sSteamCmdForcePlatformType "windows" +4. Using SteamCMD download the game, app_update 6020. +5. [Download the latest build](http://builds.openjk.org) for your operating system. +6. Extract the contents of the file into the Jedi Academy `GameData/` folder. For Steam users, this will be in `/steamapps/common/Jedi Academy/GameData`. + + +**OS X Instructions** + +If you have the Mac App Store Version of Jedi Academy, follow these steps to get OpenJK runnning under OS X: + +1. Install [Homebrew](http://brew.sh/) if you don't have it. +2. Open the Terminal app, and enter the command `brew install sdl2`. +3. Extract the contents of the OpenJK DMG ([Download the latest build](http://builds.openjk.org)) into the game directory `/Applications/Star Wars Jedi Knight: Jedi Academy.app/Contents/` +4. Run `openJK.app` or `openJK SP.app` +5. Savegames, Config Files and Log Files are stored in `/Users//Library/Application Support/OpenJK/` + ## For Developers @@ -54,6 +83,11 @@ Installing and running OpenJK: * Change the GAMEVERSION define in codemp/game/g_local.h from "OpenJK" to your project name * If you make a nice change, please consider back-porting to upstream via pull request as described above. This is so everyone benefits without having to reinvent the wheel for every project. +### Deciphering buildbot's output +* Pick the build from the operating system builder you're interested in at the [builders](https://jk.xd.cm/builders) page. +* Click on stdio for the Steps to see the command executed and the result. +* The command is at the very top, the output starts below. + ## Maintainers (in alphabetical order) * Ensiform diff --git a/appveyor.yml b/appveyor.yml index a0e95b7171..ebdc838236 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,72 +1,27 @@ version: 1.0.{build} branches: - except: - - release/1.0 + only: + - master + +os: Visual Studio 2013 -clone_depth: 1 #clone entire repository history if not defined -shallow_clone: true #GitHub specific (ignores clone_depth) +configuration: Release -#do not build on tags -skip_tags: true - -os: Visual Studio 2015 # Windows Server 2012 - -environment: - VisualStudioVersion: "14.0" - CMAKE_GENERATOR: "Visual Studio 14 2015" - matrix: - - CMAKE_BUILD_TYPE: "Debug" - - CMAKE_BUILD_TYPE: "Release" - -matrix: - fast_finish: false #finish build once one of the jobs fails - -platform: - - Win32 - - x64 - -configuration: - #- Debug - - Release - -#scripts that are called at very beginning, before repo cloning -init: - - ps: Update-AppveyorBuild -Version "1.0-git-$($env:appveyor_repo_commit.substring(0,8))" - - cmake --version - - msbuild /version - #- cmake --help #debug: view available generators - - python --version #for tests, todo (try starting the built executables) - -#scripts that run after cloning repository install: - #Add cl.exe in path (for nmake) - #'"C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64' - -#where to clone the git repository to -clone_folder: C:\projects\OpenJK - -#scripts to run before build -before_build: - - cmd: cd %APPVEYOR_BUILD_FOLDER% - #- set #debug: view all environment variables - - cmd: if "%Platform%"=="x64" set "CMAKE_GENERATOR=%CMAKE_GENERATOR% Win64" - - cmd: echo "Generator='%CMAKE_GENERATOR%', CMAKE_BUILD_TYPE='%CMAKE_BUILD_TYPE%'" - - cmd: echo "Platform='%Platform%'" - - cmd: if exist build rmdir /q /s build #remove build dir - - mkdir build - - cmd: cd build - - cmd: cmake -G "%CMAKE_GENERATOR%" "-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%" "%APPVEYOR_BUILD_FOLDER%" - - cmd: ls - #- mkdir ..\nmakebuild - #- cd ..\nmakebuild - #- cmd: cmake -G "NMake Makefiles" "-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%" "%APPVEYOR_BUILD_FOLDER%" - #- cmd: ls - -build: - parallel: true - project: C:\projects\OpenJK\build\OpenJK.sln - verbosity: minimal #quiet|minimal|normal|detailed +- cmd: >- + if not exist build mkdir build + + cd build + + cmake -G "Visual Studio 12" -T v120_xp -DCMAKE_INSTALL_PREFIX=DESTDIR .. + +build_script: +- cmd: cmake --build . --target ALL_BUILD --config Release -#scripts to run after build after_build: + - cmd: cmake --build . --target INSTALL --config Release + - cmd: 7z a jaenhanced-win32.zip DESTDIR/JediAcademy/*.exe DESTDIR/JediAcademy/jaenhanced/*.dll DESTDIR/JediAcademy/*.dll + +artifacts: + - path: build/jaenhanced-win32.zip diff --git a/changesfromOpenJK.txt b/changesfromOpenJK.txt new file mode 100644 index 0000000000..8c1f749d62 --- /dev/null +++ b/changesfromOpenJK.txt @@ -0,0 +1,147 @@ +Changes from base JA/OpenJK... + +v1.2 latest changes: +- g_playerCheatPowers should work with saber off now +- g_debugmelee at -1 prevents player from using any advanced melee abilities +- g_moonJump cvar added +- working on cultist AI, boba fett AI, grenadier AI, and melee AI +- working on multi-weapon support +- working on NPCs switching to melee when deciding to surrender +- bug with rocket launcher damage should be fixed +- version, dll, and game folder structure altered +- frame rates should be better (use release build) +- fix bug with saber deflect aiming (revert g_missile.cpp) +- fix bug with boba firing slowly (use release build?) +- fix bug with npc health/armor + + +New Cvars +------------------------------------------------------------------------------------------------------------------------------ +g_autoRoll - default=1, if 0 hold Use to roll +g_char_forcePowerMax - default=100, controls total player FP, requires level restart +g_char_forceRegen - default=100ms, controls player FP regen rate, requires level restart +g_char_parryBonus - default=0, applied to player without needing a custom saber, requires level restart +g_char_breakParryBonus - default=0, applied to player without needing a custom saber, requires level restart +g_handicap - goes up to 200 now +g_saberDamageScale - scales saber damage, and is saved with your savegame, stacks with g_saberMoreRealistic. +g_saberDeflectAutoAim - if 0, blaster deflections must be aimed with crosshair, easier to aim with better Saber Defense +g_saberForceDrains - if 1 special moves drain the amount of FP of the cvar below, katas not affected +g_saberForceDrainAmount - if above cvar is 1, special moves drain this amount of FP, katas not affected +g_saberLockSuperBreaks - if 0 no super breaks (1-hit KOs) ever happen after saber locks +g_saberMoreRealistic - no longer write-protected +g_weaponVelocity, g_weaponAltVelocity - multiply the speed of most player and NPC weapon projectiles + +Cheat Codes +------------------------------------------------------------------------------------------------------------------------------ +- g_playerCheatPowers: +If 1, player can auto-push explosives, always dodges sniper shots, has increased jumping ability and resistance to force powers. +If 2, all previous effects plus FP usage is cut in half. +If 3, unlimited fp and take no fall damage +Set when before you load your game to get all effects. "iknowkungfu" now toggles g_playerCheatPowers to 1 +- g_moonJump: +flip this on to do some crazy jumping. + +Gameplay mechanics +------------------------------------------------------------------------------------------------------------------------------ +Force powers (activated with g_forceNewPowers 1) +------------------------------------------------------------------------------------------------------------------------------ +- Force Jump: height for all levels increased by 25% +- Force Pull: level 1 pulls weapons only, level 2 can knockdown, level 3 can pull toward you a little bit +- ***Force Sense: gives limited snipershot dodging ability (you might need to hold down Use) +- Force Speed: decreases FP cost of sniper shot dodging at Level 3 (for auto-dodging anyway) +- **Force Protect 1/2/3: gives only 10/20/40% damage reduction against saber attacks instead of 25/50/75% +- Force Heal: costs slightly fewer force points at Level 3 + +NPC AI +------------------------------------------------------------------------------------------------------------------------------ +NPC Jedi changes: +- smarter use of new JA abilities; only use Force Rage if close and have >25% HP, and only kick/use saber katas if close (except CLASS_REBORN, they like to taunt you and show off sometimes when far away) +- Less exploitable; if you disarm their saber throw (rather than just stand there), recover from knockdown faster, and activate their saber more quickly after being gripped/drained (all these effects generally increase with difficulty and NPC rank). Light Jedi don't attack surrendered/unarmed enemies (unless they have a dark side class like CLASS_REBORN, CLASS_DESANN etc.) +- Also, now have their level of acrobatics determined by their move stat; 3 allows rolls and lean dodges, 4 allows cartwheels and flipping out of knockdown, this way you can restrict an NPC to be less acrobatic and more "technical" + +Force-only Cultists: react properly to explosives, sniper shots, and saber throws; *sometimes use quick punches if close, and in general try harder to dodge saber attacks/throws + +CLASS_BOBA, CLASS_MANDA, and CLASS_COMMANDO: (these have NOT been thoroughly tested...) +- only an NPC with "undying 1" or the name boba_fett is invincible now +- CLASS_MANDA acts like CLASS_BOBA but without a jetpack +- CLASS_COMMANDO acts like CLASS_BOBA but without a jetpack or flamethrower + +Multi-weapon system: +- NPCs should now change to the next best weapon they have when you disarm them +- the boba-based classes support most player weapons now, and weapons are chosen randomly (can have more than 1 similar type of weapon) + +Other changes: +- Grenadier Grans will use Melee sometimes even if the player is using a saber +- **NPCs surrender more like JK2 (more consistent animation, once they decide to surrender near player they stay surrendered, but when you leave they will get sneaky) +- Allied NPCs receive 150%/125%/100% HP for Padawan/Jedi/Jedi Knight difficulties now instead of 100%/125%/150% like enemies +- Assassin Droids now fire even with their shields up +- Class_Rodian with E11, and Class_Imperial at commander rank use alt-fire now always (like in JK2) +- fixed a bug: Stormtroopers using Flechette and Repeater can now switch back to alt-fire if they switched to primary fire because the player was too close + + +Saber System (activated with g_saberNewCombat 1) +------------------------------------------------------------------------------------------------------------------------------ +- Saber Offense gives a base "attack strength/AS" (+2/+4/+6) +- Saber Defense gives a base "defense strength/DS" (+2/+4/+6) and a number of defense points (DP;= 1 + SD level) +- AS is modified by saber style (-2/0/+2/-1/+1/-1/+1 for Fast/Med/Strong/Duals/Staff/Tavion/Desann) + +How saber combat then works: +- AS > DS = attack blocked but lose DP equivalent to AS - DS +- all DP lost = guard crushed +- DPs regenerate about 1 per second if walking/standing still +- special attacks ignore DP: either crush guard or are blocked partially + +Other Changes: +- player's defense with Medium and Strong styles is improved (you can deflect slashes and blaster fire more rapidly, but still not as quickly as NPCs) +- Saber Defense 0 no longer makes you invincible to blaster fire, but blaster fire will pass through your saber +- a custom saber with "autoblocking 0" now has narrow block arc for the player (otherwise blocking is ridiculously easy); +holding Use and standing still will allow you to block in this fashion at any time + +Melee changes +-------------------------------------------------------------------------------------------------------------------------------- +- Player can kick with any saber type, use +forcefocus to saberthrow (bind this to a key, maybe your mousewheel click-down) +- Player's melee abilities determined by Saber Offense (1 = punches and kicks, 2 = katas, 3 = spin kicks) +- if you're not cheating (using g_debugMelee or iknowkungfu)... Player melee katas have less range, must be aimed more precisely, and do not work on certain types of enemies +- Spin/Flip kicks do slightly more damage +- Spin Kicks are possible on demand with Use+AltAttack, however costs 10 FP (the auto version doesn't cost any) +- **To kick over a Jedi, must have Saber Offense >= their Saber Defense +- Player punches do 7/5 damage instead of 6/3, and aren't randomized +- **Force Speed punching should be more consistent and less cheap (never did actually test) +- **Heavy/Slow Melee is specifically associated with Chewbacca, CLASS_GRAN, and CLASS_TRANDOSHAN. Other NPC types will punch +more quickly but weakly if given WP_MELEE + +Other Minor Features +---------------------------------------------------------------------------------------------------------------------------- +- visual effect for lightsaber-blocking Force Lightning, .efx file to use determined by "cg_lightningBlockEffect" +- rolls and acrobatics are possible in 1st Person now + +New Modding Features +------------------------------------------------------------------------------------------------------------------------------ +- some new fields you can put in NPC files... + dualPistols 1 -- gives an NPC dual pistols if have WP_BLASTER_PISTOL + cortosis 1 -- cuts damage in half from lightsabers, stacks on top of any other damage reductions + blastArmor 1 -- cuts damage in half from explosives and heavy weapons, stacks on top of any other damage reductions + magPlating 1 -- cuts damage in half from blasters, stacks on top of any other damage reductions + restrictJediPowers 1 -- restricts an NPC from using force powers they don't actually have (force pushing without Push 1 for example) + altFire 1 -- this actually works now + dontFlee 1 -- forces SCF_DONT_FLEE (an icarus script thing) + dontFire 1 -- forces SCF_DONT_FIRE (like how melee cultists don't punch) + rareFire 1 -- same AI as SCF_DONT_FIRE but allows punching sometimes with melee + heavyMelee 1 -- forces heavy melee regardless of class + +- new fields are in the weapons.dat: +1.) velocity, altVelocity: use an integer number (no decimals) +2.) npcDmgMult, npcAltDmgMult, npcFireTimeMult, npcAltFireTimeMult: multipliers that use a decimal (like 0.7, 1.5, 2.3 etc.) +*read the weaponshelp.txt for overview default values for these things are + +Future ideas +---------------------------------------------------------------------------------------------------------------------------- +Things to do - +- *savegame screenshots +- *separate cvar for player HP and Shields +- force push strong version, mindtrick changes +- *camera controls (1st, 2nd, 3rd, 4th person) + + +** not completely tested but should work +*** incomplete/unfinished/not working completely \ No newline at end of file diff --git a/changestodo.txt b/changestodo.txt new file mode 100644 index 0000000000..ee655b4c2d --- /dev/null +++ b/changestodo.txt @@ -0,0 +1,11 @@ +- multiweapon support +- parse .sab fields from NPC files and cvars (player) +- camera control (smart 3rd person, 2nd person) +- finish saber system; test save games, fix specials + +creative stuff: +- jedi stats +- saber throws +- strong force push +- force sense/speed dodging +- npcs run at close range with weapon \ No newline at end of file diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 8c3daf0f84..292bd2a3a4 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -41,7 +41,8 @@ if(BuildSPEngine OR BuildJK2SPEngine) set(SPEngineIncludeDirectories ${SPEngineIncludeDirectories} ${SharedDir} - ${SPDir}) + ${SPDir} + "${GSLIncludeDirectory}") # Dependencies # OpenAL (is optionally included for Windows) @@ -69,22 +70,17 @@ if(BuildSPEngine OR BuildJK2SPEngine) endif() if(UseInternalSDL2) - if(MSVC14) - set(MSVC_SUFFIX "_2015") - else() - set(MSVC_SUFFIX "") - endif() if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(SPEngineLibraries ${SPEngineLibraries} - ${OpenJKLibDir}/SDL2/lib/x86${MSVC_SUFFIX}/SDL2main.lib - ${OpenJKLibDir}/SDL2/lib/x86${MSVC_SUFFIX}/SDL2.lib + ${OpenJKLibDir}/SDL2/lib/x86/SDL2main.lib + ${OpenJKLibDir}/SDL2/lib/x86/SDL2.lib ) else() set(SPEngineLibraries ${SPEngineLibraries} - ${OpenJKLibDir}/SDL2/lib/x64${MSVC_SUFFIX}/SDL2main.lib - ${OpenJKLibDir}/SDL2/lib/x64${MSVC_SUFFIX}/SDL2.lib + ${OpenJKLibDir}/SDL2/lib/x64/SDL2main.lib + ${OpenJKLibDir}/SDL2/lib/x64/SDL2.lib ) endif() @@ -158,10 +154,8 @@ if(BuildSPEngine OR BuildJK2SPEngine) "${SPDir}/qcommon/msg.cpp" "${SPDir}/qcommon/net_chan.cpp" - "${SPDir}/qcommon/q_math.cpp" "${SPDir}/qcommon/q_shared.cpp" "${SPDir}/qcommon/q_shared.h" - "${SPDir}/qcommon/q_platform.h" "${SPDir}/qcommon/sstring.h" "${SPDir}/qcommon/stringed_ingame.cpp" @@ -178,9 +172,27 @@ if(BuildSPEngine OR BuildJK2SPEngine) "${SPDir}/qcommon/stv_version.h" "${SPDir}/qcommon/timing.h" "${SPDir}/qcommon/MiniHeap.h" + + "${SPDir}/qcommon/ojk_i_saved_game.h" + "${SPDir}/qcommon/ojk_saved_game.h" + "${SPDir}/qcommon/ojk_saved_game.cpp" + "${SPDir}/qcommon/ojk_saved_game_helper.h" + "${SPDir}/qcommon/ojk_saved_game_helper_fwd.h" + "${SPDir}/qcommon/ojk_scope_guard.h" + + ${SharedCommonFiles} ) source_group("common" FILES ${SPEngineCommonFiles}) set(SPEngineFiles ${SPEngineFiles} ${SPEngineCommonFiles}) + + set(SPEngineCommonSafeFiles + "${SharedDir}/qcommon/safe/files.cpp" + "${SharedDir}/qcommon/safe/files.h" + ${SharedCommonSafeFiles} + "${SPDir}/qcommon/safe/memory.h" + ) + source_group("common/safe" FILES ${SPEngineCommonSafeFiles}) + set(SPEngineFiles ${SPEngineFiles} ${SPEngineCommonSafeFiles}) # Server files @@ -269,7 +281,8 @@ if(BuildSPEngine OR BuildJK2SPEngine) "${SPDir}/rd-common/tr_public.h" "${SPDir}/rd-common/tr_types.h" ) - + source_group("renderer" FILES ${SPEngineRendererFiles}) + set(SPEngineFiles ${SPEngineFiles} ${SPEngineRendererFiles}) # UI files set(SPEngineUIFiles @@ -308,6 +321,7 @@ if(BuildSPEngine OR BuildJK2SPEngine) if(WIN32) set(SPEngineWin32Files + "${SPDir}/win32/jkenhanced.ico" "${SPDir}/win32/resource.h" "${SPDir}/win32/winquake.rc" ) @@ -358,8 +372,8 @@ if(BuildSPEngine OR BuildJK2SPEngine) # project macro so we can invoke it twice: for jk2 and for ja function(add_sp_project ProjectName Label SPDirName InstallDir Component) if(MakeApplicationBundles) - set_source_files_properties(${SPDirName}/macosx/OpenJK.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - set(SPEngineFiles ${SPEngineFiles} ${SPDirName}/macosx/OpenJK.icns) + set_source_files_properties(${SPDirName}/macosx/JKEnhanced.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + set(SPEngineFiles ${SPEngineFiles} ${SPDirName}/macosx/JKEnhanced.icns) set_source_files_properties(${SPDirName}/macosx/English.lproj/InfoPlist.strings PROPERTIES MACOSX_PACKAGE_LOCATION Resources/English.lproj) set(SPEngineFiles ${SPEngineFiles} ${SPDirName}/macosx/English.lproj/InfoPlist.strings) endif(MakeApplicationBundles) @@ -369,11 +383,11 @@ if(BuildSPEngine OR BuildJK2SPEngine) if(UseInternalSDL2) if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(SPEngineExtraInstallFiles - ${OpenJKLibDir}/SDL2/bin/x86${MSVC_SUFFIX}/SDL2.dll + ${OpenJKLibDir}/SDL2/bin/x86/SDL2.dll ) else() set(SPEngineExtraInstallFiles - ${OpenJKLibDir}/SDL2/bin/x64${MSVC_SUFFIX}/SDL2.dll + ${OpenJKLibDir}/SDL2/bin/x64/SDL2.dll ) endif() endif() diff --git a/code/Ragl/graph_region.h b/code/Ragl/graph_region.h index 5879424c65..42d899c021 100644 --- a/code/Ragl/graph_region.h +++ b/code/Ragl/graph_region.h @@ -421,7 +421,7 @@ class graph_region : public ratl::ratl_base if (mRegionCount) { int RegionEdges = 0; - for (typename TEdges::iterator it=mEdges.begin(); it!=mEdges.end(); it++) + for (typename TEdges::iterator it=mEdges.begin(); it!=mEdges.end(); ++it) { RegionEdges += (*it).size(); } diff --git a/code/Ratl/map_vs.h b/code/Ratl/map_vs.h index 7f525d2e46..0d9b11f9a3 100644 --- a/code/Ratl/map_vs.h +++ b/code/Ratl/map_vs.h @@ -1160,7 +1160,7 @@ class set_base : public tree_base { // fixme, this don't work iterator ubound(this, find_index(key)); - ubound++; + ++ubound; return ubound; } diff --git a/code/cgame/FX_Clone.cpp b/code/cgame/FX_Clone.cpp new file mode 100644 index 0000000000..6a878fe62a --- /dev/null +++ b/code/cgame/FX_Clone.cpp @@ -0,0 +1,109 @@ +/* +=========================================================================== +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +// Clone Weapons + +#include "cg_headers.h" + +#include "cg_local.h" +#include "cg_media.h" +#include "FxScheduler.h" + +/* +------------------------- +FX_CloneBlasterProjectileThink +------------------------- +*/ + +void FX_CloneBlasterProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon ) +{ + vec3_t forward; + + if (cent->currentState.eFlags & EF_USE_ANGLEDELTA) + { + AngleVectors(cent->currentState.angles, forward, 0, 0); + } + else + { + if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f ) + { + if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + { + forward[2] = 1.0f; + } + } + } + + // hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly + int dif = cg.time - cent->gent->s.pos.trTime; + + if ( dif < 75 ) + { + if ( dif < 0 ) + { + dif = 0; + } + + float scale = ( dif / 75.0f ) * 0.95f + 0.05f; + + VectorScale( forward, scale, forward ); + } + + theFxScheduler.PlayEffect( cgs.effects.cloneBlasterShotEffect, cent->lerpOrigin, forward ); +} + +/* +------------------------- +FX_CloneBlasterAltFireThink +------------------------- +*/ +void FX_CloneBlasterAltFireThink( centity_t *cent, const struct weaponInfo_s *weapon ) +{ + FX_CloneBlasterProjectileThink( cent, weapon ); +} + +/* +------------------------- +FX_CloneBlasterWeaponHitWall +------------------------- +*/ +void FX_CloneBlasterWeaponHitWall( vec3_t origin, vec3_t normal ) +{ + theFxScheduler.PlayEffect( cgs.effects.cloneBlasterWallImpactEffect, origin, normal ); +} + +/* +------------------------- +FX_CloneBlasterWeaponHitPlayer +------------------------- +*/ +void FX_CloneBlasterWeaponHitPlayer( gentity_t *hit, vec3_t origin, vec3_t normal, qboolean humanoid ) +{ + //temporary? just testing out the damage skin stuff -rww + if ( hit && hit->client && hit->ghoul2.size() ) + { + CG_AddGhoul2Mark(cgs.media.bdecal_burnmark1, flrand(3.5, 4.0), origin, normal, hit->s.number, + hit->client->ps.origin, hit->client->renderInfo.legsYaw, hit->ghoul2, hit->s.modelScale, Q_irand(10000, 13000)); + } + + theFxScheduler.PlayEffect( cgs.effects.cloneBlasterFleshImpactEffect, origin, normal ); +} diff --git a/code/cgame/FX_Destruction.cpp b/code/cgame/FX_Destruction.cpp new file mode 100644 index 0000000000..c33a91a893 --- /dev/null +++ b/code/cgame/FX_Destruction.cpp @@ -0,0 +1,68 @@ +/* +=========================================================================== +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +// Force Destruction Effects + +#include "cg_headers.h" + +#include "cg_media.h" +#include "FxScheduler.h" + +/* +--------------------------- +FX_DestructionProjectileThink +--------------------------- +*/ + +void FX_DestructionProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon ) +{ + vec3_t forward; + + if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f ) + { + forward[2] = 1.0f; + } + + theFxScheduler.PlayEffect( cgs.effects.destructionProjectile, cent->lerpOrigin, forward ); +} + +/* +--------------------------- +FX_DestructionHitWall +--------------------------- +*/ + +void FX_DestructionHitWall( vec3_t origin, vec3_t normal ) +{ + theFxScheduler.PlayEffect( cgs.effects.destructionHit, origin, normal ); +} + +/* +--------------------------- +FX_DestructionHitPlayer +--------------------------- +*/ + +void FX_DestructionHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid ) +{ + theFxScheduler.PlayEffect( cgs.effects.destructionHit, origin, normal ); +} diff --git a/code/cgame/FxPrimitives.cpp b/code/cgame/FxPrimitives.cpp index 9ef7d90721..74dc874a88 100644 --- a/code/cgame/FxPrimitives.cpp +++ b/code/cgame/FxPrimitives.cpp @@ -106,7 +106,7 @@ void CParticle::Die() vec3_t norm; // Man, this just seems so, like, uncool and stuff... - VectorSet( norm, crandom(), crandom(), crandom()); + VectorSet( norm, Q_flrand(-1.0f, 1.0f), Q_flrand(-1.0f, 1.0f), Q_flrand(-1.0f, 1.0f)); VectorNormalize( norm ); theFxScheduler.PlayEffect( mDeathFxID, mOrigin1, norm ); @@ -307,9 +307,12 @@ bool CParticle::UpdateOrigin() } } - // Hit something - if ( trace.fraction < 1.0f )//|| trace.startsolid || trace.allsolid ) + if ( trace.startsolid || trace.allsolid || trace.fraction == 1.0) { + } + else + { + // Hit something if ( mFlags & FX_IMPACT_RUNS_FX && !(trace.surfaceFlags & SURF_NOIMPACT )) { theFxScheduler.PlayEffect( mImpactFxID, trace.endpos, trace.plane.normal ); @@ -422,7 +425,7 @@ void CParticle::UpdateSize() if (( mFlags & FX_SIZE_RAND )) { // Random simply modulates the existing value - perc1 = random() * perc1; + perc1 = Q_flrand(0.0f, 1.0f) * perc1; } mRefEnt.radius = (mSizeStart * perc1) + (mSizeEnd * (1.0f - perc1)); @@ -499,7 +502,7 @@ void CParticle::UpdateRGB() if (( mFlags & FX_RGB_RAND )) { // Random simply modulates the existing value - perc1 = random() * perc1; + perc1 = Q_flrand(0.0f, 1.0f) * perc1; } // Now get the correct color @@ -590,7 +593,7 @@ void CParticle::UpdateAlpha() if ( (mFlags & FX_ALPHA_RAND) ) { // Random simply modulates the existing value - perc1 = random() * perc1; + perc1 = Q_flrand(0.0f, 1.0f) * perc1; } if ( mFlags & FX_USE_ALPHA ) @@ -857,7 +860,7 @@ bool CLine::Update() //---------------------------- void CElectricity::Initialize() { - mRefEnt.frame = random() * 1265536; + mRefEnt.frame = Q_flrand(0.0f, 1.0f) * 1265536; mRefEnt.endTime = cg.time + (mTimeEnd - mTimeStart); if ( mFlags & FX_DEPTH_HACK ) @@ -1155,7 +1158,7 @@ void CTail::UpdateLength() if ( mFlags & FX_LENGTH_RAND ) { // Random simply modulates the existing value - perc1 = random() * perc1; + perc1 = Q_flrand(0.0f, 1.0f) * perc1; } mLength = (mLengthStart * perc1) + (mLengthEnd * (1.0f - perc1)); @@ -1267,7 +1270,7 @@ void CCylinder::UpdateSize2() if ( mFlags & FX_SIZE2_RAND ) { // Random simply modulates the existing value - perc1 = random() * perc1; + perc1 = Q_flrand(0.0f, 1.0f) * perc1; } mRefEnt.backlerp = (mSize2Start * perc1) + (mSize2End * (1.0f - perc1)); @@ -1373,7 +1376,7 @@ void CEmitter::Draw() #define TRAIL_RATE 8 // we "think" at about a 60hz rate // Pick a target step distance and square it - step = mDensity + crandom() * mVariance; + step = mDensity + Q_flrand(-1.0f, 1.0f) * mVariance; step *= step; dif = 0; @@ -1436,7 +1439,7 @@ void CEmitter::Draw() if ( DistanceSquared( org, mOldOrigin ) >= step ) { // Pick a new target step distance and square it - step = mDensity + crandom() * mVariance; + step = mDensity + Q_flrand(-1.0f, 1.0f) * mVariance; step *= step; // We met the step criteria so, we should add in the effect @@ -1620,7 +1623,7 @@ void CLight::UpdateSize() if ( mFlags & FX_SIZE_RAND ) { // Random simply modulates the existing value - perc1 = random() * perc1; + perc1 = Q_flrand(0.0f, 1.0f) * perc1; } mRefEnt.radius = (mSizeStart * perc1) + (mSizeEnd * (1.0f - perc1)); @@ -1697,7 +1700,7 @@ void CLight::UpdateRGB() if ( mFlags & FX_RGB_RAND ) { // Random simply modulates the existing value - perc1 = random() * perc1; + perc1 = Q_flrand(0.0f, 1.0f) * perc1; } // Now get the correct color @@ -1890,7 +1893,7 @@ void CPoly::Draw() verts[i].modulate[k] = mRefEnt.shaderRGBA[k]; // Copy the ST coords - Vector2Copy( mST[i], verts[i].st ); + VectorCopy2( mST[i], verts[i].st ); } // Add this poly @@ -2099,7 +2102,7 @@ bool CBezier::Update( void ) } //---------------------------- -inline void CBezier::DrawSegment( vec3_t start, vec3_t end, float texcoord1, float texcoord2 ) +void CBezier::DrawSegment( vec3_t start, vec3_t end, float texcoord1, float texcoord2 ) { vec3_t lineDir, cross, viewDir; static vec3_t lastEnd[2]; diff --git a/code/cgame/FxPrimitives.h b/code/cgame/FxPrimitives.h index 49ad1bb37d..d84185dabc 100644 --- a/code/cgame/FxPrimitives.h +++ b/code/cgame/FxPrimitives.h @@ -388,7 +388,7 @@ class CBezier : public CLine virtual bool Update(); - void DrawSegment( vec3_t start, vec3_t end, float texcoord1, float texcoord2 ); + inline void DrawSegment( vec3_t start, vec3_t end, float texcoord1, float texcoord2 ); inline void SetControlPoints( const vec3_t ctrl1, const vec3_t ctrl2 ) { VectorCopy( ctrl1, mControl1 ); VectorCopy( ctrl2, mControl2 ); } inline void SetControlVel( const vec3_t ctrl1v, const vec3_t ctrl2v ) { VectorCopy( ctrl1v, mControl1Vel ); VectorCopy( ctrl2v, mControl2Vel ); } @@ -590,4 +590,4 @@ class CPoly : public CParticle }; -#endif //FX_PRIMITIVES_H_INC \ No newline at end of file +#endif //FX_PRIMITIVES_H_INC diff --git a/code/cgame/FxScheduler.cpp b/code/cgame/FxScheduler.cpp index adc071b086..5942efd7e2 100644 --- a/code/cgame/FxScheduler.cpp +++ b/code/cgame/FxScheduler.cpp @@ -39,8 +39,9 @@ along with this program; if not, see . #include "../qcommon/q_shared.h" #endif +#include "qcommon/safe/string.h" #include - +#include "qcommon/ojk_saved_game_helper.h" CFxScheduler theFxScheduler; @@ -108,23 +109,40 @@ void CFxScheduler::LoadSave_Read() { Clean(); // need to get rid of old pre-cache handles, or it thinks it has some older effects when it doesn't g_vstrEffectsNeededPerSlot.clear(); // jic - gi.ReadFromSaveGame(INT_ID('F','X','L','E'), (void *) &gLoopedEffectArray, sizeof(gLoopedEffectArray), NULL); + + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.read_chunk( + INT_ID('F', 'X', 'L', 'E'), + ::gLoopedEffectArray); + // // now read in and re-register the effects we need for those structs... // for (int iFX = 0; iFX < MAX_LOOPED_FX; iFX++) { char sFX_Filename[MAX_QPATH]; - gi.ReadFromSaveGame(INT_ID('F','X','F','N'), sFX_Filename, sizeof(sFX_Filename), NULL); + + saved_game.read_chunk( + INT_ID('F', 'X', 'F', 'N'), + sFX_Filename); + g_vstrEffectsNeededPerSlot.push_back( sFX_Filename ); } } void CFxScheduler::LoadSave_Write() { + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + // bsave the data we need... // - gi.AppendToSaveGame(INT_ID('F','X','L','E'), mLoopedEffectArray, sizeof(mLoopedEffectArray)); + saved_game.write_chunk( + INT_ID('F', 'X', 'L', 'E'), + mLoopedEffectArray); + // // then cope with the fact that the mID field in each struct of the array we've just saved will not // necessarily point at the same thing when reloading, so save out the actual fx filename strings they @@ -156,7 +174,9 @@ void CFxScheduler::LoadSave_Write() // write out this string... // - gi.AppendToSaveGame(INT_ID('F','X','F','N'), sFX_Filename, sizeof(sFX_Filename)); + saved_game.write_chunk( + INT_ID('F', 'X', 'F', 'N'), + sFX_Filename); } } @@ -327,9 +347,9 @@ void CFxScheduler::Clean(bool bRemoveTemplates /*= true*/, int idToPreserve /*= while ( itr != mFxSchedule.end() ) { next = itr; - next++; + ++next; - delete *itr; + mScheduledEffectsPool.Free (*itr); mFxSchedule.erase(itr); itr = next; @@ -395,7 +415,7 @@ void CFxScheduler::Clean(bool bRemoveTemplates /*= true*/, int idToPreserve /*= // Return: // int handle to the effect //------------------------------------------------------ -int CFxScheduler::RegisterEffect( const char *file, bool bHasCorrectPath /*= false*/ ) +int CFxScheduler::RegisterEffect( const char *path, bool bHasCorrectPath /*= false*/ ) { // Dealing with file names: // File names can come from two places - the editor, in which case we should use the given @@ -403,93 +423,69 @@ int CFxScheduler::RegisterEffect( const char *file, bool bHasCorrectPath /*= fal // In either case we create a stripped file name to use for naming effects. // - char sfile[MAX_QPATH]; + // FIXME: this could maybe be a cstring_view, if mEffectIDs were to use a transparent comparator, but those were only added in C++14, which we don't support yet (sigh) + char filenameNoExt[MAX_QPATH]; // Get an extension stripped version of the file if (bHasCorrectPath) { - const char *last = file, *p = file; - + // FIXME: this is basically COM_SkipPath, except it also accepts '\\' instead of '/' + const char *last = path, *p = path; while (*p != '\0') { if ((*p == '/') || (*p == '\\')) { last = p + 1; } - p++; } - COM_StripExtension( last, sfile, sizeof(sfile) ); + COM_StripExtension( last, filenameNoExt, sizeof( filenameNoExt ) ); } else { - COM_StripExtension( file, sfile, sizeof(sfile) ); + COM_StripExtension( path, filenameNoExt, sizeof( filenameNoExt ) ); } // see if the specified file is already registered. If it is, just return the id of that file TEffectID::iterator itr; - itr = mEffectIDs.find( sfile ); + itr = mEffectIDs.find( filenameNoExt ); if ( itr != mEffectIDs.end() ) { return (*itr).second; } - CGenericParser2 parser; - int len = 0; - fileHandle_t fh; - char *data; - char temp[MAX_QPATH]; + char correctFilenameBuffer[MAX_QPATH]; const char *pfile; - char *bufParse = 0; - if (bHasCorrectPath) { - pfile = file; + pfile = path; } else { // Add on our extension and prepend the file with the default path - Com_sprintf( temp, sizeof(temp), "%s/%s.efx", FX_FILE_PATH, sfile ); - pfile = temp; - } - - len = theFxHelper.OpenFile( pfile, &fh, FS_READ ); - - if ( len < 0 ) - { - theFxHelper.Print( "RegisterEffect: failed to load: %s\n", pfile ); - return 0; + Com_sprintf( correctFilenameBuffer, sizeof( correctFilenameBuffer ), "%s/%s.efx", FX_FILE_PATH, filenameNoExt ); + pfile = correctFilenameBuffer; } - if (len == 0) + // Let the generic parser process the whole file + CGenericParser2 parser; + if( !parser.Parse( pfile ) ) { - theFxHelper.Print( "RegisterEffect: INVALID file: %s\n", pfile ); - theFxHelper.CloseFile( fh ); - return 0; + if( !parser.ValidFile() ) + { + theFxHelper.Print( "RegisterEffect: INVALID file: %s\n", pfile ); + } + return false; + if( parser.ValidFile() ) + { + return false; + } } - - // Allocate enough space to hold the file - // This should be flagged temp, but it seems ok as is. - data = new char[len+1]; - - // Get the goods and ensure Null termination - theFxHelper.ReadFile( data, len, fh ); - data[len] = '\0'; - bufParse = data; - - // Let the generic parser process the whole file - parser.Parse( &bufParse ); - - theFxHelper.CloseFile( fh ); - - // Delete our temp copy of the file - delete [] data; - // Lets convert the effect file into something that we can work with - return ParseEffect( sfile, parser.GetBaseParseGroup() ); + return ParseEffect( filenameNoExt, parser.GetBaseParseGroup() ); } @@ -507,34 +503,10 @@ int CFxScheduler::RegisterEffect( const char *file, bool bHasCorrectPath /*= fal // int handle of the effect //------------------------------------------------------ -struct primitiveType_s { const char *name; EPrimType type; } primitiveTypes[] = { - { "particle", Particle }, - { "line", Line }, - { "tail", Tail }, - { "sound", Sound }, - { "cylinder", Cylinder }, - { "electricity", Electricity }, - { "emitter", Emitter }, - { "decal", Decal }, - { "orientedparticle", OrientedParticle }, - { "fxrunner", FxRunner }, - { "light", Light }, - { "cameraShake", CameraShake }, - { "flash", ScreenFlash }, -}; -static const size_t numPrimitiveTypes = ARRAY_LEN( primitiveTypes ); - -int CFxScheduler::ParseEffect( const char *file, CGPGroup *base ) +int CFxScheduler::ParseEffect( const char *file, const CGPGroup& base ) { - CGPGroup *primitiveGroup; - CPrimitiveTemplate *prim; - const char *grpName; - SEffectTemplate *effect = 0; - EPrimType type; - int handle; - CGPValue *pair; - - effect = GetNewEffectTemplate( &handle, file ); + int handle; + SEffectTemplate* effect = GetNewEffectTemplate( &handle, file ); if ( !handle || !effect ) { @@ -542,45 +514,42 @@ int CFxScheduler::ParseEffect( const char *file, CGPGroup *base ) return 0; } - if ((pair = base->GetPairs())!=0) + for( auto& property : base.GetProperties() ) { - grpName = pair->GetName(); - if ( !Q_stricmp( grpName, "repeatDelay" )) + if( Q::stricmp( property.GetName(), CSTRING_VIEW( "repeatDelay" ) ) == Q::Ordering::EQ ) { - effect->mRepeatDelay = atoi(pair->GetTopValue()); - } - else - {//unknown - + effect->mRepeatDelay = Q::svtoi( property.GetTopValue() ); } } - primitiveGroup = base->GetSubGroups(); - - while ( primitiveGroup ) + for( const auto& primitiveGroup : base.GetSubGroups() ) { - grpName = primitiveGroup->GetName(); - - type = None; - for ( size_t i=0; i primitiveTypes{ + { CSTRING_VIEW( "particle" ), Particle }, + { CSTRING_VIEW( "line" ), Line }, + { CSTRING_VIEW( "tail" ), Tail }, + { CSTRING_VIEW( "sound" ), Sound }, + { CSTRING_VIEW( "cylinder" ), Cylinder }, + { CSTRING_VIEW( "electricity" ), Electricity }, + { CSTRING_VIEW( "emitter" ), Emitter }, + { CSTRING_VIEW( "decal" ), Decal }, + { CSTRING_VIEW( "orientedparticle" ), OrientedParticle }, + { CSTRING_VIEW( "fxrunner" ), FxRunner }, + { CSTRING_VIEW( "light" ), Light }, + { CSTRING_VIEW( "cameraShake" ), CameraShake }, + { CSTRING_VIEW( "flash" ), ScreenFlash } + }; + auto pos = primitiveTypes.find( primitiveGroup.GetName() ); + if( pos != primitiveTypes.end() ) { - prim = new CPrimitiveTemplate; + CPrimitiveTemplate *prim = new CPrimitiveTemplate; - prim->mType = type; + prim->mType = pos->second; prim->ParsePrimitive( primitiveGroup ); // Add our primitive template to the effect list AddPrimitiveToEffect( effect, prim ); } - - primitiveGroup = (CGPGroup *)primitiveGroup->GetNext(); } return handle; @@ -654,7 +623,7 @@ SEffectTemplate *CFxScheduler::GetNewEffectTemplate( int *id, const char *file ) theFxHelper.Print( "FxScheduler: Error--reached max effects\n" ); *id = 0; - return 0; + return nullptr; } //------------------------------------------------------ @@ -880,7 +849,6 @@ void CFxScheduler::PlayEffect( const char *file, int clientID, bool isPortal ) CPrimitiveTemplate *prim; int i = 0; int count = 0, delay = 0; - SScheduledEffect *sfx; float factor = 0.0f; if ( id < 1 || id >= FX_MAX_EFFECTS || !mEffectTemplates[id].mInUse ) @@ -933,9 +901,14 @@ void CFxScheduler::PlayEffect( const char *file, int clientID, bool isPortal ) } else { - // We have to create a new scheduled effect so that we can create it at a later point - // you should avoid this because it's much more expensive - sfx = new SScheduledEffect; + SScheduledEffect *sfx = mScheduledEffectsPool.Alloc(); + + if ( sfx == NULL ) + { + Com_Error (ERR_DROP, "ERROR: Failed to allocate EFX from memory pool."); + return; + } + sfx->mStartTime = theFxHelper.mTime + delay; sfx->mpTemplate = prim; sfx->mClientID = clientID; @@ -992,7 +965,7 @@ void CFxScheduler::CreateEffect( CPrimitiveTemplate *fx, int clientID, int delay // handle RGB color if ( fx->mSpawnFlags & FX_RGB_COMPONENT_INTERP ) { - float perc = random(); + float perc = Q_flrand(0.0f, 1.0f); VectorSet( sRGB, fx->mRedStart.GetVal( perc ), fx->mGreenStart.GetVal( perc ), fx->mBlueStart.GetVal( perc ) ); VectorSet( eRGB, fx->mRedEnd.GetVal( perc ), fx->mGreenEnd.GetVal( perc ), fx->mBlueEnd.GetVal( perc ) ); @@ -1240,10 +1213,14 @@ void CFxScheduler::PlayEffect( int id, vec3_t origin, vec3_t axis[3], const int } else { - // We have to create a new scheduled effect so that we can create it at a later point - // you should avoid this because it's much more expensive - SScheduledEffect *sfx; - sfx = new SScheduledEffect; + SScheduledEffect *sfx = mScheduledEffectsPool.Alloc(); + + if ( sfx == NULL ) + { + Com_Error (ERR_DROP, "ERROR: Failed to allocate EFX from memory pool."); + return; + } + sfx->mStartTime = theFxHelper.mTime + delay; sfx->mpTemplate = prim; sfx->mClientID = -1; @@ -1390,85 +1367,80 @@ void CFxScheduler::AddScheduledEffects( bool portal ) AddLoopedEffects(); } - itr = mFxSchedule.begin(); - - while ( itr != mFxSchedule.end() ) + for ( itr = mFxSchedule.begin(); itr != mFxSchedule.end(); /* do nothing */ ) { - next = itr; - next++; + SScheduledEffect *effect = *itr; - if (portal == (*itr)->mPortalEffect) + if (portal == effect->mPortalEffect && effect->mStartTime <= theFxHelper.mTime ) { - if ( *(*itr) <= theFxHelper.mTime ) + if ( effect->mClientID >= 0 ) { - if ( (*itr)->mClientID >= 0 ) + CreateEffect( effect->mpTemplate, effect->mClientID, + theFxHelper.mTime - effect->mStartTime ); + } + else if (effect->mBoltNum == -1) + {// normal effect + if ( effect->mEntNum != -1 ) // -1 { - CreateEffect( (*itr)->mpTemplate, (*itr)->mClientID, - theFxHelper.mTime - (*itr)->mStartTime ); - } - else if ((*itr)->mBoltNum == -1) - {// normal effect - if ( (*itr)->mEntNum != -1 ) - { - // Find out where the entity currently is - CreateEffect( (*itr)->mpTemplate, - cg_entities[(*itr)->mEntNum].lerpOrigin, (*itr)->mAxis, - theFxHelper.mTime - (*itr)->mStartTime ); - } - else - { - CreateEffect( (*itr)->mpTemplate, - (*itr)->mOrigin, (*itr)->mAxis, - theFxHelper.mTime - (*itr)->mStartTime ); - } + // Find out where the entity currently is + CreateEffect( effect->mpTemplate, + cg_entities[effect->mEntNum].lerpOrigin, effect->mAxis, + theFxHelper.mTime - (*itr)->mStartTime ); } else - { //bolted on effect - // do we need to go and re-get the bolt matrix again? Since it takes time lets try to do it only once - if (((*itr)->mModelNum != oldModelNum) || ((*itr)->mEntNum != oldEntNum) || ((*itr)->mBoltNum != oldBoltIndex)) + { + CreateEffect( effect->mpTemplate, + effect->mOrigin, effect->mAxis, + theFxHelper.mTime - effect->mStartTime ); + } + } + else + { //bolted on effect + // do we need to go and re-get the bolt matrix again? Since it takes time lets try to do it only once + if ((effect->mModelNum != oldModelNum) || (effect->mEntNum != oldEntNum) || (effect->mBoltNum != oldBoltIndex)) + { + const centity_t ¢ = cg_entities[effect->mEntNum]; + if (cent.gent->ghoul2.IsValid()) { - const centity_t ¢ = cg_entities[(*itr)->mEntNum]; - if (cent.gent->ghoul2.IsValid()) + if (effect->mModelNum>=0&&effect->mModelNumghoul2.size()) { - if ((*itr)->mModelNum>=0&&(*itr)->mModelNumghoul2.size()) + if (cent.gent->ghoul2[effect->mModelNum].mModelindex>=0) { - if (cent.gent->ghoul2[(*itr)->mModelNum].mModelindex>=0) - { - doesBoltExist = theFxHelper.GetOriginAxisFromBolt(cent, (*itr)->mModelNum, (*itr)->mBoltNum, origin, axis); - } + doesBoltExist = (qboolean)(theFxHelper.GetOriginAxisFromBolt(cent, effect->mModelNum, effect->mBoltNum, origin, axis) != 0); } } - - oldModelNum = (*itr)->mModelNum; - oldEntNum = (*itr)->mEntNum; - oldBoltIndex = (*itr)->mBoltNum; } - // only do this if we found the bolt - if (doesBoltExist) + oldModelNum = effect->mModelNum; + oldEntNum = effect->mEntNum; + oldBoltIndex = effect->mBoltNum; + } + + // only do this if we found the bolt + if (doesBoltExist) + { + if (effect->mIsRelative ) { - if ((*itr)->mIsRelative ) - { - CreateEffect( (*itr)->mpTemplate, - vec3_origin, axis, - 0, (*itr)->mEntNum, (*itr)->mModelNum, (*itr)->mBoltNum ); - } - else - { - CreateEffect( (*itr)->mpTemplate, - origin, axis, - theFxHelper.mTime - (*itr)->mStartTime ); - } + CreateEffect( effect->mpTemplate, + vec3_origin, axis, + 0, effect->mEntNum, effect->mModelNum, effect->mBoltNum ); + } + else + { + CreateEffect( effect->mpTemplate, + origin, axis, + theFxHelper.mTime - effect->mStartTime ); } } - - // Get 'em out of there. - delete *itr; - mFxSchedule.erase(itr); } - } - itr = next; + mScheduledEffectsPool.Free( effect ); + itr = mFxSchedule.erase( itr ); + } + else + { + ++itr; + } } // Add all active effects into the scene @@ -1511,7 +1483,7 @@ void CFxScheduler::CreateEffect( CPrimitiveTemplate *fx, const vec3_t origin, ve if( fx->mSpawnFlags & FX_RAND_ROT_AROUND_FWD ) { - RotatePointAroundVector( ax[1], ax[0], axis[1], random()*360.0f ); + RotatePointAroundVector( ax[1], ax[0], axis[1], Q_flrand(0.0f, 1.0f)*360.0f ); CrossProduct( ax[0], ax[1], ax[2] ); } @@ -1538,8 +1510,8 @@ void CFxScheduler::CreateEffect( CPrimitiveTemplate *fx, const vec3_t origin, ve float x, y; float width, height; - x = DEG2RAD( random() * 360.0f ); - y = DEG2RAD( random() * 180.0f ); + x = DEG2RAD( Q_flrand(0.0f, 1.0f) * 360.0f ); + y = DEG2RAD( Q_flrand(0.0f, 1.0f) * 180.0f ); width = fx->mRadius.GetVal(); height = fx->mHeight.GetVal(); @@ -1561,8 +1533,8 @@ void CFxScheduler::CreateEffect( CPrimitiveTemplate *fx, const vec3_t origin, ve // set up our point, then rotate around the current direction to. Make unrotated cylinder centered around 0,0,0 VectorScale( ax[1], fx->mRadius.GetVal(), pt ); - VectorMA( pt, crandom() * 0.5f * fx->mHeight.GetVal(), ax[0], pt ); - RotatePointAroundVector( temp, ax[0], pt, random() * 360.0f ); + VectorMA( pt, Q_flrand(-1.0f, 1.0f) * 0.5f * fx->mHeight.GetVal(), ax[0], pt ); + RotatePointAroundVector( temp, ax[0], pt, Q_flrand(0.0f, 1.0f) * 360.0f ); VectorAdd( org, temp, org ); @@ -1703,7 +1675,7 @@ void CFxScheduler::CreateEffect( CPrimitiveTemplate *fx, const vec3_t origin, ve { if ( fx->mSpawnFlags & FX_RGB_COMPONENT_INTERP ) { - float perc = random(); + float perc = Q_flrand(0.0f, 1.0f); VectorSet( sRGB, fx->mRedStart.GetVal( perc ), fx->mGreenStart.GetVal( perc ), fx->mBlueStart.GetVal( perc ) ); VectorSet( eRGB, fx->mRedEnd.GetVal( perc ), fx->mGreenEnd.GetVal( perc ), fx->mBlueEnd.GetVal( perc ) ); diff --git a/code/cgame/FxScheduler.h b/code/cgame/FxScheduler.h index 3ecb386938..7bf78398e7 100644 --- a/code/cgame/FxScheduler.h +++ b/code/cgame/FxScheduler.h @@ -28,6 +28,11 @@ along with this program; if not, see . #include "../qcommon/sstring.h" typedef sstring_t fxString_t; +#include "../game/genericparser2.h" +#include "qcommon/safe/string.h" + +#include + #ifndef FX_SCHEDULER_H_INC #define FX_SCHEDULER_H_INC @@ -270,88 +275,95 @@ class CPrimitiveTemplate CFxRange mElasticity; +private: // Lower level parsing utilities - bool ParseVector( const char *val, vec3_t min, vec3_t max ); - bool ParseFloat( const char *val, float *min, float *max ); - bool ParseGroupFlags( const char *val, int *flags ); + bool ParseVector( const gsl::cstring_view& val, vec3_t min, vec3_t max ); + bool ParseFloat( const gsl::cstring_view& val, float& min, float& max ); + bool ParseGroupFlags( const gsl::cstring_view& val, int& flags ); // Base key processing // Note that these all have their own parse functions in case it becomes important to do certain kinds // of validation specific to that type. - bool ParseMin( const char *val ); - bool ParseMax( const char *val ); - bool ParseDelay( const char *val ); - bool ParseCount( const char *val ); - bool ParseLife( const char *val ); - bool ParseElasticity( const char *val ); - bool ParseFlags( const char *val ); - bool ParseSpawnFlags( const char *val ); - - bool ParseOrigin1( const char *val ); - bool ParseOrigin2( const char *val ); - bool ParseRadius( const char *val ); - bool ParseHeight( const char *val ); - bool ParseWindModifier( const char *val ); - bool ParseRotation( const char *val ); - bool ParseRotationDelta( const char *val ); - bool ParseAngle( const char *val ); - bool ParseAngleDelta( const char *val ); - bool ParseVelocity( const char *val ); - bool ParseAcceleration( const char *val ); - bool ParseGravity( const char *val ); - bool ParseDensity( const char *val ); - bool ParseVariance( const char *val ); - + bool ParseMin( const gsl::cstring_view& val ); + bool ParseMax( const gsl::cstring_view& val ); + bool ParseDelay( const gsl::cstring_view& val ); + bool ParseCount( const gsl::cstring_view& val ); + bool ParseLife( const gsl::cstring_view& val ); + bool ParseElasticity( const gsl::cstring_view& val ); + bool ParseFlags( const gsl::cstring_view& val ); + bool ParseSpawnFlags( const gsl::cstring_view& val ); + + bool ParseOrigin1( const gsl::cstring_view& val ); + bool ParseOrigin2( const gsl::cstring_view& val ); + bool ParseRadius( const gsl::cstring_view& val ); + bool ParseHeight( const gsl::cstring_view& val ); + bool ParseWindModifier( const gsl::cstring_view& val ); + bool ParseRotation( const gsl::cstring_view& val ); + bool ParseRotationDelta( const gsl::cstring_view& val ); + bool ParseAngle( const gsl::cstring_view& val ); + bool ParseAngleDelta( const gsl::cstring_view& val ); + bool ParseVelocity( const gsl::cstring_view& val ); + bool ParseAcceleration( const gsl::cstring_view& val ); + bool ParseGravity( const gsl::cstring_view& val ); + bool ParseDensity( const gsl::cstring_view& val ); + bool ParseVariance( const gsl::cstring_view& val ); + + /// Case insensitive map from cstring_view to Value + template< typename Value > + using StringViewIMap = std::map< gsl::cstring_view, Value, Q::CStringViewILess >; + using ParseMethod = bool ( CPrimitiveTemplate::* )( const gsl::cstring_view& ); // Group type processing - bool ParseRGB( CGPGroup *grp ); - bool ParseAlpha( CGPGroup *grp ); - bool ParseSize( CGPGroup *grp ); - bool ParseSize2( CGPGroup *grp ); - bool ParseLength( CGPGroup *grp ); - - bool ParseModels( CGPValue *grp ); - bool ParseShaders( CGPValue *grp ); - bool ParseSounds( CGPValue *grp ); - - bool ParseImpactFxStrings( CGPValue *grp ); - bool ParseDeathFxStrings( CGPValue *grp ); - bool ParseEmitterFxStrings( CGPValue *grp ); - bool ParsePlayFxStrings( CGPValue *grp ); + bool ParseGroup( const CGPGroup& grp, const StringViewIMap< ParseMethod >& parseMethods, gsl::czstring name ); + bool ParseRGB( const CGPGroup& grp ); + bool ParseAlpha( const CGPGroup& grp ); + bool ParseSize( const CGPGroup& grp ); + bool ParseSize2( const CGPGroup& grp ); + bool ParseLength( const CGPGroup& grp ); + + bool ParseModels( const CGPProperty& grp ); + bool ParseShaders( const CGPProperty& grp ); + bool ParseSounds( const CGPProperty& grp ); + + bool ParseImpactFxStrings( const CGPProperty& grp ); + bool ParseDeathFxStrings( const CGPProperty& grp ); + bool ParseEmitterFxStrings( const CGPProperty& grp ); + bool ParsePlayFxStrings( const CGPProperty& grp ); // Group keys - bool ParseRGBStart( const char *val ); - bool ParseRGBEnd( const char *val ); - bool ParseRGBParm( const char *val ); - bool ParseRGBFlags( const char *val ); + bool ParseRGBStart( const gsl::cstring_view& val ); + bool ParseRGBEnd( const gsl::cstring_view& val ); + bool ParseRGBParm( const gsl::cstring_view& val ); + bool ParseRGBFlags( const gsl::cstring_view& val ); - bool ParseAlphaStart( const char *val ); - bool ParseAlphaEnd( const char *val ); - bool ParseAlphaParm( const char *val ); - bool ParseAlphaFlags( const char *val ); + bool ParseAlphaStart( const gsl::cstring_view& val ); + bool ParseAlphaEnd( const gsl::cstring_view& val ); + bool ParseAlphaParm( const gsl::cstring_view& val ); + bool ParseAlphaFlags( const gsl::cstring_view& val ); - bool ParseSizeStart( const char *val ); - bool ParseSizeEnd( const char *val ); - bool ParseSizeParm( const char *val ); - bool ParseSizeFlags( const char *val ); + bool ParseSizeStart( const gsl::cstring_view& val ); + bool ParseSizeEnd( const gsl::cstring_view& val ); + bool ParseSizeParm( const gsl::cstring_view& val ); + bool ParseSizeFlags( const gsl::cstring_view& val ); - bool ParseSize2Start( const char *val ); - bool ParseSize2End( const char *val ); - bool ParseSize2Parm( const char *val ); - bool ParseSize2Flags( const char *val ); + bool ParseSize2Start( const gsl::cstring_view& val ); + bool ParseSize2End( const gsl::cstring_view& val ); + bool ParseSize2Parm( const gsl::cstring_view& val ); + bool ParseSize2Flags( const gsl::cstring_view& val ); - bool ParseLengthStart( const char *val ); - bool ParseLengthEnd( const char *val ); - bool ParseLengthParm( const char *val ); - bool ParseLengthFlags( const char *val ); + bool ParseLengthStart( const gsl::cstring_view& val ); + bool ParseLengthEnd( const gsl::cstring_view& val ); + bool ParseLengthParm( const gsl::cstring_view& val ); + bool ParseLengthFlags( const gsl::cstring_view& val ); public: CPrimitiveTemplate(); + CPrimitiveTemplate( const CPrimitiveTemplate& rhs ); ~CPrimitiveTemplate() {}; - bool ParsePrimitive( CGPGroup *grp ); + bool ParsePrimitive( const CGPGroup& grp ); void operator=(const CPrimitiveTemplate &that); }; @@ -376,7 +388,180 @@ struct SEffectTemplate void operator=(const SEffectTemplate &that); }; +template +class PoolAllocator +{ +public: + PoolAllocator() + : pool (new T[N]) + , freeAndAllocated (new int[N]) + , numFree (N) + , highWatermark (0) + { + for ( int i = 0; i < N; i++ ) + { + freeAndAllocated[i] = i; + } + } + + T *Alloc() + { + if ( numFree == 0 ) + { + return NULL; + } + + T *ptr = new (&pool[freeAndAllocated[0]]) T; + + std::rotate (freeAndAllocated, freeAndAllocated + 1, freeAndAllocated + N); + numFree--; + highWatermark = Q_max(highWatermark, N - numFree); + + return ptr; + } + + void TransferTo ( PoolAllocator& allocator ) + { + allocator.freeAndAllocated = freeAndAllocated; + allocator.highWatermark = highWatermark; + allocator.numFree = numFree; + allocator.pool = pool; + + highWatermark = 0; + numFree = N; + freeAndAllocated = NULL; + pool = NULL; + } + + bool OwnsPtr ( const T *ptr ) const + { + return ptr >= pool && ptr < (pool + N); + } + + void Free ( T *ptr ) + { + for ( int i = numFree; i < N; i++ ) + { + T *p = &pool[freeAndAllocated[i]]; + + if ( p == ptr ) + { + if ( i > numFree ) + { + std::rotate (freeAndAllocated + numFree, freeAndAllocated + i, freeAndAllocated + i + 1); + } + + p->~T(); + numFree++; + + break; + } + } + } + + int GetHighWatermark() const { return highWatermark; } + + ~PoolAllocator() + { + for ( int i = numFree; i < N; i++ ) + { + T *p = &pool[freeAndAllocated[i]]; + + p->~T(); + } + + delete [] freeAndAllocated; + delete [] pool; + } + +private: + PoolAllocator ( const PoolAllocator& ); + PoolAllocator& operator = ( const PoolAllocator& ); + + T *pool; + + // The first 'numFree' elements are the indexes of the free slots. + // The remaining elements are the indexes of the allocated slots. + int *freeAndAllocated; + int numFree; + + int highWatermark; +}; + +template +class PagedPoolAllocator +{ + public: + PagedPoolAllocator () + : numPages (1) + , pages (new PoolAllocator[1]()) + { + } + + T *Alloc () + { + T *ptr = NULL; + for ( int i = 0; i < numPages && ptr == NULL; i++ ) + { + ptr = pages[i].Alloc (); + } + + if ( ptr == NULL ) + { + PoolAllocator *newPages = new PoolAllocator[numPages + 1] (); + for ( int i = 0; i < numPages; i++ ) + { + pages[i].TransferTo (newPages[i]); + } + + delete[] pages; + pages = newPages; + + ptr = pages[numPages].Alloc (); + if ( ptr == NULL ) + { + return NULL; + } + + numPages++; + } + + return ptr; + } + + void Free ( T *ptr ) + { + for ( int i = 0; i < numPages; i++ ) + { + if ( pages[i].OwnsPtr (ptr) ) + { + pages[i].Free (ptr); + break; + } + } + } + + int GetHighWatermark () const + { + int total = 0; + for ( int i = 0; i < numPages; i++ ) + { + total += pages[i].GetHighWatermark (); + } + + return total; + } + + ~PagedPoolAllocator () + { + delete[] pages; + } + + private: + int numPages; + PoolAllocator *pages; +}; //----------------------------------------------------------------- // @@ -399,6 +584,31 @@ struct SLoopedEffect int mLoopStopTime; //time to die bool mPortalEffect; // rww - render this before skyportals, and not in the normal world view. bool mIsRelative; // bolt this puppy on keep it updated + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(mId); + saved_game.write(mBoltInfo); + saved_game.write(mNextTime); + saved_game.write(mLoopStopTime); + saved_game.write(mPortalEffect); + saved_game.write(mIsRelative); + saved_game.skip(2); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(mId); + saved_game.read(mBoltInfo); + saved_game.read(mNextTime); + saved_game.read(mLoopStopTime); + saved_game.read(mPortalEffect); + saved_game.read(mIsRelative); + saved_game.skip(2); + } }; class CFxScheduler @@ -418,11 +628,6 @@ class CFxScheduler bool mIsRelative; // bolt this puppy on keep it updated vec3_t mOrigin; vec3_t mAxis[3]; - - bool operator <= (const int time) const - { - return mStartTime <= time; - } }; /* Looped Effects get stored and reschedule at mRepeatRate */ @@ -447,12 +652,13 @@ class CFxScheduler // List of scheduled effects that will need to be created at the correct time. TScheduledEffect mFxSchedule; + PagedPoolAllocator mScheduledEffectsPool; // Private function prototypes SEffectTemplate *GetNewEffectTemplate( int *id, const char *file ); void AddPrimitiveToEffect( SEffectTemplate *fx, CPrimitiveTemplate *prim ); - int ParseEffect( const char *file, CGPGroup *base ); + int ParseEffect( const char *file, const CGPGroup& base ); void CreateEffect( CPrimitiveTemplate *fx, const vec3_t origin, vec3_t axis[3], int lateTime, int clientID = -1, int modelNum = -1, int boltNum = -1 ); void CreateEffect( CPrimitiveTemplate *fx, int clientID, int lateTime ); diff --git a/code/cgame/FxSystem.cpp b/code/cgame/FxSystem.cpp index 8a8bf4e90f..6e47129b4a 100644 --- a/code/cgame/FxSystem.cpp +++ b/code/cgame/FxSystem.cpp @@ -136,21 +136,23 @@ void SFxHelper::AddFxToScene( refEntity_t *ent ) } //------------------------------------------------------ -int SFxHelper::RegisterShader( const char *shader ) +int SFxHelper::RegisterShader( const gsl::cstring_view& shader ) { - return cgi_R_RegisterShader( shader ); + // TODO: it would be nice to change the ABI here to allow for passing of string views + return cgi_R_RegisterShader( std::string( shader.begin(), shader.end() ).c_str() ); } //------------------------------------------------------ -int SFxHelper::RegisterSound( const char *sound ) +int SFxHelper::RegisterSound( const gsl::cstring_view& sound ) { - return cgi_S_RegisterSound( sound ); + // TODO: it would be nice to change the ABI here to allow for passing of string views + return cgi_S_RegisterSound( std::string( sound.begin(), sound.end() ).c_str() ); } //------------------------------------------------------ -int SFxHelper::RegisterModel( const char *model ) +int SFxHelper::RegisterModel( const gsl::cstring_view& model ) { - return cgi_R_RegisterModel( model ); + return cgi_R_RegisterModel( std::string( model.begin(), model.end() ).c_str() ); } //------------------------------------------------------ diff --git a/code/cgame/FxSystem.h b/code/cgame/FxSystem.h index c7cd1c5399..3a81749aa1 100644 --- a/code/cgame/FxSystem.h +++ b/code/cgame/FxSystem.h @@ -27,6 +27,8 @@ along with this program; if not, see . #ifndef FX_SYSTEM_H_INC #define FX_SYSTEM_H_INC +#include "qcommon/safe/gsl.h" + #define irand Q_irand #define flrand Q_flrand @@ -34,28 +36,8 @@ along with this program; if not, see . extern vmCvar_t fx_debug; extern vmCvar_t fx_freeze; -inline void Vector2Clear(vec2_t a) -{ - a[0] = 0.0f; - a[1] = 0.0f; -} - -inline void Vector2Set(vec2_t a,float b,float c) -{ - a[0] = b; - a[1] = c; -} - -inline void Vector2Copy(vec2_t src,vec2_t dst) -{ - dst[0] = src[0]; - dst[1] = src[1]; -} - - extern void CG_CalcEntityLerpPositions( centity_t * ); - struct SFxHelper { int mTime; @@ -76,7 +58,7 @@ struct SFxHelper // Sound void PlaySound( const vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ); void PlayLocalSound( sfxHandle_t sfx, int channelNum ); - int RegisterSound( const char *sound ); + int RegisterSound( const gsl::cstring_view& sound ); //G2 int GetOriginAxisFromBolt(const centity_t ¢, int modelNum, int boltNum, vec3_t /*out*/origin, vec3_t /*out*/*axis); @@ -88,8 +70,8 @@ struct SFxHelper void AddFxToScene( refEntity_t *ent ); void AddLightToScene( vec3_t org, float radius, float red, float green, float blue ); - int RegisterShader( const char *shader ); - int RegisterModel( const char *model ); + int RegisterShader( const gsl::cstring_view& shader ); + int RegisterModel( const gsl::cstring_view& model ); void AddPolyToScene( int shader, int count, polyVert_t *verts ); diff --git a/code/cgame/FxTemplate.cpp b/code/cgame/FxTemplate.cpp index 8103da1251..dc8efc6dbc 100644 --- a/code/cgame/FxTemplate.cpp +++ b/code/cgame/FxTemplate.cpp @@ -26,6 +26,11 @@ along with this program; if not, see . #include "FxScheduler.h" #endif +#include "../game/genericparser2.h" +#include "qcommon/safe/string.h" + +#include + //------------------------------------------------------ // CPrimitiveTemplate // Set up our minimal default values @@ -188,16 +193,10 @@ void CPrimitiveTemplate::operator=(const CPrimitiveTemplate &that) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseFloat( const char *val, float *min, float *max ) +bool CPrimitiveTemplate::ParseFloat( const gsl::cstring_view& val, float& min, float& max ) { - // We don't allow passing in a null for either of the fields - if ( min == 0 || max == 0 ) - { // failue - return false; - } - // attempt to read out the values - int v = sscanf( val, "%f %f", min, max ); + int v = Q::sscanf( val, min, max ); if ( v == 0 ) { // nothing was there, failure @@ -205,7 +204,7 @@ bool CPrimitiveTemplate::ParseFloat( const char *val, float *min, float *max ) } else if ( v == 1 ) { // only one field entered, this is ok, but we should copy min into max - *max = *min; + max = min; } return true; @@ -225,16 +224,16 @@ bool CPrimitiveTemplate::ParseFloat( const char *val, float *min, float *max ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseVector( const char *val, vec3_t min, vec3_t max ) +bool CPrimitiveTemplate::ParseVector( const gsl::cstring_view& val, vec3_t min, vec3_t max ) { // we don't allow passing in a null - if ( min == 0 || max == 0 ) + if ( min == nullptr || max == nullptr ) { return false; } // attempt to read out our values - int v = sscanf( val, "%f %f %f %f %f %f", &min[0], &min[1], &min[2], &max[0], &max[1], &max[2] ); + int v = Q::sscanf( val, min[0], min[1], min[2], max[0], max[1], max[2] ); // Check for completeness if ( v < 3 || v == 4 || v == 5 ) @@ -249,6 +248,38 @@ bool CPrimitiveTemplate::ParseVector( const char *val, vec3_t min, vec3_t max ) return true; } +namespace detail +{ + // calls Q::sscanf with the elements of the given array as arguments + + template< std::size_t remaining > + struct ScanStrings + { + template< std::size_t count, typename... Args > + static int call( const gsl::cstring_view& val, std::array< gsl::cstring_view, count >& arr, Args&... args ) + { + return ScanStrings< remaining - 1 >::call( val, arr, arr[ remaining - 1 ], args... ); + } + }; + + template<> + struct ScanStrings< 0 > + { + template< std::size_t count, typename... Args > + static int call( const gsl::cstring_view& val, std::array< gsl::cstring_view, count >& arr, Args&... args ) + { + return Q::sscanf( val, args... ); + } + }; +} + +template< std::size_t count > +static gsl::array_view< gsl::cstring_view > scanStrings( const gsl::cstring_view& val, std::array< gsl::cstring_view, count >& arr ) +{ + int numParsed = detail::ScanStrings< count >::call( val, arr ); + return{ arr.data(), arr.data() + numParsed }; +} + //------------------------------------------------------ // ParseGroupFlags // Group flags are generic in nature, so we can easily @@ -262,53 +293,35 @@ bool CPrimitiveTemplate::ParseVector( const char *val, vec3_t min, vec3_t max ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseGroupFlags( const char *val, int *flags ) +bool CPrimitiveTemplate::ParseGroupFlags( const gsl::cstring_view& val, int& flags ) { - // Must pass in a non-null pointer - if ( flags == 0 ) - { - return false; - } - - char flag[][32] = {"\0","\0","\0","0"}; - bool ok = true; - // For a sub group, really you probably only have one or two flags set - int v = sscanf( val, "%s %s %s %s", flag[0], flag[1], flag[2], flag[3] ); + std::array< gsl::cstring_view, 4 > flag; + + const auto availableFlag = scanStrings( val, flag ); // Clear out the flags field, then convert the flag string to an actual value ( use generic flags ) - *flags = 0; + flags = 0; - for ( int i = 0; i < 4; i++ ) + bool ok = true; + for( auto& cur : availableFlag ) { - if ( i + 1 > v ) - { - return true; - } + static StringViewIMap< int > flagNames{ + { CSTRING_VIEW( "linear" ), FX_LINEAR }, + { CSTRING_VIEW( "nonlinear" ), FX_NONLINEAR }, + { CSTRING_VIEW( "wave" ), FX_WAVE }, + { CSTRING_VIEW( "random" ), FX_RAND }, + { CSTRING_VIEW( "clamp" ), FX_CLAMP }, + }; - if ( !Q_stricmp( flag[i], "linear" )) - { - *flags |= FX_LINEAR; - } - else if ( !Q_stricmp( flag[i], "nonlinear" )) - { - *flags |= FX_NONLINEAR; - } - else if ( !Q_stricmp( flag[i], "wave" )) - { - *flags |= FX_WAVE; - } - else if ( !Q_stricmp( flag[i], "random" )) + auto pos = flagNames.find( cur ); + if( pos == flagNames.end() ) { - *flags |= FX_RAND; - } - else if ( !Q_stricmp( flag[i], "clamp" )) - { - *flags |= FX_CLAMP; + ok = false; } else - { // we have badness going on, but continue on in case there are any valid fields in here - ok = false; + { + flags |= pos->second; } } @@ -325,7 +338,7 @@ bool CPrimitiveTemplate::ParseGroupFlags( const char *val, int *flags ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseMin( const char *val ) +bool CPrimitiveTemplate::ParseMin( const gsl::cstring_view& val ) { vec3_t min; @@ -351,7 +364,7 @@ bool CPrimitiveTemplate::ParseMin( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseMax( const char *val ) +bool CPrimitiveTemplate::ParseMax( const gsl::cstring_view& val ) { vec3_t max; @@ -377,11 +390,11 @@ bool CPrimitiveTemplate::ParseMax( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseLife( const char *val ) +bool CPrimitiveTemplate::ParseLife( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mLife.SetRange( min, max ); return true; @@ -400,11 +413,11 @@ bool CPrimitiveTemplate::ParseLife( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseDelay( const char *val ) +bool CPrimitiveTemplate::ParseDelay( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mSpawnDelay.SetRange( min, max ); return true; @@ -423,11 +436,11 @@ bool CPrimitiveTemplate::ParseDelay( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseCount( const char *val ) +bool CPrimitiveTemplate::ParseCount( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mSpawnCount.SetRange( min, max ); return true; @@ -446,11 +459,11 @@ bool CPrimitiveTemplate::ParseCount( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseElasticity( const char *val ) +bool CPrimitiveTemplate::ParseElasticity( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mElasticity.SetRange( min, max ); @@ -473,7 +486,7 @@ bool CPrimitiveTemplate::ParseElasticity( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseOrigin1( const char *val ) +bool CPrimitiveTemplate::ParseOrigin1( const gsl::cstring_view& val ) { vec3_t min, max; @@ -498,7 +511,7 @@ bool CPrimitiveTemplate::ParseOrigin1( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseOrigin2( const char *val ) +bool CPrimitiveTemplate::ParseOrigin2( const gsl::cstring_view& val ) { vec3_t min, max; @@ -523,11 +536,11 @@ bool CPrimitiveTemplate::ParseOrigin2( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseRadius( const char *val ) +bool CPrimitiveTemplate::ParseRadius( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mRadius.SetRange( min, max ); return true; @@ -546,11 +559,11 @@ bool CPrimitiveTemplate::ParseRadius( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseHeight( const char *val ) +bool CPrimitiveTemplate::ParseHeight( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mHeight.SetRange( min, max ); return true; @@ -569,11 +582,11 @@ bool CPrimitiveTemplate::ParseHeight( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseWindModifier( const char *val ) +bool CPrimitiveTemplate::ParseWindModifier( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mWindModifier.SetRange( min, max ); return true; @@ -592,11 +605,11 @@ bool CPrimitiveTemplate::ParseWindModifier( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseRotation( const char *val ) +bool CPrimitiveTemplate::ParseRotation( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == qtrue ) + if ( ParseFloat( val, min, max ) == qtrue ) { mRotation.SetRange( min, max ); return true; @@ -615,11 +628,11 @@ bool CPrimitiveTemplate::ParseRotation( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseRotationDelta( const char *val ) +bool CPrimitiveTemplate::ParseRotationDelta( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == qtrue ) + if ( ParseFloat( val, min, max ) == qtrue ) { mRotationDelta.SetRange( min, max ); return true; @@ -638,7 +651,7 @@ bool CPrimitiveTemplate::ParseRotationDelta( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseAngle( const char *val ) +bool CPrimitiveTemplate::ParseAngle( const gsl::cstring_view& val ) { vec3_t min, max; @@ -663,7 +676,7 @@ bool CPrimitiveTemplate::ParseAngle( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseAngleDelta( const char *val ) +bool CPrimitiveTemplate::ParseAngleDelta( const gsl::cstring_view& val ) { vec3_t min, max; @@ -688,7 +701,7 @@ bool CPrimitiveTemplate::ParseAngleDelta( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseVelocity( const char *val ) +bool CPrimitiveTemplate::ParseVelocity( const gsl::cstring_view& val ) { vec3_t min, max; @@ -714,79 +727,43 @@ bool CPrimitiveTemplate::ParseVelocity( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseFlags( const char *val ) +bool CPrimitiveTemplate::ParseFlags( const gsl::cstring_view& val ) { - char flag[][32] = {"\0","\0","\0","\0","\0","\0","\0"}; - bool ok = true; - // For a primitive, really you probably only have two or less flags set - int v = sscanf( val, "%s %s %s %s %s %s %s", flag[0], flag[1], flag[2], flag[3], flag[4], flag[5], flag[6] ); + std::array< gsl::cstring_view, 7 > flag; - for ( int i = 0; i < 7; i++ ) - { - if ( i + 1 > v ) - { - return true; - } + const auto availableFlag = scanStrings( val, flag ); - if ( !Q_stricmp( flag[i], "useModel" )) - { - mFlags |= FX_ATTACHED_MODEL; - } - else if ( !Q_stricmp( flag[i], "useBBox" )) - { - mFlags |= FX_USE_BBOX; - } - else if ( !Q_stricmp( flag[i], "usePhysics" )) - { - mFlags |= FX_APPLY_PHYSICS; - } - else if ( !Q_stricmp( flag[i], "expensivePhysics" )) - { - mFlags |= FX_EXPENSIVE_PHYSICS; - } - //rww - begin g2 stuff - else if ( !Q_stricmp( flag[i], "ghoul2Collision" )) - { - mFlags |= (FX_GHOUL2_TRACE|FX_APPLY_PHYSICS|FX_EXPENSIVE_PHYSICS); - } - else if ( !Q_stricmp( flag[i], "ghoul2Decals" )) - { - mFlags |= FX_GHOUL2_DECALS; - } - //rww - end - else if ( !Q_stricmp( flag[i], "impactKills" )) - { - mFlags |= FX_KILL_ON_IMPACT; - } - else if ( !Q_stricmp( flag[i], "impactFx" )) - { - mFlags |= FX_IMPACT_RUNS_FX; - } - else if ( !Q_stricmp( flag[i], "deathFx" )) - { - mFlags |= FX_DEATH_RUNS_FX; - } - else if ( !Q_stricmp( flag[i], "useAlpha" )) - { - mFlags |= FX_USE_ALPHA; - } - else if ( !Q_stricmp( flag[i], "emitFx" )) - { - mFlags |= FX_EMIT_FX; - } - else if ( !Q_stricmp( flag[i], "depthHack" )) - { - mFlags |= FX_DEPTH_HACK; - } - else if ( !Q_stricmp( flag[i], "setShaderTime" )) - { - mFlags |= FX_SET_SHADER_TIME; - } - else + bool ok = true; + for( auto& cur : availableFlag ) + { + static StringViewIMap< int > flagNames{ + { CSTRING_VIEW( "useModel" ), FX_ATTACHED_MODEL }, + { CSTRING_VIEW( "useBBox" ), FX_USE_BBOX }, + { CSTRING_VIEW( "usePhysics" ), FX_APPLY_PHYSICS }, + { CSTRING_VIEW( "expensivePhysics" ), FX_EXPENSIVE_PHYSICS }, + //rww - begin g2 stuff + { CSTRING_VIEW( "ghoul2Collision" ), ( FX_GHOUL2_TRACE | FX_APPLY_PHYSICS | FX_EXPENSIVE_PHYSICS ) }, + { CSTRING_VIEW( "ghoul2Decals" ), FX_GHOUL2_DECALS }, + //rww - end + { CSTRING_VIEW( "impactKills" ), FX_KILL_ON_IMPACT }, + { CSTRING_VIEW( "impactFx" ), FX_IMPACT_RUNS_FX }, + { CSTRING_VIEW( "deathFx" ), FX_DEATH_RUNS_FX }, + { CSTRING_VIEW( "useAlpha" ), FX_USE_ALPHA }, + { CSTRING_VIEW( "emitFx" ), FX_EMIT_FX }, + { CSTRING_VIEW( "depthHack" ), FX_DEPTH_HACK }, + { CSTRING_VIEW( "setShaderTime" ), FX_SET_SHADER_TIME }, + }; + + auto pos = flagNames.find( cur ); + if( pos == flagNames.end() ) { // we have badness going on, but continue on in case there are any valid fields in here ok = false; } + else + { + mFlags |= pos->second; + } } return ok; @@ -803,80 +780,40 @@ bool CPrimitiveTemplate::ParseFlags( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSpawnFlags( const char *val ) +bool CPrimitiveTemplate::ParseSpawnFlags( const gsl::cstring_view& val ) { - char flag[][32] = {"\0","\0","\0","\0","\0","\0","\0"}; - bool ok = true; + std::array< gsl::cstring_view, 7 > flag; // For a primitive, really you probably only have two or less flags set - int v = sscanf( val, "%s %s %s %s %s %s %s", flag[0], flag[1], flag[2], flag[3], flag[4], flag[5], flag[6] ); - - for ( int i = 0; i < 7; i++ ) - { - if ( i + 1 > v ) - { - return true; - } - - if ( !Q_stricmp( flag[i], "org2fromTrace" )) - { - mSpawnFlags |= FX_ORG2_FROM_TRACE; - } - else if ( !Q_stricmp( flag[i], "traceImpactFx" )) - { - mSpawnFlags |= FX_TRACE_IMPACT_FX; - } - else if ( !Q_stricmp( flag[i], "org2isOffset" )) - { - mSpawnFlags |= FX_ORG2_IS_OFFSET; - } - else if ( !Q_stricmp( flag[i], "cheapOrgCalc" )) - { - mSpawnFlags |= FX_CHEAP_ORG_CALC; - } - else if ( !Q_stricmp( flag[i], "cheapOrg2Calc" )) - { - mSpawnFlags |= FX_CHEAP_ORG2_CALC; - } - else if ( !Q_stricmp( flag[i], "absoluteVel" )) - { - mSpawnFlags |= FX_VEL_IS_ABSOLUTE; - } - else if ( !Q_stricmp( flag[i], "absoluteAccel" )) - { - mSpawnFlags |= FX_ACCEL_IS_ABSOLUTE; - } - else if ( !Q_stricmp( flag[i], "orgOnSphere" )) // sphere/ellipsoid - { - mSpawnFlags |= FX_ORG_ON_SPHERE; - } - else if ( !Q_stricmp( flag[i], "orgOnCylinder" )) // cylinder/disk - { - mSpawnFlags |= FX_ORG_ON_CYLINDER; - } - else if ( !Q_stricmp( flag[i], "axisFromSphere" )) - { - mSpawnFlags |= FX_AXIS_FROM_SPHERE; - } - else if ( !Q_stricmp( flag[i], "randrotaroundfwd" )) - { - mSpawnFlags |= FX_RAND_ROT_AROUND_FWD; - } - else if ( !Q_stricmp( flag[i], "evenDistribution" )) - { - mSpawnFlags |= FX_EVEN_DISTRIBUTION; - } - else if ( !Q_stricmp( flag[i], "rgbComponentInterpolation" )) - { - mSpawnFlags |= FX_RGB_COMPONENT_INTERP; - } - else if ( !Q_stricmp( flag[i], "lessAttenuation" )) + const auto availableFlag = scanStrings( val, flag ); + + bool ok = true; + for( auto& cur : availableFlag ) + { + static StringViewIMap< int > flagNames{ + { CSTRING_VIEW( "org2fromTrace" ), FX_ORG2_FROM_TRACE }, + { CSTRING_VIEW( "traceImpactFx" ), FX_TRACE_IMPACT_FX }, + { CSTRING_VIEW( "org2isOffset" ), FX_ORG2_IS_OFFSET }, + { CSTRING_VIEW( "cheapOrgCalc" ), FX_CHEAP_ORG_CALC }, + { CSTRING_VIEW( "cheapOrg2Calc" ), FX_CHEAP_ORG2_CALC }, + { CSTRING_VIEW( "absoluteVel" ), FX_VEL_IS_ABSOLUTE }, + { CSTRING_VIEW( "absoluteAccel" ), FX_ACCEL_IS_ABSOLUTE }, + { CSTRING_VIEW( "orgOnSphere" ), FX_ORG_ON_SPHERE }, + { CSTRING_VIEW( "orgOnCylinder" ), FX_ORG_ON_CYLINDER }, + { CSTRING_VIEW( "axisFromSphere" ), FX_AXIS_FROM_SPHERE }, + { CSTRING_VIEW( "randrotaroundfwd" ), FX_RAND_ROT_AROUND_FWD }, + { CSTRING_VIEW( "evenDistribution" ), FX_EVEN_DISTRIBUTION }, + { CSTRING_VIEW( "rgbComponentInterpolation" ), FX_RGB_COMPONENT_INTERP }, + { CSTRING_VIEW( "lessAttenuation" ), FX_SND_LESS_ATTENUATION }, + }; + auto pos = flagNames.find( cur ); + if( pos == flagNames.end() ) { - mSpawnFlags |= FX_SND_LESS_ATTENUATION; + ok = false; } else - { // we have badness going on, but continue on in case there are any valid fields in here - ok = false; + { + mSpawnFlags |= pos->second; } } @@ -893,7 +830,7 @@ bool CPrimitiveTemplate::ParseSpawnFlags( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseAcceleration( const char *val ) +bool CPrimitiveTemplate::ParseAcceleration( const gsl::cstring_view& val ) { vec3_t min, max; @@ -918,11 +855,11 @@ bool CPrimitiveTemplate::ParseAcceleration( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseGravity( const char *val ) +bool CPrimitiveTemplate::ParseGravity( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mGravity.SetRange( min, max ); return true; @@ -943,11 +880,11 @@ bool CPrimitiveTemplate::ParseGravity( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseDensity( const char *val ) +bool CPrimitiveTemplate::ParseDensity( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mDensity.SetRange( min, max ); return true; @@ -969,11 +906,11 @@ bool CPrimitiveTemplate::ParseDensity( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseVariance( const char *val ) +bool CPrimitiveTemplate::ParseVariance( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mVariance.SetRange( min, max ); return true; @@ -992,7 +929,7 @@ bool CPrimitiveTemplate::ParseVariance( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseRGBStart( const char *val ) +bool CPrimitiveTemplate::ParseRGBStart( const gsl::cstring_view& val ) { vec3_t min, max; @@ -1017,7 +954,7 @@ bool CPrimitiveTemplate::ParseRGBStart( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseRGBEnd( const char *val ) +bool CPrimitiveTemplate::ParseRGBEnd( const gsl::cstring_view& val ) { vec3_t min, max; @@ -1042,11 +979,11 @@ bool CPrimitiveTemplate::ParseRGBEnd( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseRGBParm( const char *val ) +bool CPrimitiveTemplate::ParseRGBParm( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mRGBParm.SetRange( min, max ); return true; @@ -1065,11 +1002,11 @@ bool CPrimitiveTemplate::ParseRGBParm( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseRGBFlags( const char *val ) +bool CPrimitiveTemplate::ParseRGBFlags( const gsl::cstring_view& val ) { int flags; - if ( ParseGroupFlags( val, &flags ) == true ) + if ( ParseGroupFlags( val, flags ) == true ) { // Convert our generic flag values into type specific ones mFlags |= ( flags << FX_RGB_SHIFT ); @@ -1089,11 +1026,11 @@ bool CPrimitiveTemplate::ParseRGBFlags( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseAlphaStart( const char *val ) +bool CPrimitiveTemplate::ParseAlphaStart( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mAlphaStart.SetRange( min, max ); return true; @@ -1112,11 +1049,11 @@ bool CPrimitiveTemplate::ParseAlphaStart( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseAlphaEnd( const char *val ) +bool CPrimitiveTemplate::ParseAlphaEnd( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mAlphaEnd.SetRange( min, max ); return true; @@ -1135,11 +1072,11 @@ bool CPrimitiveTemplate::ParseAlphaEnd( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseAlphaParm( const char *val ) +bool CPrimitiveTemplate::ParseAlphaParm( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mAlphaParm.SetRange( min, max ); return true; @@ -1158,11 +1095,11 @@ bool CPrimitiveTemplate::ParseAlphaParm( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseAlphaFlags( const char *val ) +bool CPrimitiveTemplate::ParseAlphaFlags( const gsl::cstring_view& val ) { int flags; - if ( ParseGroupFlags( val, &flags ) == true ) + if ( ParseGroupFlags( val, flags ) == true ) { // Convert our generic flag values into type specific ones mFlags |= ( flags << FX_ALPHA_SHIFT ); @@ -1182,11 +1119,11 @@ bool CPrimitiveTemplate::ParseAlphaFlags( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSizeStart( const char *val ) +bool CPrimitiveTemplate::ParseSizeStart( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mSizeStart.SetRange( min, max ); return true; @@ -1205,11 +1142,11 @@ bool CPrimitiveTemplate::ParseSizeStart( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSizeEnd( const char *val ) +bool CPrimitiveTemplate::ParseSizeEnd( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mSizeEnd.SetRange( min, max ); return true; @@ -1228,11 +1165,11 @@ bool CPrimitiveTemplate::ParseSizeEnd( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSizeParm( const char *val ) +bool CPrimitiveTemplate::ParseSizeParm( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mSizeParm.SetRange( min, max ); return true; @@ -1251,11 +1188,11 @@ bool CPrimitiveTemplate::ParseSizeParm( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSizeFlags( const char *val ) +bool CPrimitiveTemplate::ParseSizeFlags( const gsl::cstring_view& val ) { int flags; - if ( ParseGroupFlags( val, &flags ) == true ) + if ( ParseGroupFlags( val, flags ) == true ) { // Convert our generic flag values into type specific ones mFlags |= ( flags << FX_SIZE_SHIFT ); @@ -1275,11 +1212,11 @@ bool CPrimitiveTemplate::ParseSizeFlags( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSize2Start( const char *val ) +bool CPrimitiveTemplate::ParseSize2Start( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mSize2Start.SetRange( min, max ); return true; @@ -1298,11 +1235,11 @@ bool CPrimitiveTemplate::ParseSize2Start( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSize2End( const char *val ) +bool CPrimitiveTemplate::ParseSize2End( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mSize2End.SetRange( min, max ); return true; @@ -1321,11 +1258,11 @@ bool CPrimitiveTemplate::ParseSize2End( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSize2Parm( const char *val ) +bool CPrimitiveTemplate::ParseSize2Parm( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mSize2Parm.SetRange( min, max ); return true; @@ -1344,11 +1281,11 @@ bool CPrimitiveTemplate::ParseSize2Parm( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSize2Flags( const char *val ) +bool CPrimitiveTemplate::ParseSize2Flags( const gsl::cstring_view& val ) { int flags; - if ( ParseGroupFlags( val, &flags ) == true ) + if ( ParseGroupFlags( val, flags ) == true ) { // Convert our generic flag values into type specific ones mFlags |= ( flags << FX_SIZE2_SHIFT ); @@ -1368,11 +1305,11 @@ bool CPrimitiveTemplate::ParseSize2Flags( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseLengthStart( const char *val ) +bool CPrimitiveTemplate::ParseLengthStart( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mLengthStart.SetRange( min, max ); return true; @@ -1391,11 +1328,11 @@ bool CPrimitiveTemplate::ParseLengthStart( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseLengthEnd( const char *val ) +bool CPrimitiveTemplate::ParseLengthEnd( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mLengthEnd.SetRange( min, max ); return true; @@ -1414,11 +1351,11 @@ bool CPrimitiveTemplate::ParseLengthEnd( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseLengthParm( const char *val ) +bool CPrimitiveTemplate::ParseLengthParm( const gsl::cstring_view& val ) { float min, max; - if ( ParseFloat( val, &min, &max ) == true ) + if ( ParseFloat( val, min, max ) == true ) { mLengthParm.SetRange( min, max ); return true; @@ -1437,11 +1374,11 @@ bool CPrimitiveTemplate::ParseLengthParm( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseLengthFlags( const char *val ) +bool CPrimitiveTemplate::ParseLengthFlags( const gsl::cstring_view& val ) { int flags; - if ( ParseGroupFlags( val, &flags ) == true ) + if ( ParseGroupFlags( val, flags ) == true ) { // Convert our generic flag values into type specific ones mFlags |= ( flags << FX_LENGTH_SHIFT ); @@ -1461,45 +1398,24 @@ bool CPrimitiveTemplate::ParseLengthFlags( const char *val ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseShaders( CGPValue *grp ) +bool CPrimitiveTemplate::ParseShaders( const CGPProperty& grp ) { - const char *val; - int handle; - - if ( grp->IsList() ) + bool any = false; + for( auto& value : grp.GetValues() ) { - // If we are a list we have to do separate processing - CGPObject *list = grp->GetList(); - - while ( list ) + if( !value.empty() ) { - // name is actually the value contained in the list - val = list->GetName(); - - handle = theFxHelper.RegisterShader( val ); + any = true; + int handle = theFxHelper.RegisterShader( value ); mMediaHandles.AddHandle( handle ); - - list = (CGPValue *)list->GetNext(); } } - else + if( !any ) { - // Let's get a value - val = grp->GetTopValue(); - - if ( val ) - { - handle = theFxHelper.RegisterShader( val ); - mMediaHandles.AddHandle( handle ); - } - else - { - // empty "list" - theFxHelper.Print( "CPrimitiveTemplate::ParseShaders called with an empty list!\n" ); - return false; - } + // empty "list" + theFxHelper.Print( "CPrimitiveTemplate::ParseShaders called with an empty list!\n" ); + return false; } - return true; } @@ -1513,45 +1429,24 @@ bool CPrimitiveTemplate::ParseShaders( CGPValue *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSounds( CGPValue *grp ) +bool CPrimitiveTemplate::ParseSounds( const CGPProperty& grp ) { - const char *val; - int handle; - - if ( grp->IsList() ) + bool any = false; + for( auto& value : grp.GetValues() ) { - // If we are a list we have to do separate processing - CGPObject *list = grp->GetList(); - - while ( list ) + if( !value.empty() ) { - // name is actually the value contained in the list - val = list->GetName(); - - handle = theFxHelper.RegisterSound( val ); + any = true; + int handle = theFxHelper.RegisterSound( value ); mMediaHandles.AddHandle( handle ); - - list = (CGPValue *)list->GetNext(); } } - else + if( !any ) { - // Let's get a value - val = grp->GetTopValue(); - - if ( val ) - { - handle = theFxHelper.RegisterSound( val ); - mMediaHandles.AddHandle( handle ); - } - else - { - // empty "list" - theFxHelper.Print( "CPrimitiveTemplate::ParseSounds called with an empty list!\n" ); - return false; - } + // empty "list" + theFxHelper.Print( "CPrimitiveTemplate::ParseSounds called with an empty list!\n" ); + return false; } - return true; } @@ -1565,119 +1460,75 @@ bool CPrimitiveTemplate::ParseSounds( CGPValue *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseModels( CGPValue *grp ) +bool CPrimitiveTemplate::ParseModels( const CGPProperty& grp ) { - const char *val; - int handle; - - if ( grp->IsList() ) + bool any = false; + for( auto& value : grp.GetValues() ) { - // If we are a list we have to do separate processing - CGPObject *list = grp->GetList(); - - while ( list ) + if( !value.empty() ) { - // name is actually the value contained in the list - val = list->GetName(); - - handle = theFxHelper.RegisterModel( val ); + any = true; + int handle = theFxHelper.RegisterModel( value ); mMediaHandles.AddHandle( handle ); - - list = (CGPValue *)list->GetNext(); } } - else + if( !any ) { - // Let's get a value - val = grp->GetTopValue(); - - if ( val ) - { - handle = theFxHelper.RegisterModel( val ); - mMediaHandles.AddHandle( handle ); - } - else - { - // empty "list" - theFxHelper.Print( "CPrimitiveTemplate::ParseModels called with an empty list!\n" ); - return false; - } + // empty "list" + theFxHelper.Print( "CPrimitiveTemplate::ParseModels called with an empty list!\n" ); + return false; } - mFlags |= FX_ATTACHED_MODEL; - return true; } -//------------------------------------------------------ -// ParseImpactFxStrings -// Reads in a group of fx file names and registers them -// -// input: -// Parse group that contains the list of fx to parse -// -// return: -// success of parse operation. -//------------------------------------------------------ -bool CPrimitiveTemplate::ParseImpactFxStrings( CGPValue *grp ) +static bool ParseFX( const CGPProperty& grp, CFxScheduler& scheduler, CMediaHandles& handles, SFxHelper& helper, int& flags, int successFlags, gsl::czstring loadError, gsl::czstring emptyError ) { - const char *val; - int handle; - - if ( grp->IsList() ) + bool any = false; + for( auto& value : grp.GetValues() ) { - // If we are a list we have to do separate processing - CGPObject *list = grp->GetList(); - - while ( list ) + if( !value.empty() ) { - // name is actually the value contained in the list - val = list->GetName(); - handle = theFxScheduler.RegisterEffect( val ); - - if ( handle ) + any = true; + // TODO: string_view parameter + int handle = scheduler.RegisterEffect( std::string( value.begin(), value.end() ).c_str() ); + if( handle ) { - mImpactFxHandles.AddHandle( handle ); + handles.AddHandle( handle ); + flags |= successFlags; } else { - theFxHelper.Print( "FxTemplate: Impact effect file not found.\n" ); - return false; + helper.Print( "%s", loadError ); } - - list = (CGPValue *)list->GetNext(); } } - else + if( !any ) { - // Let's get a value - val = grp->GetTopValue(); - - if ( val ) - { - handle = theFxScheduler.RegisterEffect( val ); - - if ( handle ) - { - mImpactFxHandles.AddHandle( handle ); - } - else - { - theFxHelper.Print( "FxTemplate: Impact effect file not found.\n" ); - return false; - } - } - else - { - // empty "list" - theFxHelper.Print( "CPrimitiveTemplate::ParseImpactFxStrings called with an empty list!\n" ); - return false; - } + helper.Print( "%s", emptyError ); } + return any; +} - mFlags |= FX_IMPACT_RUNS_FX | FX_APPLY_PHYSICS; - - return true; +//------------------------------------------------------ +// ParseImpactFxStrings +// Reads in a group of fx file names and registers them +// +// input: +// Parse group that contains the list of fx to parse +// +// return: +// success of parse operation. +//------------------------------------------------------ +bool CPrimitiveTemplate::ParseImpactFxStrings( const CGPProperty& grp ) +{ + return ParseFX( + grp, + theFxScheduler, mImpactFxHandles, theFxHelper, + mFlags, FX_IMPACT_RUNS_FX | FX_APPLY_PHYSICS, + "FxTemplate: Impact effect file not found.\n", + "CPrimitiveTemplate::ParseImpactFxStrings called with an empty list!\n" + ); } //------------------------------------------------------ @@ -1690,65 +1541,15 @@ bool CPrimitiveTemplate::ParseImpactFxStrings( CGPValue *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseDeathFxStrings( CGPValue *grp ) +bool CPrimitiveTemplate::ParseDeathFxStrings( const CGPProperty& grp ) { - const char *val; - int handle; - - if ( grp->IsList() ) - { - // If we are a list we have to do separate processing - CGPObject *list = grp->GetList(); - - while ( list ) - { - // name is actually the value contained in the list - val = list->GetName(); - handle = theFxScheduler.RegisterEffect( val ); - - if ( handle ) - { - mDeathFxHandles.AddHandle( handle ); - } - else - { - theFxHelper.Print( "FxTemplate: Death effect file not found.\n" ); - return false; - } - - list = (CGPValue *)list->GetNext(); - } - } - else - { - // Let's get a value - val = grp->GetTopValue(); - - if ( val ) - { - handle = theFxScheduler.RegisterEffect( val ); - - if ( handle ) - { - mDeathFxHandles.AddHandle( handle ); - } - else - { - theFxHelper.Print( "FxTemplate: Death effect file not found.\n" ); - return false; - } - } - else - { - // empty "list" - theFxHelper.Print( "CPrimitiveTemplate::ParseDeathFxStrings called with an empty list!\n" ); - return false; - } - } - - mFlags |= FX_DEATH_RUNS_FX; - - return true; + return ParseFX( + grp, + theFxScheduler, mDeathFxHandles, theFxHelper, + mFlags, FX_DEATH_RUNS_FX, + "FxTemplate: Death effect file not found.\n", + "CPrimitiveTemplate::ParseDeathFxStrings called with an empty list!\n" + ); } //------------------------------------------------------ @@ -1761,65 +1562,15 @@ bool CPrimitiveTemplate::ParseDeathFxStrings( CGPValue *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseEmitterFxStrings( CGPValue *grp ) +bool CPrimitiveTemplate::ParseEmitterFxStrings( const CGPProperty& grp ) { - const char *val; - int handle; - - if ( grp->IsList() ) - { - // If we are a list we have to do separate processing - CGPObject *list = grp->GetList(); - - while ( list ) - { - // name is actually the value contained in the list - val = list->GetName(); - handle = theFxScheduler.RegisterEffect( val ); - - if ( handle ) - { - mEmitterFxHandles.AddHandle( handle ); - } - else - { - theFxHelper.Print( "FxTemplate: Emitter effect file not found.\n" ); - return false; - } - - list = (CGPValue *)list->GetNext(); - } - } - else - { - // Let's get a value - val = grp->GetTopValue(); - - if ( val ) - { - handle = theFxScheduler.RegisterEffect( val ); - - if ( handle ) - { - mEmitterFxHandles.AddHandle( handle ); - } - else - { - theFxHelper.Print( "FxTemplate: Emitter effect file not found.\n" ); - return false; - } - } - else - { - // empty "list" - theFxHelper.Print( "CPrimitiveTemplate::ParseEmitterFxStrings called with an empty list!\n" ); - return false; - } - } - - mFlags |= FX_EMIT_FX; - - return true; + return ParseFX( + grp, + theFxScheduler, mEmitterFxHandles, theFxHelper, + mFlags, FX_EMIT_FX, + "FxTemplate: Emitter effect file not found.\n", + "CPrimitiveTemplate::ParseEmitterFxStrings called with an empty list!\n" + ); } //------------------------------------------------------ @@ -1832,62 +1583,32 @@ bool CPrimitiveTemplate::ParseEmitterFxStrings( CGPValue *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParsePlayFxStrings( CGPValue *grp ) +bool CPrimitiveTemplate::ParsePlayFxStrings( const CGPProperty& grp ) { - const char *val; - int handle; - - if ( grp->IsList() ) - { - // If we are a list we have to do separate processing - CGPObject *list = grp->GetList(); - - while ( list ) - { - // name is actually the value contained in the list - val = list->GetName(); - handle = theFxScheduler.RegisterEffect( val ); - - if ( handle ) - { - mPlayFxHandles.AddHandle( handle ); - } - else - { - theFxHelper.Print( "FxTemplate: Effect file not found.\n" ); - return false; - } + return ParseFX( + grp, + theFxScheduler, mPlayFxHandles, theFxHelper, + mFlags, 0, + "FxTemplate: Effect file not found.\n", + "CPrimitiveTemplate::ParsePlayFxStrings called with an empty list!\n" + ); +} - list = (CGPValue *)list->GetNext(); - } - } - else +bool CPrimitiveTemplate::ParseGroup( const CGPGroup& grp, const StringViewIMap< ParseMethod >& parseMethods, gsl::czstring name ) +{ + for( auto& cur : grp.GetProperties() ) { - // Let's get a value - val = grp->GetTopValue(); - - if ( val ) + auto pos = parseMethods.find( cur.GetName() ); + if( pos == parseMethods.end() ) { - handle = theFxScheduler.RegisterEffect( val ); - - if ( handle ) - { - mPlayFxHandles.AddHandle( handle ); - } - else - { - theFxHelper.Print( "FxTemplate: Effect file not found.\n" ); - return false; - } + theFxHelper.Print( "Unknown key parsing %s group!", name ); } else { - // empty "list" - theFxHelper.Print( "CPrimitiveTemplate::ParsePlayFxStrings called with an empty list!\n" ); - return false; + ParseMethod method = pos->second; + ( this->*method )( cur.GetTopValue() ); } } - return true; } @@ -1902,47 +1623,20 @@ bool CPrimitiveTemplate::ParsePlayFxStrings( CGPValue *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseRGB( CGPGroup *grp ) +bool CPrimitiveTemplate::ParseRGB( const CGPGroup& grp ) { - CGPValue *pairs; - const char *key; - const char *val; + static StringViewIMap< ParseMethod > parseMethods{ + { CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseRGBStart }, - // Inside of the group, we should have a series of pairs - pairs = grp->GetPairs(); + { CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseRGBEnd }, - while( pairs ) - { - // Let's get the key field - key = pairs->GetName(); - val = pairs->GetTopValue(); + { CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseRGBParm }, + { CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseRGBParm }, - // Huge stricmp lists suxor - if ( !Q_stricmp( key, "start" )) - { - ParseRGBStart( val ); - } - else if ( !Q_stricmp( key, "end" )) - { - ParseRGBEnd( val ); - } - else if ( !Q_stricmp( key, "parm" ) || !Q_stricmp( key, "parms" )) - { - ParseRGBParm( val ); - } - else if ( !Q_stricmp( key, "flags" ) || !Q_stricmp( key, "flag" )) - { - ParseRGBFlags( val ); - } - else - { - theFxHelper.Print( "Unknown key parsing an RGB group: %s\n", key ); - } - - pairs = (CGPValue *)pairs->GetNext(); - } - - return true; + { CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseRGBFlags }, + { CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseRGBFlags }, + }; + return ParseGroup( grp, parseMethods, "RGB" ); } //------------------------------------------------------ @@ -1956,47 +1650,20 @@ bool CPrimitiveTemplate::ParseRGB( CGPGroup *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseAlpha( CGPGroup *grp ) +bool CPrimitiveTemplate::ParseAlpha( const CGPGroup& grp ) { - CGPValue *pairs; - const char *key; - const char *val; - - // Inside of the group, we should have a series of pairs - pairs = grp->GetPairs(); + static StringViewIMap< ParseMethod > parseMethods{ + { CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseAlphaStart }, - while( pairs ) - { - // Let's get the key field - key = pairs->GetName(); - val = pairs->GetTopValue(); - - // Huge stricmp lists suxor - if ( !Q_stricmp( key, "start" )) - { - ParseAlphaStart( val ); - } - else if ( !Q_stricmp( key, "end" )) - { - ParseAlphaEnd( val ); - } - else if ( !Q_stricmp( key, "parm" ) || !Q_stricmp( key, "parms" )) - { - ParseAlphaParm( val ); - } - else if ( !Q_stricmp( key, "flags" ) || !Q_stricmp( key, "flag" )) - { - ParseAlphaFlags( val ); - } - else - { - theFxHelper.Print( "Unknown key parsing an Alpha group: %s\n", key ); - } + { CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseAlphaEnd }, - pairs = (CGPValue *)pairs->GetNext(); - } + { CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseAlphaParm }, + { CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseAlphaParm }, - return true; + { CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseAlphaFlags }, + { CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseAlphaFlags }, + }; + return ParseGroup( grp, parseMethods, "Alpha" ); } //------------------------------------------------------ @@ -2010,47 +1677,20 @@ bool CPrimitiveTemplate::ParseAlpha( CGPGroup *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSize( CGPGroup *grp ) +bool CPrimitiveTemplate::ParseSize( const CGPGroup& grp ) { - CGPValue *pairs; - const char *key; - const char *val; + static StringViewIMap< ParseMethod > parseMethods{ + { CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseSizeStart }, - // Inside of the group, we should have a series of pairs - pairs = grp->GetPairs(); + { CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseSizeEnd }, - while( pairs ) - { - // Let's get the key field - key = pairs->GetName(); - val = pairs->GetTopValue(); + { CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseSizeParm }, + { CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseSizeParm }, - // Huge stricmp lists suxor - if ( !Q_stricmp( key, "start" )) - { - ParseSizeStart( val ); - } - else if ( !Q_stricmp( key, "end" )) - { - ParseSizeEnd( val ); - } - else if ( !Q_stricmp( key, "parm" ) || !Q_stricmp( key, "parms" )) - { - ParseSizeParm( val ); - } - else if ( !Q_stricmp( key, "flags" ) || !Q_stricmp( key, "flag" )) - { - ParseSizeFlags( val ); - } - else - { - theFxHelper.Print( "Unknown key parsing a Size group: %s\n", key ); - } - - pairs = (CGPValue *)pairs->GetNext(); - } - - return true; + { CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseSizeFlags }, + { CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseSizeFlags }, + }; + return ParseGroup( grp, parseMethods, "Size" ); } //------------------------------------------------------ @@ -2064,47 +1704,20 @@ bool CPrimitiveTemplate::ParseSize( CGPGroup *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseSize2( CGPGroup *grp ) +bool CPrimitiveTemplate::ParseSize2( const CGPGroup& grp ) { - CGPValue *pairs; - const char *key; - const char *val; + static StringViewIMap< ParseMethod > parseMethods{ + { CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseSize2Start }, - // Inside of the group, we should have a series of pairs - pairs = grp->GetPairs(); + { CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseSize2End }, - while( pairs ) - { - // Let's get the key field - key = pairs->GetName(); - val = pairs->GetTopValue(); + { CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseSize2Parm }, + { CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseSize2Parm }, - // Huge stricmp lists suxor - if ( !Q_stricmp( key, "start" )) - { - ParseSize2Start( val ); - } - else if ( !Q_stricmp( key, "end" )) - { - ParseSize2End( val ); - } - else if ( !Q_stricmp( key, "parm" ) || !Q_stricmp( key, "parms" )) - { - ParseSize2Parm( val ); - } - else if ( !Q_stricmp( key, "flags" ) || !Q_stricmp( key, "flag" )) - { - ParseSize2Flags( val ); - } - else - { - theFxHelper.Print( "Unknown key parsing a Size2 group: %s\n", key ); - } - - pairs = (CGPValue *)pairs->GetNext(); - } - - return true; + { CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseSize2Flags }, + { CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseSize2Flags }, + }; + return ParseGroup( grp, parseMethods, "Size2" ); } //------------------------------------------------------ @@ -2118,241 +1731,145 @@ bool CPrimitiveTemplate::ParseSize2( CGPGroup *grp ) // return: // success of parse operation. //------------------------------------------------------ -bool CPrimitiveTemplate::ParseLength( CGPGroup *grp ) +bool CPrimitiveTemplate::ParseLength( const CGPGroup& grp ) { - CGPValue *pairs; - const char *key; - const char *val; + static StringViewIMap< ParseMethod > parseMethods{ + { CSTRING_VIEW( "start" ), &CPrimitiveTemplate::ParseLengthStart }, - // Inside of the group, we should have a series of pairs - pairs = grp->GetPairs(); - - while( pairs ) - { - // Let's get the key field - key = pairs->GetName(); - val = pairs->GetTopValue(); - - // Huge stricmp lists suxor - if ( !Q_stricmp( key, "start" )) - { - ParseLengthStart( val ); - } - else if ( !Q_stricmp( key, "end" )) - { - ParseLengthEnd( val ); - } - else if ( !Q_stricmp( key, "parm" ) || !Q_stricmp( key, "parms" )) - { - ParseLengthParm( val ); - } - else if ( !Q_stricmp( key, "flags" ) || !Q_stricmp( key, "flag" )) - { - ParseLengthFlags( val ); - } - else - { - theFxHelper.Print( "Unknown key parsing a Length group: %s\n", key ); - } + { CSTRING_VIEW( "end" ), &CPrimitiveTemplate::ParseLengthEnd }, - pairs = (CGPValue *)pairs->GetNext(); - } + { CSTRING_VIEW( "parm" ), &CPrimitiveTemplate::ParseLengthParm }, + { CSTRING_VIEW( "parms" ), &CPrimitiveTemplate::ParseLengthParm }, - return true; + { CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseLengthFlags }, + { CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseLengthFlags }, + }; + return ParseGroup( grp, parseMethods, "Length" ); } // Parse a primitive, apply defaults first, grab any base level // key pairs, then process any sub groups we may contain. //------------------------------------------------------ -bool CPrimitiveTemplate::ParsePrimitive( CGPGroup *grp ) +bool CPrimitiveTemplate::ParsePrimitive( const CGPGroup& grp ) { - CGPGroup *subGrp; - CGPValue *pairs; - const char *key; - const char *val; - - // Lets work with the pairs first - pairs = grp->GetPairs(); - - while( pairs ) - { - // the fields - key = pairs->GetName(); - val = pairs->GetTopValue(); - - // Huge stricmp lists suxor - if ( !Q_stricmp( key, "count" )) - { - ParseCount( val ); - } - else if ( !Q_stricmp( key, "shaders" ) || !Q_stricmp( key, "shader" )) - { - ParseShaders( pairs ); - } - else if ( !Q_stricmp( key, "models" ) || !Q_stricmp( key, "model" )) - { - ParseModels( pairs ); - } - else if ( !Q_stricmp( key, "sounds" ) || !Q_stricmp( key, "sound" )) - { - ParseSounds( pairs ); - } - else if ( !Q_stricmp( key, "impactfx" )) - { - ParseImpactFxStrings( pairs ); - } - else if ( !Q_stricmp( key, "deathfx" )) - { - ParseDeathFxStrings( pairs ); - } - else if ( !Q_stricmp( key, "emitfx" )) - { - ParseEmitterFxStrings( pairs ); - } - else if ( !Q_stricmp( key, "playfx" )) - { - ParsePlayFxStrings( pairs ); + // Property + for( auto& prop : grp.GetProperties() ) + { + // Single Value Parsing + { + static StringViewIMap< ParseMethod > parseMethods{ + { CSTRING_VIEW( "count" ), &CPrimitiveTemplate::ParseCount }, + { CSTRING_VIEW( "life" ), &CPrimitiveTemplate::ParseLife }, + { CSTRING_VIEW( "delay" ), &CPrimitiveTemplate::ParseDelay }, + { CSTRING_VIEW( "bounce" ), &CPrimitiveTemplate::ParseElasticity }, + { CSTRING_VIEW( "intensity" ), &CPrimitiveTemplate::ParseElasticity }, + { CSTRING_VIEW( "min" ), &CPrimitiveTemplate::ParseMin }, + { CSTRING_VIEW( "max" ), &CPrimitiveTemplate::ParseMax }, + { CSTRING_VIEW( "angle" ), &CPrimitiveTemplate::ParseAngle }, + { CSTRING_VIEW( "angles" ), &CPrimitiveTemplate::ParseAngle }, + { CSTRING_VIEW( "angleDelta" ), &CPrimitiveTemplate::ParseAngleDelta }, + { CSTRING_VIEW( "velocity" ), &CPrimitiveTemplate::ParseVelocity }, + { CSTRING_VIEW( "vel" ), &CPrimitiveTemplate::ParseVelocity }, + { CSTRING_VIEW( "acceleration" ), &CPrimitiveTemplate::ParseAcceleration }, + { CSTRING_VIEW( "accel" ), &CPrimitiveTemplate::ParseAcceleration }, + { CSTRING_VIEW( "gravity" ), &CPrimitiveTemplate::ParseGravity }, + { CSTRING_VIEW( "density" ), &CPrimitiveTemplate::ParseDensity }, + { CSTRING_VIEW( "variance" ), &CPrimitiveTemplate::ParseVariance }, + { CSTRING_VIEW( "origin" ), &CPrimitiveTemplate::ParseOrigin1 }, + { CSTRING_VIEW( "origin2" ), &CPrimitiveTemplate::ParseOrigin2 }, + { CSTRING_VIEW( "radius" ), &CPrimitiveTemplate::ParseRadius }, + { CSTRING_VIEW( "height" ), &CPrimitiveTemplate::ParseHeight }, + { CSTRING_VIEW( "wind" ), &CPrimitiveTemplate::ParseWindModifier }, + { CSTRING_VIEW( "rotation" ), &CPrimitiveTemplate::ParseRotation }, + { CSTRING_VIEW( "rotationDelta" ), &CPrimitiveTemplate::ParseRotationDelta }, + { CSTRING_VIEW( "flags" ), &CPrimitiveTemplate::ParseFlags }, + { CSTRING_VIEW( "flag" ), &CPrimitiveTemplate::ParseFlags }, + { CSTRING_VIEW( "spawnFlags" ), &CPrimitiveTemplate::ParseSpawnFlags }, + { CSTRING_VIEW( "spawnFlag" ), &CPrimitiveTemplate::ParseSpawnFlags }, + }; + auto pos = parseMethods.find( prop.GetName() ); + if( pos != parseMethods.end() ) + { + ParseMethod method = pos->second; + ( this->*method )( prop.GetTopValue() ); + continue; + } } - else if ( !Q_stricmp( key, "life" )) - { - ParseLife( val ); + // Property Parsing + { + using PropertyParseMethod = bool( CPrimitiveTemplate::* )( const CGPProperty& ); + static StringViewIMap< PropertyParseMethod > parseMethods{ + { CSTRING_VIEW( "shaders" ), &CPrimitiveTemplate::ParseShaders }, + { CSTRING_VIEW( "shader" ), &CPrimitiveTemplate::ParseShaders }, + { CSTRING_VIEW( "models" ), &CPrimitiveTemplate::ParseModels }, + { CSTRING_VIEW( "model" ), &CPrimitiveTemplate::ParseModels }, + { CSTRING_VIEW( "sounds" ), &CPrimitiveTemplate::ParseSounds }, + { CSTRING_VIEW( "sound" ), &CPrimitiveTemplate::ParseSounds }, + { CSTRING_VIEW( "impactfx" ), &CPrimitiveTemplate::ParseImpactFxStrings }, + { CSTRING_VIEW( "deathfx" ), &CPrimitiveTemplate::ParseDeathFxStrings }, + { CSTRING_VIEW( "emitfx" ), &CPrimitiveTemplate::ParseEmitterFxStrings }, + { CSTRING_VIEW( "playfx" ), &CPrimitiveTemplate::ParsePlayFxStrings }, + }; + auto pos = parseMethods.find( prop.GetName() ); + if( pos != parseMethods.end() ) + { + PropertyParseMethod method = pos->second; + ( this->*method )( prop ); + continue; + } } - else if ( !Q_stricmp( key, "cullrange" )) + // Special Cases + if( Q::stricmp( prop.GetName(), CSTRING_VIEW( "cullrange" ) ) == Q::Ordering::EQ ) { - mCullRange = atoi( val ); + mCullRange = Q::svtoi( prop.GetTopValue() ); mCullRange *= mCullRange; // Square } - else if ( !Q_stricmp( key, "delay" )) + else if( Q::stricmp( prop.GetName(), CSTRING_VIEW( "name" ) ) == Q::Ordering::EQ ) { - ParseDelay( val ); - } - else if ( !Q_stricmp( key, "bounce" ) || !Q_stricmp( key, "intensity" )) // me==bad for reusing this...but it shouldn't hurt anything) - { - ParseElasticity( val ); - } - else if ( !Q_stricmp( key, "min" )) - { - ParseMin( val ); - } - else if ( !Q_stricmp( key, "max" )) - { - ParseMax( val ); - } - else if ( !Q_stricmp( key, "angle" ) || !Q_stricmp( key, "angles" )) - { - ParseAngle( val ); - } - else if ( !Q_stricmp( key, "angleDelta" )) - { - ParseAngleDelta( val ); - } - else if ( !Q_stricmp( key, "velocity" ) || !Q_stricmp( key, "vel" )) - { - ParseVelocity( val ); - } - else if ( !Q_stricmp( key, "acceleration" ) || !Q_stricmp( key, "accel" )) - { - ParseAcceleration( val ); - } - else if ( !Q_stricmp( key, "gravity" )) - { - ParseGravity( val ); - } - else if ( !Q_stricmp( key, "density" )) - { - ParseDensity( val ); - } - else if ( !Q_stricmp( key, "variance" )) - { - ParseVariance( val ); - } - else if ( !Q_stricmp( key, "origin" )) - { - ParseOrigin1( val ); - } - else if ( !Q_stricmp( key, "origin2" )) - { - ParseOrigin2( val ); - } - else if ( !Q_stricmp( key, "radius" )) // part of ellipse/cylinder calcs. - { - ParseRadius( val ); - } - else if ( !Q_stricmp( key, "height" )) // part of ellipse/cylinder calcs. - { - ParseHeight( val ); - } - else if ( !Q_stricmp( key, "wind" )) - { - ParseWindModifier( val ); - } - else if ( !Q_stricmp( key, "rotation" )) - { - ParseRotation( val ); - } - else if ( !Q_stricmp( key, "rotationDelta" )) - { - ParseRotationDelta( val ); - } - else if ( !Q_stricmp( key, "flags" ) || !Q_stricmp( key, "flag" )) - { // these need to get passed on to the primitive - ParseFlags( val ); - } - else if ( !Q_stricmp( key, "spawnFlags" ) || !Q_stricmp( key, "spawnFlag" )) - { // these are used to spawn things in cool ways, but don't ever get passed on to prims. - ParseSpawnFlags( val ); - } - else if ( !Q_stricmp( key, "name" )) - { - if ( val ) + if( !prop.GetTopValue().empty() ) { // just stash the descriptive name of the primitive - strcpy( mName, val ); + std::size_t len = std::min< std::size_t >( prop.GetTopValue().size(), FX_MAX_PRIM_NAME - 1 ); + auto begin = prop.GetTopValue().begin(); + std::copy( begin, begin + len, &mName[ 0 ] ); + mName[ len ] = '\0'; } } + // Error else { - theFxHelper.Print( "Unknown key parsing an effect primitive: %s\n", key ); + theFxHelper.Print( "Unknown key parsing an effect primitive!\n" ); } - - pairs = (CGPValue *)pairs->GetNext(); } - subGrp = grp->GetSubGroups(); - - // Lets chomp on the groups now - while ( subGrp ) + for( auto& subGrp : grp.GetSubGroups() ) { - key = subGrp->GetName(); + using GroupParseMethod = bool ( CPrimitiveTemplate::* )( const CGPGroup& ); + static StringViewIMap< GroupParseMethod > parseMethods{ + { CSTRING_VIEW( "rgb" ), &CPrimitiveTemplate::ParseRGB }, - if ( !Q_stricmp( key, "rgb" )) - { - ParseRGB( subGrp ); - } - else if ( !Q_stricmp( key, "alpha" )) - { - ParseAlpha( subGrp ); - } - else if ( !Q_stricmp( key, "size" ) || !Q_stricmp( key, "width" )) - { - ParseSize( subGrp ); - } - else if ( !Q_stricmp( key, "size2" ) || !Q_stricmp( key, "width2" )) - { - ParseSize2( subGrp ); - } - else if ( !Q_stricmp( key, "length" ) || !Q_stricmp( key, "height" )) + { CSTRING_VIEW( "alpha" ), &CPrimitiveTemplate::ParseAlpha }, + + { CSTRING_VIEW( "size" ), &CPrimitiveTemplate::ParseSize }, + { CSTRING_VIEW( "width" ), &CPrimitiveTemplate::ParseSize }, + + { CSTRING_VIEW( "size2" ), &CPrimitiveTemplate::ParseSize2 }, + { CSTRING_VIEW( "width2" ), &CPrimitiveTemplate::ParseSize2 }, + + { CSTRING_VIEW( "length" ), &CPrimitiveTemplate::ParseLength }, + { CSTRING_VIEW( "height" ), &CPrimitiveTemplate::ParseLength }, + }; + auto pos = parseMethods.find( subGrp.GetName() ); + if( pos == parseMethods.end() ) { - ParseLength( subGrp ); + theFxHelper.Print( "Unknown group key parsing a particle!\n" ); } else { - theFxHelper.Print( "Unknown group key parsing a particle: %s\n", key ); + GroupParseMethod method = pos->second; + ( this->*method )( subGrp ); } - - subGrp = (CGPGroup *)subGrp->GetNext(); } - return true; } \ No newline at end of file diff --git a/code/cgame/FxUtil.cpp b/code/cgame/FxUtil.cpp index 8bb9c87a12..499dad065c 100644 --- a/code/cgame/FxUtil.cpp +++ b/code/cgame/FxUtil.cpp @@ -1137,7 +1137,7 @@ CPoly *FX_AddPoly( vec3_t *verts, vec2_t *st, int numVerts, for ( int i = 0; i < numVerts; i++ ) { VectorCopy( verts[i], fx->mOrg[i] ); - Vector2Copy( st[i], fx->mST[i] ); + VectorCopy2( st[i], fx->mST[i] ); } fx->SetVel( vel ); diff --git a/code/cgame/animtable.h b/code/cgame/animtable.h index c4353fb453..e71e54c98f 100644 --- a/code/cgame/animtable.h +++ b/code/cgame/animtable.h @@ -1812,3 +1812,48 @@ stringID_table_t animTable [MAX_ANIMATIONS+1] = //must be terminated { NULL,-1 } }; + +stringID_table_t vmAnimTable [MAX_VIEWMODEL_ANIMATIONS+1] = { + ENUM2STRING(VM_READY), + ENUM2STRING(VM_IDLE), + ENUM2STRING(VM_RAISE), + ENUM2STRING(VM_LOWER), + ENUM2STRING(VM_FIRE), + ENUM2STRING(VM_MELEE), + ENUM2STRING(VM_RELOAD), + + // Force powers + ENUM2STRING(VM_FPUSH), + ENUM2STRING(VM_FPULL), + ENUM2STRING(VM_FGRIP), + ENUM2STRING(VM_FGRIP_HOLD), + ENUM2STRING(VM_FGRIP_RELEASE), + ENUM2STRING(VM_FGRIP_THROW), + ENUM2STRING(VM_FHEAL_QUICK), + ENUM2STRING(VM_FHEAL_START), + ENUM2STRING(VM_FHEAL_STOP), + ENUM2STRING(VM_FLIGHTNING), + ENUM2STRING(VM_FLIGHTNING_START), + ENUM2STRING(VM_FLIGHTNING_HOLD), + ENUM2STRING(VM_FLIGHTNING_RELEASE), + ENUM2STRING(VM_FRESISTPUSH), + ENUM2STRING(VM_FMINDTRICK1), + ENUM2STRING(VM_FMINDTRICK2), + + // JA stuff + ENUM2STRING(VM_2H_FLIGHTNING), + ENUM2STRING(VM_2H_FLIGHTNING_HOLD), + ENUM2STRING(VM_2H_FLIGHTNING_RELEASE), + ENUM2STRING(VM_FDRAIN), + ENUM2STRING(VM_FDRAIN_START), + ENUM2STRING(VM_FDRAIN_HOLD), + ENUM2STRING(VM_FDRAIN_RELEASE), + ENUM2STRING(VM_FPROTECT), + ENUM2STRING(VM_FPROTECT_FAST), + ENUM2STRING(VM_FRAGE), + ENUM2STRING(VM_FABSORB), + ENUM2STRING(VM_FABSORB_START), + ENUM2STRING(VM_FABSORB_END), + + { NULL,-1 } +}; \ No newline at end of file diff --git a/code/cgame/cg_camera.cpp b/code/cgame/cg_camera.cpp index d186359292..72752164b8 100644 --- a/code/cgame/cg_camera.cpp +++ b/code/cgame/cg_camera.cpp @@ -38,7 +38,7 @@ void CGCam_FollowDisable( void ); void CGCam_TrackDisable( void ); void CGCam_Distance( float distance, qboolean initLerp ); void CGCam_DistanceDisable( void ); -extern int CG_CalcFOVFromX( float fov_x ); +extern qboolean CG_CalcFOVFromX( float fov_x ); extern void WP_SaberCatch( gentity_t *self, gentity_t *saber, qboolean switchToSaber ); /* @@ -418,8 +418,8 @@ void CGCam_SetFade( vec4_t dest ) {//Instant completion client_camera.info_state &= ~CAMERA_FADING; client_camera.fade_duration = 0; - Vector4Copy( dest, client_camera.fade_source ); - Vector4Copy( dest, client_camera.fade_color ); + VectorCopy4( dest, client_camera.fade_source ); + VectorCopy4( dest, client_camera.fade_color ); } /* @@ -436,8 +436,8 @@ void CGCam_Fade( vec4_t source, vec4_t dest, float duration ) return; } - Vector4Copy( source, client_camera.fade_source ); - Vector4Copy( dest, client_camera.fade_dest ); + VectorCopy4( source, client_camera.fade_source ); + VectorCopy4( dest, client_camera.fade_dest ); client_camera.fade_duration = duration; client_camera.fade_time = cg.time; @@ -1066,7 +1066,7 @@ void CGCam_UpdateFade( void ) { if ( client_camera.fade_time + client_camera.fade_duration < cg.time ) { - Vector4Copy( client_camera.fade_dest, client_camera.fade_color ); + VectorCopy4( client_camera.fade_dest, client_camera.fade_color ); client_camera.info_state &= ~CAMERA_FADING; } else @@ -1394,7 +1394,7 @@ void CGCam_UpdateShake( vec3_t origin, vec3_t angles ) for ( int i = 0; i < 3; i++ ) { - moveDir[i] = ( crandom() * intensity ); + moveDir[i] = ( Q_flrand(-1.0f, 1.0f) * intensity ); } //FIXME: Lerp @@ -1403,7 +1403,7 @@ void CGCam_UpdateShake( vec3_t origin, vec3_t angles ) VectorAdd( origin, moveDir, origin ); for ( int i = 0; i < 2; i++ ) // Don't do ROLL - moveDir[i] = ( crandom() * intensity ); + moveDir[i] = ( Q_flrand(-1.0f, 1.0f) * intensity ); //FIXME: Lerp diff --git a/code/cgame/cg_consolecmds.cpp b/code/cgame/cg_consolecmds.cpp index 454ffbffae..beee63c588 100644 --- a/code/cgame/cg_consolecmds.cpp +++ b/code/cgame/cg_consolecmds.cpp @@ -146,7 +146,7 @@ void CG_ToggleBinoculars( void ) cg.zoomTime = cg.time; cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomEnd ); - if( cg.weaponSelect == WP_NONE && cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << WP_SABER ) ) + if( cg.weaponSelect == WP_NONE && cg.snap->ps.weapons[WP_SABER] ) { // FIXME: this is pretty damn ugly but whatever cg.weaponSelect = WP_SABER; @@ -269,7 +269,7 @@ Cmd_Argc() / Cmd_Argv() qboolean CG_ConsoleCommand( void ) { consoleCommand_t *command = NULL; - command = (consoleCommand_t *)bsearch( CG_Argv( 0 ), commands, numCommands, sizeof( commands[0] ), cmdcmp ); + command = (consoleCommand_t *)Q_LinearSearch( CG_Argv( 0 ), commands, numCommands, sizeof( commands[0] ), cmdcmp ); if ( !command ) return qfalse; @@ -280,21 +280,30 @@ qboolean CG_ConsoleCommand( void ) { static const char *gcmds[] = { "bow", + "customsaber", "entitylist", + "difficulty", "flourish", "force_absorb", + "force_blinding", + "force_deadlysight", + "force_destruction", "force_distract", "force_grip", "force_heal", + "force_insanity", + "force_invulnerability", "force_protect", "force_pull", "force_rage", "force_sight", "force_speed", + "force_stasis", "force_throw", "give", "gloat", "god", + "headplayermodel", "invuse", "kill", "meditate", @@ -309,7 +318,9 @@ static const char *gcmds[] = { "saber", "saberAttackCycle", "saberColor", + "saberCrystal", "saberblade", + "secrets", "setForceAll", "setSaberAll", "setobjective", diff --git a/code/cgame/cg_credits.cpp b/code/cgame/cg_credits.cpp index 46365a2162..85b3a93c09 100644 --- a/code/cgame/cg_credits.cpp +++ b/code/cgame/cg_credits.cpp @@ -112,9 +112,9 @@ struct CreditData_t CreditCards_t CreditCards; CreditLines_t CreditLines; - bool Running(void) + qboolean Running(void) { - return !!( CreditCards.size() || CreditLines.size() ); + return (qboolean)( CreditCards.size() || CreditLines.size() ); } }; @@ -235,12 +235,12 @@ void CG_Credits_Init( const char *psStripReference, vec4_t *pv4Color) // Play the light side end credits music. if ( g_entities[0].client->sess.mission_objectives[0].status != 2 ) { - cgi_S_StartBackgroundTrack( "music/endcredits.mp3", NULL, false ); + cgi_S_StartBackgroundTrack( "music/endcredits.mp3", NULL, qfalse ); } // Play the dark side end credits music. else { - cgi_S_StartBackgroundTrack( "music/vjun3/vjun3_explore.mp3", NULL, false ); + cgi_S_StartBackgroundTrack( "music/vjun3/vjun3_explore.mp3", NULL, qfalse ); } // could make these into parameters later, but for now... diff --git a/code/cgame/cg_draw.cpp b/code/cgame/cg_draw.cpp index f36f3d485a..fe4765f398 100644 --- a/code/cgame/cg_draw.cpp +++ b/code/cgame/cg_draw.cpp @@ -297,6 +297,11 @@ static void CG_DrawSaberStyle(const centity_t *cent,const int xPos,const int yPo } cgi_R_SetColor( otherHUDBits[index].color); + if (cg.saberAnimLevelPending == SS_KATARN) + { + cgi_R_SetColor( otherHUDBits[OHB_ARMORAMOUNT].color ); + index = OHB_SABERSTYLE_MEDIUM; + } CG_DrawPic( otherHUDBits[index].xPos, @@ -1822,10 +1827,13 @@ static void CG_DrawSimpleForcePower( const centity_t *cent ) CG_DrawHUD ================ */ +extern void WorkshopDrawClientsideInformation(); static void CG_DrawHUD( centity_t *cent ) { int value; int sectionXPos,sectionYPos,sectionWidth,sectionHeight; + + WorkshopDrawClientsideInformation(); if ( cg_hudFiles.integer ) { @@ -2131,9 +2139,9 @@ static void CG_DrawZoomMask( void ) if ( power ) { // Flickery color - color1[0] = 0.7f + crandom() * 0.1f; - color1[1] = 0.8f + crandom() * 0.1f; - color1[2] = 0.7f + crandom() * 0.1f; + color1[0] = 0.7f + Q_flrand(-1.0f, 1.0f) * 0.1f; + color1[1] = 0.8f + Q_flrand(-1.0f, 1.0f) * 0.1f; + color1[2] = 0.7f + Q_flrand(-1.0f, 1.0f) * 0.1f; color1[3] = 1.0f; cgi_R_SetColor( color1 ); @@ -2159,9 +2167,9 @@ static void CG_DrawZoomMask( void ) CG_DrawPic( 307, 40, 26, 30, cgs.media.binocularTri ); } - if ( random() > 0.98f && ( cg.time & 1024 )) + if ( Q_flrand(0.0f, 1.0f) > 0.98f && ( cg.time & 1024 )) { - flip = !flip; + flip = (qboolean)!flip; } if ( power ) @@ -2298,9 +2306,9 @@ static void CG_DrawZoomMask( void ) float pos2 = 220 + cos( cg.time * 0.0004f + light * 0.05f ) * 40 + sin( cg.time * 0.0013f + 1 ) * 20 + sin( cg.time * 0.0021f ) * 5; // Flickery color - color1[0] = 0.7f + crandom() * 0.2f; - color1[1] = 0.8f + crandom() * 0.2f; - color1[2] = 0.7f + crandom() * 0.2f; + color1[0] = 0.7f + Q_flrand(-1.0f, 1.0f) * 0.2f; + color1[1] = 0.8f + Q_flrand(-1.0f, 1.0f) * 0.2f; + color1[2] = 0.7f + Q_flrand(-1.0f, 1.0f) * 0.2f; color1[3] = 1.0f; cgi_R_SetColor( color1 ); @@ -2919,6 +2927,7 @@ static void CG_ScanForRocketLock( void ) CG_ScanForCrosshairEntity ================= */ +extern vec3_t g_crosshairWorldCoord; extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent ); extern float forcePushPullRadius[]; static void CG_ScanForCrosshairEntity( qboolean scanAll ) @@ -3057,6 +3066,16 @@ static void CG_ScanForCrosshairEntity( qboolean scanAll ) } AngleVectors( cg_entities[cg.snap->ps.viewEntity].lerpAngles, d_f, d_rt, d_up ); } + //temporary fix for third person aiming with gun/saber dual wield. shots should come from left hand anyway + else if (cg.snap->ps.weapon == WP_SABER && cg.snap->ps.viewEntity == 0 && cg_entities[0].gent->client->ps.saberAnimLevel == SS_KATARN) + { + extern void CalcMuzzlePoint( gentity_t *const ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint, float lead_in ); + AngleVectors( cg_entities[0].lerpAngles, d_f, d_rt, d_up ); + int oldWeapon = g_entities[0].s.weapon; + g_entities[0].s.weapon = WP_BRYAR_PISTOL;//Should be whichever weapon is being dual wielded, not always set to bryar! + CalcMuzzlePoint( &g_entities[0], d_f, d_rt, d_up, start , 0 ); + g_entities[0].s.weapon = oldWeapon; + } else { VectorCopy( g_entities[0].client->renderInfo.eyePoint, start ); @@ -3110,6 +3129,7 @@ static void CG_ScanForCrosshairEntity( qboolean scanAll ) //draw crosshair at endpoint CG_DrawCrosshair( trace.endpos ); + VectorCopy( trace.endpos, g_crosshairWorldCoord ); g_crosshairEntNum = trace.entityNum; g_crosshairEntDist = 4096*trace.fraction; @@ -3274,6 +3294,11 @@ static void CG_DrawRocketLocking( int lockEntNum, int lockTime ) { sz -= ( cg.overrides.fov - cg_zoomFov ) / 80.0f; } + else if ( !cg.renderingThirdPerson && (cg_trueguns.integer || cg.snap->ps.weapon == WP_SABER + || cg.snap->ps.weapon == WP_MELEE) && cg_truefov.value ) + { + sz -= ( cg_truefov.value - cg_zoomFov ) / 80.0f; + } else { sz -= ( cg_fov.value - cg_zoomFov ) / 80.0f; @@ -3473,6 +3498,312 @@ static float CG_DrawTimer( float y ) { return y + BIGCHAR_HEIGHT + 10; } +/* + ===================== + CG_DrawRadar + ===================== + */ +float cg_radarRange = 2500.0f; + +#define RADAR_RADIUS 60 +#define RADAR_X (580 - RADAR_RADIUS) +#define RADAR_CHAT_DURATION 6000 +#define RADAR_MISSILE_RANGE 3000.0f +#define RADAR_ASTEROID_RANGE 10000.0f +#define RADAR_MIN_ASTEROID_SURF_WARN_DIST 1200.0f + +float CG_DrawRadar ( float y ) +{ + vec4_t color; + float arrow_w; + float arrow_h; + int i; + float arrowBaseScale; + float zScale; + int xOffset = 0; + + if (!cg.snap) + { + return y; + } + + // Make sure the radar should be showing + if ( cg.snap->ps.stats[STAT_HEALTH] <= 0 ) + { + return y; + } + + if ((cg.snap->ps.viewEntity>0&&cg.snap->ps.viewEntity 0 ) + { + CG_DrawPic( RADAR_X + xOffset + RADAR_RADIUS*0.2f, y + RADAR_RADIUS*0.2f, RADAR_RADIUS*1.6f, RADAR_RADIUS*1.6f, cgs.media.radarMaskShader ); + + vec2_t texBottomLeft; + vec2_t texTopRight; + + float xScale = 1.0f / (cgs.radarMap.bottomRight[0] - cgs.radarMap.topLeft[0]); + float yScale = 1.0f / (cgs.radarMap.topLeft[1] - cgs.radarMap.bottomRight[1]); + + texBottomLeft[0] = (cg.predicted_player_state.origin[0] - cg_radarRange - cgs.radarMap.topLeft[0])*xScale; + texBottomLeft[1] = (cg.predicted_player_state.origin[1] - cg_radarRange - cgs.radarMap.bottomRight[1])*yScale; + texTopRight[0] = (cg.predicted_player_state.origin[0] + cg_radarRange - cgs.radarMap.topLeft[0])*xScale; + texTopRight[1] = (cg.predicted_player_state.origin[1] + cg_radarRange - cgs.radarMap.bottomRight[1])*yScale; + cgi_R_DrawRotatePic2( RADAR_X + RADAR_RADIUS, RADAR_RADIUS + y, RADAR_RADIUS*2, RADAR_RADIUS*2, texBottomLeft[0], (1 - texTopRight[1]), texTopRight[0], (1 - texBottomLeft[1]), -90 + cg.predicted_player_state.viewangles[YAW], cgs.radarMap.minimapImage[0] ); + } + + // Draw the radar background image + color[0] = color[1] = color[2] = 1.0f; + color[3] = 0.6f; + cgi_R_SetColor ( color ); + CG_DrawPic( RADAR_X + xOffset, y, RADAR_RADIUS*2, RADAR_RADIUS*2, cgs.media.radarShader ); + + // Draw all of the radar entities. Draw them backwards so players are drawn last + for ( i = cg.radarEntityCount -1 ; i >= 0 ; i-- ) + { + vec3_t dirLook; + vec3_t dirPlayer; + float angleLook; + float anglePlayer; + float angle; + float distance, actualDist; + centity_t* cent; + qboolean farAway = qfalse; + + cent = &cg_entities[cg.radarEntities[i]]; + + // Get the distances first + VectorSubtract ( cg.predicted_player_state.origin, cent->lerpOrigin, dirPlayer ); + dirPlayer[2] = 0; + actualDist = distance = VectorNormalize ( dirPlayer ); + + if ( distance > cg_radarRange * 0.8f) + { + if ( (cent->currentState.eFlags2 & EF2_RADAROBJECT) )//still want to draw the direction + { + distance = cg_radarRange*0.8f; + farAway = qtrue; + } + else + { + continue; + } + } + + distance = distance / cg_radarRange; + distance *= RADAR_RADIUS; + + AngleVectors ( cg.predicted_player_state.viewangles, dirLook, NULL, NULL ); + + dirLook[2] = 0; + anglePlayer = atan2(dirPlayer[0],dirPlayer[1]); + VectorNormalize ( dirLook ); + angleLook = atan2(dirLook[0],dirLook[1]); + angle = angleLook - anglePlayer; + + switch ( cent->currentState.eType ) + { + default: + { + float x; + float ly; + qhandle_t shader; + vec4_t color; + + x = (float)RADAR_X + (float)RADAR_RADIUS + (float)sin (angle) * distance; + ly = y + (float)RADAR_RADIUS + (float)cos (angle) * distance; + + arrowBaseScale = 9.0f; + shader = 0; + zScale = 1.0f; + + if ( !farAway ) + { + //we want to scale the thing up/down based on the relative Z (up/down) positioning + if (cent->lerpOrigin[2] > cg.predicted_player_state.origin[2]) + { //higher, scale up (between 16 and 24) + float dif = (cent->lerpOrigin[2] - cg.predicted_player_state.origin[2]); + + //max out to 1.5x scale at 512 units above local player's height + dif /= 1024.0f; + if (dif > 0.5f) + { + dif = 0.5f; + } + zScale += dif; + } + else if (cent->lerpOrigin[2] < cg.predicted_player_state.origin[2]) + { //lower, scale down (between 16 and 8) + float dif = (cg.predicted_player_state.origin[2] - cent->lerpOrigin[2]); + + //half scale at 512 units below local player's height + dif /= 1024.0f; + if (dif > 0.5f) + { + dif = 0.5f; + } + zScale -= dif; + } + } + + arrowBaseScale *= zScale; + + color[0] = color[1] = color[2] = color[3] = 1.0f; + + // generic enemy index specifies a shader to use for the radar entity. + if ( cent->currentState.radarIcon ) + { + shader = cgs.media.radarIcons[cent->currentState.radarIcon]; + } + else + { + shader = cgs.media.siegeItemShader; + } + + if ( shader ) + { + // Pulse the alpha if time2 is set. time2 gets set when the entity takes pain + if ( (cent->currentState.time2 && cg.time - cent->currentState.time2 < 5000) || + (cent->currentState.time2 == 0xFFFFFFFF) ) + { + if ( (cg.time / 200) & 1 ) + { + color[3] = 0.1f + 0.9f * (float) (cg.time % 200) / 200.0f; + } + else + { + color[3] = 1.0f - 0.9f * (float) (cg.time % 200) / 200.0f; + } + } + + cgi_R_SetColor ( color ); + CG_DrawPic ( x - 4 + xOffset, ly - 4, arrowBaseScale, arrowBaseScale, shader ); + } + } + break; + + + case ET_PLAYER: + { + qhandle_t shader; + vec4_t color; + + gentity_t *radarEnt = &g_entities[ cent->currentState.number ]; + + + if (radarEnt->client->ps.stats[STAT_HEALTH] <= 0) + { + continue; + } + + switch ( radarEnt->client->playerTeam ) + { + case TEAM_ENEMY: + VectorCopy ( colorTable[CT_DKORANGE], color ); + break; + case TEAM_NEUTRAL: + VectorCopy ( colorTable[CT_YELLOW], color ); + break; + case TEAM_PLAYER: + VectorCopy ( colorTable[CT_GREEN], color ); + break; + default: + VectorCopy ( colorTable[CT_DKORANGE], color ); + break; + } + + color[3] = 1.0f; + + arrowBaseScale = 16.0f; + zScale = 1.0f; + + cgi_R_SetColor ( color ); + + if ( cent->currentState.radarIcon ) + { + shader = cgs.media.radarIcons[cent->currentState.radarIcon]; + } + else + { + shader = cgs.media.mAutomapPlayerIcon; + } + + if ( !farAway ) + { + //we want to scale the thing up/down based on the relative Z (up/down) positioning + if (cent->lerpOrigin[2] > cg.predicted_player_state.origin[2]) + { //higher, scale up (between 16 and 32) + float dif = (cent->lerpOrigin[2] - cg.predicted_player_state.origin[2]); + + //max out to 2x scale at 1024 units above local player's height + dif /= 1024.0f; + if (dif > 1.0f) + { + dif = 1.0f; + } + zScale += dif; + } + else if (cent->lerpOrigin[2] < cg.predicted_player_state.origin[2]) + { //lower, scale down (between 16 and 8) + float dif = (cg.predicted_player_state.origin[2] - cent->lerpOrigin[2]); + + //half scale at 512 units below local player's height + dif /= 1024.0f; + if (dif > 0.5f) + { + dif = 0.5f; + } + zScale -= dif; + } + } + + arrowBaseScale *= zScale; + + arrow_w = arrowBaseScale * RADAR_RADIUS / 128; + arrow_h = arrowBaseScale * RADAR_RADIUS / 128; + + if ( cent->currentState.radarIcon ) + { + arrow_w *= 2.0f; + arrow_h *= 2.0f; + CG_DrawPic(0, 0, 0, 0, cgs.media.whiteShader); + CG_DrawRotatePic2( RADAR_X + RADAR_RADIUS + sin (angle) * distance + xOffset, + y + RADAR_RADIUS + cos (angle) * distance, + arrow_w, arrow_h, + 0, shader ); + } + else + { + CG_DrawPic(0, 0, 0, 0, cgs.media.whiteShader); + CG_DrawRotatePic2( RADAR_X + RADAR_RADIUS + sin (angle) * distance + xOffset, + y + RADAR_RADIUS + cos (angle) * distance, + arrow_w, arrow_h, + (360 - cent->lerpAngles[YAW]) + cg.predicted_player_state.viewangles[YAW], shader ); + } + break; + } + } + } + + arrowBaseScale = 16.0f; + + arrow_w = arrowBaseScale * RADAR_RADIUS / 128; + arrow_h = arrowBaseScale * RADAR_RADIUS / 128; + + cgi_R_SetColor ( colorTable[CT_WHITE] ); + CG_DrawRotatePic2( RADAR_X + RADAR_RADIUS + xOffset, y + RADAR_RADIUS, arrow_w, arrow_h, + 0, cgs.media.mAutomapPlayerIcon ); + + return y+(RADAR_RADIUS*2); +} /* ================= @@ -4033,6 +4364,9 @@ static void CG_Draw2D( void ) if (cg_drawTimer.integer) { y=CG_DrawTimer(y); } + if (cg_drawRadar.integer) { + y=CG_DrawRadar(y); + } // don't draw center string if scoreboard is up if ( !CG_DrawScoreboard() ) { diff --git a/code/cgame/cg_effects.cpp b/code/cgame/cg_effects.cpp index 29a8cc9cba..bd58dc4fa1 100644 --- a/code/cgame/cg_effects.cpp +++ b/code/cgame/cg_effects.cpp @@ -199,11 +199,11 @@ void CG_SurfaceExplosion( vec3_t origin, vec3_t normal, float radius, float shak int i, numSparks; //Sparks - numSparks = 16 + (random() * 16.0f); + numSparks = 16 + (Q_flrand(0.0f, 1.0f) * 16.0f); for ( i = 0; i < numSparks; i++ ) { - scale = 0.25f + (random() * 2.0f); + scale = 0.25f + (Q_flrand(0.0f, 1.0f) * 2.0f); dscale = -scale*0.5; particle = FX_AddTrail( origin, @@ -232,17 +232,17 @@ void CG_SurfaceExplosion( vec3_t origin, vec3_t normal, float radius, float shak for ( i = 0; i < 4; i++ ) { - VectorSet( temp_org, new_org[0] + (crandom() * 16.0f), new_org[1] + (crandom() * 16.0f), new_org[2] + (random() * 4.0f) ); - VectorSet( temp_vel, velocity[0] + (crandom() * 8.0f), velocity[1] + (crandom() * 8.0f), velocity[2] + (crandom() * 8.0f) ); + VectorSet( temp_org, new_org[0] + (Q_flrand(-1.0f, 1.0f) * 16.0f), new_org[1] + (Q_flrand(-1.0f, 1.0f) * 16.0f), new_org[2] + (Q_flrand(0.0f, 1.0f) * 4.0f) ); + VectorSet( temp_vel, velocity[0] + (Q_flrand(-1.0f, 1.0f) * 8.0f), velocity[1] + (Q_flrand(-1.0f, 1.0f) * 8.0f), velocity[2] + (Q_flrand(-1.0f, 1.0f) * 8.0f) ); FX_AddSprite( temp_org, temp_vel, NULL, - 64.0f + (random() * 32.0f), + 64.0f + (Q_flrand(0.0f, 1.0f) * 32.0f), 16.0f, 1.0f, 0.0f, - 20.0f + (crandom() * 90.0f), + 20.0f + (Q_flrand(-1.0f, 1.0f) * 90.0f), 0.5f, 1500.0f, cgs.media.smokeShader, FXF_USE_ALPHA_CHAN ); @@ -255,14 +255,14 @@ void CG_SurfaceExplosion( vec3_t origin, vec3_t normal, float radius, float shak VectorNormalize( direction ); //Tag the last one with a light - le = CG_MakeExplosion( origin, direction, cgs.media.explosionModel, 6, cgs.media.surfaceExplosionShader, 500, qfalse, radius * 0.02f + (random() * 0.3f) ); + le = CG_MakeExplosion( origin, direction, cgs.media.explosionModel, 6, cgs.media.surfaceExplosionShader, 500, qfalse, radius * 0.02f + (Q_flrand(0.0f, 1.0f) * 0.3f) ); le->light = 150; VectorSet( le->lightColor, 0.9f, 0.8f, 0.5f ); for ( i = 0; i < NUM_EXPLOSIONS-1; i ++) { - VectorSet( new_org, (origin[0] + (16 + (crandom() * 8))*crandom()), (origin[1] + (16 + (crandom() * 8))*crandom()), (origin[2] + (16 + (crandom() * 8))*crandom()) ); - le = CG_MakeExplosion( new_org, direction, cgs.media.explosionModel, 6, cgs.media.surfaceExplosionShader, 300 + (rand() & 99), qfalse, radius * 0.05f + (crandom() *0.3f) ); + VectorSet( new_org, (origin[0] + (16 + (Q_flrand(-1.0f, 1.0f) * 8))*Q_flrand(-1.0f, 1.0f)), (origin[1] + (16 + (Q_flrand(-1.0f, 1.0f) * 8))*Q_flrand(-1.0f, 1.0f)), (origin[2] + (16 + (Q_flrand(-1.0f, 1.0f) * 8))*Q_flrand(-1.0f, 1.0f)) ); + le = CG_MakeExplosion( new_org, direction, cgs.media.explosionModel, 6, cgs.media.surfaceExplosionShader, 300 + (rand() & 99), qfalse, radius * 0.05f + (Q_flrand(-1.0f, 1.0f) *0.3f) ); } //Shake the camera @@ -274,11 +274,11 @@ void CG_SurfaceExplosion( vec3_t origin, vec3_t normal, float radius, float shak if ( smoke ) { VectorMA( origin, -8, normal, temp_org ); -// FX_AddSpawner( temp_org, normal, NULL, NULL, 100, random()*25.0f, 5000.0f, (void *) CG_SmokeSpawn ); +// FX_AddSpawner( temp_org, normal, NULL, NULL, 100, Q_flrand(0.0f, 1.0f)*25.0f, 5000.0f, (void *) CG_SmokeSpawn ); //Impact mark //FIXME: Replace mark - //CG_ImpactMark( cgs.media.burnMarkShader, origin, normal, random()*360, 1,1,1,1, qfalse, 8, qfalse ); + //CG_ImpactMark( cgs.media.burnMarkShader, origin, normal, Q_flrand(0.0f, 1.0f)*360, 1,1,1,1, qfalse, 8, qfalse ); } } */ @@ -371,7 +371,7 @@ void CG_MiscModelExplosion( vec3_t mins, vec3_t maxs, int size, material_t chunk { for( int j = 0; j < 3; j++ ) { - r = random() * 0.8f + 0.1f; + r = Q_flrand(0.0f, 1.0f) * 0.8f + 0.1f; org[j] = ( r * mins[j] + ( 1 - r ) * maxs[j] ); } @@ -567,12 +567,12 @@ void CG_Chunks( int owner, vec3_t origin, const vec3_t normal, const vec3_t mins re->hModel = chunkModel; le->leType = LE_FRAGMENT; - le->endTime = cg.time + 1300 + random() * 900; + le->endTime = cg.time + 1300 + Q_flrand(0.0f, 1.0f) * 900; // spawn chunk roughly in the bbox of the thing...bias towards center in case thing blowing up doesn't complete fill its bbox. for( j = 0; j < 3; j++ ) { - r = random() * 0.8f + 0.1f; + r = Q_flrand(0.0f, 1.0f) * 0.8f + 0.1f; re->origin[j] = ( r * mins[j] + ( 1 - r ) * maxs[j] ); } VectorCopy( re->origin, le->pos.trBase ); @@ -583,18 +583,18 @@ void CG_Chunks( int owner, vec3_t origin, const vec3_t normal, const vec3_t mins VectorScale( dir, Q_flrand( speed * 0.5f, speed * 1.25f ) * speedMod, le->pos.trDelta ); // Angular Velocity - VectorSet( le->angles.trBase, random() * 360, random() * 360, random() * 360 ); + VectorSet( le->angles.trBase, Q_flrand(0.0f, 1.0f) * 360, Q_flrand(0.0f, 1.0f) * 360, Q_flrand(0.0f, 1.0f) * 360 ); - le->angles.trDelta[0] = crandom(); - le->angles.trDelta[1] = crandom(); + le->angles.trDelta[0] = Q_flrand(-1.0f, 1.0f); + le->angles.trDelta[1] = Q_flrand(-1.0f, 1.0f); le->angles.trDelta[2] = 0; // don't do roll - VectorScale( le->angles.trDelta, random() * 600.0f + 200.0f, le->angles.trDelta ); + VectorScale( le->angles.trDelta, Q_flrand(0.0f, 1.0f) * 600.0f + 200.0f, le->angles.trDelta ); le->pos.trType = TR_GRAVITY; le->angles.trType = TR_LINEAR; le->pos.trTime = le->angles.trTime = cg.time; - le->bounceFactor = 0.2f + random() * 0.2f; + le->bounceFactor = 0.2f + Q_flrand(0.0f, 1.0f) * 0.2f; le->leFlags |= LEF_TUMBLE; le->ownerGentNum = owner; le->leBounceSoundType = bounce; @@ -671,7 +671,7 @@ static void CG_DoGlassQuad( vec3_t p[4], vec2_t uv[4], bool stick, int time, vec vec3_t vel, accel; vec3_t rgb1; - VectorSet( vel, crandom() * 12, crandom() * 12, -1 ); + VectorSet( vel, Q_flrand(-1.0f, 1.0f) * 12, Q_flrand(-1.0f, 1.0f) * 12, -1 ); if ( !stick ) { @@ -680,26 +680,26 @@ static void CG_DoGlassQuad( vec3_t p[4], vec2_t uv[4], bool stick, int time, vec } // Set up acceleration due to gravity, 800 is standard QuakeIII gravity, so let's use something close - VectorSet( accel, 0.0f, 0.0f, -(600.0f + random() * 100.0f ) ); + VectorSet( accel, 0.0f, 0.0f, -(600.0f + Q_flrand(0.0f, 1.0f) * 100.0f ) ); VectorSet( rgb1, 1.0f, 1.0f, 1.0f ); // Being glass, we don't want to bounce much - bounce = random() * 0.2f + 0.15f; + bounce = Q_flrand(0.0f, 1.0f) * 0.2f + 0.15f; // Set up our random rotate, we only do PITCH and YAW, not ROLL. This is something like degrees per second - VectorSet( rotDelta, crandom() * 40.0f, crandom() * 40.0f, 0.0f ); + VectorSet( rotDelta, Q_flrand(-1.0f, 1.0f) * 40.0f, Q_flrand(-1.0f, 1.0f) * 40.0f, 0.0f ); CPoly *pol = FX_AddPoly(p, uv, 4, // verts, ST, vertCount vel, accel, // motion 0.15f, 0.0f, 85.0f, // alpha start, alpha end, alpha parm ( begin alpha fade when 85% of life is complete ) rgb1, rgb1, 0.0f, // rgb start, rgb end, rgb parm ( not used ) rotDelta, bounce, time, // rotation amount, bounce, and time to delay motion for ( zero if no delay ); - 3500 + random() * 1000, // life + 3500 + Q_flrand(0.0f, 1.0f) * 1000, // life cgi_R_RegisterShader( "gfx/misc/test_crackle" ), FX_APPLY_PHYSICS | FX_ALPHA_NONLINEAR | FX_USE_ALPHA ); - if ( random() > 0.95f && pol ) + if ( Q_flrand(0.0f, 1.0f) > 0.95f && pol ) { pol->AddFlags( FX_IMPACT_RUNS_FX | FX_KILL_ON_IMPACT ); pol->SetImpactFxID( theFxScheduler.RegisterEffect( "misc/glass_impact" )); @@ -779,8 +779,8 @@ void CG_InitGlass( void ) { for ( t = 0; t < 20; t++ ) { - offX[t][i] = crandom() * 0.03f; - offZ[i][t] = crandom() * 0.03f; + offX[t][i] = Q_flrand(-1.0f, 1.0f) * 0.03f; + offZ[i][t] = Q_flrand(-1.0f, 1.0f) * 0.03f; } } } @@ -874,7 +874,7 @@ void CG_DoGlass( vec3_t verts[4], vec3_t normal, vec3_t dmgPt, vec3_t dmgDir, fl zz = z; } - Vector2Set( biPoints[0], xx, zz ); + VectorSet2( biPoints[0], xx, zz ); if ( t + 1 > 0 && t + 1 < mxWidth ) { @@ -894,7 +894,7 @@ void CG_DoGlass( vec3_t verts[4], vec3_t normal, vec3_t dmgPt, vec3_t dmgDir, fl zz = z; } - Vector2Set( biPoints[1], xx + stepWidth, zz ); + VectorSet2( biPoints[1], xx + stepWidth, zz ); if ( t + 1 > 0 && t + 1 < mxWidth ) { @@ -914,7 +914,7 @@ void CG_DoGlass( vec3_t verts[4], vec3_t normal, vec3_t dmgPt, vec3_t dmgDir, fl zz = z; } - Vector2Set( biPoints[2], xx + stepWidth, zz + stepHeight); + VectorSet2( biPoints[2], xx + stepWidth, zz + stepHeight); if ( t > 0 && t < mxWidth ) { @@ -934,11 +934,11 @@ void CG_DoGlass( vec3_t verts[4], vec3_t normal, vec3_t dmgPt, vec3_t dmgDir, fl zz = z; } - Vector2Set( biPoints[3], xx, zz + stepHeight ); + VectorSet2( biPoints[3], xx, zz + stepHeight ); CG_CalcBiLerp( verts, subVerts, biPoints ); - float dif = DistanceSquared( subVerts[0], dmgPt ) * timeDecay - random() * 32; + float dif = DistanceSquared( subVerts[0], dmgPt ) * timeDecay - Q_flrand(0.0f, 1.0f) * 32; // If we decrease dif, we are increasing the impact area, making it more likely to blow out large holes dif -= dmgRadius * dmgRadius; @@ -946,7 +946,7 @@ void CG_DoGlass( vec3_t verts[4], vec3_t normal, vec3_t dmgPt, vec3_t dmgDir, fl if ( dif > 1 ) { stick = true; - time = dif + random() * 200; + time = dif + Q_flrand(0.0f, 1.0f) * 200; } else { diff --git a/code/cgame/cg_ents.cpp b/code/cgame/cg_ents.cpp index 7af9ad1f67..de5ab0f7c8 100644 --- a/code/cgame/cg_ents.cpp +++ b/code/cgame/cg_ents.cpp @@ -307,6 +307,16 @@ void ScaleModelAxis(refEntity_t *ent) Ghoul2 Insert End */ +void CG_AddRadarEnt(centity_t *cent) +{ + static const size_t numRadarEnts = ARRAY_LEN( cg.radarEntities ); + if (cg.radarEntityCount >= numRadarEnts) + { + return; + } + cg.radarEntities[cg.radarEntityCount++] = cent->currentState.number; +} + /* ================== CG_General @@ -317,7 +327,14 @@ static void CG_General( centity_t *cent ) refEntity_t ent; entityState_t *s1; +//Check if it's a radar entity + if (cent->currentState.eFlags2 & EF2_RADAROBJECT) + { + CG_AddRadarEnt(cent); + } + s1 = ¢->currentState; + /* Ghoul2 Insert Start */ @@ -339,7 +356,7 @@ Ghoul2 Insert End memset (&ent, 0, sizeof(ent)); // set frame - + if ( cent->currentState.eFlags & EF_DISABLE_SHADER_ANIM ) { // by setting the shader time to the current time, we can force an animating shader to not animate @@ -835,7 +852,7 @@ Ghoul2 Insert End // Only display when we have damage if ( t >= 0.0f && t <= 1.0f ) { - t *= random(); + t *= Q_flrand(0.0f, 1.0f); ent.shaderRGBA[0] = ent.shaderRGBA[1] = ent.shaderRGBA[2] = 255.0f * t; ent.shaderRGBA[3] = 255; @@ -886,7 +903,7 @@ static void CG_Speaker( centity_t *cent ) { // ent->s.frame = ent->wait * 10; // ent->s.clientNum = ent->random * 10; - cent->miscTime = (int)(cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * crandom()); + cent->miscTime = (int)(cg.time + cent->currentState.frame * 100 + cent->currentState.clientNum * 100 * Q_flrand(-1.0f, 1.0f)); } /* @@ -933,12 +950,14 @@ Ghoul2 Insert End memset( &ent, 0, sizeof( ent ) ); ent.reType = RT_SPRITE; VectorCopy( cent->lerpOrigin, ent.origin ); + ent.origin[2] += 16; ent.radius = 14; ent.customShader = cg_items[es->modelindex].icon; ent.shaderRGBA[0] = 255; ent.shaderRGBA[1] = 255; ent.shaderRGBA[2] = 255; ent.shaderRGBA[3] = 255; + ent.renderfx |= RF_FORCE_ENT_ALPHA; cgi_R_AddRefEntityToScene(&ent); return; } @@ -1113,6 +1132,12 @@ static void CG_Missile( centity_t *cent ) { if ( s1->weapon >= WP_NUM_WEAPONS ) { s1->weapon = 0; } + + if (cent->currentState.eFlags2 & EF2_RADAROBJECT) + { + CG_AddRadarEnt(cent); + } + weapon = &cg_weapons[s1->weapon]; wData = &weaponData[s1->weapon]; @@ -1172,6 +1197,18 @@ static void CG_Missile( centity_t *cent ) { return; } } + else if (s1->powerups & (1<weapon == WP_CONCUSSION ) + { + FX_DestructionProjectileThink( cent, weapon ); + cgi_R_AddLightToScene(cent->lerpOrigin, 125, + 1.0, 0.25, 0.75 ); + cgi_S_AddLoopingSound( cent->currentState.number, cent->lerpOrigin, vec3_origin, cgs.media.destructionSound); + return; + + } + } else if ( cent->gent->alt_fire ) { // add trails @@ -1310,6 +1347,10 @@ Ghoul2 Insert Start /* Ghoul2 Insert End */ + if (cent->currentState.eFlags2 & EF2_RADAROBJECT) + { + CG_AddRadarEnt(cent); + } ent.renderfx = RF_NOSHADOW; @@ -2488,6 +2529,9 @@ void CG_AddPacketEntities( qboolean isPortal ) { AnglesToAxis( cg.autoAngles, cg.autoAxis ); AnglesToAxis( cg.autoAnglesFast, cg.autoAxisFast ); + + // Reset radar entities + cg.radarEntityCount = 0; // generate and add the entity from the playerstate ps = &cg.predicted_player_state; diff --git a/code/cgame/cg_event.cpp b/code/cgame/cg_event.cpp index 41e0ea51f4..142a39bb86 100644 --- a/code/cgame/cg_event.cpp +++ b/code/cgame/cg_event.cpp @@ -106,6 +106,11 @@ void CG_ItemPickup( int itemNum, qboolean bHadItem ) { cgi_Cvar_Set( "cg_WeaponPickupText", va("%s %s\n", text, data)); cg.weaponPickupTextTime = cg.time + 5000; } + else if ( cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",bg_itemlist[itemNum].classname ), data, sizeof( data ))) + { + cgi_Cvar_Set( "cg_WeaponPickupText", va("%s %s\n", text, data)); + cg.weaponPickupTextTime = cg.time + 5000; + } } } @@ -115,7 +120,7 @@ void CG_ItemPickup( int itemNum, qboolean bHadItem ) { const int nCurWpn = cg.predicted_player_state.weapon; const int nNewWpn = bg_itemlist[itemNum].giTag; - if ( nCurWpn == WP_SABER || bHadItem) + if ( (nCurWpn == WP_SABER && nNewWpn != WP_EMPLACED_GUN) || bHadItem) {//never switch away from the saber! return; } @@ -129,7 +134,12 @@ void CG_ItemPickup( int itemNum, qboolean bHadItem ) { // NOTE: automatically switching to any weapon you pick up is stupid and annoying and we won't do it. // - if ( nNewWpn == WP_SABER ) + if ( nNewWpn == WP_EMPLACED_GUN ) + { + SetWeaponSelectTime(); + cg.weaponSelect = nNewWpn; + } + else if ( nNewWpn == WP_SABER ) {//always switch to saber SetWeaponSelectTime(); cg.weaponSelect = nNewWpn; @@ -536,7 +546,14 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { case EV_DISRUPTOR_MAIN_SHOT: DEBUGNAME("EV_DISRUPTOR_MAIN_SHOT"); - FX_DisruptorMainShot( cent->currentState.origin2, cent->lerpOrigin ); + if ( cent->currentState.weapon == WP_SONIC_BLASTER ) + { + FX_KothosBeam( cent->currentState.origin2, cent->lerpOrigin ); + } + else + { + FX_DisruptorMainShot( cent->currentState.origin2, cent->lerpOrigin ); + } break; case EV_DISRUPTOR_SNIPER_SHOT: @@ -644,15 +661,6 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) { { if ( disintSound1 && disintSound2 ) {//play an extra sound - /* - if ( cent->gent->owner->client->playerTeam == TEAM_STARFLEET || - cent->gent->owner->client->playerTeam == TEAM_SCAVENGERS || - cent->gent->owner->client->playerTeam == TEAM_MALON || - cent->gent->owner->client->playerTeam == TEAM_IMPERIAL || - cent->gent->owner->client->playerTeam == TEAM_HIROGEN || - cent->gent->owner->client->playerTeam == TEAM_DISGUISE || - cent->gent->owner->client->playerTeam == TEAM_KLINGON ) - */ // listed all the non-humanoids, because there's a lot more humanoids class_t npc_class = cent->gent->owner->client->NPC_class; if( npc_class != CLASS_ATST && npc_class != CLASS_GONK && diff --git a/code/cgame/cg_info.cpp b/code/cgame/cg_info.cpp index 17abcfee02..31dfd8844a 100644 --- a/code/cgame/cg_info.cpp +++ b/code/cgame/cg_info.cpp @@ -55,6 +55,8 @@ int obj_graphics[MAX_OBJ_GRAPHICS]; qboolean CG_ForcePower_Valid(int forceKnownBits, int index); +//extern vmCvar_t ui_loadScreen_iconScaling; + /* ==================== ObjectivePrint_Line @@ -436,11 +438,11 @@ int CG_WeaponCheck( int weaponIndex ); // For printing load screen icons const int MAXLOADICONSPERROW = 8; // Max icons displayed per row -const int MAXLOADWEAPONS = 16; +const int MAXLOADWEAPONS = WP_NUM_WEAPONS; const int MAXLOAD_FORCEICONSIZE = 40; // Size of force power icons const int MAXLOAD_FORCEICONPAD = 12; // Padding space between icons -static int CG_DrawLoadWeaponsPrintRow( const char *itemName, int weaponsBits,int rowIconCnt, int startIndex) +static int CG_DrawLoadWeaponsPrintRow( const char *itemName, char *weapons,int rowIconCnt, int startIndex) { int i,endIndex=0, printedIconCnt=0; int iconSize; @@ -468,12 +470,25 @@ static int CG_DrawLoadWeaponsPrintRow( const char *itemName, int weaponsBits,int iconSize = 60; pad = 12; + /* + if (ui_loadScreen_iconScaling.integer) + { + iconSize = height; + pad = width; + } + else + { + iconSize = 60; + pad = 12; + } + */ + // calculate placement of weapon icons holdX = x + (width - ((iconSize*rowIconCnt) + (pad * (rowIconCnt-1))))/2; for (i=startIndex;ips.stats[STAT_HEALTH], &iDummy, // &client->ps.stats[STAT_ARMOR], - &*weaponBits,// &client->ps.stats[STAT_WEAPONS], &iDummy, // &client->ps.stats[STAT_ITEMS], &iDummy, // &client->ps.weapon, &iDummy, // &client->ps.weaponstate, @@ -692,6 +707,26 @@ static void CG_GetLoadScreenInfo(int *weaponBits,int *forceBits) ); } + + gi.Cvar_VariableStringBuffer( "playerweaps", s, sizeof(s) ); + i=0; + if (s[0]) + { + var = strtok( s, " " ); + while( var != NULL ) + { + /* While there are tokens in "s" */ + weapons[i++] = atoi(var); + /* Get next token: */ + var = strtok( NULL, " " ); + } + assert (i==WP_NUM_WEAPONS); + } + else + { + weapons[0] = -1; + } + // the new JK2 stuff - force powers, etc... // @@ -714,12 +749,13 @@ CG_DrawLoadingScreen Load screen displays the map pic, the mission briefing and weapons/force powers ==================== */ -static void CG_DrawLoadingScreen( qhandle_t levelshot ,const char *mapName) +static void CG_DrawLoadingScreen( qhandle_t levelshot, qhandle_t savepic, const char *mapName) { int xPos,yPos,width,height; vec4_t color; qhandle_t background; - int weapons=0, forcepowers=0; + int forcepowers=0; + char weapons[WP_NUM_WEAPONS]; // Get mission briefing for load screen if (cgi_SP_GetStringTextString( va("BRIEFINGS_%s",mapName), NULL, 0 ) == 0) @@ -768,12 +804,31 @@ static void CG_DrawLoadingScreen( qhandle_t levelshot ,const char *mapName) CG_DrawPic( xPos, yPos, width, height, levelshot ); } } + + // Print savegame pic + /* + if (cgi_UI_GetMenuItemInfo( + "loadScreen", + "savepic", + &xPos, + &yPos, + &width, + &height, + color, + &background)) + { + { + cgi_R_SetColor(color); + CG_DrawPic(xPos, yPos, width, height, savepic); + } + } + */ // Get player weapons and force power info - CG_GetLoadScreenInfo(&weapons,&forcepowers); + CG_GetLoadScreenInfo(weapons,&forcepowers); // Print weapon icons - if (weapons) + if (weapons[0] > -1) { CG_DrawLoadWeapons(weapons); } @@ -798,8 +853,11 @@ void CG_DrawInformation( void ) { // draw the dialog background const char *info = CG_ConfigString( CS_SERVERINFO ); const char *s = Info_ValueForKey( info, "mapname" ); - - qhandle_t levelshot; + char t[1024]; + gi.Cvar_VariableStringBuffer(sCVARNAME_PLAYERSAVE, t, sizeof(t)); //better way to get savegame name? + + qhandle_t levelshot; //the map picture + qhandle_t savepic = 0; //the screenshot of the player's game extern SavedGameJustLoaded_e g_eSavedGameJustLoaded; // hack! (hey, it's the last week of coding, ok? // if ( g_eSavedGameJustLoaded == eFULL ) @@ -818,8 +876,13 @@ void CG_DrawInformation( void ) { if (!levelshot) { levelshot = cgi_R_RegisterShaderNoMip( "menu/art/unknownmap" ); } + if (!savepic) { + savepic = cgi_R_RegisterShaderNoMip("menu/art/unknownmap"); + } } + //savepic = cgi_R_RegisterShaderNoMip(va("saves/screenshots/%s", t)); + if ( g_eSavedGameJustLoaded != eFULL && !strcmp(s,"yavin1") )//special case for first map! { char text[1024]={0}; @@ -835,7 +898,7 @@ void CG_DrawInformation( void ) { } else { - CG_DrawLoadingScreen(levelshot, s); + CG_DrawLoadingScreen(levelshot, savepic, s); cgi_UI_Menu_Paint( cgi_UI_GetMenuByName( "loadscreen" ), qtrue ); //cgi_UI_MenuPaintAll(); } diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index b9d02eac83..dffc528cf0 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -158,6 +158,7 @@ struct centity_s qboolean currentValid; // true if cg.frame holds this entity int muzzleFlashTime; // move to playerEntity? + int muzzleFlashWeapon; qboolean altFire; // move to playerEntity? int previousEvent; @@ -513,15 +514,17 @@ Ghoul2 Insert Start Ghoul2 Insert End */ overrides_t overrides; //for overriding certain third-person camera properties - + + short radarEntityCount; + short radarEntities[32]; } cg_t; -#define MAX_SHOWPOWERS 12 +#define MAX_SHOWPOWERS 20 extern int showPowers[MAX_SHOWPOWERS]; extern const char *showPowersName[MAX_SHOWPOWERS]; extern int force_icons[NUM_FORCE_POWERS]; -#define MAX_DPSHOWPOWERS 16 +#define MAX_DPSHOWPOWERS 23 //============================================================================== @@ -655,6 +658,23 @@ extern vmCvar_t cg_speedTrail; extern vmCvar_t cg_fovViewmodel; extern vmCvar_t cg_fovViewmodelAdjust; +extern vmCvar_t cg_scaleVehicleSensitivity; +extern vmCvar_t cg_lightningBlockEffect; + +extern vmCvar_t cg_trueguns; +extern vmCvar_t cg_fpls; +extern vmCvar_t cg_drawRadar; + +extern vmCvar_t cg_trueroll; +extern vmCvar_t cg_trueflip; +extern vmCvar_t cg_truespin; +extern vmCvar_t cg_truemoveroll; +extern vmCvar_t cg_truesaberonly; +extern vmCvar_t cg_trueeyeposition; +extern vmCvar_t cg_trueinvertsaber; +extern vmCvar_t cg_truefov; +extern vmCvar_t cg_truebobbing; + void CG_NewClientinfo( int clientNum ); // // cg_main.c @@ -822,6 +842,7 @@ void CG_DPPrevForcePower_f( void ); void CG_RegisterWeapon( int weaponNum ); +void CG_DeregisterWeapon( int weaponNum ); void CG_RegisterItemVisuals( int itemNum ); void CG_RegisterItemSounds( int itemNum ); @@ -1162,6 +1183,15 @@ void FX_NoghriShotProjectileThink( centity_t *cent, const struct weaponInfo_s *w void FX_NoghriShotWeaponHitWall( vec3_t origin, vec3_t normal ); void FX_NoghriShotWeaponHitPlayer( gentity_t *hit, vec3_t origin, vec3_t normal, qboolean humanoid ); +void FX_CloneBlasterProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon ); +void FX_CloneBlasterAltFireThink( centity_t *cent, const struct weaponInfo_s *weapon ); +void FX_CloneBlasterWeaponHitWall( vec3_t origin, vec3_t normal ); +void FX_CloneBlasterWeaponHitPlayer( gentity_t *hit, vec3_t origin, vec3_t normal, qboolean humanoid ); + +void FX_DestructionProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon ); +void FX_DestructionHitWall( vec3_t origin, vec3_t normal ); +void FX_DestructionHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid ); + void CG_BounceEffect( centity_t *cent, int weapon, vec3_t origin, vec3_t normal ); void CG_MissileStick( centity_t *cent, int weapon, vec3_t origin ); @@ -1231,4 +1261,8 @@ void CG_ClearLightStyles( void ); void CG_RunLightStyles( void ); void CG_SetLightstyle( int i ); +//trueview stuff +void CG_TrueViewInit( void ); +void CG_AdjustEyePos (const char *modelName); + #endif //__CG_LOCAL_H__ diff --git a/code/cgame/cg_main.cpp b/code/cgame/cg_main.cpp index ddd9efc126..2b05c4c14e 100644 --- a/code/cgame/cg_main.cpp +++ b/code/cgame/cg_main.cpp @@ -28,6 +28,8 @@ along with this program; if not, see . #include "g_local.h" #include "../qcommon/sstring.h" +#include "qcommon/ojk_saved_game_helper.h" + //NOTENOTE: Be sure to change the mirrored code in g_shared.h typedef std::map< sstring_t, unsigned char > namePrecache_m; extern namePrecache_m *as_preCacheMap; @@ -337,6 +339,47 @@ vmCvar_t cg_speedTrail; vmCvar_t cg_fovViewmodel; vmCvar_t cg_fovViewmodelAdjust; +vmCvar_t cg_scaleVehicleSensitivity; +vmCvar_t cg_lightningBlockEffect; + +//new cvars - Dusty +/* +vmCvar_t cg_lightningBolts; +vmCvar_t cg_lightningWideBolts; +vmCvar_t cg_pushBlurSpeed; +vmCvar_t cg_pushBlurSize; +vmCvar_t cg_pullBlurSpeed; +vmCvar_t cg_pullBlurSize; +*/ + +vmCvar_t cg_SFXSabers; +vmCvar_t cg_SFXSabersGlowSize; +vmCvar_t cg_SFXSabersCoreSize; + +vmCvar_t cg_ignitionFlare; +vmCvar_t cg_ignitionSpeed; + +vmCvar_t cg_gunMomentumDamp; +vmCvar_t cg_gunMomentumFall; +vmCvar_t cg_gunMomentumEnable; +vmCvar_t cg_gunMomentumInterval; + + +vmCvar_t cg_trueguns; +vmCvar_t cg_fpls; + +vmCvar_t cg_drawRadar; + +vmCvar_t cg_trueroll; +vmCvar_t cg_trueflip; +vmCvar_t cg_truespin; +vmCvar_t cg_truemoveroll; +vmCvar_t cg_truesaberonly; +vmCvar_t cg_trueeyeposition; +vmCvar_t cg_trueinvertsaber; +vmCvar_t cg_truefov; +vmCvar_t cg_truebobbing; + typedef struct { vmCvar_t *vmCvar; const char *cvarName; @@ -453,6 +496,39 @@ static cvarTable_t cvarTable[] = { { &cg_speedTrail, "cg_speedTrail", "1", CVAR_ARCHIVE }, { &cg_fovViewmodel, "cg_fovViewmodel", "0", CVAR_ARCHIVE }, { &cg_fovViewmodelAdjust, "cg_fovViewmodelAdjust", "1", CVAR_ARCHIVE }, + + { &cg_scaleVehicleSensitivity, "cg_scaleVehicleSensitivity", "1", CVAR_ARCHIVE }, + { &cg_lightningBlockEffect, "cg_lightningBlockEffect", "force/lightning", CVAR_ARCHIVE }, + + { &cg_SFXSabers, "cg_SFXSabers", "1", CVAR_ARCHIVE }, + { &cg_SFXSabersGlowSize, "cg_SFXSabersGlowSize", "1.0", CVAR_ARCHIVE }, + { &cg_SFXSabersCoreSize, "cg_SFXSabersCoreSize", "1.0", CVAR_ARCHIVE }, + + { &cg_ignitionFlare, "cg_ignitionFlare", "1", CVAR_ARCHIVE }, + { &cg_ignitionSpeed, "cg_ignitionSpeed", "1.0", CVAR_ARCHIVE }, + + { &cg_ignitionSpeed, "cg_ignitionSpeed", "1.0", CVAR_ARCHIVE }, + + { &cg_gunMomentumDamp, "cg_gunMomentumDamp", "0.001", CVAR_ARCHIVE }, + { &cg_gunMomentumFall, "cg_gunMomentumFall", "0.5", CVAR_ARCHIVE }, + { &cg_gunMomentumEnable, "cg_gunMomentumEnable", "0", CVAR_ARCHIVE }, + { &cg_gunMomentumInterval, "cg_gunMomentumInterval", "75", CVAR_ARCHIVE }, + + + { &cg_drawRadar, "cg_drawRadar", "1", CVAR_ARCHIVE }, + + //True View Control cvars + { &cg_trueguns, "cg_trueguns", "0", CVAR_ARCHIVE }, + { &cg_fpls, "cg_fpls", "0", CVAR_ARCHIVE }, + { &cg_trueroll, "cg_trueroll", "0", CVAR_ARCHIVE }, + { &cg_trueflip, "cg_trueflip", "0", CVAR_ARCHIVE }, + { &cg_truespin, "cg_truespin", "0", CVAR_ARCHIVE }, + { &cg_truemoveroll, "cg_truemoveroll", "0", CVAR_ARCHIVE }, + { &cg_truesaberonly, "cg_truesaberonly", "0", CVAR_ARCHIVE }, + { &cg_trueeyeposition, "cg_trueeyeposition", "0.0", 0}, + { &cg_trueinvertsaber, "cg_trueinvertsaber", "0", CVAR_ARCHIVE}, + { &cg_truefov, "cg_truefov", "80", CVAR_ARCHIVE}, + { &cg_truebobbing, "cg_truebobbing", "1", CVAR_ARCHIVE}, }; static const size_t cvarTableSize = ARRAY_LEN( cvarTable ); @@ -536,6 +612,12 @@ int CG_GetCameraPos( vec3_t camerapos ) { VectorCopy( cg.refdef.vieworg, camerapos ); return 1; } + else if ( cg_trueguns.integer && !cg.zoomMode ) + {//in third person + //FIXME: what about hacks that render in third person regardless of this value? + VectorCopy( cg.refdef.vieworg, camerapos ); + return 1; + } return 0; } @@ -1118,7 +1200,7 @@ static void CG_RegisterEffects( void ) cgi_R_WorldEffectCommand( effectName ); } - + // Set up the glass effects mini-system. CG_InitGlass(); @@ -1260,6 +1342,66 @@ HUDMenuItem_t otherHUDBits[] = "gfx/mp/f_icon_saber_throw" //FP_SABERTHROW }; */ + +extern int BG_SiegeGetPairedValue(char *buf, char *key, char *outbuf); + +void CG_LoadMinimapImages( void ) +{ + int len = 0; + fileHandle_t f; + char minimapData[1024]; + char readBuffer[MAX_QPATH]; + char fileName[MAX_QPATH]; + + cgs.radarMap.numMinimapImages = 0; + + const char *info = CG_ConfigString( CS_SERVERINFO ); + const char *s = Info_ValueForKey( info, "mapname" ); + + Com_sprintf(fileName, sizeof(fileName), "minimaps/%s.mmp", s); + + len = gi.FS_FOpenFile(fileName, &f, FS_READ); + + if (!f) + { + return; + } + + if (len >= 1024) + { + gi.FS_FCloseFile( f ); + return; + } + + gi.FS_Read(minimapData, len, f); + gi.FS_FCloseFile( f ); + + if ( !BG_SiegeGetPairedValue(minimapData, "bottomRight", readBuffer) ) + { + return; + } + sscanf( readBuffer, "%f %f", &cgs.radarMap.bottomRight[0], &cgs.radarMap.bottomRight[1]); + + if ( !BG_SiegeGetPairedValue(minimapData, "topLeft", readBuffer) ) + { + return; + } + sscanf( readBuffer, "%f %f", &cgs.radarMap.topLeft[0], &cgs.radarMap.topLeft[1]); + + if ( !BG_SiegeGetPairedValue(minimapData, "image", readBuffer) ) + { + return; + } + cgs.radarMap.minimapImage[0] = cgi_R_RegisterShaderNoMip( readBuffer ); + + if ( BG_SiegeGetPairedValue(minimapData, "height", readBuffer) ) + { + cgs.radarMap.minimapHeights[0] = atof(readBuffer); + } + + cgs.radarMap.numMinimapImages = 1; +} + extern void CG_NPC_Precache ( gentity_t *spawner ); qboolean NPCsPrecached = qfalse; /* @@ -1330,6 +1472,8 @@ static void CG_RegisterGraphics( void ) { CG_LoadingString( cgs.mapname ); cgi_R_LoadWorldMap( cgs.mapname ); + + CG_LoadMinimapImages(); cg.loadLCARSStage = 4; CG_LoadingString( "game media shaders" ); @@ -1340,6 +1484,11 @@ static void CG_RegisterGraphics( void ) { cgs.media.smallnumberShaders[i] = cgi_R_RegisterShaderNoMip( sb_t_nums[i] ); cgs.media.chunkyNumberShaders[i] = cgi_R_RegisterShaderNoMip( sb_c_nums[i] ); } + + cgs.media.radarShader = cgi_R_RegisterShaderNoMip ( "gfx/menus/radar/radar.png" ); + cgs.media.siegeItemShader = cgi_R_RegisterShaderNoMip ( "gfx/menus/radar/goalitem_new" ); + cgs.media.mAutomapPlayerIcon = cgi_R_RegisterShaderNoMip ( "gfx/menus/radar/arrow_w_new" ); + cgs.media.radarMaskShader = cgi_R_RegisterShaderNoMip ( "gfx/menus/spradar/mask" ); // FIXME: conditionally do this?? Something must be wrong with inventory item caching..? cgi_R_RegisterModel( "models/items/remote.md3" ); @@ -1362,7 +1511,7 @@ static void CG_RegisterGraphics( void ) { // FIXME: do these conditionally cgi_R_RegisterShader( "gfx/2d/workingCamera" ); cgi_R_RegisterShader( "gfx/2d/brokenCamera" ); - //cgi_R_RegisterShader( "gfx/effects/irid_shield" ); // for galak, but he doesn't have his own weapon so I can't register the shader there. + cgi_R_RegisterShader( "gfx/effects/irid_shield" ); // for galak, but he doesn't have his own weapon so I can't register the shader there. //interface for ( i = 0 ; i < NUM_CROSSHAIRS ; i++ ) { @@ -1378,7 +1527,18 @@ static void CG_RegisterGraphics( void ) { //gore decal shaders -rww cgs.media.bdecal_burnmark1 = cgi_R_RegisterShader( "gfx/damage/burnmark1" ); cgs.media.bdecal_saberglowmark = cgi_R_RegisterShader( "gfx/damage/saberglowmark" ); - + + cgs.media.SaberTrailShader = cgi_R_RegisterShader( "SFX_Sabers/saber_trail" ); + cgs.media.SaberBladeShader = cgi_R_RegisterShader( "SFX_Sabers/saber_blade" ); + cgs.media.SaberEndShader = cgi_R_RegisterShader( "SFX_Sabers/saber_end" ); + + cgs.media.blackSaberTrailShader = cgi_R_RegisterShader( "SFX_Sabers/black_trail" ); + cgs.media.blackSaberBladeShader = cgi_R_RegisterShader( "SFX_Sabers/black_blade" ); + cgs.media.blackSaberEndShader = cgi_R_RegisterShader( "SFX_Sabers/black_end" ); + + cgs.media.ignitionFlare = cgi_R_RegisterShader( "gfx/effects/flare1" ); + cgs.media.blackIgnitionFlare = cgi_R_RegisterShader( "gfx/effects/sabers/flare1black" ); + cg.loadLCARSStage = 5; CG_LoadingString( "game media models" ); @@ -1635,6 +1795,22 @@ Ghoul2 Insert End { cgi_R_RegisterShader( g_entities[i].client->ps.saber[0].g2WeaponMarkShader2 ); } + if ( g_entities[i].client->ps.saber[0].ignitionFlare[0] ) + { + cgi_R_RegisterShader( g_entities[i].client->ps.saber[0].ignitionFlare ); + } + if ( g_entities[i].client->ps.saber[0].ignitionFlare2[0] ) + { + cgi_R_RegisterShader( g_entities[i].client->ps.saber[0].ignitionFlare2 ); + } + if ( g_entities[i].client->ps.saber[0].blackIgnitionFlare[0] ) + { + cgi_R_RegisterShader( g_entities[i].client->ps.saber[0].blackIgnitionFlare ); + } + if ( g_entities[i].client->ps.saber[0].blackIgnitionFlare2[0] ) + { + cgi_R_RegisterShader( g_entities[i].client->ps.saber[0].blackIgnitionFlare2 ); + } if ( g_entities[i].client->ps.saber[1].g2MarksShader[0] ) { cgi_R_RegisterShader( g_entities[i].client->ps.saber[1].g2MarksShader ); @@ -1651,6 +1827,22 @@ Ghoul2 Insert End { cgi_R_RegisterShader( g_entities[i].client->ps.saber[1].g2WeaponMarkShader2 ); } + if ( g_entities[i].client->ps.saber[1].ignitionFlare[0] ) + { + cgi_R_RegisterShader( g_entities[i].client->ps.saber[1].ignitionFlare ); + } + if ( g_entities[i].client->ps.saber[1].ignitionFlare2[0] ) + { + cgi_R_RegisterShader( g_entities[i].client->ps.saber[1].ignitionFlare2 ); + } + if ( g_entities[i].client->ps.saber[1].blackIgnitionFlare[0] ) + { + cgi_R_RegisterShader( g_entities[i].client->ps.saber[1].blackIgnitionFlare ); + } + if ( g_entities[i].client->ps.saber[1].blackIgnitionFlare2[0] ) + { + cgi_R_RegisterShader( g_entities[i].client->ps.saber[1].blackIgnitionFlare2 ); + } CG_RegisterNPCCustomSounds( &g_entities[i].client->clientInfo ); //CG_RegisterNPCEffects( g_entities[i].client->playerTeam ); } @@ -1750,6 +1942,21 @@ Ghoul2 Insert End // Send off the terrainInfo to the renderer cgi_RE_InitRendererTerrain( terrainInfo ); } + + const char *iconName; + + for ( i = 1 ; i < MAX_ICONS ; i++ ) + { + iconName = ( char *)CG_ConfigString( CS_ICONS + i ); + + if ( !iconName[0] ) + { + break; + } + + cgs.media.radarIcons[i] = cgi_R_RegisterShaderNoMip( iconName ); + } + } //=========================================================================== @@ -1795,7 +2002,7 @@ void CG_StartMusic( qboolean bForceStart ) { Q_strncpyz( parm2, COM_Parse( &s ), sizeof( parm2 ) ); COM_EndParseSession(); - cgi_S_StartBackgroundTrack( parm1, parm2, !bForceStart ); + cgi_S_StartBackgroundTrack( parm1, parm2, (qboolean)!bForceStart ); } /* @@ -1899,16 +2106,33 @@ static void CG_GameStateReceived( void ) { } -void CG_WriteTheEvilCGHackStuff(void) +void CG_WriteTheEvilCGHackStuff() { - gi.AppendToSaveGame(INT_ID('F','P','S','L'), &cg.forcepowerSelect, sizeof(cg.forcepowerSelect)); - gi.AppendToSaveGame(INT_ID('I','V','S','L'), &cg.inventorySelect, sizeof(cg.inventorySelect)); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + saved_game.write_chunk( + INT_ID('F', 'P', 'S', 'L'), + ::cg.forcepowerSelect); + + saved_game.write_chunk( + INT_ID('I', 'V', 'S', 'L'), + ::cg.inventorySelect); } -void CG_ReadTheEvilCGHackStuff(void) + +void CG_ReadTheEvilCGHackStuff() { - gi.ReadFromSaveGame(INT_ID('F','P','S','L'), (void *)&gi_cg_forcepowerSelect, sizeof(gi_cg_forcepowerSelect), NULL); - gi.ReadFromSaveGame(INT_ID('I','V','S','L'), (void *)&gi_cg_inventorySelect, sizeof(gi_cg_inventorySelect), NULL); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.read_chunk( + INT_ID('F', 'P', 'S', 'L'), + ::gi_cg_forcepowerSelect); + + saved_game.read_chunk( + INT_ID('I', 'V', 'S', 'L'), + ::gi_cg_inventorySelect); + gbUseTheseValuesFromLoadSave = qtrue; } @@ -2116,6 +2340,13 @@ void CG_Init( int serverCommandSequence ) { "gfx/mp/f_icon_lt_absorb", //FP_ABSORB, "gfx/mp/f_icon_dk_drain", //FP_DRAIN, "gfx/mp/f_icon_sight", //FP_SEE, + "gfx/mp/f_icon_dk_destruction", //FP_DESTRUCTION, + "gfx/mp/f_icon_dk_insanity", //FP_INSANITY, + "gfx/mp/f_icon_lt_stasis", //FP_STASIS, + "gfx/mp/f_icon_lt_blinding", //FP_BLINDING, + "gfx/mp/f_icon_dk_deadlysight", //FP_DEADLYSIGHT + "gfx/mp/f_icon_repulse", //FP_REPULSE + "gfx/mp/f_icon_lt_invulnerability", //FP_INVULNERABILITY }; // Precache inventory icons @@ -2141,6 +2372,8 @@ void CG_Init( int serverCommandSequence ) { CG_GameStateReceived(); CG_InitConsoleCommands(); + + CG_TrueViewInit(); cg.weaponPickupTextTime = 0; @@ -2162,6 +2395,10 @@ void CG_Shutdown( void ) { in_camera = false; FX_Free(); + + for (int i = 0; i < WP_NUM_WEAPONS; i++) { + CG_DeregisterWeapon(i); + } } //// DEBUG STUFF @@ -3382,6 +3619,17 @@ void CG_DrawInventorySelect( void ) cgi_R_Font_DrawString( x, (SCREEN_HEIGHT - 24), data, textColor, cgs.media.qhFontSmall, -1, 1.0f); } + else + { + Com_sprintf( itemName, sizeof(itemName), "SPMOD_INGAME_%s", item->classname ); + if ( cgi_SP_GetStringTextString( itemName, data, sizeof( data ))) + { + int w = cgi_R_Font_StrLenPixels( data, cgs.media.qhFontSmall, 1.0f ); + int x = ( SCREEN_WIDTH - w ) / 2; + + cgi_R_Font_DrawString( x, (SCREEN_HEIGHT - 24), data, textColor, cgs.media.qhFontSmall, -1, 1.0f); + } + } } } } @@ -3600,7 +3848,10 @@ void CG_DrawDataPadInventorySelect( void ) // draw the weapon description if ((cg.DataPadInventorySelect>=0) && (cg.DataPadInventorySelect<13)) { - cgi_SP_GetStringTextString( va("SP_INGAME_%s",inventoryDesc[cg.DataPadInventorySelect]), text, sizeof(text) ); + if (!cgi_SP_GetStringTextString( va("SP_INGAME_%s",inventoryDesc[cg.DataPadInventorySelect]), text, sizeof(text) )) + { + cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",inventoryDesc[cg.DataPadInventorySelect]), text, sizeof(text) ); + } if (text[0]) { @@ -3639,16 +3890,27 @@ int showPowers[MAX_SHOWPOWERS] = FP_HEAL, FP_PROTECT, FP_TELEPATHY, + + FP_STASIS, + FP_BLINDING, + FP_INVULNERABILITY, FP_SPEED, FP_PUSH, FP_PULL, FP_SEE, + FP_REPULSE, FP_DRAIN, FP_LIGHTNING, FP_RAGE, FP_GRIP, + + FP_DESTRUCTION, + FP_INSANITY, + FP_DEADLYSIGHT, + + FP_SABERTHROW, }; const char *showPowersName[MAX_SHOWPOWERS] = @@ -3657,16 +3919,27 @@ const char *showPowersName[MAX_SHOWPOWERS] = "SP_INGAME_HEAL2", "SP_INGAME_PROTECT2", "SP_INGAME_MINDTRICK2", + + "SPMOD_INGAME_STASIS2", + "SPMOD_INGAME_BLINDING2", + "SPMOD_INGAME_INVULNERABILITY2", "SP_INGAME_SPEED2", "SP_INGAME_PUSH2", "SP_INGAME_PULL2", "SP_INGAME_SEEING2", + "SPMOD_INGAME_REPULSE2", "SP_INGAME_DRAIN2", "SP_INGAME_LIGHTNING2", "SP_INGAME_DARK_RAGE2", "SP_INGAME_GRIP2", + + "SPMOD_INGAME_DESTRUCTION2", + "SPMOD_INGAME_INSANITY2", + "SPMOD_INGAME_DEADLYSIGHT2", + + "SP_INGAME_SABER_THROW2", }; // Keep these with groups light side, core, and dark side @@ -3677,6 +3950,10 @@ int showDataPadPowers[MAX_DPSHOWPOWERS] = FP_HEAL, FP_PROTECT, FP_TELEPATHY, + + FP_STASIS, + FP_BLINDING, + FP_INVULNERABILITY, // Core Powers FP_LEVITATION, @@ -3687,12 +3964,17 @@ int showDataPadPowers[MAX_DPSHOWPOWERS] = FP_SABER_DEFENSE, FP_SABER_OFFENSE, FP_SEE, + FP_REPULSE, //Dark Side FP_DRAIN, FP_LIGHTNING, FP_RAGE, FP_GRIP, + + FP_DESTRUCTION, + FP_INSANITY, + FP_DEADLYSIGHT, }; /* @@ -4053,6 +4335,10 @@ const char *forcepowerDesc[NUM_FORCE_POWERS] = "FORCE_HEAL_DESC", "FORCE_PROTECT_DESC", "FORCE_MIND_TRICK_DESC", + +"FORCE_STASIS_DESC", +"FORCE_BLINDING_DESC", +"FORCE_INVULNERABILITY_DESC", "FORCE_JUMP_DESC", "FORCE_SPEED_DESC", @@ -4062,11 +4348,16 @@ const char *forcepowerDesc[NUM_FORCE_POWERS] = "FORCE_SABER_DEFENSE_DESC", "FORCE_SABER_OFFENSE_DESC", "FORCE_SENSE_DESC", +"FORCE_REPULSE_DESC", "FORCE_DRAIN_DESC", "FORCE_LIGHTNING_DESC", "FORCE_RAGE_DESC", "FORCE_GRIP_DESC", + +"FORCE_DESTRUCTION_DESC", +"FORCE_INSANITY_DESC", +"FORCE_DEADLYSIGHT_DESC", }; @@ -4077,6 +4368,10 @@ const char *forcepowerLvl1Desc[NUM_FORCE_POWERS] = "FORCE_PROTECT_LVL1_DESC", "FORCE_MIND_TRICK_LVL1_DESC", +"FORCE_STASIS_LVL1_DESC", +"FORCE_BLINDING_LVL1_DESC", +"FORCE_INVULNERABILITY_LVL1_DESC", + "FORCE_JUMP_LVL1_DESC", "FORCE_SPEED_LVL1_DESC", "FORCE_PUSH_LVL1_DESC", @@ -4085,11 +4380,16 @@ const char *forcepowerLvl1Desc[NUM_FORCE_POWERS] = "FORCE_SABER_DEFENSE_LVL1_DESC", "FORCE_SABER_OFFENSE_LVL1_DESC", "FORCE_SENSE_LVL1_DESC", +"FORCE_REPULSE_LVL1_DESC", "FORCE_DRAIN_LVL1_DESC", "FORCE_LIGHTNING_LVL1_DESC", "FORCE_RAGE_LVL1_DESC", "FORCE_GRIP_LVL1_DESC", + +"FORCE_DESTRUCTION_LVL1_DESC", +"FORCE_INSANITY_LVL1_DESC", +"FORCE_DEADLYSIGHT_LVL1_DESC", }; const char *forcepowerLvl2Desc[NUM_FORCE_POWERS] = @@ -4098,6 +4398,10 @@ const char *forcepowerLvl2Desc[NUM_FORCE_POWERS] = "FORCE_HEAL_LVL2_DESC", "FORCE_PROTECT_LVL2_DESC", "FORCE_MIND_TRICK_LVL2_DESC", + +"FORCE_STASIS_LVL2_DESC", +"FORCE_BLINDING_LVL2_DESC", +"FORCE_INVULNERABILITY_LVL2_DESC", "FORCE_JUMP_LVL2_DESC", "FORCE_SPEED_LVL2_DESC", @@ -4107,11 +4411,16 @@ const char *forcepowerLvl2Desc[NUM_FORCE_POWERS] = "FORCE_SABER_DEFENSE_LVL2_DESC", "FORCE_SABER_OFFENSE_LVL2_DESC", "FORCE_SENSE_LVL2_DESC", +"FORCE_REPULSE_LVL2_DESC", "FORCE_DRAIN_LVL2_DESC", "FORCE_LIGHTNING_LVL2_DESC", "FORCE_RAGE_LVL2_DESC", "FORCE_GRIP_LVL2_DESC", + +"FORCE_DESTRUCTION_LVL2_DESC", +"FORCE_INSANITY_LVL2_DESC", +"FORCE_DEADLYSIGHT_LVL2_DESC", }; const char *forcepowerLvl3Desc[NUM_FORCE_POWERS] = @@ -4121,6 +4430,10 @@ const char *forcepowerLvl3Desc[NUM_FORCE_POWERS] = "FORCE_PROTECT_LVL3_DESC", "FORCE_MIND_TRICK_LVL3_DESC", +"FORCE_STASIS_LVL3_DESC", +"FORCE_BLINDING_LVL3_DESC", +"FORCE_INVULNERABILITY_LVL3_DESC", + "FORCE_JUMP_LVL3_DESC", "FORCE_SPEED_LVL3_DESC", "FORCE_PUSH_LVL3_DESC", @@ -4129,11 +4442,16 @@ const char *forcepowerLvl3Desc[NUM_FORCE_POWERS] = "FORCE_SABER_DEFENSE_LVL3_DESC", "FORCE_SABER_OFFENSE_LVL3_DESC", "FORCE_SENSE_LVL3_DESC", +"FORCE_REPULSE_LVL3_DESC", "FORCE_DRAIN_LVL3_DESC", "FORCE_LIGHTNING_LVL3_DESC", "FORCE_RAGE_LVL3_DESC", "FORCE_GRIP_LVL3_DESC", + +"FORCE_DESTRUCTION_LVL3_DESC", +"FORCE_INSANITY_LVL3_DESC", +"FORCE_DEADLYSIGHT_LVL3_DESC", }; /* @@ -4306,19 +4624,31 @@ void CG_DrawDataPadForceSelect( void ) } } - cgi_SP_GetStringTextString( va("SP_INGAME_%s",forcepowerDesc[cg.DataPadforcepowerSelect]), text, sizeof(text) ); + if (!cgi_SP_GetStringTextString( va("SP_INGAME_%s",forcepowerDesc[cg.DataPadforcepowerSelect]), text, sizeof(text) )) + { + cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",forcepowerDesc[cg.DataPadforcepowerSelect]), text, sizeof(text) ); + } if (player->client->ps.forcePowerLevel[showDataPadPowers[cg.DataPadforcepowerSelect]]==1) { - cgi_SP_GetStringTextString( va("SP_INGAME_%s",forcepowerLvl1Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) ); + if (!cgi_SP_GetStringTextString( va("SP_INGAME_%s",forcepowerLvl1Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) )) + { + cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",forcepowerLvl1Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) ); + } } else if (player->client->ps.forcePowerLevel[showDataPadPowers[cg.DataPadforcepowerSelect]]==2) { - cgi_SP_GetStringTextString( va("SP_INGAME_%s",forcepowerLvl2Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) ); + if (!cgi_SP_GetStringTextString( va("SP_INGAME_%s",forcepowerLvl2Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) )) + { + cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",forcepowerLvl2Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) ); + } } else { - cgi_SP_GetStringTextString( va("SP_INGAME_%s",forcepowerLvl3Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) ); + if (!cgi_SP_GetStringTextString( va("SP_INGAME_%s",forcepowerLvl3Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) )) + { + cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",forcepowerLvl3Desc[cg.DataPadforcepowerSelect]), text2, sizeof(text2) ); + } } if (text[0]) diff --git a/code/cgame/cg_media.h b/code/cgame/cg_media.h index 6e045f3187..5dfb1a5ca2 100644 --- a/code/cgame/cg_media.h +++ b/code/cgame/cg_media.h @@ -174,6 +174,15 @@ typedef struct { qhandle_t blueSaberCoreShader; qhandle_t purpleSaberGlowShader; qhandle_t purpleSaberCoreShader; + qhandle_t rgbSaberGlowShader; + qhandle_t rgbSaberCoreShader; + + qhandle_t blackSaberBlurShader; + qhandle_t blackSaberGlowShader; + qhandle_t blackSaberCoreShader; + + qhandle_t unstableBlurShader; + qhandle_t rgbUnstableCoreShader; qhandle_t explosionModel; qhandle_t surfaceExplosionShader; @@ -257,6 +266,26 @@ typedef struct { qhandle_t forceShell; qhandle_t sightShell; qhandle_t drainShader; + + //SFX Sabers + qhandle_t SaberTrailShader; + qhandle_t SaberBladeShader; + qhandle_t SaberEndShader; + + qhandle_t blackSaberTrailShader; + qhandle_t blackSaberBladeShader; + qhandle_t blackSaberEndShader; + + qhandle_t ignitionFlare; + qhandle_t blackIgnitionFlare; + + //Radar + qhandle_t radarShader; + qhandle_t siegeItemShader; + qhandle_t mAutomapPlayerIcon; + qhandle_t radarMaskShader; + + qhandle_t radarIcons[MAX_ICONS]; // sounds sfxHandle_t disintegrateSound; @@ -306,6 +335,8 @@ typedef struct { //new stuff for Jedi Academy sfxHandle_t drainSound; + + sfxHandle_t destructionSound; } cgMedia_t; @@ -339,6 +370,11 @@ typedef struct fxHandle_t flechetteShotDeathEffect; fxHandle_t flechetteFleshImpactEffect; fxHandle_t flechetteRicochetEffect; + + // CLONE BLASTER + fxHandle_t cloneBlasterShotEffect; + fxHandle_t cloneBlasterWallImpactEffect; + fxHandle_t cloneBlasterFleshImpactEffect; //FORCE fxHandle_t forceConfusion; @@ -351,6 +387,9 @@ typedef struct fxHandle_t forceDrain; fxHandle_t forceDrainWide; fxHandle_t forceDrained; + + fxHandle_t destructionProjectile; + fxHandle_t destructionHit; //footstep effects fxHandle_t footstepMud; @@ -365,6 +404,16 @@ typedef struct fxHandle_t landingGravel; } cgEffects_t; +#define MAX_MINIMAPS 1 +typedef struct +{ + int numMinimapImages; + qhandle_t minimapImage[MAX_MINIMAPS]; + float minimapHeights[MAX_MINIMAPS]; + vec2_t topLeft; + vec2_t bottomRight; +} cgRadarMap_t; + // The client game static (cgs) structure hold everything // loaded or calculated from the gamestate. It will NOT @@ -406,6 +455,8 @@ typedef struct { // effects cgEffects_t effects; + + cgRadarMap_t radarMap; } cgs_t; diff --git a/code/cgame/cg_players.cpp b/code/cgame/cg_players.cpp index 1b13065a87..2941cc8f38 100644 --- a/code/cgame/cg_players.cpp +++ b/code/cgame/cg_players.cpp @@ -41,6 +41,17 @@ extern qboolean WP_SaberBladeUseSecondBladeStyle( saberInfo_t *saber, int bladeN extern void WP_SaberSwingSound( gentity_t *ent, int saberNum, swingType_t swingType ); extern vmCvar_t cg_debugHealthBars; + +extern vmCvar_t cg_SFXSabers; +extern vmCvar_t cg_SFXSabersGlowSize; +extern vmCvar_t cg_SFXSabersCoreSize; + +extern vmCvar_t cg_ignitionFlare; +extern vmCvar_t cg_ignitionSpeed; + +//True View Camera Position Check Function +extern void CheckCameraLocation( vec3_t OldeyeOrigin ); + /* player entities generate a great deal of information from implicit ques @@ -130,7 +141,7 @@ qboolean CG_RegisterClientModelname( clientInfo_t *ci, const char *headModelName static void CG_PlayerFootsteps( centity_t *const cent, footstepType_t footStepType ); static void CG_PlayerAnimEvents( int animFileIndex, qboolean torso, int oldFrame, int frame, int entNum ); extern void BG_G2SetBoneAngles( centity_t *cent, gentity_t *gent, int boneIndex, const vec3_t angles, const int flags, - const Eorientations up, const Eorientations left, const Eorientations forward, qhandle_t *modelList ); + const Eorientations up, const Eorientations left, const Eorientations forward, qhandle_t *modelList, qboolean isHead = qfalse ); extern void FX_BorgDeathSparkParticles( vec3_t origin, vec3_t angles, vec3_t vel, vec3_t user ); extern qboolean PM_SaberInSpecialAttack( int anim ); extern qboolean PM_SaberInAttack( int move ); @@ -552,19 +563,19 @@ void CG_NewClientinfo( int clientNum ) v = Info_ValueForKey( configstring, "legsModel" ); Q_strncpyz( g_entities[clientNum].client->renderInfo.legsModelName, v, - sizeof( g_entities[clientNum].client->renderInfo.legsModelName), qtrue); + sizeof( g_entities[clientNum].client->renderInfo.legsModelName)); // torsoModel v = Info_ValueForKey( configstring, "torsoModel" ); Q_strncpyz( g_entities[clientNum].client->renderInfo.torsoModelName, v, - sizeof( g_entities[clientNum].client->renderInfo.torsoModelName), qtrue); + sizeof( g_entities[clientNum].client->renderInfo.torsoModelName)); // headModel v = Info_ValueForKey( configstring, "headModel" ); Q_strncpyz( g_entities[clientNum].client->renderInfo.headModelName, v, - sizeof( g_entities[clientNum].client->renderInfo.headModelName), qtrue); + sizeof( g_entities[clientNum].client->renderInfo.headModelName)); // sounds v = Info_ValueForKey( configstring, "snd" ); @@ -940,27 +951,6 @@ static void CG_PlayerAnimation( centity_t *cent, int *legsOld, int *legs, float } } - //HACKHACKHACK - /* - if ( cent->gent->client->playerTeam == TEAM_BORG && - cent->gent->client->ps.weapon == WP_BORG_ASSIMILATOR ) - { - if ( cent->gent->NPC->attackHoldTime > cg.time ) - { - if ( cent->pe.torso.frame >= 468 && cent->pe.torso.frame < 478 ) - {//Do not animate - *torsoOld = *torso = cent->pe.torso.frame = 468; - *torsoBackLerp = 0; - if ( ValidAnimFileIndex( ci->animFileIndex ) ) - { - CG_PlayerAnimSounds(ci->animFileIndex, qtrue, cent->pe.torso.frame, cent->pe.torso.frame, cent->currentState.number ); - } - return; - } - } - } - */ - newTorsoFrame = CG_RunLerpFrame( ci, ¢->pe.torso, cent->gent->client->ps.torsoAnim, cent->gent->client->renderInfo.torsoFpsMod, cent->gent->s.number ); *torsoOld = cent->pe.torso.oldFrame; @@ -998,6 +988,7 @@ static void CG_PlayerAnimEventDo( centity_t *cent, animevent_t *animEvent ) const int holdSnd = animEvent->eventData[ AED_SOUNDINDEX_START+Q_irand( 0, animEvent->eventData[AED_SOUND_NUMRANDOMSNDS] ) ]; if ( holdSnd > 0 ) { + if (cgs.sound_precache[holdSnd]) if ( cgs.sound_precache[ holdSnd ] ) { cgi_S_StartSound( NULL, cent->currentState.clientNum, channel, cgs.sound_precache[holdSnd ] ); @@ -1005,6 +996,23 @@ static void CG_PlayerAnimEventDo( centity_t *cent, animevent_t *animEvent ) else {//try a custom sound const char *s = CG_ConfigString( CS_SOUNDS + holdSnd ); + + if (cent->gent && cent->gent->client) + { + if (cent->gent->client->ps.torsoAnim == BOTH_KYLE_PA_1 + || cent->gent->client->ps.torsoAnim == BOTH_KYLE_PA_2 + || cent->gent->client->ps.torsoAnim == BOTH_KYLE_PA_3 + || cent->gent->client->ps.torsoAnim == BOTH_PLAYER_PA_1 + || cent->gent->client->ps.torsoAnim == BOTH_PLAYER_PA_2 + || cent->gent->client->ps.torsoAnim == BOTH_PLAYER_PA_3) + { + if (Q_stristr(s, "force") && cent->gent && cent->gent->flags&FL_MELEEKATA_NOFORCEFX) + { + break; //don't play any force power sounds + } + } + } + CG_TryPlayCustomSound(NULL, cent->currentState.clientNum, channel, va("%s.wav",s), CS_TRY_ALL ); } } @@ -1231,7 +1239,7 @@ static void CG_PlayerAnimEvents( int animFileIndex, qboolean torso, int oldFrame {//still in same anim, check for looping anim inSameAnim = qtrue; animation_t *animation = &level.knownAnimFileSets[animFileIndex].animations[anim]; - animBackward = (animation->frameLerp<0); + animBackward = (qboolean)(animation->frameLerp<0); if ( animation->loopFrames != -1 ) {//a looping anim! loopAnim = qtrue; @@ -1670,7 +1678,7 @@ Added 11/06/02 by Aurelio Reis. extern vmCvar_t cg_drawBreath; static void CG_BreathPuffs( centity_t *cent, vec3_t angles, vec3_t origin ) { - gclient_s *client = cent->gent->client; + gclient_t *client = cent->gent->client; /* cg_drawBreath.integer == 0 - Don't draw at all. == 1 - Draw both (but bubbles only when under water). @@ -1679,7 +1687,7 @@ static void CG_BreathPuffs( centity_t *cent, vec3_t angles, vec3_t origin ) if ( !client || cg_drawBreath.integer == 0 - || !cg.renderingThirdPerson + || (!cg.renderingThirdPerson && !(cg_trueguns.integer || client->ps.weapon == WP_MELEE || client->ps.weapon == WP_SABER )) || client->ps.pm_type == PM_DEAD || client->breathPuffTime > cg.time ) { @@ -1961,7 +1969,7 @@ static void CG_ATSTLegsYaw( centity_t *cent, vec3_t trailingLegsAngles ) float legAngleDiff = AngleNormalize180(ATSTLegsYaw) - AngleNormalize180(cent->pe.legs.yawAngle); int legsAnim = cent->currentState.legsAnim; - qboolean moving = (!VectorCompare(cent->gent->client->ps.velocity, vec3_origin)); + qboolean moving = (qboolean)!VectorCompare(cent->gent->client->ps.velocity, vec3_origin); if ( moving || legsAnim == BOTH_TURN_LEFT1 || legsAnim == BOTH_TURN_RIGHT1 || fabs(legAngleDiff) > 45 ) {//moving or turning or beyond the turn allowance if ( legsAnim == BOTH_STAND1 && !moving ) @@ -2217,8 +2225,13 @@ static void CG_G2ClientSpineAngles( centity_t *cent, vec3_t viewAngles, const ve } else { - BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw); BG_G2SetBoneAngles( cent, cent->gent, cent->gent->lowerLumbarBone, llAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw); + if (cent->gent->headModel > 0) + { + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headUpperLumbarBone, ulAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headLowerLumbarBone, llAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue); + } } } @@ -2418,8 +2431,15 @@ static void CG_G2ClientNeckAngles( centity_t *cent, const vec3_t lookAngles, vec else { BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, headAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); - BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, neckAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, neckAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw); BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, thoracicAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw); + + if (cent->gent->headModel > 0) + { + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headCraniumBone, headAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headCervicalBone, neckAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headThoracicBone, thoracicAngles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue); + } } } @@ -2520,19 +2540,33 @@ static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) qboolean looking = qfalse, talking = qfalse; if ( cent->gent - && (cent->gent->flags&FL_NO_ANGLES) ) + && ((cent->gent->flags&FL_NO_ANGLES) + || (cent->gent->client && cent->gent->client->ps.stasisTime > cg.time ))) {//flatten out all bone angles we might have been overriding cent->lerpAngles[PITCH] = cent->lerpAngles[ROLL] = 0; VectorCopy( cent->lerpAngles, angles ); - BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); - BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->craniumBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); + + if ( cent->gent->headModel > 0) + { + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headCraniumBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headCervicalBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headThoracicBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + } cent->pe.torso.pitchAngle = 0; cent->pe.torso.yawAngle = 0; - BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); BG_G2SetBoneAngles( cent, cent->gent, cent->gent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); + + if (cent->gent->headModel > 0) + { + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headUpperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headLowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw , qtrue); + } cent->pe.legs.pitchAngle = angles[0]; cent->pe.legs.yawAngle = angles[1]; @@ -2550,6 +2584,11 @@ static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) { gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone ); } + + if ( cent->gent->headModel > 0 && cent->gent->headHipsBone != -1) + { + gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->headModel], cent->gent->headHipsBone ); + } VectorCopy( cent->lerpAngles, angles ); @@ -2557,10 +2596,23 @@ static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) BG_G2SetBoneAngles( cent, cent->gent, cent->gent->cervicalBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); BG_G2SetBoneAngles( cent, cent->gent, cent->gent->thoracicBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); + if ( cent->gent->headModel > 0) + { + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headCraniumBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headCervicalBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headThoracicBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + } + cent->pe.torso.pitchAngle = 0; cent->pe.torso.yawAngle = 0; - BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->upperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); BG_G2SetBoneAngles( cent, cent->gent, cent->gent->lowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw ); + + if (cent->gent->headModel > 0) + { + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headUpperLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + BG_G2SetBoneAngles( cent, cent->gent, cent->gent->headLowerLumbarBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, cgs.model_draw, qtrue ); + } cent->pe.legs.pitchAngle = angles[0]; cent->pe.legs.yawAngle = angles[1]; @@ -2701,6 +2753,10 @@ static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) if ( angles[YAW] == cent->pe.legs.yawAngle ) { gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone ); + if ( cent->gent->headModel > 0 && cent->gent->headHipsBone >= 0 ) + { + gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->headModel], cent->gent->headHipsBone ); + } } else if ( VectorCompare( vec3_origin, cent->gent->client->ps.velocity ) ) {//FIXME: because of LegsYawFromMovement, we play the turnAnims when we stop running, which looks really bad. @@ -2716,16 +2772,30 @@ static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) gi.G2API_SetBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone, animations[turnAnim].firstFrame, animations[turnAnim].firstFrame+animations[turnAnim].numFrames, BONE_ANIM_OVERRIDE_LOOP/*|BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND*/, animSpeed, cg.time, -1, 100 ); + if ( cent->gent->headModel > 0 && cent->gent->headHipsBone >= 0 ) + { + gi.G2API_SetBoneAnimIndex( ¢->gent->ghoul2[cent->gent->headModel], cent->gent->headHipsBone, + animations[turnAnim].firstFrame, animations[turnAnim].firstFrame+animations[turnAnim].numFrames, + BONE_ANIM_OVERRIDE_LOOP/*|BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND*/, animSpeed, cg.time, -1, 100 ); + } } } else { gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone ); + if ( cent->gent->headModel > 0 && cent->gent->headHipsBone >= 0 ) + { + gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->headModel], cent->gent->headHipsBone ); + } } } else { gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->playerModel], cent->gent->hipsBone ); + if ( cent->gent->headModel > 0 && cent->gent->headHipsBone >= 0 ) + { + gi.G2API_StopBoneAnimIndex( ¢->gent->ghoul2[cent->gent->headModel], cent->gent->headHipsBone ); + } } } @@ -2796,12 +2866,12 @@ static void CG_G2PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t angles ) { cent->gent->client->renderInfo.legsYaw = angles[YAW]; } - if ( ((cent->gent->client->ps.eFlags&EF_FORCE_GRIPPED)||((cent->gent->client->NPC_class == CLASS_BOBAFETT||cent->gent->client->NPC_class == CLASS_ROCKETTROOPER)&¢->gent->client->moveType==MT_FLYSWIM)) + if (((cent->gent->client->ps.eFlags&EF_FORCE_GRIPPED) || ((cent->gent->client->NPC_class == CLASS_BOBAFETT || cent->gent->client->NPC_class == CLASS_ROCKETTROOPER || cent->gent->client->NPC_class == CLASS_MANDA) && cent->gent->client->moveType == MT_FLYSWIM)) && cent->gent->client->ps.groundEntityNum == ENTITYNUM_NONE ) { vec3_t centFwd, centRt; float divFactor = 1.0f; - if ( (cent->gent->client->NPC_class == CLASS_BOBAFETT||cent->gent->client->NPC_class == CLASS_ROCKETTROOPER) + if ((cent->gent->client->NPC_class == CLASS_BOBAFETT || cent->gent->client->NPC_class == CLASS_ROCKETTROOPER || cent->gent->client->NPC_class == CLASS_MANDA) && cent->gent->client->moveType == MT_FLYSWIM ) { divFactor = 3.0f; @@ -3065,7 +3135,7 @@ static void CG_PlayerAngles( centity_t *cent, vec3_t legs[3], vec3_t torso[3], v int i; qboolean looking = qfalse, talking = qfalse; - if ( cg.renderingThirdPerson && cent->gent && cent->gent->s.number == 0 ) + if ( (cg.renderingThirdPerson || (cg_trueguns.integer && !cg.zoomMode) || cent->gent->client->ps.weapon == WP_SABER || cent->gent->client->ps.weapon == WP_MELEE) && cent->gent && cent->gent->s.number == 0 ) { // If we are rendering third person, we should just force the player body to always fully face // whatever way they are looking, otherwise, you can end up with gun shots coming off of the @@ -3561,9 +3631,9 @@ static qboolean CG_PlayerShadow( centity_t *const cent, float *const shadowPlane cgs.model_draw, cent->currentState.modelScale); gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, sideOrigin ); sideOrigin[2] += 30; //fudge up a bit for coplaner - bShadowed = _PlayerShadow(sideOrigin, 0, shadowPlane, 28, cgs.media.shadowMarkShader) || bShadowed; + bShadowed = (qboolean)(_PlayerShadow(sideOrigin, 0, shadowPlane, 28, cgs.media.shadowMarkShader) || bShadowed); - bShadowed = _PlayerShadow(rootOrigin, cent->pe.legs.yawAngle, shadowPlane, 64, cgs.media.shadowMarkShader) || bShadowed; + bShadowed = (qboolean)( _PlayerShadow(rootOrigin, cent->pe.legs.yawAngle, shadowPlane, 64, cgs.media.shadowMarkShader) || bShadowed); return bShadowed; } else if ( cent->gent->client->NPC_class == CLASS_RANCOR ) @@ -3899,8 +3969,8 @@ static void _PlayerSplash( const vec3_t origin, const vec3_t velocity, const flo VectorCopy( trace.endpos, end ); - end[0] += crandom() * 3.0f; - end[1] += crandom() * 3.0f; + end[0] += Q_flrand(-1.0f, 1.0f) * 3.0f; + end[1] += Q_flrand(-1.0f, 1.0f) * 3.0f; end[2] += 1.0f; //fudge up int t = VectorLengthSquared( velocity ); @@ -3913,10 +3983,10 @@ static void _PlayerSplash( const vec3_t origin, const vec3_t velocity, const flo float alpha = ( t / 8192.0f ) * 0.6f + 0.2f; FX_AddOrientedParticle( -1, end, trace.plane.normal, NULL, NULL, - 6.0f, radius + random() * 48.0f, 0, + 6.0f, radius + Q_flrand(0.0f, 1.0f) * 48.0f, 0, alpha, 0.0f, 0.0f, WHITE, WHITE, 0.0f, - random() * 360, crandom() * 6.0f, NULL, NULL, 0.0f, 0 ,0, 1200, + Q_flrand(0.0f, 1.0f) * 360, Q_flrand(-1.0f, 1.0f) * 6.0f, NULL, NULL, 0.0f, 0 ,0, 1200, cgs.media.wakeMarkShader, FX_ALPHA_LINEAR | FX_SIZE_LINEAR ); } @@ -3970,7 +4040,7 @@ static void CG_PlayerSplash( centity_t *cent ) _PlayerSplash( cent->lerpOrigin, cl->ps.velocity, 36, cl->renderInfo.eyePoint[2] - cent->lerpOrigin[2] + 5 ); } - cent->gent->disconnectDebounceTime = cg.time + 125 + random() * 50.0f; + cent->gent->disconnectDebounceTime = cg.time + 125 + Q_flrand(0.0f, 1.0f) * 50.0f; } } } @@ -4026,7 +4096,7 @@ static void CG_LightningBolt( centity_t *cent, vec3_t origin ) if ( cent->gent->fx_time < cg.time && !(trace.surfaceFlags & SURF_NOIMPACT )) { spark = qtrue; - cent->gent->fx_time = cg.time + random() * 100 + 100; + cent->gent->fx_time = cg.time + Q_flrand(0.0f, 1.0f) * 100 + 100; } // Don't draw certain kinds of impacts when it hits a player and such..or when we hit a surface with a NOIMPACT flag @@ -4058,7 +4128,7 @@ extern void FX_DEMP2_AltBeam( vec3_t start, vec3_t end, vec3_t normal, //qboolea //------------------------------------------- #define REFRACT_EFFECT_DURATION 500 void CG_ForcePushBlur( const vec3_t org, qboolean darkSide = qfalse ); -static void CG_ForcePushRefraction( vec3_t org, centity_t *cent ) +static void CG_ForcePushRefraction( vec3_t org, centity_t *cent, float scaleFactor, vec3_t colour ) { refEntity_t ent; vec3_t ang; @@ -4094,7 +4164,11 @@ static void CG_ForcePushRefraction( vec3_t org, centity_t *cent ) } //scale from 1.0f to 0.1f then hold at 0.1 for the rest of the duration - if (cent->gent->client->ps.forcePowersActive & ( 1 << FP_PULL ) ) + if (cent->gent->client->ps.forcePowersActive & ( 1 << FP_REPULSE ) ) + { + scale = 1.0f; + } + else if (cent->gent->client->ps.forcePowersActive & ( 1 << FP_PULL ) ) { scale = (float)(REFRACT_EFFECT_DURATION-tDif)*0.003f; } @@ -4112,7 +4186,14 @@ static void CG_ForcePushRefraction( vec3_t org, centity_t *cent ) } //start alpha at 244, fade to 10 - alpha = (float)tDif*0.488f; + if (cent->gent->client->ps.forcePowersActive & ( 1 << FP_REPULSE ) ) + { + alpha = 244.0f; + } + else + { + alpha = (float)tDif*0.488f; + } if (alpha > 244.0f) { @@ -4158,6 +4239,11 @@ static void CG_ForcePushRefraction( vec3_t org, centity_t *cent ) ent.radius = 32; } + if (scaleFactor) + { + scale *= scaleFactor; + } + VectorScale(ent.axis[0], scale, ent.axis[0]); VectorScale(ent.axis[1], scale, ent.axis[1]); VectorScale(ent.axis[2], scale, ent.axis[2]); @@ -4173,6 +4259,12 @@ static void CG_ForcePushRefraction( vec3_t org, centity_t *cent ) ent.shaderRGBA[2] = 255.0f; ent.shaderRGBA[3] = alpha; + if (colour) + { + ent.shaderRGBA[0] = colour[0]; + ent.shaderRGBA[1] = colour[1]; + ent.shaderRGBA[2] = colour[2]; + } cgi_R_AddRefEntityToScene( &ent ); } @@ -4376,7 +4468,7 @@ static void CG_ForceElectrocution( centity_t *cent, const vec3_t origin, vec3_t if ( found ) { gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); - if ( random() > 0.5f ) + if ( Q_flrand(0.0f, 1.0f) > 0.5f ) { gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_X, dir ); } @@ -4386,15 +4478,15 @@ static void CG_ForceElectrocution( centity_t *cent, const vec3_t origin, vec3_t } // Add some fudge, makes us not normalized, but that isn't really important - dir[0] += crandom() * 0.4f; - dir[1] += crandom() * 0.4f; - dir[2] += crandom() * 0.4f; + dir[0] += Q_flrand(-1.0f, 1.0f) * 0.4f; + dir[1] += Q_flrand(-1.0f, 1.0f) * 0.4f; + dir[2] += Q_flrand(-1.0f, 1.0f) * 0.4f; } else { // Just use the lerp Origin and a random direction VectorCopy( cent->lerpOrigin, fxOrg ); - VectorSet( dir, crandom(), crandom(), crandom() ); // Not normalized, but who cares. + VectorSet( dir, Q_flrand(-1.0f, 1.0f), Q_flrand(-1.0f, 1.0f), Q_flrand(-1.0f, 1.0f) ); // Not normalized, but who cares. if ( cent->gent && cent->gent->client ) { switch ( cent->gent->client->NPC_class ) @@ -4414,19 +4506,19 @@ static void CG_ForceElectrocution( centity_t *cent, const vec3_t origin, vec3_t } } - VectorMA( fxOrg, random() * 40 + 40, dir, fxOrg2 ); + VectorMA( fxOrg, Q_flrand(0.0f, 1.0f) * 40 + 40, dir, fxOrg2 ); trace_t tr; CG_Trace( &tr, fxOrg, NULL, NULL, fxOrg2, -1, CONTENTS_SOLID ); - if ( tr.fraction < 1.0f || random() > 0.94f || alwaysDo ) + if ( tr.fraction < 1.0f || Q_flrand(0.0f, 1.0f) > 0.94f || alwaysDo ) { FX_AddElectricity( -1, fxOrg, tr.endpos, 1.5f, 4.0f, 0.0f, 1.0f, 0.5f, 0.0f, rgb, rgb, 0.0f, - 5.5f, random() * 50 + 100, shader, FX_ALPHA_LINEAR | FX_SIZE_LINEAR | FX_BRANCH | FX_GROW | FX_TAPER, -1, -1 ); + 5.5f, Q_flrand(0.0f, 1.0f) * 50 + 100, shader, FX_ALPHA_LINEAR | FX_SIZE_LINEAR | FX_BRANCH | FX_GROW | FX_TAPER, -1, -1 ); } } @@ -4613,6 +4705,34 @@ void CG_AddForceSightShell( refEntity_t *ent, centity_t *cent ) cgi_R_AddRefEntityToScene( ent ); } +static void CG_RGBForSaberColor( saber_colors_t color, vec3_t rgb ) +{ + switch( color ) + { + case SABER_RED: + VectorSet( rgb, 1.0f, 0.2f, 0.2f ); + break; + case SABER_ORANGE: + VectorSet( rgb, 1.0f, 0.5f, 0.1f ); + break; + case SABER_YELLOW: + VectorSet( rgb, 1.0f, 1.0f, 0.2f ); + break; + case SABER_GREEN: + VectorSet( rgb, 0.2f, 1.0f, 0.2f ); + break; + case SABER_BLUE: + VectorSet( rgb, 0.2f, 0.4f, 1.0f ); + break; + case SABER_PURPLE: + VectorSet( rgb, 0.9f, 0.2f, 1.0f ); + break; + default://SABER_RGB + VectorSet( rgb, ((color) & 0xff)/255.0f, ((color >> 8) & 0xff)/255.0f, ((color >> 16) & 0xff)/255.0f ); + break; + } +} + /* =============== CG_AddRefEntityWithPowerups @@ -4654,7 +4774,51 @@ void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cen ent->shaderRGBA[1] = gent->client->renderInfo.customRGBA[1]; ent->shaderRGBA[2] = gent->client->renderInfo.customRGBA[2]; ent->shaderRGBA[3] = gent->client->renderInfo.customRGBA[3]; - + + for (int index = 0; index < MAX_CVAR_TINT; index++) + { + ent->newShaderRGBA[index][0] = gent->client->renderInfo.newCustomRGBA[index][0]; + ent->newShaderRGBA[index][1] = gent->client->renderInfo.newCustomRGBA[index][1]; + ent->newShaderRGBA[index][2] = gent->client->renderInfo.newCustomRGBA[index][2]; + ent->newShaderRGBA[index][3] = gent->client->renderInfo.newCustomRGBA[index][3]; + } + + //get the saber colours + if (1/*gent->client->ps.weapons[WP_SABER]*/) + { + vec3_t rgb={1,1,1}; + if ( gent->client->ps.saber[0].crystals & SABER_CRYSTAL_BLACK ) + { + ent->newShaderRGBA[TINT_BLADE1][0] = 0; + ent->newShaderRGBA[TINT_BLADE1][1] = 0; + ent->newShaderRGBA[TINT_BLADE1][2] = 0; + } + else + { + CG_RGBForSaberColor( gent->client->ps.saber[0].blade[0].color, rgb ); + ent->newShaderRGBA[TINT_BLADE1][0] = 0xff * rgb[0]; + ent->newShaderRGBA[TINT_BLADE1][1] = 0xff * rgb[1]; + ent->newShaderRGBA[TINT_BLADE1][2] = 0xff * rgb[2]; + } + + if (gent->client->ps.dualSabers) + { + if ( gent->client->ps.saber[0].crystals & SABER_CRYSTAL_BLACK ) + { + ent->newShaderRGBA[TINT_BLADE2][0] = 0; + ent->newShaderRGBA[TINT_BLADE2][1] = 0; + ent->newShaderRGBA[TINT_BLADE2][2] = 0; + } + else + { + CG_RGBForSaberColor( gent->client->ps.saber[1].blade[0].color, rgb ); + ent->newShaderRGBA[TINT_BLADE2][0] = 0xff * rgb[0]; + ent->newShaderRGBA[TINT_BLADE2][1] = 0xff * rgb[1]; + ent->newShaderRGBA[TINT_BLADE2][2] = 0xff * rgb[2]; + } + } + } + // If certain states are active, we don't want to add in the regular body if ( !gent->client->ps.powerups[PW_CLOAKED] && !gent->client->ps.powerups[PW_UNCLOAKING] && @@ -4687,7 +4851,7 @@ void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cen ent->customShader = 0; cgi_R_AddRefEntityToScene( ent ); - if ( cg.time - ent->endTime < 1000 && (cg_timescale.value * cg_timescale.value * random()) > 0.05f ) + if ( cg.time - ent->endTime < 1000 && (cg_timescale.value * cg_timescale.value * Q_flrand(0.0f, 1.0f)) > 0.05f ) { vec3_t fxOrg; mdxaBone_t boltMatrix; @@ -4698,10 +4862,10 @@ void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cen gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, fxOrg ); VectorMA( fxOrg, -18, cg.refdef.viewaxis[0], fxOrg ); - fxOrg[2] += crandom() * 20; + fxOrg[2] += Q_flrand(-1.0f, 1.0f) * 20; theFxScheduler.PlayEffect( "disruptor/death_smoke", fxOrg ); - if ( random() > 0.5f ) + if ( Q_flrand(0.0f, 1.0f) > 0.5f ) { theFxScheduler.PlayEffect( "disruptor/death_smoke", fxOrg ); } @@ -4778,7 +4942,7 @@ void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cen { int dif = gent->client->ps.powerups[PW_SHOCKED] - cg.time; - if ( dif > 0 && random() > 0.4f ) + if ( dif > 0 && Q_flrand(0.0f, 1.0f) > 0.4f ) { // fade out over the last 500 ms int brightness = 255; @@ -4803,7 +4967,7 @@ void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cen cgi_R_AddRefEntityToScene( ent ); - if ( random() > 0.9f ) + if ( Q_flrand(0.0f, 1.0f) > 0.9f ) cgi_S_StartSound ( ent->origin, gent->s.number, CHAN_AUTO, cgi_S_RegisterSound( "sound/effects/energy_crackle.wav" ) ); } } @@ -4874,19 +5038,19 @@ void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cen // Galak Mech shield bubble //------------------------------------------------------ - if ( powerups & ( 1 << PW_GALAK_SHIELD )) + if ( powerups & ( 1 << PW_GALAK_SHIELD ) && cent->gent->client->NPC_class == CLASS_GALAKMECH ) { -/* refEntity_t tent; + refEntity_t tent; memset( &tent, 0, sizeof( refEntity_t )); tent.reType = RT_LATHE; // Setting up the 2d control points, these get swept around to make a 3D lathed model - Vector2Set( tent.axis[0], 0.5, 0 ); // start point of curve - Vector2Set( tent.axis[1], 50, 85 ); // control point 1 - Vector2Set( tent.axis[2], 135, -100 ); // control point 2 - Vector2Set( tent.oldorigin, 0, -90 ); // end point of curve + VectorSet2( tent.axis[0], 0.5, 0 ); // start point of curve + VectorSet2( tent.axis[1], 50, 85 ); // control point 1 + VectorSet2( tent.axis[2], 135, -100 ); // control point 2 + VectorSet2( tent.oldorigin, 0, -90 ); // end point of curve if ( gent->client->poisonTime && gent->client->poisonTime + 1000 > cg.time ) { @@ -4903,7 +5067,7 @@ void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cen tent.endTime = gent->fx_time + 1000; // if you want the shell to build around the guy, pass in a time that is 1000ms after the start of the turn-on-effect tent.customShader = cgi_R_RegisterShader( "gfx/effects/irid_shield" ); - cgi_R_AddRefEntityToScene( &tent );*/ + cgi_R_AddRefEntityToScene( &tent ); } // Invincibility -- effect needs work @@ -5056,6 +5220,19 @@ void CG_AddRefEntityWithPowerups( refEntity_t *ent, int powerups, centity_t *cen cgi_R_AddRefEntityToScene( ent); } + + if ( cent->gent->client->ps.stasisTime > (cg.time?cg.time:level.time) ) + { //stasis is represented by grey.. + ent->shaderRGBA[0] = 100; + ent->shaderRGBA[1] = 100; + ent->shaderRGBA[2] = 100; + ent->shaderRGBA[3] = 254; + + ent->renderfx &= ~RF_RGB_TINT; + ent->customShader = cgs.media.playerShieldDamage; + + cgi_R_AddRefEntityToScene( ent ); + } } @@ -5078,7 +5255,7 @@ static void CG_G2SetHeadBlink( centity_t *cent, qboolean bStart ) { return; } - + vec3_t desiredAngles = {0}; int blendTime = 80; qboolean bWink = qfalse; @@ -5086,7 +5263,7 @@ static void CG_G2SetHeadBlink( centity_t *cent, qboolean bStart ) if (bStart) { desiredAngles[YAW] = -38; - if ( !in_camera && random() > 0.95f ) + if ( !in_camera && Q_flrand(0.0f, 1.0f) > 0.95f ) { bWink = qtrue; blendTime /=3; @@ -5094,6 +5271,17 @@ static void CG_G2SetHeadBlink( centity_t *cent, qboolean bStart ) } gi.G2API_SetBoneAnglesIndex( &gent->ghoul2[gent->playerModel], hLeye, desiredAngles, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time ); + + if (gent->headModel > 0) + { + const int headLeye = gi.G2API_GetBoneIndex( &gent->ghoul2[gent->headModel], "leye", qtrue ); + if (headLeye != -1) + { + gi.G2API_SetBoneAnglesIndex( &gent->ghoul2[gent->headModel], headLeye, desiredAngles, + BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time ); + } + } + const int hReye = gi.G2API_GetBoneIndex( &gent->ghoul2[0], "reye", qtrue ); if (hReye == -1) { @@ -5101,8 +5289,20 @@ static void CG_G2SetHeadBlink( centity_t *cent, qboolean bStart ) } if (!bWink) - gi.G2API_SetBoneAnglesIndex( &gent->ghoul2[gent->playerModel], hReye, desiredAngles, + { + gi.G2API_SetBoneAnglesIndex( &gent->ghoul2[gent->playerModel], hReye, desiredAngles, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time ); + + if (gent->headModel > 0) + { + const int headReye = gi.G2API_GetBoneIndex( &gent->ghoul2[gent->headModel], "reye", qtrue ); + if (headReye != -1) + { + gi.G2API_SetBoneAnglesIndex( &gent->ghoul2[gent->headModel], headReye, desiredAngles, + BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, blendTime, cg.time ); + } + } + } } /* @@ -5153,6 +5353,12 @@ static void CG_G2SetHeadAnim( centity_t *cent, int anim ) gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], cent->gent->faceBone, firstFrame, lastFrame, animFlags, animSpeed, cg.time, -1, blendTime); } + + if (gent->headModel > 0) + { + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->headModel], cent->gent->headFaceBone, + firstFrame, lastFrame, animFlags, animSpeed, cg.time, -1, blendTime); + } } static qboolean CG_G2PlayerHeadAnims( centity_t *cent ) @@ -5625,31 +5831,6 @@ void CG_SaberDoWeaponHitMarks( gclient_t *client, gentity_t *saberEnt, gentity_t } } -static void CG_RGBForSaberColor( saber_colors_t color, vec3_t rgb ) -{ - switch( color ) - { - case SABER_RED: - VectorSet( rgb, 1.0f, 0.2f, 0.2f ); - break; - case SABER_ORANGE: - VectorSet( rgb, 1.0f, 0.5f, 0.1f ); - break; - case SABER_YELLOW: - VectorSet( rgb, 1.0f, 1.0f, 0.2f ); - break; - case SABER_GREEN: - VectorSet( rgb, 0.2f, 1.0f, 0.2f ); - break; - case SABER_BLUE: - VectorSet( rgb, 0.2f, 0.4f, 1.0f ); - break; - case SABER_PURPLE: - VectorSet( rgb, 0.9f, 0.2f, 1.0f ); - break; - } -} - static void CG_DoSaberLight( saberInfo_t *saber ) { int firstBlade = 0; @@ -5760,11 +5941,425 @@ static void CG_DoSaberLight( saberInfo_t *saber ) } } - cgi_R_AddLightToScene( mid, diameter + (random()*8.0f), rgb[0], rgb[1], rgb[2] ); + cgi_R_AddLightToScene( mid, diameter + (Q_flrand(0.0f, 1.0f)*8.0f), rgb[0], rgb[1], rgb[2] ); } } -static void CG_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax, float radius, saber_colors_t color, int rfx, qboolean doLight ) +void CG_DoSFXSaber( vec3_t blade_muz, vec3_t blade_tip, vec3_t trail_tip, vec3_t trail_muz, float lengthMax, float radius, saber_colors_t color, int rfx, qboolean doLight, saber_crystals_t crystals ) +{ + vec3_t dif, mid, blade_dir, end_dir, trail_dir, base_dir; + float radiusmult, effectradius, coreradius, effectalpha, AngleScale; + float blade_len, end_len, trail_len, base_len, DisTip, DisMuz, DisDif; + float glowscale = 0.5; + float v1, v2, len; + + qhandle_t glow = 0; + refEntity_t saber; + + VectorSubtract( blade_tip, blade_muz, blade_dir ); + VectorSubtract( trail_tip, trail_muz, trail_dir ); + blade_len = VectorLength(blade_dir); + trail_len = VectorLength(trail_dir); + VectorNormalize(blade_dir); + VectorNormalize(trail_dir); + + if ( blade_len < MIN_SABERBLADE_DRAW_LENGTH ) + { + return; + } + + VectorSubtract( trail_tip, blade_tip, end_dir ); + VectorSubtract( trail_muz, blade_muz, base_dir ); + end_len = VectorLength(end_dir); + base_len = VectorLength(base_dir); + VectorNormalize(end_dir); + VectorNormalize(base_dir); + + switch( color ) + { + case SABER_RED: + glow = cgs.media.redSaberGlowShader; + break; + case SABER_ORANGE: + glow = cgs.media.orangeSaberGlowShader; + break; + case SABER_YELLOW: + glow = cgs.media.yellowSaberGlowShader; + break; + case SABER_GREEN: + glow = cgs.media.greenSaberGlowShader; + break; + case SABER_PURPLE: + glow = cgs.media.purpleSaberGlowShader; + break; + case SABER_BLUE: + glow = cgs.media.blueSaberGlowShader; + break; + default://SABER_RGB + glow = cgs.media.rgbSaberGlowShader; + break; + } + + if ( crystals & SABER_CRYSTAL_BLACK ) + { + glow = cgs.media.blackSaberGlowShader; + } + + VectorMA( blade_muz, blade_len * 0.5f, blade_dir, mid ); + + if (doLight) + { + if ( crystals & SABER_CRYSTAL_BLACK ) + { + } + else + { + vec3_t rgb={1,1,1}; + CG_RGBForSaberColor( color, rgb ); + VectorScale( rgb, 0.66f, rgb ); + cgi_R_AddLightToScene( mid, (blade_len*2.0f) + (Q_flrand(0.0f, 1.0f)*10.0f), rgb[0], rgb[1], rgb[2] ); + } + } + + //Distance Scale + { + VectorSubtract( mid, cg.refdef.vieworg, dif ); + len = VectorLength( dif ); + if ( len > 4000 ) + { + len = 4000; + } + else if ( len < 1 ) + { + len = 1; + } + + v1 = ((len+400) / 400); + v2 = ((len+4000) / 4000); + + if(end_len > 1 || base_len > 1) + { + if(end_len > base_len) + glowscale = (end_len+4)*0.1; + else + glowscale = (base_len+4)*0.1; + + if(glowscale > 1.0) + glowscale = 1.0; + } + effectalpha = glowscale; + } + + //Angle Scale + { + VectorSubtract( blade_tip, cg.refdef.vieworg, dif ); + DisTip = VectorLength( dif ); + + VectorSubtract( blade_muz, cg.refdef.vieworg, dif ); + DisMuz = VectorLength( dif ); + + if(DisTip > DisMuz) + { + DisDif = DisTip - DisMuz; + } + else if(DisTip < DisMuz) + { + DisDif = DisMuz - DisTip; + } + else + { + DisDif = 0; + } + + AngleScale = 1.2 - (DisDif/blade_len)*(DisDif/blade_len); + + if(AngleScale > 1.0) + AngleScale = 1.0; + if(AngleScale < 0.2) + AngleScale = 0.2; + + effectalpha *= AngleScale; + + AngleScale += 0.3; + + if(AngleScale > 1.0) + AngleScale = 1.0; + if(AngleScale < 0.4) + AngleScale = 0.4; + } + + memset( &saber, 0, sizeof( refEntity_t )); + + if (blade_len < lengthMax) + { + radiusmult = 0.5 + ((blade_len / lengthMax)/2); + } + else + { + radiusmult = 1.0; + } + + effectradius = ((radius * 1.6 * v1) + Q_flrand(-1.0f, 1.0f) * 0.1f)*radiusmult*cg_SFXSabersGlowSize.value; + coreradius = ((radius * 0.4 * v2) + Q_flrand(-1.0f, 1.0f) * 0.1f)*radiusmult*cg_SFXSabersCoreSize.value; + if ( crystals & SABER_CRYSTAL_UNSTABLE ) + { + coreradius *= 0.9; + } + + { + saber.renderfx = rfx; + if(blade_len-((effectradius*AngleScale)/2) > 0) + { + saber.radius = effectradius*AngleScale; + saber.saberLength = (blade_len - (saber.radius/2)); + VectorCopy( blade_muz, saber.origin ); + VectorCopy( blade_dir, saber.axis[0] ); + saber.reType = RT_SABER_GLOW; + saber.customShader = glow; + saber.shaderRGBA[0] = 0xff * effectalpha; + saber.shaderRGBA[1] = 0xff * effectalpha; + saber.shaderRGBA[2] = 0xff * effectalpha; + saber.shaderRGBA[3] = 0xff * effectalpha; + + if (color >= SABER_RGB) + { + saber.shaderRGBA[0] = ((color) & 0xff) * effectalpha; + saber.shaderRGBA[1] = ((color >> 8) & 0xff) * effectalpha; + saber.shaderRGBA[2] = ((color >> 16) & 0xff) * effectalpha; + } + + cgi_R_AddRefEntityToScene( &saber ); + } + + // Do the hot core + VectorMA( blade_muz, blade_len, blade_dir, saber.origin ); + VectorMA( blade_muz, -1, blade_dir, saber.oldorigin ); + + saber.customShader = cgs.media.SaberBladeShader; + if ( crystals & SABER_CRYSTAL_BLACK ) + { + saber.customShader = cgs.media.blackSaberBladeShader; + } + else if ( crystals & SABER_CRYSTAL_UNSTABLE ) + { + saber.customShader = cgs.media.rgbUnstableCoreShader; + } + + saber.reType = RT_LINE; + + saber.radius = coreradius; + + saber.shaderTexCoord[0] = saber.shaderTexCoord[1] = 1.0f; + saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; + + cgi_R_AddRefEntityToScene( &saber ); + } + + { + saber.renderfx = rfx; + if(trail_len-((effectradius*AngleScale)/2) > 0) + { + saber.radius = effectradius*AngleScale; + saber.saberLength = (trail_len - (saber.radius/2)); + VectorCopy( trail_muz, saber.origin ); + VectorCopy( trail_dir, saber.axis[0] ); + saber.reType = RT_SABER_GLOW; + saber.customShader = glow; + saber.shaderRGBA[0] = 0xff * effectalpha; + saber.shaderRGBA[1] = 0xff * effectalpha; + saber.shaderRGBA[2] = 0xff * effectalpha; + saber.shaderRGBA[3] = 0xff * effectalpha; + + if (color >= SABER_RGB) + { + saber.shaderRGBA[0] = ((color) & 0xff) * effectalpha; + saber.shaderRGBA[1] = ((color >> 8) & 0xff) * effectalpha; + saber.shaderRGBA[2] = ((color >> 16) & 0xff) * effectalpha; + } + + cgi_R_AddRefEntityToScene( &saber ); + } + + // Do the hot core + VectorMA( trail_muz, trail_len, trail_dir, saber.origin ); + VectorMA( trail_muz, -1, trail_dir, saber.oldorigin ); + + saber.customShader = cgs.media.SaberBladeShader; + if ( crystals & SABER_CRYSTAL_BLACK ) + { + saber.customShader = cgs.media.blackSaberBladeShader; + } + else if ( crystals & SABER_CRYSTAL_UNSTABLE ) + { + saber.customShader = cgs.media.rgbUnstableCoreShader; + } + saber.reType = RT_LINE; + + saber.radius = coreradius; + + saber.shaderTexCoord[0] = saber.shaderTexCoord[1] = 1.0f; + saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; + + cgi_R_AddRefEntityToScene( &saber ); + } + + VectorMA( blade_muz, blade_len - 0.5, blade_dir, blade_tip ); + VectorMA( trail_muz, trail_len - 0.5, trail_dir, trail_tip ); + + if(base_len > 2) + { + saber.renderfx = rfx; + if(base_len-(effectradius*AngleScale) > 0) + { + saber.radius = effectradius*AngleScale; + saber.saberLength = (base_len - (effectradius*AngleScale)); + VectorMA( blade_muz, ((effectradius*AngleScale)/2), base_dir, saber.origin ); + VectorCopy( base_dir, saber.axis[0] ); + saber.reType = RT_SABER_GLOW; + saber.customShader = glow; + saber.shaderRGBA[0] = 0xff * effectalpha; + saber.shaderRGBA[1] = 0xff * effectalpha; + saber.shaderRGBA[2] = 0xff * effectalpha; + saber.shaderRGBA[3] = 0xff * effectalpha; + + if (color >= SABER_RGB) + { + saber.shaderRGBA[0] = ((color) & 0xff) * effectalpha; + saber.shaderRGBA[1] = ((color >> 8) & 0xff) * effectalpha; + saber.shaderRGBA[2] = ((color >> 16) & 0xff) * effectalpha; + } + + cgi_R_AddRefEntityToScene( &saber ); + } + + // Do the hot core + VectorMA( blade_muz, base_len, base_dir, saber.origin ); + VectorMA( blade_muz, -0.1, base_dir, saber.oldorigin ); + + saber.customShader = cgs.media.SaberBladeShader; + if ( crystals & SABER_CRYSTAL_BLACK ) + { + saber.customShader = cgs.media.blackSaberBladeShader; + } + else if ( crystals & SABER_CRYSTAL_UNSTABLE ) + { + saber.customShader = cgs.media.rgbUnstableCoreShader; + } + saber.reType = RT_LINE; + + saber.radius = coreradius; + saber.saberLength = base_len; + + saber.shaderTexCoord[0] = saber.shaderTexCoord[1] = 1.0f; + saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; + + cgi_R_AddRefEntityToScene( &saber ); + } + + if(end_len > 1) + { + { + VectorSubtract( blade_tip, cg.refdef.vieworg, dif ); + DisTip = VectorLength( dif ); + + VectorSubtract( trail_tip, cg.refdef.vieworg, dif ); + DisMuz = VectorLength( dif ); + + if(DisTip > DisMuz) + { + DisDif = DisTip - DisMuz; + } + else if(DisTip < DisMuz) + { + DisDif = DisMuz - DisTip; + } + else + { + DisDif = 0; + } + + if(DisDif > end_len * 0.9) + { + effectalpha *= 0.3; + } + else if(DisDif > end_len * 0.8) + { + effectalpha *= 0.5; + } + else if(DisDif > end_len * 0.7) + { + effectalpha *= 0.7; + } + } + + + saber.renderfx = rfx; + if(end_len-(effectradius*AngleScale) > 0) + { + saber.radius = effectradius*AngleScale; + saber.saberLength = (end_len - (effectradius*AngleScale)); + VectorMA( blade_tip, ((effectradius*AngleScale)/2), end_dir, saber.origin ); + VectorCopy( end_dir, saber.axis[0] ); + saber.reType = RT_SABER_GLOW; + saber.customShader = glow; + saber.shaderRGBA[0] = 0xff * effectalpha; + saber.shaderRGBA[1] = 0xff * effectalpha; + saber.shaderRGBA[2] = 0xff * effectalpha; + saber.shaderRGBA[3] = 0xff * effectalpha; + + if (color >= SABER_RGB) + { + saber.shaderRGBA[0] = ((color) & 0xff) * effectalpha; + saber.shaderRGBA[1] = ((color >> 8) & 0xff) * effectalpha; + saber.shaderRGBA[2] = ((color >> 16) & 0xff) * effectalpha; + } + + cgi_R_AddRefEntityToScene( &saber ); + } + + // Do the hot core + VectorMA( blade_tip, end_len, end_dir, saber.origin ); + VectorMA( blade_tip, -0.1, end_dir, saber.oldorigin ); + + saber.customShader = cgs.media.SaberEndShader; + if ( crystals & SABER_CRYSTAL_BLACK ) + { + saber.customShader = cgs.media.blackSaberEndShader; + } + + saber.reType = RT_LINE; + + if(end_len > 9) + { + AngleScale = 5; + } + else if(end_len < 3) + { + AngleScale = 1; + } + else + { + AngleScale = end_len/5; + } + + { + AngleScale -= (((DisDif/end_len)*(DisDif/end_len))*AngleScale); + + if(AngleScale < 0.8) + AngleScale = 0.8; + } + + saber.radius = (coreradius * AngleScale); + saber.saberLength = end_len; + + saber.shaderTexCoord[0] = saber.shaderTexCoord[1] = 1.0f; + saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; + + cgi_R_AddRefEntityToScene( &saber ); + } +} + +static void CG_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax, float radius, saber_colors_t color, int rfx, qboolean doLight, saber_crystals_t crystals ) { vec3_t mid; qhandle_t blade = 0, glow = 0; @@ -5806,14 +6401,35 @@ static void CG_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax glow = cgs.media.purpleSaberGlowShader; blade = cgs.media.purpleSaberCoreShader; break; + default://SABER_RGB + glow = cgs.media.rgbSaberGlowShader; + blade = cgs.media.rgbSaberCoreShader; + break; + } + + if ( crystals & SABER_CRYSTAL_BLACK ) + { + glow = cgs.media.blackSaberGlowShader; + blade = cgs.media.blackSaberCoreShader; + } + else if ( crystals & SABER_CRYSTAL_UNSTABLE ) + { + glow = cgs.media.rgbSaberGlowShader; + blade = cgs.media.rgbUnstableCoreShader; } // always add a light because sabers cast a nice glow before they slice you in half!! or something... if ( doLight ) {//FIXME: RGB combine all the colors of the sabers you're using into one averaged color! - vec3_t rgb={1,1,1}; - CG_RGBForSaberColor( color, rgb ); - cgi_R_AddLightToScene( mid, (length*1.4f) + (random()*3.0f), rgb[0], rgb[1], rgb[2] ); + if ( crystals & SABER_CRYSTAL_BLACK ) + { + } + else + { + vec3_t rgb={1,1,1}; + CG_RGBForSaberColor( color, rgb ); + cgi_R_AddLightToScene( mid, (length*1.4f) + (Q_flrand(0.0f, 1.0f)*3.0f), rgb[0], rgb[1], rgb[2] ); + } } memset( &saber, 0, sizeof( refEntity_t )); @@ -5836,8 +6452,8 @@ static void CG_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax float radiusRange = radius * 0.075f; float radiusStart = radius-radiusRange; - saber.radius = (radiusStart + crandom() * radiusRange)*radiusmult; - //saber.radius = (2.8f + crandom() * 0.2f)*radiusmult; + saber.radius = (radiusStart + Q_flrand(-1.0f, 1.0f) * radiusRange)*radiusmult; + //saber.radius = (2.8f + Q_flrand(-1.0f, 1.0f) * 0.2f)*radiusmult; VectorCopy( origin, saber.origin ); @@ -5846,6 +6462,21 @@ static void CG_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax saber.customShader = glow; saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; saber.renderfx = rfx; + + if (color >= SABER_RGB) + { + saber.shaderRGBA[0] = ((color) & 0xff); + saber.shaderRGBA[1] = ((color >> 8) & 0xff); + saber.shaderRGBA[2] = ((color >> 16) & 0xff); + } + else if ( (crystals & SABER_CRYSTAL_UNSTABLE) && !(crystals & SABER_CRYSTAL_BLACK) ) + { + vec3_t rgb={1,1,1}; + CG_RGBForSaberColor( color, rgb ); + saber.shaderRGBA[0] = 0xff * rgb[0]; + saber.shaderRGBA[1] = 0xff * rgb[1]; + saber.shaderRGBA[2] = 0xff * rgb[2]; + } cgi_R_AddRefEntityToScene( &saber ); @@ -5855,8 +6486,8 @@ static void CG_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax saber.customShader = blade; saber.reType = RT_LINE; radiusStart = radius/3.0f; - saber.radius = (radiusStart + crandom() * radiusRange)*radiusmult; -// saber.radius = (1.0 + crandom() * 0.2f)*radiusmult; + saber.radius = (radiusStart + Q_flrand(-1.0f, 1.0f) * radiusRange)*radiusmult; +// saber.radius = (1.0 + Q_flrand(-1.0f, 1.0f) * 0.2f)*radiusmult; cgi_R_AddRefEntityToScene( &saber ); } @@ -5922,8 +6553,8 @@ static void CG_CreateSaberMarks( vec3_t start, vec3_t end, vec3_t normal ) VectorScale( mid, 0.5f, mid ); VectorSubtract( v->xyz, mid, delta ); - v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * (0.05f + random() * 0.03f); - v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * (0.15f + random() * 0.05f); + v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * (0.05f + Q_flrand(0.0f, 1.0f) * 0.03f); + v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * (0.15f + Q_flrand(0.0f, 1.0f) * 0.05f); } // save it persistantly, do burn first @@ -5942,9 +6573,9 @@ static void CG_CreateSaberMarks( vec3_t start, vec3_t end, vec3_t normal ) mark->alphaFade = qfalse; mark->markShader = cgi_R_RegisterShader("gfx/effects/saberDamageGlow" ); mark->poly.numVerts = mf->numPoints; - mark->color[0] = 215 + random() * 40.0f; - mark->color[1] = 96 + random() * 32.0f; - mark->color[2] = mark->color[3] = random()*15.0f; + mark->color[0] = 215 + Q_flrand(0.0f, 1.0f) * 40.0f; + mark->color[1] = 96 + Q_flrand(0.0f, 1.0f) * 32.0f; + mark->color[2] = mark->color[3] = Q_flrand(0.0f, 1.0f)*15.0f; memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) ); } } @@ -5953,7 +6584,7 @@ extern void FX_AddPrimitive( CEffect **effect, int killTime ); //------------------------------------------------------- void CG_CheckSaberInWater( centity_t *cent, centity_t *scent, int saberNum, int modelIndex, vec3_t origin, vec3_t angles ) { - gclient_s *client = cent->gent->client; + gclient_t *client = cent->gent->client; if ( !client ) { return; @@ -6002,7 +6633,7 @@ static void CG_AddSaberBladeGo( centity_t *cent, centity_t *scent, refEntity_t * mdxaBone_t boltMatrix; qboolean tagHack = qfalse; - gclient_s *client = cent->gent->client; + gclient_t *client = cent->gent->client; if ( !client ) { @@ -6071,7 +6702,7 @@ Ghoul2 Insert Start //New way, multiple blade tags: char *tagName = va( "*blade%d", bladeNum+1 ); bolt = gi.G2API_AddBolt( &scent->gent->ghoul2[modelIndex], tagName ); - + if ( bolt == -1 ) { tagHack = qtrue;//use the hacked switch statement below to position and orient the blades @@ -6590,6 +7221,17 @@ Ghoul2 Insert End return; } + qboolean noDlight = qfalse; + if ( client->ps.saber[saberNum].numBlades >= 3 + || (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_DLIGHT) ) + || ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_DLIGHT2) ) + ) + { + noDlight = qtrue; + } + +if (cg_SFXSabers.integer == 0) +{ if ( (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && client->ps.saber[saberNum].trailStyle < 2 ) || ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && client->ps.saber[saberNum].trailStyle2 < 2 ) ) {//okay to draw the trail @@ -6637,6 +7279,12 @@ Ghoul2 Insert End case SABER_PURPLE: VectorSet( rgb1, 220.0f, 0.0f, 255.0f ); break; + default://SABER_RGB + VectorSet( rgb1, ((client->ps.saber[saberNum].blade[bladeNum].color) & 0xff), + ((client->ps.saber[saberNum].blade[bladeNum].color >> 8) & 0xff), + ((client->ps.saber[saberNum].blade[bladeNum].color >> 16) & 0xff) ); + break; + } } @@ -6659,6 +7307,16 @@ Ghoul2 Insert End duration = saberTrail->duration/2.0f; // stay around twice as long VectorSet( rgb1, 32.0f, 32.0f, 32.0f ); // make the sith sword trail pretty faint } + else if ( cent->gent->client->ps.saber[saberNum].crystals & SABER_CRYSTAL_BLACK ) + { + fx->mShader = cgs.media.blackSaberBlurShader; + duration = saberTrail->duration/5.0f; + } + else if ( cent->gent->client->ps.saber[saberNum].crystals & SABER_CRYSTAL_UNSTABLE ) + { + fx->mShader = cgs.media.unstableBlurShader; + duration = saberTrail->duration/5.0f; + } else { fx->mShader = cgs.media.saberBlurShader; @@ -6722,24 +7380,14 @@ Ghoul2 Insert End saberTrail->lastTime = cg.time; } } - if ( cent->gent->client->ps.saber[saberNum].type == SABER_SITH_SWORD) { // don't need to do nuthin else return; } - - qboolean noDlight = qfalse; - if ( client->ps.saber[saberNum].numBlades >= 3 - || (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_DLIGHT) ) - || ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_DLIGHT2) ) - ) - { - noDlight = qtrue; - } - + if ( (!WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_BLADE) ) - || ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_BLADE2) ) ) + || ( WP_SaberBladeUseSecondBladeStyle( &client->ps.saber[saberNum], bladeNum ) && (client->ps.saber[saberNum].saberFlags2&SFL2_NO_BLADE2) ) ) {//don't draw a blade if ( !noDlight ) {//but still do dlight @@ -6749,7 +7397,196 @@ Ghoul2 Insert End } // Pass in the renderfx flags attached to the saber weapon model...this is done so that saber glows // will get rendered properly in a mirror...not sure if this is necessary?? - CG_DoSaber( org_, axis_[0], length, client->ps.saber[saberNum].blade[bladeNum].lengthMax, client->ps.saber[saberNum].blade[bladeNum].radius, client->ps.saber[saberNum].blade[bladeNum].color, renderfx, (noDlight==qfalse) ); + CG_DoSaber( + org_, axis_[0], length, + client->ps.saber[saberNum].blade[bladeNum].lengthMax, + client->ps.saber[saberNum].blade[bladeNum].radius, + client->ps.saber[saberNum].blade[bladeNum].color, + renderfx, (qboolean)!noDlight, + client->ps.saber[saberNum].crystals ); +} +else +{ + + saberTrail_t *saberTrail = &client->ps.saber[saberNum].blade[bladeNum].trail; + saberTrail->duration = 0; + + if ( saberTrail->lastTime > cg.time ) + {//after a pause, cg.time jumps ahead in time for one frame + //and lastTime gets set to that and will freak out, so, since + //it's never valid for saberTrail->lastTime to be > cg.time, + //cap it to cg.time here + saberTrail->lastTime = cg.time; + } + + if(!saberTrail->base || !saberTrail->tip || !saberTrail->dualtip || !saberTrail->dualbase || !saberTrail->lastTime/* || !saberTrail->inAction*/) + { + VectorCopy( org_, saberTrail->base ); + VectorMA( end, -1.5f, axis_[0], saberTrail->tip ); + VectorCopy( saberTrail->tip, saberTrail->dualtip ); + VectorCopy( saberTrail->base, saberTrail->dualbase ); + saberTrail->lastTime = cg.time; + saberTrail->inAction = cg.time; + return; + } + else if ( cg.time > saberTrail->lastTime ) + { + float dirlen0, dirlen1, dirlen2, lagscale; + vec3_t dir0, dir1, dir2; + + VectorCopy( saberTrail->base, saberTrail->dualbase ); + VectorCopy( saberTrail->tip, saberTrail->dualtip ); + + VectorCopy( org_, saberTrail->base ); + VectorMA( end, -1.5f, axis_[0], saberTrail->tip ); + + VectorSubtract( saberTrail->dualtip, saberTrail->tip, dir0 ); + VectorSubtract( saberTrail->dualbase, saberTrail->base, dir1 ); + VectorSubtract( saberTrail->dualtip, saberTrail->dualbase, dir2 ); + + dirlen0 = VectorLength(dir0); + dirlen1 = VectorLength(dir1); + dirlen2 = VectorLength(dir2); + + if ( saberMoveData[client->ps.saberMove].trailLength == 0 ) + { + dirlen0 *= 0.5; + dirlen1 *= 0.3; + } + else + { + dirlen0 *= 1.0; + dirlen1 *= 0.5; + } + + lagscale = (cg.time - saberTrail->lastTime); + lagscale = 1-(lagscale*3/200); + + if(lagscale < 0.1) + lagscale = 0.1; + + VectorNormalize( dir0 ); + VectorNormalize( dir1 ); + + VectorMA( saberTrail->tip, dirlen0*lagscale, dir0, saberTrail->dualtip ); + VectorMA( saberTrail->base, dirlen1*lagscale, dir1, saberTrail->dualbase ); + VectorSubtract( saberTrail->dualtip, saberTrail->dualbase, dir1 ); + VectorNormalize( dir1 ); + + VectorMA( saberTrail->dualbase, dirlen2, dir1, saberTrail->dualtip ); + + saberTrail->lastTime = cg.time; + } + + vec3_t rgb1={255.0f,255.0f,255.0f}; + + switch( client->ps.saber[saberNum].blade[bladeNum].color ) + { + case SABER_RED: + VectorSet( rgb1, 255.0f, 0.0f, 0.0f ); + break; + case SABER_ORANGE: + VectorSet( rgb1, 253.0f, 125.0f, 80.0f ); + break; + case SABER_YELLOW: + VectorSet( rgb1, 250.0f, 250.0f, 160.0f ); + break; + case SABER_GREEN: + VectorSet( rgb1, 100.0f, 240.0f, 100.0f ); + break; + case SABER_PURPLE: + VectorSet( rgb1, 196.0f, 0.0f, 196.0f ); + break; + case SABER_BLUE: + VectorSet( rgb1, 0.0f, 0.0f, 255.0f ); + break; + default://SABER_RGB + VectorSet( rgb1, ((client->ps.saber[saberNum].blade[bladeNum].color) & 0xff), + ((client->ps.saber[saberNum].blade[bladeNum].color >> 8) & 0xff), + ((client->ps.saber[saberNum].blade[bladeNum].color >> 16) & 0xff) ); + break; + } + + CTrail *fx = new CTrail; + + VectorCopy( saberTrail->base, fx->mVerts[0].origin ); + VectorCopy( saberTrail->tip, fx->mVerts[1].origin ); + VectorCopy( saberTrail->dualtip, fx->mVerts[2].origin ); + VectorCopy( saberTrail->dualbase, fx->mVerts[3].origin ); + + + if ( !(cent->gent->client->ps.saber[saberNum].type == SABER_SITH_SWORD || client->ps.saber[saberNum].saberFlags2&SFL2_NO_BLADE) ) + { + CG_DoSFXSaber( saberTrail->base, saberTrail->tip, saberTrail->dualtip, saberTrail->dualbase, (client->ps.saber[saberNum].blade[bladeNum].lengthMax), (client->ps.saber[saberNum].blade[bladeNum].radius), client->ps.saber[saberNum].blade[bladeNum].color, renderfx, (qboolean)!noDlight, client->ps.saber[saberNum].crystals ); + } + + if ( cg.time > saberTrail->inAction ) + { + saberTrail->inAction = cg.time; + + if ( cent->gent->client->ps.saber[saberNum].type == SABER_SITH_SWORD || client->ps.saber[saberNum].trailStyle == 1 ) + { + fx->mShader = cgs.media.swordTrailShader; + VectorSet( rgb1, 32.0f, 32.0f, 32.0f ); // make the sith sword trail pretty faint + } + else if ( cent->gent->client->ps.saber[saberNum].crystals & SABER_CRYSTAL_BLACK ) + { + fx->mShader = cgs.media.blackSaberTrailShader; + } + else + { + fx->mShader = cgs.media.SaberTrailShader; + } + fx->SetFlags( FX_USE_ALPHA ); + + // New muzzle + VectorCopy( rgb1, fx->mVerts[0].rgb ); + fx->mVerts[0].alpha = 255.0f; + + fx->mVerts[0].ST[0] = 0.0f; + fx->mVerts[0].ST[1] = 4.0f; + fx->mVerts[0].destST[0] = 4.0f; + fx->mVerts[0].destST[1] = 4.0f; + + // new tip + VectorCopy( rgb1, fx->mVerts[1].rgb ); + fx->mVerts[1].alpha = 255.0f; + + fx->mVerts[1].ST[0] = 0.0f; + fx->mVerts[1].ST[1] = 0.0f; + fx->mVerts[1].destST[0] = 4.0f; + fx->mVerts[1].destST[1] = 0.0f; + + // old tip + VectorCopy( rgb1, fx->mVerts[2].rgb ); + fx->mVerts[2].alpha = 255.0f; + + fx->mVerts[2].ST[0] = 4.0f; + fx->mVerts[2].ST[1] = 0.0f; + fx->mVerts[2].destST[0] = 4.0f; + fx->mVerts[2].destST[1] = 0.0f; + + // old muzzle + VectorCopy( rgb1, fx->mVerts[3].rgb ); + fx->mVerts[3].alpha = 255.0f; + + fx->mVerts[3].ST[0] = 4.0f; + fx->mVerts[3].ST[1] = 4.0f; + fx->mVerts[3].destST[0] = 4.0f; + fx->mVerts[3].destST[1] = 4.0f; + + FX_AddPrimitive( (CEffect**)&fx, 0 ); + } + + if ( (client->ps.saber[saberNum].saberFlags2&SFL2_NO_BLADE) ) + { + if ( !noDlight ) + { + CG_DoSaberLight( &client->ps.saber[saberNum] ); + } + } + +} } void CG_AddSaberBlade( centity_t *cent, centity_t *scent, refEntity_t *saber, int renderfx, int modelIndex, vec3_t origin, vec3_t angles ) @@ -6782,6 +7619,407 @@ static void CG_AddSaberBlades( centity_t *cent, int renderfx, vec3_t origin, vec */ //--------------- END SABER STUFF -------- +/* + ================ + GetSelfLegAnimPoint + ================ + */ +//Get the point in the leg animation and return a percentage of the current point in the anim between 0 and the total anim length (0.0f - 1.0f) +float GetSelfLegAnimPoint(void) +{ + float current = 0.0f; + int end = 0; + int start = 0; + if (!!gi.G2API_GetBoneAnimIndex(& + cg_entities[cg.snap->ps.viewEntity].gent->ghoul2[cg_entities[cg.snap->ps.viewEntity].gent->playerModel], + cg_entities[cg.snap->ps.viewEntity].gent->rootBone, + level.time, + ¤t, + &start, + &end, + NULL, + NULL, + NULL)) + { + float percentComplete = (current-start)/(end-start); + + return percentComplete; + } + + return 0.0f; + +} + + +/* + ================ + GetSelfTorsoAnimPoint + + ================ + */ +//Get the point in the torso animation and return a percentage of the current point in the anim between 0 and the total anim length (0.0f - 1.0f) +float GetSelfTorsoAnimPoint(void) +{ + float current = 0.0f; + int end = 0; + int start = 0; + if (!!gi.G2API_GetBoneAnimIndex(& + cg_entities[cg.snap->ps.viewEntity].gent->ghoul2[cg_entities[cg.snap->ps.viewEntity].gent->playerModel], + cg_entities[cg.snap->ps.viewEntity].gent->lowerLumbarBone, + level.time, + ¤t, + &start, + &end, + NULL, + NULL, + NULL)) + { + float percentComplete = (current-start)/(end-start); + + return percentComplete; + } + + return 0.0f; +} + + +/* + =============== + SmoothTrueView + + Purpose: Uses the currently setup model-based First Person View to calculation the final viewangles. Features the + following: + 1. Simulates allowable eye movement by makes a deadzone around the inputed viewangles vs the desired + viewangles of cg.refdef.viewangles + 2. Prevents the sudden view flipping during moves where your camera is suppose to flip 360 on the pitch (x) + pitch (x) axis. + =============== + */ + +void SmoothTrueView(vec3_t eyeAngles) +{ + float LegAnimPoint = GetSelfLegAnimPoint(); + float TorsoAnimPoint = GetSelfTorsoAnimPoint(); + + //counter + int i; + + //cg.refdef.viewangles in relation to eyeAngles + float AngDiff; + + qboolean eyeRange = qtrue; + qboolean UseRefDef = qfalse; + qboolean DidSpecial = qfalse; + + //Debug messages + //CG_Printf("eyeAngles: %f, %f, %f\n", eyeAngles[0], eyeAngles[1], eyeAngles[2]); + //CG_Printf("cg.refdef.viewangles: %f, %f, %f\n", cg.refdefViewAngles[0], cg.refdefViewAngles[1], cg.refdefViewAngles[2]); + + + + //RAFIXME: See if I can find a link this to the prediction stuff. I think the snap is of just the last gamestate snap + + //Rolls + if ( cg_trueroll.integer ) + { + if ( ( (cg.snap->ps.legsAnim) == BOTH_WALL_RUN_LEFT ) + || ( (cg.snap->ps.legsAnim) == BOTH_WALL_RUN_RIGHT ) + || ( (cg.snap->ps.legsAnim) == BOTH_WALL_RUN_LEFT_STOP ) + || ( (cg.snap->ps.legsAnim) == BOTH_WALL_RUN_RIGHT_STOP ) + || ( (cg.snap->ps.legsAnim) == BOTH_WALL_RUN_LEFT_FLIP ) + || ( (cg.snap->ps.legsAnim) == BOTH_WALL_RUN_RIGHT_FLIP ) + || ( (cg.snap->ps.legsAnim) == BOTH_WALL_FLIP_LEFT ) + || ( (cg.snap->ps.legsAnim) == BOTH_WALL_FLIP_RIGHT ) ) + {//Roll moves that look good with eye range + eyeRange = qtrue; + DidSpecial = qtrue; + } + else if ( cg_trueroll.integer == 1 ) + {//Use simple roll for the more complicated rolls + if ( ( (cg.snap->ps.legsAnim) == BOTH_FLIP_L ) + || ( (cg.snap->ps.legsAnim) == BOTH_ROLL_L ) ) + {//Left rolls + VectorCopy( cg.refdefViewAngles, eyeAngles ); + eyeAngles[2] += AngleNormalize180( (360 * LegAnimPoint) ); + AngleNormalize180( eyeAngles[2] ); + eyeRange = qfalse; + DidSpecial = qtrue; + } + else if( ((cg.snap->ps.legsAnim) == BOTH_FLIP_R) + || ((cg.snap->ps.legsAnim) == BOTH_ROLL_R) ) + {//Right rolls + VectorCopy( cg.refdefViewAngles, eyeAngles ); + eyeAngles[2] += AngleNormalize180( ( 360 - (360 * LegAnimPoint) ) ); + AngleNormalize180( eyeAngles[2] ); + eyeRange = qfalse; + DidSpecial = qtrue; + } + } + else + {//You're here because you're using cg_trueroll.integer == 2 + if ( ((cg.snap->ps.legsAnim) == BOTH_FLIP_L) + || ((cg.snap->ps.legsAnim) == BOTH_ROLL_L) + || ((cg.snap->ps.legsAnim) == BOTH_FLIP_R) + || ((cg.snap->ps.legsAnim) == BOTH_ROLL_R) ) + {//Roll animation, lock the eyemovement + eyeRange = qfalse; + DidSpecial = qtrue; + } + } + } + else if ( ((cg.snap->ps.legsAnim) == BOTH_WALL_RUN_LEFT) + || ((cg.snap->ps.legsAnim) == BOTH_WALL_RUN_RIGHT) + || ((cg.snap->ps.legsAnim) == BOTH_WALL_RUN_LEFT_STOP) + || ((cg.snap->ps.legsAnim) == BOTH_WALL_RUN_RIGHT_STOP) + || ((cg.snap->ps.legsAnim) == BOTH_WALL_RUN_LEFT_FLIP) + || ((cg.snap->ps.legsAnim) == BOTH_WALL_RUN_RIGHT_FLIP) + || ((cg.snap->ps.legsAnim) == BOTH_WALL_FLIP_LEFT) + || ((cg.snap->ps.legsAnim) == BOTH_WALL_FLIP_RIGHT) + || ((cg.snap->ps.legsAnim) == BOTH_FLIP_L) + || ((cg.snap->ps.legsAnim) == BOTH_ROLL_L) + || ((cg.snap->ps.legsAnim) == BOTH_FLIP_R) + || ((cg.snap->ps.legsAnim) == BOTH_ROLL_R) ) + {//you don't want rolling so use cg.refdef.viewangles as the view + UseRefDef = qtrue; + } + + //Flips + if( cg_trueflip.integer ) + { + if( cg.snap->ps.legsAnim == BOTH_WALL_FLIP_BACK1 ) + {//Flip moves that look good with the eyemovement locked + eyeRange = qfalse; + DidSpecial = qtrue; + } + else if ( cg_trueflip.integer == 1 ) + {//Use simple flip for the more complicated flips + if ( ((cg.snap->ps.legsAnim) == BOTH_FLIP_F) + || ((cg.snap->ps.legsAnim) == BOTH_ROLL_F) ) + {//forward flips + VectorCopy( cg.refdefViewAngles, eyeAngles ); + eyeAngles[0] += AngleNormalize180( 360 - (360 * LegAnimPoint) ); + AngleNormalize180( eyeAngles[0] ); + eyeRange = qfalse; + DidSpecial = qtrue; + } + else if ( ((cg.snap->ps.legsAnim) == BOTH_FLIP_B) + || ((cg.snap->ps.legsAnim) == BOTH_ROLL_B) + || ((cg.snap->ps.legsAnim) == BOTH_FLIP_BACK1) ) + {//back flips + VectorCopy( cg.refdefViewAngles, eyeAngles ); + eyeAngles[0] += AngleNormalize180( (360 * LegAnimPoint) ); + AngleNormalize180( eyeAngles[0] ); + eyeRange = qfalse; + DidSpecial = qtrue; + } + } + else + {//You're here because you're using cg_trueflip.integer = 2 + if ( ( (cg.snap->ps.legsAnim) == BOTH_FLIP_F ) + || ( (cg.snap->ps.legsAnim) == BOTH_ROLL_F ) + || ( (cg.snap->ps.legsAnim) == BOTH_FLIP_B ) + || ( (cg.snap->ps.legsAnim) == BOTH_ROLL_B ) + || ( (cg.snap->ps.legsAnim) == BOTH_FLIP_BACK1 ) ) + {//Flip animation and using cg_trueflip.integer = 2, lock the eyemovement + eyeRange = qfalse; + DidSpecial = qtrue; + } + } + } + else if ( ((cg.snap->ps.legsAnim) == BOTH_WALL_FLIP_BACK1) + || ((cg.snap->ps.legsAnim) == BOTH_FLIP_F) + || ((cg.snap->ps.legsAnim) == BOTH_ROLL_F) + || ((cg.snap->ps.legsAnim) == BOTH_FLIP_B) + || ((cg.snap->ps.legsAnim) == BOTH_ROLL_B) + || ((cg.snap->ps.legsAnim) == BOTH_FLIP_BACK1) ) + {//you don't want flipping so use cg.refdef.viewangles as the view + UseRefDef = qtrue; + } + + + + if ( cg_truespin.integer ) + { + if ( cg_truespin.integer == 1 ) + {//Do a simulated Spin for the more complicated spins + if ( ((cg.snap->ps.torsoAnim) == BOTH_T1_TL_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T1__L_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T1__L__R) + || ((cg.snap->ps.torsoAnim) == BOTH_T1_BL_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T1_BL__R) + || ((cg.snap->ps.torsoAnim) == BOTH_T1_BL_TR) + || ((cg.snap->ps.torsoAnim) == BOTH_T2__L_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T2_BL_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T2_BL__R) + || ((cg.snap->ps.torsoAnim) == BOTH_T3__L_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T3_BL_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T3_BL__R) + || ((cg.snap->ps.torsoAnim) == BOTH_T4__L_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T4_BL_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T4_BL__R) + || ((cg.snap->ps.torsoAnim) == BOTH_T5_TL_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T5__L_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T5__L__R) + || ((cg.snap->ps.torsoAnim) == BOTH_T5_BL_BR) + || ((cg.snap->ps.torsoAnim) == BOTH_T5_BL__R) + || ((cg.snap->ps.torsoAnim) == BOTH_T5_BL_TR) + || ((cg.snap->ps.torsoAnim) == BOTH_ATTACK_BACK) + || ((cg.snap->ps.torsoAnim) == BOTH_CROUCHATTACKBACK1) + || ((cg.snap->ps.torsoAnim) == BOTH_BUTTERFLY_LEFT) + //This technically has 2 spins and seems to have been labeled wrong + || ((cg.snap->ps.legsAnim) == BOTH_FJSS_TR_BL) ) + {//Left Spins + VectorCopy( cg.refdefViewAngles, eyeAngles ); + eyeAngles[1] += AngleNormalize180( (360 - (360 * TorsoAnimPoint)) ); + AngleNormalize180( eyeAngles[1] ); + eyeRange = qfalse; + DidSpecial = qtrue; + } + else if ( ((cg.snap->ps.torsoAnim) == BOTH_T1_BR_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T1__R__L) + || ((cg.snap->ps.torsoAnim) == BOTH_T1__R_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T1_TR_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T1_BR_TL) + || ((cg.snap->ps.torsoAnim) == BOTH_T1_BR__L) + || ((cg.snap->ps.torsoAnim) == BOTH_T2_BR__L) + || ((cg.snap->ps.torsoAnim) == BOTH_T2_BR_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T2__R_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T3_BR__L) + || ((cg.snap->ps.torsoAnim) == BOTH_T3_BR_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T3__R_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T4_BR__L) + || ((cg.snap->ps.torsoAnim) == BOTH_T4_BR_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T4__R_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T5_BR_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T5__R__L) + || ((cg.snap->ps.torsoAnim) == BOTH_T5__R_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T5_TR_BL) + || ((cg.snap->ps.torsoAnim) == BOTH_T5_BR_TL) + || ((cg.snap->ps.torsoAnim) == BOTH_T5_BR__L) + //This technically has 2 spins + || ((cg.snap->ps.legsAnim) == BOTH_BUTTERFLY_RIGHT) + //This technically has 2 spins and seems to have been labeled wrong + || ((cg.snap->ps.legsAnim) == BOTH_FJSS_TL_BR) ) + {//Right Spins + VectorCopy( cg.refdefViewAngles, eyeAngles ); + eyeAngles[1] += AngleNormalize180( (360 * TorsoAnimPoint) ); + AngleNormalize180( eyeAngles[1] ); + eyeRange = qfalse; + DidSpecial = qtrue; + } + } + else + {//You're here because you're using cg_truespin.integer == 2 + if ( PM_SpinningSaberAnim( (cg.snap->ps.torsoAnim) ) + && ((cg.snap->ps.torsoAnim) != BOTH_JUMPFLIPSLASHDOWN1) + && ((cg.snap->ps.torsoAnim) != BOTH_JUMPFLIPSTABDOWN) ) + {//Flip animation and using cg_trueflip.integer = 2, lock the eyemovement + eyeRange = qfalse; + DidSpecial = qtrue; + } + } + } + else if ( PM_SpinningSaberAnim( (cg.snap->ps.torsoAnim) ) + && ((cg.snap->ps.torsoAnim) != BOTH_JUMPFLIPSLASHDOWN1) + && ((cg.snap->ps.torsoAnim) != BOTH_JUMPFLIPSTABDOWN) ) + {//you don't want spinning so use cg.refdef.viewangles as the view + UseRefDef = qtrue; + } + else if ( (cg.snap->ps.legsAnim) == BOTH_JUMPATTACK6) + { + UseRefDef = qtrue; + } + + //Prevent camera flicker while landing. + if ( ((cg.snap->ps.legsAnim) == BOTH_LAND1) + || ((cg.snap->ps.legsAnim) == BOTH_LAND2) + || ((cg.snap->ps.legsAnim) == BOTH_LANDBACK1) + || ((cg.snap->ps.legsAnim) == BOTH_LANDLEFT1) + || ((cg.snap->ps.legsAnim) == BOTH_LANDRIGHT1) ) + { + UseRefDef = qtrue; + } + + //Prevent the camera flicker while switching to the saber. + if ( ( (cg.snap->ps.torsoAnim) == BOTH_STAND2TO1 ) + || ( (cg.snap->ps.torsoAnim) == BOTH_STAND1TO2 ) ) + { + UseRefDef = qtrue; + } + + //special camera view for blue backstab + if ( (cg.snap->ps.torsoAnim) == BOTH_A2_STABBACK1) + { + eyeRange = qfalse; + DidSpecial = qtrue; + } + + if ( ( (cg.snap->ps.torsoAnim) == BOTH_JUMPFLIPSLASHDOWN1) + || ( (cg.snap->ps.torsoAnim) == BOTH_JUMPFLIPSLASHDOWN1) ) + { + eyeRange = qfalse; + DidSpecial = qtrue; + } + + + if ( UseRefDef ) + { + VectorCopy( cg.refdefViewAngles, eyeAngles ); + } + else + { + //Movement Roll dampener + if ( !DidSpecial ) + { + if ( !cg_truemoveroll.integer ) + { + eyeAngles[2] = cg.refdefViewAngles[2]; + } + else if ( cg_truemoveroll.integer == 1 ) + {//dampen the movement leaning + eyeAngles[2] *= .5; + } + } + + //eye movement + if ( eyeRange ) + {//allow eye motion + for (i = 0; i < 2; i++ ) + { + int fov; + if ( cg_truefov.integer ) + { + fov = cg_truefov.value; + } + else + { + fov = cg_fov.value; + } + + AngDiff = eyeAngles[i] - cg.refdefViewAngles[i]; + + AngDiff = AngleNormalize180( AngDiff ); + if ( fabs( AngDiff ) > fov ) + { + if ( AngDiff < 0 ) + { + eyeAngles[i] += fov; + } + else + { + eyeAngles[i] -= fov; + } + } + else + { + eyeAngles[i] = cg.refdefViewAngles[i]; + } + AngleNormalize180( eyeAngles[i] ); + } + } + } +} + /* =============== CG_Player @@ -6830,6 +8068,7 @@ CG_Player extern qboolean G_GetRootSurfNameWithVariant( gentity_t *ent, const char *rootSurfName, char *returnSurfName, int returnSize ); extern qboolean G_ControlledByPlayer( gentity_t *self ); extern qboolean G_RagDoll(gentity_t *ent, vec3_t forcedAngles); +extern void CG_AddRadarEnt(centity_t *cent); int cg_saberOnSoundTime[MAX_GENTITIES] = {0}; void CG_Player( centity_t *cent ) { @@ -6870,7 +8109,7 @@ void CG_Player( centity_t *cent ) { return; } - if(cent->currentState.number == 0 && !cg.renderingThirdPerson )//!cg_thirdPerson.integer ) + if(cent->currentState.number == 0 && !cg.renderingThirdPerson && (!cg_trueguns.integer || cg.zoomMode))//!cg_thirdPerson.integer ) { calcedMp = qtrue; } @@ -6881,6 +8120,8 @@ void CG_Player( centity_t *cent ) { { return; } + + CG_AddRadarEnt(cent); G_RagDoll(cent->gent, cent->lerpAngles); @@ -6921,7 +8162,7 @@ Ghoul2 Insert Start {//no viewentity if ( cent->currentState.number == cg.snap->ps.clientNum ) {//I am the player - if ( cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE ) + if ( cg.zoomMode || (!cg_trueguns.integer && cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE) || (cg.snap->ps.weapon == WP_SABER && cg_truesaberonly.integer) ) {//not using saber or fists ent.renderfx = RF_THIRD_PERSON; // only draw in mirrors } @@ -6929,7 +8170,7 @@ Ghoul2 Insert Start } else if ( cent->currentState.number == cg.snap->ps.viewEntity ) {//I am the view entity - if ( cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE ) + if ( cg.zoomMode || (!cg_trueguns.integer && cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE) || (cg.snap->ps.weapon == WP_SABER && cg_truesaberonly.integer) ) {//not using first person saber test or, if so, not using saber ent.renderfx = RF_THIRD_PERSON; // only draw in mirrors } @@ -7271,6 +8512,7 @@ extern vmCvar_t cg_thirdPersonAlpha; if ( cent->currentState.number != 0 || cg.renderingThirdPerson || cg.snap->ps.stats[STAT_HEALTH] <= 0 + || ( cg_trueguns.integer && !cg.zoomMode ) || ( !cg.renderingThirdPerson && (cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE) )//First person saber ) {//in some third person mode or NPC @@ -7321,6 +8563,121 @@ extern vmCvar_t cg_thirdPersonAlpha; gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.footRPoint ); } + //[TrueView] + //Restrict True View Model changes to the player and do the True View camera view work. + if (cg.snap && cent->currentState.number == cg.snap->ps.viewEntity && cg_truebobbing.integer) + { + if ( !cg.renderingThirdPerson && (cg_trueguns.integer || cent->currentState.weapon == WP_SABER + || cent->currentState.weapon == WP_MELEE) && !cg.zoomMode) + { + //gent->ghoul2[cent->gent->playerModel], "*head_eyes"); + if( !gi.G2API_GetBoltMatrix(cent->gent->ghoul2, cent->gent->playerModel, eyesBolt, &eyeMatrix, tempAngles, cent->lerpOrigin, + cg.time, cgs.model_draw, cent->currentState.modelScale) ) + {//Something prevented you from getting the "*head_eyes" information. The model probably doesn't have a + //*head_eyes tag surface. Try using *head_front instead + + eyesBolt = gi.G2API_AddBolt(¢->gent->ghoul2[cent->gent->playerModel], "*head_front"); + if( !gi.G2API_GetBoltMatrix(cent->gent->ghoul2, cent->gent->playerModel, eyesBolt, &eyeMatrix, tempAngles, cent->lerpOrigin, + cg.time, cgs.model_draw, cent->currentState.modelScale) ) + { + eyesBolt = gi.G2API_AddBolt(¢->gent->ghoul2[cent->gent->playerModel], "reye"); + boneBased = qtrue; + if( !gi.G2API_GetBoltMatrix(cent->gent->ghoul2, cent->gent->playerModel, eyesBolt, &eyeMatrix, tempAngles, cent->lerpOrigin, + cg.time, cgs.model_draw, cent->currentState.modelScale) ) + { +/* if( !trueviewwarning ) + {//first failure. Do a single warning then turn the warnings off. + CG_Printf("WARNING: This Model seems to have missing the *head_eyes and *head_front tag surfaces. True View Disabled.\n"); + trueviewwarning = qtrue; + }*/ + + goto SkipTrueView; + } + } + } + + //Set the original eye Origin + VectorCopy( cg.refdef.vieworg, OldeyeOrigin); + + //set the player's view origin + gi.G2API_GiveMeVectorFromMatrix(eyeMatrix, ORIGIN, cg.refdef.vieworg); + + //Find the orientation of the eye tag surface + //I based this on coordsys.h that I found at http://www.xs4all.nl/~hkuiper/cwmtx/html/coordsys_8h-source.html + //According to the file, Harry Kuiper, Will DeVore deserve credit for making that file that I based this on. + + if(boneBased) + {//the eye bone has different default axis orientation than the tag surfaces. + EyeAxis[0][0] = eyeMatrix.matrix[0][1]; + EyeAxis[1][0] = eyeMatrix.matrix[1][1]; + EyeAxis[2][0] = eyeMatrix.matrix[2][1]; + + EyeAxis[0][1] = eyeMatrix.matrix[0][0]; + EyeAxis[1][1] = eyeMatrix.matrix[1][0]; + EyeAxis[2][1] = eyeMatrix.matrix[2][0]; + + EyeAxis[0][2] = -eyeMatrix.matrix[0][2]; + EyeAxis[1][2] = -eyeMatrix.matrix[1][2]; + EyeAxis[2][2] = -eyeMatrix.matrix[2][2]; + } + else + { + EyeAxis[0][0] = eyeMatrix.matrix[0][0]; + EyeAxis[1][0] = eyeMatrix.matrix[1][0]; + EyeAxis[2][0] = eyeMatrix.matrix[2][0]; + + EyeAxis[0][1] = eyeMatrix.matrix[0][1]; + EyeAxis[1][1] = eyeMatrix.matrix[1][1]; + EyeAxis[2][1] = eyeMatrix.matrix[2][1]; + + EyeAxis[0][2] = eyeMatrix.matrix[0][2]; + EyeAxis[1][2] = eyeMatrix.matrix[1][2]; + EyeAxis[2][2] = eyeMatrix.matrix[2][2]; + } + + eyeAngles[YAW] = ( atan2(EyeAxis[1][0], EyeAxis[0][0]) * 180 / M_PI ); + + //I want asin but it's not setup in the libraries so I'm useing the statement asin x = (M_PI / 2) - acos x + eyeAngles[PITCH] = ( ( (M_PI / 2) - acos (-EyeAxis[2][0]) ) * 180 / M_PI ); + eyeAngles[ROLL] = ( atan2(EyeAxis[2][1], EyeAxis[2][2]) * 180 / M_PI ); + + //END Find the orientation of the eye tag surface + + //Shift the camera origin by cg_trueeyeposition + AngleVectors( eyeAngles, EyeAxis[0], NULL, NULL ); + VectorMA( cg.refdef.vieworg, cg_trueeyeposition.value, EyeAxis[0], cg.refdef.vieworg ); + + //Trace to see if the bolt eye origin is ok to move to. If it's not, place it at the last safe position. + CheckCameraLocation( OldeyeOrigin ); + + //Singleplayer TrueView fix (ghoul2 axes calculated differently to in MP!) + eyeAngles[YAW] -= 90; + + //Do all the Eye "movement" and simplified moves here. + SmoothTrueView(eyeAngles); + + //set the player view angles + VectorCopy( eyeAngles, cg.refdefViewAngles ); + + //set the player view axis + AnglesToAxis( eyeAngles, cg.refdef.viewaxis ); + + } + } + +SkipTrueView: + //Handle saber if ( cent->gent && cent->gent->client @@ -7349,11 +8706,11 @@ extern vmCvar_t cg_thirdPersonAlpha; { if ( cent->gent->client->ps.stats[STAT_HEALTH] <= 0 ) {//dead, didn't actively turn it off - cent->gent->client->ps.saber[saberNum].blade[bladeNum].length -= cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax/10 * cg.frametime/100; + cent->gent->client->ps.saber[saberNum].blade[bladeNum].length -= cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax/10 * cg.frametime/100 * cg_ignitionSpeed.value; } else {//actively turned it off, shrink faster - cent->gent->client->ps.saber[saberNum].blade[bladeNum].length -= cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax/10 * cg.frametime/100; + cent->gent->client->ps.saber[saberNum].blade[bladeNum].length -= cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax/10 * cg.frametime/100 * cg_ignitionSpeed.value; } } } @@ -7388,6 +8745,73 @@ extern vmCvar_t cg_thirdPersonAlpha; cg_saberOnSoundTime[cent->currentState.number] = cg.time;//so we don't play multiple on sounds at one time } } + if (( cg_ignitionFlare.integer == 1 || + (cg_ignitionFlare.integer > 0 && cg_SFXSabers.integer == 0)) && !(cent->gent->client->ps.saber[saberNum].type == SABER_SITH_SWORD || cent->gent->client->ps.saber[saberNum].saberFlags2&SFL2_NO_BLADE)) + { + vec3_t org_; + vec3_t RGB = {1.0f,0.75f,0.0f}; + qhandle_t shader = cgs.media.ignitionFlare; + + CG_RGBForSaberColor( cent->gent->client->ps.saber[saberNum].blade[bladeNum].color, RGB ); + char *tagName = va( "*blade%d", bladeNum+1 ); + int bolt = gi.G2API_AddBolt( ¢->gent->ghoul2[cent->gent->weaponModel[saberNum]], tagName ); + + if ( bolt == -1 ) + { + //hmm, just fall back to the most basic tag (this will also make it work with pre-JKA saber models + bolt = gi.G2API_AddBolt( ¢->gent->ghoul2[cent->gent->weaponModel[saberNum]], "*flash" ); + if ( bolt == -1 ) + {//no tag_flash either?!! + bolt = 0; + } + } + + mdxaBone_t boltMatrix; + gi.G2API_GetBoltMatrix(cent->gent->ghoul2, cent->gent->weaponModel[saberNum], bolt, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale); + + // work the matrix axis stuff into the original axis and origins used. + gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, org_); + + if ( cent->gent->client->ps.saber[saberNum].crystals & SABER_CRYSTAL_BLACK ) + { + shader = cgs.media.blackIgnitionFlare; + + if ( WP_SaberBladeUseSecondBladeStyle(¢->gent->client->ps.saber[saberNum], bladeNum )) + { + if ( cent->gent->client->ps.saber[saberNum].blackIgnitionFlare2[0] ) + { + shader = cgi_R_RegisterShader( cent->gent->client->ps.saber[saberNum].blackIgnitionFlare2 ); + } + } + else + { + if ( cent->gent->client->ps.saber[saberNum].blackIgnitionFlare[0] ) + { + shader = cgi_R_RegisterShader( cent->gent->client->ps.saber[saberNum].blackIgnitionFlare ); + } + } + } + else + { + if ( WP_SaberBladeUseSecondBladeStyle(¢->gent->client->ps.saber[saberNum], bladeNum )) + { + if ( cent->gent->client->ps.saber[saberNum].ignitionFlare2[0] ) + { + shader = cgi_R_RegisterShader( cent->gent->client->ps.saber[saberNum].ignitionFlare2 ); + } + } + else + { + if ( cent->gent->client->ps.saber[saberNum].ignitionFlare[0] ) + { + shader = cgi_R_RegisterShader( cent->gent->client->ps.saber[saberNum].ignitionFlare ); + } + } + } + + FX_AddSprite( org_, cent->gent->client->ps.velocity, NULL, 40.0f, 0.0f, 1.0f, 0.7f, RGB, RGB, Q_flrand(0.0f, 1.0f) * 360, 0.0f, 100.0f, shader, FX_USE_ALPHA ); + } + } if ( cg.frametime > 0 ) { @@ -7398,7 +8822,7 @@ extern vmCvar_t cg_thirdPersonAlpha; } else { - cent->gent->client->ps.saber[saberNum].blade[bladeNum].length += cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax/10 * cg.frametime/100; + cent->gent->client->ps.saber[saberNum].blade[bladeNum].length += cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax/10 * cg.frametime/100 * cg_ignitionSpeed.value; } } if ( cent->gent->client->ps.saber[saberNum].blade[bladeNum].length > cent->gent->client->ps.saber[saberNum].blade[bladeNum].lengthMax ) @@ -7452,6 +8876,10 @@ extern vmCvar_t cg_thirdPersonAlpha; && (cent->gent->client->ps.saber[saberNum].blade[bladeNum].length > 0 || cent->gent->client->ps.saberInFlight) ) { calcedMp = qtrue; + if (cent->gent->client->ps.saberAnimLevel == SS_KATARN) + { + calcedMp = qfalse; + } } } } @@ -7487,7 +8915,8 @@ extern vmCvar_t cg_thirdPersonAlpha; if ( cent->currentState.number != 0 || cg.renderingThirdPerson || cg.snap->ps.stats[STAT_HEALTH] <= 0 - || ( !cg.renderingThirdPerson && (cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE) )//First person saber + || ( !cg.renderingThirdPerson && (cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE) ) //First person saber + || ( cg_trueguns.integer && !cg.zoomMode ) ) {//if NPC, third person, or dead, unless using saber //Get eyePoint & eyeAngles @@ -7706,6 +9135,21 @@ extern vmCvar_t cg_thirdPersonAlpha; gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, oldMD ); } } + else if ((cent->gent->client->ps.saberAnimLevel == SS_KATARN) + && cent->gent->s.weapon == WP_SABER + && cent->gent->weaponModel[1] + && cent->gent->weaponModel[1] != -1 + && ( cent->gent->ghoul2.size() > cent->gent->weaponModel[1] ) + && ( cent->gent->ghoul2[cent->gent->weaponModel[1]].mModelindex != -1))//one in each hand + { + mdxaBone_t boltMatrix; + // figure out where the actual model muzzle is + gi.G2API_GetBoltMatrix( cent->gent->ghoul2, cent->gent->weaponModel[1], 0, &boltMatrix, tempAngles, ent.origin, cg.time, cgs.model_draw, cent->currentState.modelScale ); + // work the matrix axis stuff into the original axis and origins used. + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, cent->gent->client->renderInfo.muzzlePoint ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, cent->gent->client->renderInfo.muzzleDir ); + + } else if (( cent->gent->weaponModel[0] != -1) && ( cent->gent->ghoul2.size() > cent->gent->weaponModel[0] ) && ( cent->gent->ghoul2[cent->gent->weaponModel[0]].mModelindex != -1)) @@ -7758,7 +9202,13 @@ extern vmCvar_t cg_thirdPersonAlpha; const char *effect = NULL; cent->muzzleFlashTime = 0; - + + if (cent->muzzleFlashWeapon) + { + wData = &weaponData[cent->muzzleFlashWeapon]; + cent->muzzleFlashWeapon = 0; + } + // Try and get a default muzzle so we have one to fall back on if ( wData->mMuzzleEffect[0] ) { @@ -7914,11 +9364,21 @@ extern vmCvar_t cg_thirdPersonAlpha; //"refraction" effect -rww if ( cent->gent->client->ps.powerups[PW_FORCE_PUSH] > cg.time ) { - CG_ForcePushRefraction(cent->gent->client->renderInfo.handLPoint, cent); + CG_ForcePushRefraction(cent->gent->client->renderInfo.handLPoint, cent, 1.0f, NULL); } else if ( cent->gent->client->ps.powerups[PW_FORCE_PUSH_RHAND] > cg.time ) { - CG_ForcePushRefraction(cent->gent->client->renderInfo.handRPoint, cent); + CG_ForcePushRefraction(cent->gent->client->renderInfo.handRPoint, cent, 1.0f, NULL); + } + else if ( cent->gent->client->ps.powerups[PW_REFRACT_MUZZLE] > cg.time ) + { + vec3_t orange; + VectorScale(colorTable[CT_VLTORANGE], 255.0f, orange); + CG_ForcePushRefraction(cent->gent->client->renderInfo.muzzlePoint, cent, 1.0f, orange); + } + else if ( cent->gent->client->ps.powerups[PW_FORCE_REPULSE] > cg.time ) + { + CG_ForcePushRefraction(cent->gent->client->renderInfo.crotchPoint, cent, 2.0f, NULL); } else { @@ -8017,7 +9477,7 @@ Ghoul2 Insert End {//no viewentity if ( cent->currentState.number == cg.snap->ps.clientNum ) {//I am the player - if ( cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE ) + if ( cg.zoomMode || (!cg_trueguns.integer && cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE) || (cg.snap->ps.weapon == WP_SABER && cg_truesaberonly.integer) ) {//not using saber or fists renderfx = RF_THIRD_PERSON; // only draw in mirrors } @@ -8025,7 +9485,7 @@ Ghoul2 Insert End } else if ( cent->currentState.number == cg.snap->ps.viewEntity ) {//I am the view entity - if ( cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE ) + if ( cg.zoomMode || (!cg_trueguns.integer && cg.snap->ps.weapon != WP_SABER && cg.snap->ps.weapon != WP_MELEE) || (cg.snap->ps.weapon == WP_SABER && cg_truesaberonly.integer) ) {//not using saber or fists renderfx = RF_THIRD_PERSON; // only draw in mirrors } @@ -8367,7 +9827,7 @@ Ghoul2 Insert End } //FIXME: for debug, allow to draw a cone of the NPC's FOV... - if ( cent->currentState.number == 0 && cg.renderingThirdPerson ) + if ( cent->currentState.number == 0 && (cg.renderingThirdPerson || (cg_trueguns.integer && !cg.zoomMode)) ) { playerState_t *ps = &cg.predicted_player_state; @@ -8415,9 +9875,9 @@ Ghoul2 Insert End CGCam_Shake( val * val * 0.3f, 100 ); } - val += random() * 0.5f; + val += Q_flrand(0.0f, 1.0f) * 0.5f; - FX_AddSprite( cent->gent->client->renderInfo.muzzlePoint, NULL, NULL, 3.0f * val * scale, 0.0f, 0.7f, 0.7f, WHITE, WHITE, random() * 360, 0.0f, 1.0f, shader, FX_USE_ALPHA ); + FX_AddSprite( cent->gent->client->renderInfo.muzzlePoint, NULL, NULL, 3.0f * val * scale, 0.0f, 0.7f, 0.7f, WHITE, WHITE, Q_flrand(0.0f, 1.0f) * 360, 0.0f, 1.0f, shader, FX_USE_ALPHA ); } } } diff --git a/code/cgame/cg_playerstate.cpp b/code/cgame/cg_playerstate.cpp index 3d75db3e57..b151b26e5b 100644 --- a/code/cgame/cg_playerstate.cpp +++ b/code/cgame/cg_playerstate.cpp @@ -46,40 +46,6 @@ void CG_CheckAmmo( void ) int previous; // int weapons; -#if 0 - - // see about how many seconds of ammo we have remaining - weapons = cg.snap->ps.stats[ STAT_WEAPONS ]; - total = 0; - - for ( i = WP_SABER; i < WP_NUM_WEAPONS i++ ) - { - if ( ! ( weapons & ( 1 << i ) ) ) - continue; - - /* - switch ( i ) - { - case WP_ROCKET_LAUNCHER: - case WP_GRENADE_LAUNCHER: - case WP_RAILGUN: - case WP_SHOTGUN: - total += cg.snap->ps.ammo[i] * 1000; - break; - default: - total += cg.snap->ps.ammo[i] * 200; - break; - } - */ - - if ( total >= 5000 ) - { - cg.lowAmmoWarning = 0; - return; - } - } -#endif - // Don't bother drawing the ammo warning when have no weapon selected if ( cg.weaponSelect == WP_NONE ) { diff --git a/code/cgame/cg_predict.cpp b/code/cgame/cg_predict.cpp index 070fa6711f..32166b53c4 100644 --- a/code/cgame/cg_predict.cpp +++ b/code/cgame/cg_predict.cpp @@ -536,7 +536,7 @@ void CG_TouchItem( centity_t *cent ) { // if its a weapon, give them some predicted ammo so the autoswitch will work if ( item->giType == IT_WEAPON ) { int ammotype = weaponData[item->giTag].ammoIndex; - cg.predicted_player_state.stats[ STAT_WEAPONS ] |= 1 << item->giTag; + cg.predicted_player_state.weapons[item->giTag] = 1; if ( !cg.predicted_player_state.ammo[ ammotype] ) { cg.predicted_player_state.ammo[ ammotype ] = 1; } @@ -565,7 +565,7 @@ void CG_TouchTriggerPrediction( void ) { return; } - spectator = ( cg.predicted_player_state.pm_type == PM_SPECTATOR ); + spectator = (qboolean)( cg.predicted_player_state.pm_type == PM_SPECTATOR ); if ( cg.predicted_player_state.pm_type != PM_NORMAL && !spectator ) { return; @@ -637,6 +637,7 @@ to ease the jerk. ================= */ extern qboolean player_locked; +extern qboolean PlayerAffectedByStasis( void ); void CG_PredictPlayerState( void ) { int cmdNum, current; playerState_t oldPlayerState; @@ -674,7 +675,7 @@ void CG_PredictPlayerState( void ) { cg_pmove.trace = CG_Trace; cg_pmove.pointcontents = CG_PointContents; cg_pmove.tracemask = MASK_PLAYERSOLID; - cg_pmove.noFootsteps = 0;//( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0; + cg_pmove.noFootsteps = qfalse;//( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0; // save the state before the pmove so we can detect transitions oldPlayerState = cg.predicted_player_state; @@ -748,6 +749,7 @@ void CG_PredictPlayerState( void ) { gentity_t *ent = &g_entities[0];//cheating and dirty, I know, but this is a SP game so prediction can cheat if ( player_locked || + PlayerAffectedByStasis() || (ent && !ent->s.number&&ent->aimDebounceTime>level.time) || (ent && ent->client && ent->client->ps.pm_time && (ent->client->ps.pm_flags&PMF_TIME_KNOCKBACK)) || (ent && ent->forcePushTime > level.time) ) diff --git a/code/cgame/cg_scoreboard.cpp b/code/cgame/cg_scoreboard.cpp index b4e54e97fd..806331783e 100644 --- a/code/cgame/cg_scoreboard.cpp +++ b/code/cgame/cg_scoreboard.cpp @@ -236,7 +236,10 @@ w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 0.8f); if ( wpn ) { gitem_t *wItem= FindItemForWeapon( (weapon_t)wpn); - cgi_SP_GetStringTextString( va("SP_INGAME_%s",wItem->classname ), text, sizeof( text )); + if (!cgi_SP_GetStringTextString( va("SP_INGAME_%s",wItem->classname ), text, sizeof( text ))) + { + cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",wItem->classname ), text, sizeof( text )); + } // cgi_R_Font_DrawString(x+w, y, va("%d",wpn), colorTable[CT_WHITE], cgs.media.qhFontSmall, -1, 0.8f); cgi_R_Font_DrawString(x+w, y, text, colorTable[CT_WHITE], cgs.media.qhFontSmall, -1, 0.8f); } diff --git a/code/cgame/cg_servercmds.cpp b/code/cgame/cg_servercmds.cpp index 30d19ebb14..390fe61141 100644 --- a/code/cgame/cg_servercmds.cpp +++ b/code/cgame/cg_servercmds.cpp @@ -203,7 +203,7 @@ typedef struct serverCommand_s { void (*func)(void); } serverCommand_t; -int svcmdcmp( const void *a, const void *b ) { +static int svcmdcmp( const void *a, const void *b ) { return Q_stricmp( (const char *)a, ((serverCommand_t*)b)->cmd ); } @@ -239,7 +239,7 @@ static void CG_ServerCommand( void ) { return; } - command = (serverCommand_t *)bsearch( cmd, commands, numCommands, sizeof( commands[0] ), svcmdcmp ); + command = (serverCommand_t *)Q_LinearSearch( cmd, commands, numCommands, sizeof( commands[0] ), svcmdcmp ); if ( command ) { command->func(); diff --git a/code/cgame/cg_syscalls.cpp b/code/cgame/cg_syscalls.cpp index 7ff27054cc..264e5c48a2 100644 --- a/code/cgame/cg_syscalls.cpp +++ b/code/cgame/cg_syscalls.cpp @@ -277,12 +277,12 @@ int cgi_R_Font_HeightPixels(const int iFontIndex, const float scale /*= 1.0f*/) qboolean cgi_Language_IsAsian( void ) { - return Q_syscall( CG_LANGUAGE_ISASIAN ); + return (qboolean)(Q_syscall( CG_LANGUAGE_ISASIAN ) != 0); } qboolean cgi_Language_UsesSpaces(void) { - return Q_syscall( CG_LANGUAGE_USESSPACES ); + return (qboolean)(Q_syscall( CG_LANGUAGE_USESSPACES ) != 0); } unsigned int cgi_AnyLanguage_ReadCharFromString( const char *psText, int *piAdvanceCount, qboolean *pbIsTrailingPunctuation /* = NULL */ ) @@ -310,7 +310,7 @@ void cgi_R_AddRefEntityToScene( const refEntity_t *re ) { qboolean cgi_R_inPVS( vec3_t p1, vec3_t p2 ) { - return Q_syscall( CG_R_INPVS, p1, p2 ); + return (qboolean)(Q_syscall( CG_R_INPVS, p1, p2 ) != 0); } @@ -389,16 +389,16 @@ void cgi_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) { } qboolean cgi_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) { - return Q_syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot ); + return (qboolean)(Q_syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot ) != 0); } qboolean cgi_GetDefaultState(int entityIndex, entityState_t *state ) { - return Q_syscall( CG_GETDEFAULTSTATE, entityIndex, state ); + return (qboolean)(Q_syscall( CG_GETDEFAULTSTATE, entityIndex, state ) != 0); } qboolean cgi_GetServerCommand( int serverCommandNumber ) { - return Q_syscall( CG_GETSERVERCOMMAND, serverCommandNumber ); + return (qboolean)(Q_syscall( CG_GETSERVERCOMMAND, serverCommandNumber ) != 0); } int cgi_GetCurrentCmdNumber( void ) { @@ -406,7 +406,7 @@ int cgi_GetCurrentCmdNumber( void ) { } qboolean cgi_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) { - return Q_syscall( CG_GETUSERCMD, cmdNumber, ucmd ); + return (qboolean)(Q_syscall( CG_GETUSERCMD, cmdNumber, ucmd ) != 0); } void cgi_SetUserCmdValue( int stateValue, float sensitivityScale, float mPitchOverride, float mYawOverride ) { diff --git a/code/cgame/cg_text.cpp b/code/cgame/cg_text.cpp index 44f3d97880..d535bcec2c 100644 --- a/code/cgame/cg_text.cpp +++ b/code/cgame/cg_text.cpp @@ -435,7 +435,7 @@ void CG_DrawCaptionText(void) if((textcolor_caption[0] == 0) && (textcolor_caption[1] == 0) && (textcolor_caption[2] == 0) && (textcolor_caption[3] == 0)) { - Vector4Copy( colorTable[CT_WHITE], textcolor_caption ); + VectorCopy4( colorTable[CT_WHITE], textcolor_caption ); } cgi_R_SetColor(textcolor_caption); @@ -733,7 +733,7 @@ void CG_DrawCenterString( void ) if((textcolor_center[0] == 0) && (textcolor_center[1] == 0) && (textcolor_center[2] == 0) && (textcolor_center[3] == 0)) { - Vector4Copy( colorTable[CT_WHITE], textcolor_center ); + VectorCopy4( colorTable[CT_WHITE], textcolor_center ); } start = cg.centerPrint; diff --git a/code/cgame/cg_trueview.cpp b/code/cgame/cg_trueview.cpp new file mode 100644 index 0000000000..b03bb98a07 --- /dev/null +++ b/code/cgame/cg_trueview.cpp @@ -0,0 +1,247 @@ +/* + =========================================================================== + Copyright (C) 2000 - 2013, Raven Software, Inc. + Copyright (C) 2001 - 2013, Activision, Inc. + Copyright (C) 2013 - 2015, OpenJK contributors + + This file is part of the OpenJK source code. + + OpenJK is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License version 2 as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . + =========================================================================== + */ + +#include "cg_headers.h" + +#define SIEGECHAR_TAB 9 //perhaps a bit hacky, but I don't think there's any define existing for "tab" + +#define MAX_TRUEVIEW_INFO_SIZE 8192 +char true_view_info[MAX_TRUEVIEW_INFO_SIZE]; +int true_view_valid; + +int BG_SiegeGetPairedValue(char *buf, char *key, char *outbuf) +{ + int i = 0; + int j; + int k; + char checkKey[4096]; + + while (buf[i]) + { + if (buf[i] != ' ' && buf[i] != '{' && buf[i] != '}' && buf[i] != '\n' && buf[i] != '\r') + { //we're on a valid character + if (buf[i] == '/' && + buf[i+1] == '/') + { //this is a comment, so skip over it + while (buf[i] && buf[i] != '\n' && buf[i] != '\r') + { + i++; + } + } + else + { //parse to the next space/endline/eos and check this value against our key value. + j = 0; + + while (buf[i] != ' ' && buf[i] != '\n' && buf[i] != '\r' && buf[i] != SIEGECHAR_TAB && buf[i]) + { + if (buf[i] == '/' && buf[i+1] == '/') + { //hit a comment, break out. + break; + } + + checkKey[j] = buf[i]; + j++; + i++; + } + checkKey[j] = 0; + + k = i; + + while (buf[k] && (buf[k] == ' ' || buf[k] == '\n' || buf[k] == '\r')) + { + k++; + } + + if (buf[k] == '{') + { //this is not the start of a value but rather of a group. We don't want to look in subgroups so skip over the whole thing. + int openB = 0; + + while (buf[i] && (buf[i] != '}' || openB)) + { + if (buf[i] == '{') + { + openB++; + } + else if (buf[i] == '}') + { + openB--; + } + + if (openB < 0) + { + Com_Error(ERR_DROP, "Unexpected closing bracket (too many) while parsing to end of group '%s'", checkKey); + } + + if (buf[i] == '}' && !openB) + { //this is the end of the group + break; + } + i++; + } + + if (buf[i] == '}') + { + i++; + } + } + else + { + //Is this the one we want? + if (buf[i] != '/' || buf[i+1] != '/') + { //make sure we didn't stop on a comment, if we did then this is considered an error in the file. + if (!Q_stricmp(checkKey, key)) + { //guess so. Parse along to the next valid character, then put that into the output buffer and return 1. + while ((buf[i] == ' ' || buf[i] == '\n' || buf[i] == '\r' || buf[i] == SIEGECHAR_TAB) && buf[i]) + { + i++; + } + + if (buf[i]) + { //We're at the start of the value now. + qboolean parseToQuote = qfalse; + + if (buf[i] == '\"') + { //if the value is in quotes, then stop at the next quote instead of ' ' + i++; + parseToQuote = qtrue; + } + + j = 0; + while ( ((!parseToQuote && buf[i] != ' ' && buf[i] != '\n' && buf[i] != '\r') || (parseToQuote && buf[i] != '\"')) ) + { + if (buf[i] == '/' && + buf[i+1] == '/') + { //hit a comment after the value? This isn't an ideal way to be writing things, but we'll support it anyway. + break; + } + outbuf[j] = buf[i]; + j++; + i++; + + if (!buf[i]) + { + if (parseToQuote) + { + Com_Error(ERR_DROP, "Unexpected EOF while looking for endquote, error finding paired value for '%s'", key); + } + else + { + Com_Error(ERR_DROP, "Unexpected EOF while looking for space or endline, error finding paired value for '%s'", key); + } + } + } + outbuf[j] = 0; + + return 1; //we got it, so return 1. + } + else + { + Com_Error(ERR_DROP, "Error parsing file, unexpected EOF while looking for valud '%s'", key); + } + } + else + { //if that wasn't the desired key, then make sure we parse to the end of the line, so we don't mistake a value for a key + while (buf[i] && buf[i] != '\n') + { + i++; + } + } + } + else + { + Com_Error(ERR_DROP, "Error parsing file, found comment, expected value for '%s'", key); + } + } + } + } + + if (!buf[i]) + { + break; + } + i++; + } + + return 0; //guess we never found it. +} + +//Loads in the True View auto eye positioning data so you don't have to worry about disk access later in the +//game +//Based on CG_InitSagaMode and tck's tck_InitBuffer +void CG_TrueViewInit( void ) +{ + int len = 0; + fileHandle_t f; + + + len = gi.FS_FOpenFile("trueview.cfg", &f, FS_READ); + + if (!f) + { + CG_Printf("Error: File Not Found: trueview.cfg\n"); + true_view_valid = 0; + return; + } + + if( len >= MAX_TRUEVIEW_INFO_SIZE ) + { + CG_Printf("Error: trueview.cfg is over the trueview.cfg filesize limit.\n"); + gi.FS_FCloseFile( f ); + true_view_valid = 0; + return; + } + + + gi.FS_Read(true_view_info, len, f); + + true_view_valid = 1; + + gi.FS_FCloseFile( f ); + + return; + +} + +void CG_AdjustEyePos (const char *modelName) +{ + //eye position + char eyepos[MAX_QPATH]; + + if ( true_view_valid ) + { + + if( BG_SiegeGetPairedValue(true_view_info, (char*) modelName, eyepos) ) + { + CG_Printf("True View Eye Adjust Loaded for %s.\n", modelName); + gi.cvar_set( "cg_trueeyeposition", eyepos ); + } + else + {//Couldn't find an entry for the desired model. Not nessicarily a bad thing. + gi.cvar_set( "cg_trueeyeposition", "0" ); + } + } + else + {//The model eye position list is messed up. Default to 0.0 for the eye position + gi.cvar_set( "cg_trueeyeposition", "0" ); + } + +} \ No newline at end of file diff --git a/code/cgame/cg_view.cpp b/code/cgame/cg_view.cpp index 7119bb82e2..243e758ecd 100644 --- a/code/cgame/cg_view.cpp +++ b/code/cgame/cg_view.cpp @@ -1324,17 +1324,26 @@ float CG_ForceSpeedFOV( void ) float timeLeft = player->client->ps.forcePowerDuration[FP_SPEED] - cg.time; float length = FORCE_SPEED_DURATION*forceSpeedValue[player->client->ps.forcePowerLevel[FP_SPEED]]; float amt = forceSpeedFOVMod[player->client->ps.forcePowerLevel[FP_SPEED]]; + if ( !cg.renderingThirdPerson && ((!cg.zoomMode && cg_trueguns.integer) || cg.snap->ps.weapon == WP_SABER + || cg.snap->ps.weapon == WP_MELEE) && cg_truefov.value ) + { + fov = cg_truefov.value; + } + else + { + fov = cg_fov.value; + } if ( timeLeft < 500 ) {//start going back - fov = cg_fov.value + (timeLeft)/500*amt; + fov = fov + (timeLeft)/500*amt; } else if ( length - timeLeft < 1000 ) {//start zooming in - fov = cg_fov.value + (length - timeLeft)/1000*amt; + fov = fov + (length - timeLeft)/1000*amt; } else {//stay at this FOV - fov = cg_fov.value+amt; + fov = fov+amt; } return fov; } @@ -1394,6 +1403,11 @@ static qboolean CG_CalcFov( void ) { { fov_x = cg.overrides.fov; } + else if ( !cg.renderingThirdPerson && ((!cg.zoomMode && cg_trueguns.integer) || cg.snap->ps.weapon == WP_SABER + || cg.snap->ps.weapon == WP_MELEE) && cg_truefov.value ) + { + fov_x = cg_truefov.value; + } else { fov_x = cg_fov.value; @@ -1665,7 +1679,7 @@ static qboolean CG_CalcViewValues( void ) { } } - if ( (cg.renderingThirdPerson||cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE) + if ( (cg.renderingThirdPerson) && !cg.zoomMode && !viewEntIsCam ) { @@ -1677,7 +1691,7 @@ static qboolean CG_CalcViewValues( void ) { // else // { // First person saber - if ( !cg.renderingThirdPerson ) + /*if ( !cg.renderingThirdPerson ) { if ( cg.snap->ps.weapon == WP_SABER||cg.snap->ps.weapon == WP_MELEE ) { @@ -1687,7 +1701,7 @@ static qboolean CG_CalcViewValues( void ) { AngleVectors( cg.refdefViewAngles, dir, NULL, NULL ); VectorMA( cg.refdef.vieworg, -2, dir, cg.refdef.vieworg ); } - } + }*/ CG_OffsetThirdPersonView(); // } } @@ -2053,7 +2067,7 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView ) { float mPitchOverride = 0.0f; float mYawOverride = 0.0f; - if ( cg.snap->ps.clientNum == 0 ) + if ( cg.snap->ps.clientNum == 0 && cg_scaleVehicleSensitivity.integer ) {//pointless check, but.. if ( cg_entities[0].gent->s.eFlags & EF_LOCKED_TO_WEAPON ) { @@ -2087,12 +2101,28 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView ) { cg.zoomMode = 0; } // decide on third person view - cg.renderingThirdPerson = cg_thirdPerson.integer + cg.renderingThirdPerson = (qboolean)(cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0) || (cg.snap->ps.eFlags&EF_HELD_BY_SAND_CREATURE) || ((g_entities[0].client&&g_entities[0].client->NPC_class==CLASS_ATST) - || (cg.snap->ps.weapon == WP_SABER || cg.snap->ps.weapon == WP_MELEE) ); + || ((cg.snap->ps.weapon == WP_SABER || cg.snap->ps.weapon == WP_MELEE) && !cg_fpls.integer) + || (cg.snap->ps.weapon == WP_EMPLACED_GUN && !(cg.snap->ps.eFlags & EF_LOCKED_TO_WEAPON)) + || (!cg_trueguns.integer && (cg.snap->ps.weapon == WP_TUSKEN_RIFLE || cg.snap->ps.weapon == WP_NOGHRI_STICK) && cg.snap->ps.torsoAnim >= BOTH_TUSKENATTACK1 && cg.snap->ps.torsoAnim <= BOTH_TUSKENATTACK3))); + + if (cg_fpls.integer && cg_trueinvertsaber.integer == 2 && (cg.snap->ps.weapon == WP_SABER || cg.snap->ps.weapon == WP_MELEE)) + {//force thirdperson for sabers/melee if in cg_trueinvertsaber.integer == 2 + cg.renderingThirdPerson = qtrue; + } + else if (cg_fpls.integer && cg_trueinvertsaber.integer == 1 && !cg_thirdPerson.integer && (cg.snap->ps.weapon == WP_SABER || cg.snap->ps.weapon == WP_MELEE)) + { + cg.renderingThirdPerson = qtrue; + } + else if (cg_fpls.integer && cg_trueinvertsaber.integer == 1 && cg_thirdPerson.integer && (cg.snap->ps.weapon == WP_SABER || cg.snap->ps.weapon == WP_MELEE)) + { + cg.renderingThirdPerson = qfalse; + } + if ( cg.zoomMode ) { // zoomed characters should never do third person stuff?? @@ -2260,3 +2290,15 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView ) { */ } +//Checks to see if the current camera position is valid based on the last known safe location. If it's not safe, place +//the camera at the last position safe location +void CheckCameraLocation( vec3_t OldeyeOrigin ) +{ + trace_t trace; + + CG_Trace(&trace, OldeyeOrigin, cameramins, cameramaxs, cg.refdef.vieworg, cg.snap->ps.clientNum, MASK_CAMERACLIP); + if (trace.fraction <= 1.0) + { + VectorCopy(trace.endpos, cg.refdef.vieworg); + } +} diff --git a/code/cgame/cg_weapons.cpp b/code/cgame/cg_weapons.cpp index 020412c72d..3d720946dd 100644 --- a/code/cgame/cg_weapons.cpp +++ b/code/cgame/cg_weapons.cpp @@ -30,6 +30,11 @@ along with this program; if not, see . #include "../game/anims.h" +extern vmCvar_t cg_gunMomentumDamp; +extern vmCvar_t cg_gunMomentumFall; +extern vmCvar_t cg_gunMomentumEnable; +extern vmCvar_t cg_gunMomentumInterval; + extern void CG_LightningBolt( centity_t *cent, vec3_t origin ); #define PHASER_HOLDFRAME 2 @@ -37,6 +42,7 @@ extern void G_SoundOnEnt( gentity_t *ent, soundChannel_t channel, const char *so const char *CG_DisplayBoxedText(int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight, const char *psText, int iFontHandle, float fScale, const vec4_t v4Color); +void CG_LoadViewmodelAnimations (CGhoul2Info* ghl2, const char *modelName, viewModelAnimSet_t* ptAnims); /* ================= @@ -80,29 +86,27 @@ void CG_RegisterWeapon( int weaponNum ) { } CG_RegisterItemVisuals( item - bg_itemlist ); + + Q_strncpyz( path, weaponData[weaponNum].weaponMdl, sizeof(path) ); // set up in view weapon model - weaponInfo->weaponModel = cgi_R_RegisterModel( weaponData[weaponNum].weaponMdl ); - {//in case the weaponmodel isn't _w, precache the _w.glm - char weaponModel[64]; - - Q_strncpyz (weaponModel, weaponData[weaponNum].weaponMdl, sizeof(weaponModel)); - if (char *spot = strstr(weaponModel, ".md3") ) + if (Q_stristr(path, ".glm")) { + //EXPERIMENTAL: GHOUL2 viewmodels + weaponInfo->bUsesGhoul2 = true; + } + + if (!weaponInfo->bUsesGhoul2) + { + weaponInfo->weaponModel = cgi_R_RegisterModel( weaponData[weaponNum].weaponMdl ); + + if ( weaponInfo->weaponModel == 0 ) { - *spot = 0; - spot = strstr(weaponModel, "_w");//i'm using the in view weapon array instead of scanning the item list, so put the _w back on - if (!spot) - { - Q_strcat (weaponModel, sizeof(weaponModel), "_w"); - } - Q_strcat (weaponModel, sizeof(weaponModel), ".glm"); //and change to ghoul2 + CG_Error( "Couldn't find weapon model %s for weapon %s\n", weaponData[weaponNum].weaponMdl, weaponData[weaponNum].classname); + return; } - gi.G2API_PrecacheGhoul2Model( weaponModel ); // correct way is item->world_model } - - if ( weaponInfo->weaponModel == 0 ) - { - CG_Error( "Couldn't find weapon model %s for weapon %s\n", weaponData[weaponNum].weaponMdl, weaponData[weaponNum].classname); - return; + + {//precache the _w.glm + gi.G2API_PrecacheGhoul2Model( weaponData[weaponNum].worldModel ); // correct way is item->world_model } // calc midpoint for rotation @@ -150,14 +154,34 @@ void CG_RegisterWeapon( int weaponNum ) { weaponInfo->weaponWorldModel = weaponInfo->weaponModel; } - // set up the hand that holds the in view weapon - assuming we have one Q_strncpyz( path, weaponData[weaponNum].weaponMdl, sizeof(path) ); - COM_StripExtension( path, path, sizeof(path) ); - Q_strcat( path, sizeof(path), "_hand.md3" ); - weaponInfo->handsModel = cgi_R_RegisterModel( path ); - - if ( !weaponInfo->handsModel ) { - weaponInfo->handsModel = cgi_R_RegisterModel( "models/weapons2/briar_pistol/briar_pistol_hand.md3" ); + if ( weaponInfo->bUsesGhoul2 ) { + // Init the ghoul2 model + weaponInfo->g2_skin = gi.RE_RegisterSkin(weaponData[weaponNum].skinPath); + weaponInfo->g2_index = gi.G2API_InitGhoul2Model(weaponInfo->ghoul2, path, + G_ModelIndex(path), G_SkinIndex(weaponData[weaponNum].skinPath), NULL, 0, 0); + gi.G2API_SetSkin(&weaponInfo->ghoul2[weaponInfo->g2_index], 0, weaponInfo->g2_skin); + // Add flash bolt + weaponInfo->g2_flashbolt = gi.G2API_AddBolt(&weaponInfo->ghoul2[weaponInfo->g2_index], "*flash"); + weaponInfo->g2_effectsbolt = gi.G2API_AddBolt(&weaponInfo->ghoul2[weaponInfo->g2_index], "*l_hand"); + if(!weaponData[weaponNum].bNoHandModel) + weaponInfo->handsModel = cgi_R_RegisterModel( "models/weapons2/briar_pistol/briar_pistol_hand.md3" ); + + // Load the animation.cfg + CG_LoadViewmodelAnimations(&weaponInfo->ghoul2[weaponInfo->g2_index], path, &weaponInfo->g2_anims); + } + else { + // Normal -- MD3 viewmodels + // set up the hand that holds the in view weapon - assuming we have one + if(!weaponData[weaponNum].bNoHandModel) { + COM_StripExtension( path, path, sizeof(path) ); + Q_strcat( path, sizeof(path), "_hand.md3" ); + weaponInfo->handsModel = cgi_R_RegisterModel( path ); + + if ( !weaponInfo->handsModel ) { + weaponInfo->handsModel = cgi_R_RegisterModel( "models/weapons2/briar_pistol/briar_pistol_hand.md3" ); + } + } } // register the sounds for the weapon @@ -242,7 +266,11 @@ void CG_RegisterWeapon( int weaponNum ) { cgs.effects.forceDrain = theFxScheduler.RegisterEffect( "mp/drain" ); cgs.effects.forceDrainWide = theFxScheduler.RegisterEffect( "mp/drainwide" ); //cgs.effects.forceDrained = theFxScheduler.RegisterEffect( "mp/drainhit"); - + + cgs.effects.destructionProjectile = theFxScheduler.RegisterEffect( "destruction/shot" ); + cgs.effects.destructionHit = theFxScheduler.RegisterEffect( "destruction/explosion" ); + cgs.media.destructionSound = cgi_S_RegisterSound( "sound/weapons/concussion/missleloop.wav" ); + //saber sounds //cgi_S_RegisterSound( "sound/weapons/saber/saberon.wav" ); //cgi_S_RegisterSound( "sound/weapons/saber/enemy_saber_on.wav" ); @@ -361,6 +389,15 @@ void CG_RegisterWeapon( int weaponNum ) { cgs.media.blueSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/blue_line" ); cgs.media.purpleSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/purple_glow" ); cgs.media.purpleSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/purple_line" ); + cgs.media.rgbSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/rgb_glow" ); + cgs.media.rgbSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/rgb_line" ); + + cgs.media.blackSaberGlowShader = cgi_R_RegisterShader( "gfx/effects/sabers/black_glow" ); + cgs.media.blackSaberCoreShader = cgi_R_RegisterShader( "gfx/effects/sabers/black_line" ); + cgs.media.blackSaberBlurShader = cgi_R_RegisterShader("gfx/effects/sabers/blackSaberBlur"); + + cgs.media.rgbUnstableCoreShader = cgi_R_RegisterShader("gfx/effects/sabers/jkg/blade_unstable"); + cgs.media.unstableBlurShader = cgi_R_RegisterShader("gfx/effects/sabers/jkg/trail_unstable"); cgs.media.forceCoronaShader = cgi_R_RegisterShaderNoMip( "gfx/hud/force_swirl" ); @@ -618,7 +655,165 @@ void CG_RegisterWeapon( int weaponNum ) { case WP_TIE_FIGHTER: theFxScheduler.RegisterEffect( "ships/imp_blastershot" ); break; + + case WP_E5_CARBINE: + cgs.effects.blasterShotEffect = theFxScheduler.RegisterEffect( "blaster/shot" ); + theFxScheduler.RegisterEffect( "blaster/NPCshot" ); + cgs.effects.blasterWallImpactEffect = theFxScheduler.RegisterEffect( "blaster/wall_impact" ); + cgs.effects.blasterFleshImpactEffect = theFxScheduler.RegisterEffect( "blaster/flesh_impact" ); + break; + + case WP_DC15S_CARBINE: + case WP_Z6_ROTARY: + case WP_DC15A_RIFLE: + cgs.effects.cloneBlasterShotEffect = theFxScheduler.RegisterEffect( "clone/shot" ); + cgs.effects.cloneBlasterWallImpactEffect = theFxScheduler.RegisterEffect( "blaster/wall_impact" ); + cgs.effects.cloneBlasterFleshImpactEffect = theFxScheduler.RegisterEffect( "blaster/flesh_impact" ); + break; + + case WP_SONIC_BLASTER: + theFxScheduler.RegisterEffect( "disruptor/wall_impact" ); + theFxScheduler.RegisterEffect( "disruptor/flesh_impact" ); + theFxScheduler.RegisterEffect( "disruptor/line_cap" ); + theFxScheduler.RegisterEffect( "disruptor/death_smoke" ); + + cgi_R_RegisterShader( "gfx/misc/dr1" ); + cgi_R_RegisterShader( "gfx/misc/whiteline2" ); + cgi_R_RegisterShader( "gfx/effects/smokeTrail" ); + cgi_R_RegisterShader( "gfx/effects/burn" ); + break; + + } +} + +/* +================= +CG_DeregisterWeapon + +Clean up Ghoul2 instances (if they exist) +================= +*/ +void CG_DeregisterWeapon (int weaponNum) { + weaponInfo_t *weaponInfo = &cg_weapons[weaponNum]; + if(!gi.G2API_HaveWeGhoul2Models(weaponInfo->ghoul2)) + return; + gi.G2API_RemoveBolt(&weaponInfo->ghoul2[weaponInfo->g2_index], weaponInfo->g2_flashbolt); + gi.G2API_RemoveBolt(&weaponInfo->ghoul2[weaponInfo->g2_index], weaponInfo->g2_effectsbolt); + gi.G2API_CleanGhoul2Models(weaponInfo->ghoul2); +} + +/* + ================= + CG_LoadViewmodelAnimations + Loads animation.cfg for viewmodel + ================= + */ + +void CG_LoadViewmodelAnimations (CGhoul2Info* ghl2, const char *modelName, viewModelAnimSet_t* ptAnims) { + + // Basic NULL checks, nothin' fishy better be in here... + if(!ghl2 || !modelName || !ptAnims) { + return; + } + + // Get the GLA's path. + char *GLAname = gi.G2API_GetGLAName( ghl2 ); + if ( !GLAname ) { + return; + } + + // From the GLA's path, determine the path to animation.cfg and stuff the value into animName. + char animName[MAX_QPATH]; + char *slash = NULL; + + Q_strncpyz( animName, GLAname, sizeof( animName ) ); + slash = strrchr( animName, '/' ); + if ( slash ) + { + *slash = 0; + } + Q_strcat( animName, sizeof(animName), "/animation.cfg" ); + + + // Load the file (erroring out if none found) + fileHandle_t f; + int len = cgi_FS_FOpenFile(animName, &f, FS_READ); + if(f == -1) { + Com_Printf("^1ERROR: Failed to load %s: file not found\n", animName); + return; + } + else if(len <= 0) { + Com_Printf("^1ERROR: Failed to load %s: file blank or not found\n", animName); + cgi_FS_FCloseFile(f); + return; } + + // Read the file, and close it. + char buffer[16535]; + cgi_FS_Read(buffer, len, f); + cgi_FS_FCloseFile(f); + buffer[len] = 0; + + // Set initial data in the animation.cfg data bufffer. + // This is slightly optimized from base's method - we do this in one step by ZeroMemory as opposed to looping. + Q_strncpyz(ptAnims->filename, animName, sizeof(ptAnims->filename)); + // FIXME: shouldn't just sizeof(ptAnims->animations) do? + memset(ptAnims->animations, 0, sizeof(animation_t) * MAX_VIEWMODEL_ANIMATIONS); + + + // Actually parse the file, woot. + // This is more or less ripped from MP, bad styling and all. + char *token; + const char *s = (const char*)buffer; + + COM_BeginParseSession(); + while(1) { + token = COM_Parse(&s); + if (!token || !token[0]) { + break; + } + + int animNum = GetIDForString(vmAnimTable, token); + if (animNum == -1) { + if (Q_stricmp(token, "ROOT")) { + Com_Printf(S_COLOR_RED"WARNING: Unknown token %s in %s\n", token, ptAnims->filename); + } + continue; + } + + token = COM_Parse(&s); + if (!token || !token[0]) { + break; + } + ptAnims->animations[animNum].firstFrame = atoi(token); + + token = COM_Parse(&s); + if (!token || !token[0]) { + break; + } + ptAnims->animations[animNum].numFrames = atoi(token); + + token = COM_Parse(&s); + if (!token || !token[0]) { + break; + } + ptAnims->animations[animNum].loopFrames = atoi(token); + + token = COM_Parse(&s); + if (!token || !token[0]) { + break; + } + float fps = atof(token); + if(fps == 0) + fps = 1; + if(fps < 0) + ptAnims->animations[animNum].frameLerp = floor(1000.0f / fps); + else + ptAnims->animations[animNum].frameLerp = ceil(1000.0f / fps); + + //ptAnims->animations[animNum].initialLerp = ceil(1000.0f / fabs(fps)); + } + COM_EndParseSession(); } /* @@ -860,6 +1055,30 @@ void CG_CalculateWeaponPosition( vec3_t origin, vec3_t angles ) angles[ROLL] += scale * fracsin * 0.01; angles[YAW] += scale * fracsin * 0.01; angles[PITCH] += (scale * 0.5f ) * fracsin * 0.01; + + if ( cg_gunMomentumEnable.integer ) { + // sway viewmodel when changing viewangles + static vec3_t previousAngles = {0, 0, 0}; + static int previousTime = 0; + + vec3_t deltaAngles; + AnglesSubtract( angles, previousAngles, deltaAngles ); + VectorScale( deltaAngles, 1.0f, deltaAngles ); + + const double dampTime = (double)(cg.time - previousTime) * (1.0 / (double)cg_gunMomentumInterval.integer); + const double dampRatio = std::pow( std::abs( (double)cg_gunMomentumDamp.value ), dampTime ); + VectorMA( previousAngles, dampRatio, deltaAngles, angles ); + VectorCopy( angles, previousAngles ); + previousTime = cg.time; + + // move viewmodel downwards when jumping etc + static float previousOriginZ = 0.0f; + const float deltaZ = origin[2] - previousOriginZ; + if ( deltaZ > 0.0f ) { + origin[2] = origin[2] - deltaZ * cg_gunMomentumFall.value; + } + previousOriginZ = origin[2]; + } } /* @@ -958,6 +1177,134 @@ static void CG_DoMuzzleFlash( centity_t *cent, vec3_t org, vec3_t dir, weaponDat Ghoul2 Insert End */ +/* + ============== + CG_AnimateViewmodel + Only for GHOUL 2 weapons --eez + ============== + */ + +static int lastAnimPlayed = 0; + +int CG_MapTorsoToG2VMAnimation(playerState_t *ps) { + switch(ps->torsoAnim) { + case TORSO_WEAPONREADY1: + case TORSO_WEAPONREADY2: + case TORSO_WEAPONREADY3: + case TORSO_WEAPONREADY4: +// case TORSO_WEAPONREADY5: +// case TORSO_WEAPONREADY6: +// case TORSO_WEAPONREADY7: +// case TORSO_WEAPONREADY8: +// case TORSO_WEAPONREADY9: + case TORSO_WEAPONREADY10: +// case TORSO_WEAPONREADY11: +// case TORSO_WEAPONREADY12: +// case TORSO_WEAPONIDLE1: + case TORSO_WEAPONIDLE2: + case TORSO_WEAPONIDLE3: + case TORSO_WEAPONIDLE4: +// case TORSO_WEAPONIDLE5: +// case TORSO_WEAPONIDLE6: +// case TORSO_WEAPONIDLE7: +// case TORSO_WEAPONIDLE8: +// case TORSO_WEAPONIDLE9: + case TORSO_WEAPONIDLE10: +// case TORSO_WEAPONIDLE11: +// case TORSO_WEAPONIDLE12: + default: + return VM_READY; + case BOTH_STAND1IDLE1: + case BOTH_STAND2IDLE1: + case BOTH_STAND3IDLE1: +// case BOTH_STAND4IDLE1: + case BOTH_STAND5IDLE1: + return VM_IDLE; + case TORSO_DROPWEAP1: + return VM_LOWER; + case TORSO_RAISEWEAP1: + return VM_RAISE; + case BOTH_ATTACK1: + case BOTH_ATTACK2: + case BOTH_ATTACK3: + case BOTH_ATTACK4: + return VM_FIRE; + case BOTH_RESISTPUSH: + return VM_FRESISTPUSH; + case BOTH_FORCEPUSH: + return VM_FPUSH; + case BOTH_FORCEPULL: + return VM_FPULL; + case BOTH_MINDTRICK1: + return VM_FMINDTRICK1; + case BOTH_MINDTRICK2: + return VM_FMINDTRICK2; + case BOTH_FORCELIGHTNING: + return VM_FLIGHTNING; + case BOTH_FORCELIGHTNING_START: + return VM_FLIGHTNING_START; + case BOTH_FORCELIGHTNING_HOLD: + return VM_FLIGHTNING_HOLD; + case BOTH_FORCELIGHTNING_RELEASE: + return VM_FLIGHTNING_RELEASE; + case BOTH_FORCEHEAL_START: + return VM_FHEAL_START; + case BOTH_FORCEHEAL_STOP: + return VM_FHEAL_STOP; + case BOTH_FORCEHEAL_QUICK: + return VM_FHEAL_QUICK; + case BOTH_FORCEGRIP1: + case BOTH_FORCEGRIP3: + return VM_FGRIP; + case BOTH_FORCEGRIP3THROW: + return VM_FGRIP_THROW; + case BOTH_FORCEGRIP_RELEASE: + return VM_FGRIP_RELEASE; + case BOTH_FORCEGRIP_HOLD: + return VM_FGRIP_HOLD; + } +} + +extern void CG_ForcePushBlur(const vec3_t org, qboolean darkSide = qfalse); +void CG_AnimateViewmodel( centity_t* cent, playerState_t *ps ) { + CG_RegisterWeapon(ps->weapon); + weaponInfo_t* weapon = &cg_weapons[ps->weapon]; + int desiredAnim = CG_MapTorsoToG2VMAnimation(ps); + int flags = BONE_ANIM_OVERRIDE; + + switch(desiredAnim) { + case VM_FIRE: + if(cent->muzzleFlashTime <= 0) + return; + break; + case VM_2H_FLIGHTNING_HOLD: + case VM_FDRAIN_HOLD: + case VM_FGRIP_HOLD: + case VM_FLIGHTNING_HOLD: + flags = BONE_ANIM_OVERRIDE_FREEZE; + if(ps->torsoAnim == lastAnimPlayed) + return; + break; + case VM_READY: + flags = BONE_ANIM_OVERRIDE_LOOP; + if(ps->torsoAnim == lastAnimPlayed) + return; + break; + default: + if(ps->torsoAnim == lastAnimPlayed) + return; + break; + } + + lastAnimPlayed = ps->torsoAnim; + + gi.G2API_SetBoneAnim(&weapon->ghoul2[weapon->g2_index], "model_root", + weapon->g2_anims.animations[desiredAnim].firstFrame, + weapon->g2_anims.animations[desiredAnim].firstFrame+weapon->g2_anims.animations[desiredAnim].numFrames, + flags, 100.0f/weapon->g2_anims.animations[desiredAnim].frameLerp, + cg.time, weapon->g2_anims.animations[desiredAnim].firstFrame, -1); +} + /* ============== CG_AddViewWeapon @@ -981,6 +1328,9 @@ void CG_AddViewWeapon( playerState_t *ps ) // no gun if in third person view if ( cg.renderingThirdPerson ) return; + + if ( cg_trueguns.integer && !cg.zoomMode ) + return; if ( ps->pm_type == PM_INTERMISSION ) return; @@ -1142,7 +1492,7 @@ void CG_AddViewWeapon( playerState_t *ps ) } else #endif - { + if ( !weapon->bUsesGhoul2 ) { // get clientinfo for animation map const clientInfo_t *ci = ¢->gent->client->clientInfo; int torsoAnim = cent->gent->client->ps.torsoAnim;//pe.torso.animationNumber; @@ -1167,6 +1517,10 @@ void CG_AddViewWeapon( playerState_t *ps ) hand.backlerp=0.0f; } } + else { + // Using ghoul2 (question mark?) + CG_AnimateViewmodel(cent, ps); + } // add the weapon(s) - FIXME: allow for 2 weapons generically, not just 2 sabers? int numSabers = 1; @@ -1178,15 +1532,26 @@ void CG_AddViewWeapon( playerState_t *ps ) { refEntity_t gun; memset (&gun, 0, sizeof(gun)); + AnglesToAxis( angles, gun.axis ); - gun.hModel = weapon->weaponModel; - if (!gun.hModel) + if ( !weapon->bUsesGhoul2 ) { + gun.hModel = weapon->weaponModel; + if (!gun.hModel) + { + return; + } + } + else { - return; + gun.ghoul2 = const_cast(&weapon->ghoul2); + gun.radius = 60; + gun.customSkin = weapon->g2_skin; + VectorCopy(hand.axis[0], gun.axis[0]); } - - AnglesToAxis( angles, gun.axis ); - CG_PositionEntityOnTag( &gun, &hand, weapon->handsModel, "tag_weapon"); + if(!wData->bNoHandModel) + CG_PositionEntityOnTag( &gun, &hand, weapon->handsModel, "tag_weapon"); + else + VectorCopy(hand.origin, gun.origin); gun.renderfx = RF_DEPTHHACK | RF_FIRST_PERSON; @@ -1210,7 +1575,7 @@ void CG_AddViewWeapon( playerState_t *ps ) cent->gent->client->ps.saber[0].blade[0].length = cent->gent->client->ps.saber[0].blade[0].lengthMax; } } - // FX_Saber( org_, axis_[0], cent->gent->client->ps.saberLength, 2.0 + crandom() * 0.2f, cent->gent->client->ps.saberColor ); + // FX_Saber( org_, axis_[0], cent->gent->client->ps.saberLength, 2.0 + Q_flrand(-1.0f, 1.0f) * 0.2f, cent->gent->client->ps.saberColor ); if ( saberNum == 0 && bladeNum == 0 ) { VectorCopy( axis_[0], cent->gent->client->renderInfo.muzzleDir ); @@ -1237,42 +1602,83 @@ void CG_AddViewWeapon( playerState_t *ps ) } */ // add the spinning barrel[s] - for (int i = 0; (i < wData->numBarrels); i++) - { - refEntity_t barrel; - memset( &barrel, 0, sizeof( barrel ) ); - barrel.hModel = weapon->barrelModel[i]; - - //VectorCopy( parent->lightingOrigin, barrel.lightingOrigin ); - //barrel.shadowPlane = parent->shadowPlane; - barrel.renderfx = gun.renderfx; - angles[YAW] = 0; - angles[PITCH] = 0; - // if ( ps->weapon == WP_TETRION_DISRUPTOR) { - // angles[ROLL] = CG_MachinegunSpinAngle( cent ); - // } else { - angles[ROLL] = 0;//CG_MachinegunSpinAngle( cent ); - // } - - AnglesToAxis( angles, barrel.axis ); - if (!i) - { - CG_PositionRotatedEntityOnTag( &barrel, &hand, weapon->handsModel, "tag_barrel", NULL ); - } else + if ( !weapon->bUsesGhoul2 ) + { + for (int i = 0; (i < wData->numBarrels); i++) { - CG_PositionRotatedEntityOnTag( &barrel, &hand, weapon->handsModel, va("tag_barrel%d",i+1), NULL ); - } + refEntity_t barrel; + memset( &barrel, 0, sizeof( barrel ) ); + barrel.hModel = weapon->barrelModel[i]; + + //VectorCopy( parent->lightingOrigin, barrel.lightingOrigin ); + //barrel.shadowPlane = parent->shadowPlane; + barrel.renderfx = gun.renderfx; + angles[YAW] = 0; + angles[PITCH] = 0; + // if ( ps->weapon == WP_TETRION_DISRUPTOR) { + // angles[ROLL] = CG_MachinegunSpinAngle( cent ); + // } else { + angles[ROLL] = 0;//CG_MachinegunSpinAngle( cent ); + // } + + AnglesToAxis( angles, barrel.axis ); + if (!i) + { + CG_PositionRotatedEntityOnTag( &barrel, &hand, weapon->handsModel, "tag_barrel", NULL ); + } else + { + CG_PositionRotatedEntityOnTag( &barrel, &hand, weapon->handsModel, va("tag_barrel%d",i+1), NULL ); + } - cgi_R_AddRefEntityToScene( &barrel ); + cgi_R_AddRefEntityToScene( &barrel ); + } } memset (&flash, 0, sizeof(flash)); // Seems like we should always do this in case we have an animating muzzle flash....that way we can always store the correct muzzle dir, etc. - CG_PositionEntityOnTag( &flash, &gun, gun.hModel, "tag_flash"); - - CG_DoMuzzleFlash( cent, flash.origin, flash.axis[0], wData ); + if ( !weapon->bUsesGhoul2 ) + { + CG_PositionEntityOnTag( &flash, &gun, gun.hModel, "tag_flash"); + CG_DoMuzzleFlash( cent, flash.origin, flash.axis[0], wData ); + } + else { + CGhoul2Info_v& s = *gun.ghoul2; + mdxaBone_t boltMatrix; + vec3_t setAngles; + + VectorSet(setAngles, cent->lerpAngles[PITCH], cent->lerpAngles[YAW], 0); + + gi.G2API_GetBoltMatrix (s, weapon->g2_index, weapon->g2_flashbolt, &boltMatrix, setAngles, gun.origin, + (cg.time?cg.time:level.time), NULL, gun.modelScale); + gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, flash.origin); + VectorCopy(cg.snap->ps.viewangles, flash.angles); + + gi.G2API_GiveMeVectorFromMatrix(boltMatrix, POSITIVE_X, flash.axis[0]); + gi.G2API_GiveMeVectorFromMatrix(boltMatrix, POSITIVE_Y, flash.axis[1]); + gi.G2API_GiveMeVectorFromMatrix(boltMatrix, POSITIVE_Z, flash.axis[2]); + + // Play effects if requested + if(ps->powerups[PW_FORCE_PUSH] > cg.time || + ps->forcePowersActive & (1 << FP_GRIP)) { + vec3_t effectOrigin; + VectorSet(setAngles, cent->lerpAngles[PITCH], cent->lerpAngles[YAW], 0); + gi.G2API_GetBoltMatrix(s, weapon->g2_index, weapon->g2_effectsbolt, &boltMatrix, setAngles, gun.origin, + (cg.time ? cg.time : level.time), NULL, gun.modelScale); + gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, effectOrigin); + CG_ForcePushBlur(effectOrigin); + } + + // The effect position gets broken with differences in FOV. This should (hopefully) fix that. + if(cg_fovViewmodel.integer) { + float fracDistFOV = tanf(cg.refdef.fov_x * (M_PI / 180) * 0.5f); + float fracWeapFOV = (1.0f / fracDistFOV) * tanf(actualFOV * (M_PI / 180) * 0.5f); + VectorScale(flash.axis[0], fracWeapFOV, flash.axis[0]); + } + CG_DoMuzzleFlash( cent, flash.origin, flash.angles, wData ); + } + if ( cent->gent && cent->gent->client ) { if ( saberNum == 0 ) @@ -1287,6 +1693,7 @@ void CG_AddViewWeapon( playerState_t *ps ) } } + // Do special charge bits //----------------------- if (( ps->weaponstate == WEAPON_CHARGING_ALT && ps->weapon == WP_BRYAR_PISTOL ) @@ -1333,9 +1740,9 @@ void CG_AddViewWeapon( playerState_t *ps ) CGCam_Shake( val * val * 0.3f, 100 ); } - val += random() * 0.5f; + val += Q_flrand(0.0f, 1.0f) * 0.5f; - FX_AddSprite( flash.origin, NULL, NULL, 3.0f * val * scale, 0.0f, 0.7f, 0.7f, WHITE, WHITE, random() * 360, 0.0f, 1.0f, shader, FX_USE_ALPHA | FX_DEPTH_HACK ); + FX_AddSprite( flash.origin, NULL, NULL, 3.0f * val * scale, 0.0f, 0.7f, 0.7f, WHITE, WHITE, Q_flrand(0.0f, 1.0f) * 360, 0.0f, 1.0f, shader, FX_USE_ALPHA | FX_DEPTH_HACK ); } // Check if the heavy repeater is finishing up a sustained burst @@ -1414,7 +1821,7 @@ int CG_WeaponCheck( int weaponIndex ) int cgi_UI_GetItemText(char *menuFile,char *itemName, char *text); -const char *weaponDesc[13] = +const char *weaponDesc[WP_NUM_WEAPONS - 1] = { "SABER_DESC", "NEW_BLASTER_PISTOL_DESC", @@ -1429,6 +1836,28 @@ const char *weaponDesc[13] = "TRIP_MINE_DESC", "DET_PACK_DESC", "CONCUSSION_DESC", +"MELEE_DESC", +"ATST_MAIN_DESC", +"ATST_SIDE_DESC", +"STUN_BATON_DESC", +"BLASTER_PISTOL_DESC", +"EMPLACED_GUN_DESC", +"BOT_LASER_DESC", +"TURRET_DESC", +"TIE_FIGHTER_DESC", +"RAPID_CONCUSSION_DESC", +"JAWA_DESC", +"TUSKEN_RIFLE_DESC", +"TUSKEN_STAFF_DESC", +"SCEPTER_DESC", +"NOGHRI_STICK_DESC", + +"SONIC_BLASTER_DESC", + +"E5_CARBINE_DESC", +"DC15S_CARBINE_DESC", +"DC15A_RIFLE_DESC", +"Z6_ROTARY_DESC", }; /* @@ -1441,7 +1870,7 @@ Allows user to cycle through the various weapons currently owned and view the de void CG_DrawDataPadWeaponSelect( void ) { int i; - int weaponBitFlag,weaponCount,weaponSelectI; + int weaponCount,weaponSelectI; int holdX; int sideLeftIconCnt,sideRightIconCnt; int holdCount,iconCnt; @@ -1451,13 +1880,11 @@ void CG_DrawDataPadWeaponSelect( void ) // showing weapon select clears pickup item display, but not the blend blob cg.itemPickupTime = 0; - weaponBitFlag = cg.snap->ps.stats[ STAT_WEAPONS ]; - // count the number of weapons owned weaponCount = 0; - for ( i = 1 ; i < 16 ; i++ ) + for ( i = 1 ; i < WP_NUM_WEAPONS ; i++ ) { - if ( weaponBitFlag & ( 1 << i ) ) + if ( cg.snap->ps.weapons[i] ) { weaponCount++; } @@ -1493,9 +1920,9 @@ void CG_DrawDataPadWeaponSelect( void ) { cg.DataPadWeaponSelect = FIRST_WEAPON; } - else if (cg.DataPadWeaponSelect>13) + else if (cg.DataPadWeaponSelect>=WP_NUM_WEAPONS) { - cg.DataPadWeaponSelect = 13; + cg.DataPadWeaponSelect = WP_NUM_WEAPONS - 1; } // What weapon does the player currently have selected @@ -1509,8 +1936,8 @@ void CG_DrawDataPadWeaponSelect( void ) } if (weaponSelectI<1) { - weaponSelectI = 13; - } + weaponSelectI = WP_NUM_WEAPONS - 1; + } const int smallIconSize = 40; const int bigIconSize = 80; @@ -1539,10 +1966,10 @@ void CG_DrawDataPadWeaponSelect( void ) if (weaponSelectI<1) { - weaponSelectI = 13; + weaponSelectI = WP_NUM_WEAPONS - 1; } - if ( !(weaponBitFlag & ( 1 << weaponSelectI ))) // Does he have this weapon? + if ( !(cg.snap->ps.weapons[weaponSelectI])) // Does he have this weapon? { if ( weaponSelectI == WP_CONCUSSION ) { @@ -1608,7 +2035,7 @@ void CG_DrawDataPadWeaponSelect( void ) weaponSelectI = cg.DataPadWeaponSelect + 1; } - if (weaponSelectI> 13) + if (weaponSelectI>= WP_NUM_WEAPONS) { weaponSelectI = 1; } @@ -1627,12 +2054,12 @@ void CG_DrawDataPadWeaponSelect( void ) { weaponSelectI = WP_CONCUSSION; } - if (weaponSelectI>13) + if (weaponSelectI>= WP_NUM_WEAPONS) { weaponSelectI = 1; } - if ( !(weaponBitFlag & ( 1 << weaponSelectI ))) // Does he have this weapon? + if ( !(cg.snap->ps.weapons[weaponSelectI])) // Does he have this weapon? { if ( weaponSelectI == WP_CONCUSSION ) { @@ -1671,7 +2098,10 @@ void CG_DrawDataPadWeaponSelect( void ) } // Print the weapon description - cgi_SP_GetStringTextString( va("SP_INGAME_%s",weaponDesc[cg.DataPadWeaponSelect-1]), text, sizeof(text) ); + if (!cgi_SP_GetStringTextString( va("SP_INGAME_%s",weaponDesc[cg.DataPadWeaponSelect-1]), text, sizeof(text) )) + { + cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",weaponDesc[cg.DataPadWeaponSelect-1]), text, sizeof(text) ); + } if (text[0]) { @@ -1772,7 +2202,6 @@ extern bool G_IsRidingTurboVehicle( gentity_t *ent ); void CG_DrawWeaponSelect( void ) { int i; - int bits; int count; int smallIconSize,bigIconSize; int holdX,x,y,x2,y2,w2,h2,pad; @@ -1801,15 +2230,14 @@ void CG_DrawWeaponSelect( void ) // showing weapon select clears pickup item display, but not the blend blob //cg.itemPickupTime = 0; - bits = cg.snap->ps.stats[ STAT_WEAPONS ]; - // count the number of weapons owned count = 0; isOnVeh = (G_IsRidingVehicle(cg_entities[0].gent)!=0); - for ( i = 1 ; i < MAX_PLAYER_WEAPONS ; i++ ) + for ( i = 1 ; i < WP_NUM_WEAPONS ; i++ ) { - if ((bits & ( 1 << i )) && - (!isOnVeh || i==WP_NONE || i==WP_SABER || i==WP_BLASTER)) + if ((cg.snap->ps.weapons[i]) && + playerUsableWeapons[i] && + (!isOnVeh || i==WP_NONE || i==WP_SABER || i==WP_BLASTER)) { count++; } @@ -1850,7 +2278,7 @@ void CG_DrawWeaponSelect( void ) } if (i<1) { - i = MAX_PLAYER_WEAPONS; + i = WP_NUM_WEAPONS; } smallIconSize = 40; @@ -1888,10 +2316,10 @@ void CG_DrawWeaponSelect( void ) } if (i<1) { - i = MAX_PLAYER_WEAPONS; + i = WP_NUM_WEAPONS; } - if ( !(bits & ( 1 << i ))) // Does he have this weapon? + if ( !(cg.snap->ps.weapons[i] && playerUsableWeapons[i]) ) // Does he have this weapon? { if ( i == WP_CONCUSSION ) { @@ -1966,7 +2394,7 @@ void CG_DrawWeaponSelect( void ) { i = cg.weaponSelect + 1; } - if (i> MAX_PLAYER_WEAPONS) + if (i>= WP_NUM_WEAPONS) { i = 1; } @@ -1987,12 +2415,12 @@ void CG_DrawWeaponSelect( void ) { i = WP_CONCUSSION; } - if (i>MAX_PLAYER_WEAPONS) + if (i>= WP_NUM_WEAPONS) { i = 1; } - if ( !(bits & ( 1 << i ))) // Does he have this weapon? + if ( !(cg.snap->ps.weapons[i] && playerUsableWeapons[i])) // Does he have this weapon? { if ( i == WP_CONCUSSION ) { @@ -2054,6 +2482,12 @@ void CG_DrawWeaponSelect( void ) int x = ( SCREEN_WIDTH - w ) / 2; cgi_R_Font_DrawString(x, (SCREEN_HEIGHT - 24)+yOffset, text, textColor, cgs.media.qhFontSmall, -1, 1.0f); } + else if ( cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",item->classname), text, sizeof( text ))) + { + int w = cgi_R_Font_StrLenPixels(text, cgs.media.qhFontSmall, 1.0f); + int x = ( SCREEN_WIDTH - w ) / 2; + cgi_R_Font_DrawString(x, (SCREEN_HEIGHT - 24)+yOffset, text, textColor, cgs.media.qhFontSmall, -1, 1.0f); + } } cgi_R_SetColor( NULL ); @@ -2069,8 +2503,8 @@ qboolean CG_WeaponSelectable( int i, int original, qboolean dpMode ) { int usage_for_weap; - if (i > MAX_PLAYER_WEAPONS) - { + if (i >= WP_NUM_WEAPONS || !playerUsableWeapons[i]) + { #ifndef FINAL_BUILD Com_Printf("CG_WeaponSelectable() passed illegal index of %d!\n",i); #endif @@ -2112,7 +2546,7 @@ qboolean CG_WeaponSelectable( int i, int original, qboolean dpMode ) } } - if (!(cg.snap->ps.stats[ STAT_WEAPONS ] & ( 1 << i ))) + if (!(cg.snap->ps.weapons[i])) { // Don't have this weapon to start with. return qfalse; @@ -2147,7 +2581,7 @@ extern qboolean Q3_TaskIDPending( gentity_t *ent, taskID_t taskType ); {//not waiting on a scripted sound to finish if( !jumping ) { - if( random() > 0.5 ) + if( Q_flrand(0.0f, 1.0f) > 0.5 ) { G_SoundOnEnt( player, CHAN_VOICE, va( "sound/chars/kyle/09kyk015.wav" )); } @@ -2220,7 +2654,7 @@ void CG_NextWeapon_f( void ) { firstWeapon = 0; // include WP_NONE here } - for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ ) + for ( i = 0 ; i < WP_NUM_WEAPONS ; i++ ) { //*SIGH*... Hack to put concussion rifle before rocketlauncher @@ -2234,15 +2668,15 @@ void CG_NextWeapon_f( void ) { } else if ( cg.weaponSelect == WP_DET_PACK ) { - cg.weaponSelect = firstWeapon; + cg.weaponSelect = WP_MELEE; } else { cg.weaponSelect++; } - if ( cg.weaponSelect < firstWeapon || cg.weaponSelect > MAX_PLAYER_WEAPONS) { - cg.weaponSelect = firstWeapon; + if ( cg.weaponSelect < firstWeapon || cg.weaponSelect >= WP_NUM_WEAPONS) { + cg.weaponSelect = firstWeapon; } if ( CG_WeaponSelectable( cg.weaponSelect, original, qfalse ) ) @@ -2276,7 +2710,7 @@ void CG_DPNextWeapon_f( void ) { original = cg.DataPadWeaponSelect; - for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ ) + for ( i = 0 ; i < WP_NUM_WEAPONS ; i++ ) { //*SIGH*... Hack to put concussion rifle before rocketlauncher @@ -2290,15 +2724,15 @@ void CG_DPNextWeapon_f( void ) { } else if ( cg.DataPadWeaponSelect == WP_DET_PACK ) { - cg.DataPadWeaponSelect = FIRST_WEAPON; + cg.DataPadWeaponSelect = WP_MELEE; } else { cg.DataPadWeaponSelect++; } - if ( cg.DataPadWeaponSelect < FIRST_WEAPON || cg.DataPadWeaponSelect > MAX_PLAYER_WEAPONS) { - cg.DataPadWeaponSelect = FIRST_WEAPON; + if ( cg.DataPadWeaponSelect < FIRST_WEAPON || cg.DataPadWeaponSelect >= WP_NUM_WEAPONS ) { + cg.DataPadWeaponSelect = FIRST_WEAPON; } if ( CG_WeaponSelectable( cg.DataPadWeaponSelect, original, qtrue ) ) @@ -2334,7 +2768,7 @@ void CG_DPPrevWeapon_f( void ) original = cg.DataPadWeaponSelect; - for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ ) + for ( i = 0 ; i < WP_NUM_WEAPONS ; i++ ) { //*SIGH*... Hack to put concussion rifle before rocketlauncher @@ -2355,9 +2789,9 @@ void CG_DPPrevWeapon_f( void ) cg.DataPadWeaponSelect--; } - if ( cg.DataPadWeaponSelect < FIRST_WEAPON || cg.DataPadWeaponSelect > MAX_PLAYER_WEAPONS) - { - cg.DataPadWeaponSelect = MAX_PLAYER_WEAPONS; + if ( cg.DataPadWeaponSelect < FIRST_WEAPON || cg.DataPadWeaponSelect >= WP_NUM_WEAPONS ) + { + cg.DataPadWeaponSelect = WP_NUM_WEAPONS; } if ( CG_WeaponSelectable( cg.DataPadWeaponSelect, original, qtrue ) ) @@ -2424,8 +2858,8 @@ void CG_PrevWeapon_f( void ) { firstWeapon = 0; // include WP_NONE here } - for ( i = 0 ; i <= MAX_PLAYER_WEAPONS ; i++ ) { - + for ( i = 0 ; i < WP_NUM_WEAPONS ; i++ ) { + //*SIGH*... Hack to put concussion rifle before rocketlauncher if ( cg.weaponSelect == WP_ROCKET_LAUNCHER ) { @@ -2445,8 +2879,8 @@ void CG_PrevWeapon_f( void ) { } - if ( cg.weaponSelect < firstWeapon || cg.weaponSelect > MAX_PLAYER_WEAPONS) { - cg.weaponSelect = MAX_PLAYER_WEAPONS; + if ( cg.weaponSelect < firstWeapon || cg.weaponSelect >= WP_NUM_WEAPONS ) { + cg.weaponSelect = WP_NUM_WEAPONS; } if ( CG_WeaponSelectable( cg.weaponSelect, original, qfalse ) ) @@ -2480,7 +2914,7 @@ void CG_ChangeWeapon( int num ) return; } - if ( player->client != NULL && !(player->client->ps.stats[STAT_WEAPONS] & ( 1 << num )) ) + if ( player->client != NULL && !(player->client->ps.weapons[num]) ) { return; // don't have the weapon } @@ -2568,9 +3002,9 @@ void CG_Weapon_f( void ) if ( num == WP_SABER ) {//lightsaber - if ( ! ( cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << num ) ) ) + if ( ! ( cg.snap->ps.weapons[num] ) ) {//don't have saber, try stun baton - if ( ( cg.snap->ps.stats[STAT_WEAPONS] & ( 1 << WP_STUN_BATON ) ) ) + if ( ( cg.snap->ps.weapons[WP_STUN_BATON] ) ) { num = WP_STUN_BATON; } @@ -2607,6 +3041,12 @@ void CG_Weapon_f( void ) else {//turn them both on cg_entities[0].gent->client->ps.SaberActivate(); + //if we'd holstered the second saber, best make sure it's in the left hand! + if ( cg_entities[0].gent->client->ps.dualSabers ) + { + G_RemoveHolsterModels( cg_entities[0].gent ); + WP_SaberAddG2SaberModels( cg_entities[0].gent, qtrue ); + } } } } @@ -2648,6 +3088,10 @@ void CG_Weapon_f( void ) i++; } } + else if (num == WP_BLASTER_PISTOL && cg.snap->ps.weapon == WP_BLASTER_PISTOL) + { + num = WP_BRYAR_PISTOL; + } if (!CG_WeaponSelectable(num, cg.snap->ps.weapon, qfalse)) { @@ -2752,7 +3196,7 @@ void CG_FireWeapon( centity_t *cent, qboolean alt_fire ) CG_Error( "CG_FireWeapon: ent->weapon >= WP_NUM_WEAPONS" ); return; } - if ( ent->weapon == WP_TUSKEN_RIFLE && cent->gent->client) + if ( (ent->weapon == WP_TUSKEN_RIFLE || ent->weapon == WP_NOGHRI_STICK) && cent->gent->client) { if (cent->gent->client->ps.torsoAnim==BOTH_TUSKENATTACK1 || cent->gent->client->ps.torsoAnim==BOTH_TUSKENATTACK2 || @@ -2984,7 +3428,14 @@ void CG_MissileHitWall( centity_t *cent, int weapon, vec3_t origin, vec3_t dir, break; case WP_CONCUSSION: - FX_ConcHitWall( origin, dir ); + if (cent->currentState.powerups & (1<gent&¢->gent->alt_fire) ); + FX_EmplacedHitWall( origin, dir, (qboolean)(cent->gent&¢->gent->alt_fire) ); break; case WP_ATST_MAIN: @@ -3030,6 +3481,16 @@ void CG_MissileHitWall( centity_t *cent, int weapon, vec3_t origin, vec3_t dir, case WP_NOGHRI_STICK: FX_NoghriShotWeaponHitWall( origin, dir ); break; + + case WP_E5_CARBINE: + FX_BlasterWeaponHitWall( origin, dir ); + break; + + case WP_DC15S_CARBINE: + case WP_Z6_ROTARY: + case WP_DC15A_RIFLE: + FX_CloneBlasterWeaponHitWall( origin, dir ); + break; } } @@ -3125,7 +3586,14 @@ void CG_MissileHitPlayer( centity_t *cent, int weapon, vec3_t origin, vec3_t dir break; case WP_CONCUSSION: - FX_ConcHitPlayer( origin, dir, humanoid ); + if (cent->currentState.powerups & (1<gent&¢->gent->alt_fire) ); + FX_EmplacedHitPlayer( origin, dir, (qboolean)(cent->gent&¢->gent->alt_fire) ); break; case WP_TRIP_MINE: @@ -3170,5 +3638,15 @@ void CG_MissileHitPlayer( centity_t *cent, int weapon, vec3_t origin, vec3_t dir case WP_NOGHRI_STICK: FX_NoghriShotWeaponHitPlayer( other, origin, dir, humanoid ); break; + + case WP_E5_CARBINE: + FX_BlasterWeaponHitPlayer( other, origin, dir, humanoid ); + break; + + case WP_DC15S_CARBINE: + case WP_Z6_ROTARY: + case WP_DC15A_RIFLE: + FX_CloneBlasterWeaponHitPlayer( other, origin, dir, humanoid ); + break; } } diff --git a/code/client/cl_cgame.cpp b/code/client/cl_cgame.cpp index ca7e9c68b9..c70e8d929c 100644 --- a/code/client/cl_cgame.cpp +++ b/code/client/cl_cgame.cpp @@ -70,7 +70,7 @@ qboolean CL_InitCGameVM( void *gameLibrary ) #ifdef JK2_MODE const char *gamename = "jospgame"; #else - const char *gamename = "jagame"; + const char *gamename = "jaenhancedgame"; #endif Com_Printf( "CL_InitCGameVM: client game entry point not found in %s" ARCH_STRING DLL_EXT ": %s\n", @@ -868,7 +868,7 @@ intptr_t CL_CgameSystemCalls( intptr_t *args ) { return 0; case CG_CM_LOADMAP: - CL_CM_LoadMap( (const char *) VMA(1), args[2] ); + CL_CM_LoadMap( (const char *) VMA(1), (qboolean)(args[2] != 0) ); return 0; case CG_CM_NUMINLINEMODELS: return CM_NumInlineModels(); @@ -944,12 +944,12 @@ intptr_t CL_CgameSystemCalls( intptr_t *args ) { S_UpdateEntityPosition( args[1], (const float *) VMA(2) ); return 0; case CG_S_RESPATIALIZE: - S_Respatialize( args[1], (const float *) VMA(2), (float(*)[3]) VMA(3), args[4] ); + S_Respatialize( args[1], (const float *) VMA(2), (float(*)[3]) VMA(3), (qboolean)(args[4] != 0) ); return 0; case CG_S_REGISTERSOUND: return S_RegisterSound( (const char *) VMA(1) ); case CG_S_STARTBACKGROUNDTRACK: - S_StartBackgroundTrack( (const char *) VMA(1), (const char *) VMA(2), args[3]); + S_StartBackgroundTrack( (const char *) VMA(1), (const char *) VMA(2), (qboolean)(args[3] != 0) ); return 0; case CG_S_GETSAMPLELENGTH: return S_GetSampleLengthInMilliSeconds( args[1]); @@ -1190,7 +1190,7 @@ Ghoul2 Insert End return 0; case CG_OPENJK_MENU_PAINT: - Menu_Paint( (menuDef_t *)VMA(1), (intptr_t)VMA(2) ); + Menu_Paint( (menuDef_t *)VMA(1), (qboolean)(args[2] != 0) ); return 0; case CG_OPENJK_GETMENU_BYNAME: @@ -1444,7 +1444,7 @@ qboolean CL_GameCommand( void ) { return qfalse; } - return VM_Call( CG_CONSOLE_COMMAND ); + return (qboolean)(VM_Call( CG_CONSOLE_COMMAND ) != 0); } diff --git a/code/client/cl_cin.cpp b/code/client/cl_cin.cpp index 91cc6ef856..7cf2db8c2f 100644 --- a/code/client/cl_cin.cpp +++ b/code/client/cl_cin.cpp @@ -592,8 +592,18 @@ static unsigned short yuv_to_rgb( long y, long u, long v ) g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 8; b = (YY + ROQ_UB_tab[u]) >> 9; - if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0; - if (r > 31) r = 31; if (g > 63) g = 63; if (b > 31) b = 31; + if (r<0) + r = 0; + if (g<0) + g = 0; + if (b<0) + b = 0; + if (r > 31) + r = 31; + if (g > 63) + g = 63; + if (b > 31) + b = 31; return (unsigned short)((r<<11)+(g<<5)+(b)); } @@ -614,8 +624,18 @@ static unsigned int yuv_to_rgb24( long y, long u, long v ) g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6; b = (YY + ROQ_UB_tab[u]) >> 6; - if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0; - if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; + if (r<0) + r = 0; + if (g<0) + g = 0; + if (b<0) + b = 0; + if (r > 255) + r = 255; + if (g > 255) + g = 255; + if (b > 255) + b = 255; return LittleLong ((r)|(g<<8)|(b<<16)|(255<<24)); } @@ -1162,7 +1182,7 @@ static void RoQInterrupt(void) case ZA_SOUND_MONO: if (!cinTable[currentHandle].silent) { ssize = RllDecodeMonoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags); - S_RawSamples( ssize, 22050, 2, 1, (byte *)sbuf, s_volume->value, 1 ); + S_RawSamples( ssize, 22050, 2, 1, (byte *)sbuf, s_volume->value, qtrue ); } break; case ZA_SOUND_STEREO: @@ -1172,7 +1192,7 @@ static void RoQInterrupt(void) s_rawend = s_soundtime; } ssize = RllDecodeStereoToStereo( framedata, sbuf, cinTable[currentHandle].RoQFrameSize, 0, (unsigned short)cinTable[currentHandle].roq_flags); - S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, s_volume->value, 1 ); + S_RawSamples( ssize, 22050, 2, 2, (byte *)sbuf, s_volume->value, qtrue ); } break; case ROQ_QUAD_INFO: @@ -1494,15 +1514,15 @@ int CIN_PlayCinematic( const char *arg, int x, int y, int w, int h, int systemBi } CIN_SetExtents(currentHandle, x, y, w, h); - CIN_SetLooping(currentHandle, (systemBits & CIN_loop)!=0); + CIN_SetLooping(currentHandle, (qboolean)((systemBits & CIN_loop) != 0)); cinTable[currentHandle].CIN_HEIGHT = DEFAULT_CIN_HEIGHT; cinTable[currentHandle].CIN_WIDTH = DEFAULT_CIN_WIDTH; - cinTable[currentHandle].holdAtEnd = (systemBits & CIN_hold) != 0; - cinTable[currentHandle].alterGameState = (systemBits & CIN_system) != 0; + cinTable[currentHandle].holdAtEnd = (qboolean)((systemBits & CIN_hold) != 0); + cinTable[currentHandle].alterGameState = (qboolean)((systemBits & CIN_system) != 0); cinTable[currentHandle].playonwalls = 1; - cinTable[currentHandle].silent = (systemBits & CIN_silent) != 0; - cinTable[currentHandle].shader = (systemBits & CIN_shader) != 0; + cinTable[currentHandle].silent = (qboolean)((systemBits & CIN_silent) != 0); + cinTable[currentHandle].shader = (qboolean)((systemBits & CIN_shader) != 0); if (psAudioFile) { cinTable[currentHandle].hSFX = S_RegisterSound(psAudioFile); @@ -1625,8 +1645,8 @@ static void CIN_AddTextCrawl() verts[i].modulate[3] = 255*fadeDown; } - _VectorScale( verts[2].modulate, 0.1f, verts[2].modulate ); // darken at the top?? - _VectorScale( verts[3].modulate, 0.1f, verts[3].modulate ); + VectorScaleM( verts[2].modulate, 0.1f, verts[2].modulate ); // darken at the top?? + VectorScaleM( verts[3].modulate, 0.1f, verts[3].modulate ); #define TIMEOFFSET +(cls.realtime-CL_iPlaybackStartTime-TC_DELAY)*0.000015f -1 VectorSet( verts[0].xyz, TC_PLANE_NEAR, -TC_PLANE_WIDTH, TC_PLANE_TOP ); @@ -1856,7 +1876,7 @@ static void PlayCinematic(const char *arg, const char *s, qboolean qbInGame) // work out associated audio-overlay file, if any... // extern cvar_t *s_language; - qboolean bIsForeign = s_language && Q_stricmp(s_language->string,"english") && Q_stricmp(s_language->string,""); + qboolean bIsForeign = (qboolean)(s_language && Q_stricmp(s_language->string,"english") && Q_stricmp(s_language->string,"")); const char *psAudioFile = NULL; qhandle_t hCrawl = 0; if (!Q_stricmp(arg,"video/jk0101_sw.roq")) @@ -1949,7 +1969,7 @@ qboolean CL_CheckPendingCinematic(void) if ( gbPendingCinematic && CIN_HardwareReadyToPlayVideos() ) { gbPendingCinematic = qfalse; // BEFORE next line, or we get recursion - PlayCinematic(sPendingCinematic_Arg,sPendingCinematic_s[0]?sPendingCinematic_s:NULL,false); + PlayCinematic(sPendingCinematic_Arg,sPendingCinematic_s[0]?sPendingCinematic_s:NULL,qfalse); return qtrue; } return qfalse; diff --git a/code/client/cl_input.cpp b/code/client/cl_input.cpp index 575c1db121..f6c37d1c50 100644 --- a/code/client/cl_input.cpp +++ b/code/client/cl_input.cpp @@ -70,11 +70,6 @@ qboolean in_mlooking; extern cvar_t *in_joystick; -#ifndef NO_XINPUT -void IN_UnloadXInput ( void ); -#endif - - static void IN_UseGivenForce(void) { const char *c = Cmd_Argv(1); @@ -88,9 +83,12 @@ static void IN_UseGivenForce(void) } switch(forceNum) { +#ifndef JK2_MODE case FP_DRAIN: genCmdNum = GENCMD_FORCE_DRAIN; break; +#endif // !JK2_MODE + case FP_PUSH: genCmdNum = GENCMD_FORCE_THROW; break; @@ -109,6 +107,8 @@ static void IN_UseGivenForce(void) case FP_LIGHTNING: genCmdNum = GENCMD_FORCE_LIGHTNING; break; + +#ifndef JK2_MODE case FP_RAGE: genCmdNum = GENCMD_FORCE_RAGE; break; @@ -121,9 +121,32 @@ static void IN_UseGivenForce(void) case FP_SEE: genCmdNum = GENCMD_FORCE_SEEING; break; +#endif // !JK2_MODE + case FP_HEAL: genCmdNum = GENCMD_FORCE_HEAL; break; + case FP_DESTRUCTION: + genCmdNum = GENCMD_FORCE_DESTRUCTION; + break; + case FP_INSANITY: + genCmdNum = GENCMD_FORCE_INSANITY; + break; + case FP_STASIS: + genCmdNum = GENCMD_FORCE_STASIS; + break; + case FP_BLINDING: + genCmdNum = GENCMD_FORCE_BLINDING; + break; + case FP_DEADLYSIGHT: + genCmdNum = GENCMD_FORCE_DEADLYSIGHT; + break; + case FP_REPULSE: + genCmdNum = GENCMD_FORCE_REPULSE; + break; + case FP_INVULNERABILITY: + genCmdNum = GENCMD_FORCE_INVULNERABILITY; + break; default: assert(0); break; @@ -140,6 +163,7 @@ void IN_MLookDown( void ) { in_mlooking = qtrue; } +void IN_CenterView( void ); void IN_MLookUp( void ) { in_mlooking = qfalse; if ( !cl_freelook->integer ) { @@ -733,23 +757,27 @@ void CL_CreateNewCommands( void ) { int cmdNum; // no need to create usercmds until we have a gamestate -// if ( cls.state < CA_PRIMED ) { +// if ( cls.state < CA_PRIMED ) // return; -// } frame_msec = com_frameTime - old_com_frameTime; + // if running over 1000fps, act as if each frame is 1ms + // prevents divisions by zero + if ( frame_msec < 1 ) + frame_msec = 1; + // if running less than 5fps, truncate the extra time to prevent // unexpected moves after a hitch - if ( frame_msec > 200 ) { + if ( frame_msec > 200 ) frame_msec = 200; - } + old_com_frameTime = com_frameTime; // generate a command for this frame cl.cmdNumber++; cmdNum = cl.cmdNumber & CMD_MASK; - cl.cmds[cmdNum] = CL_CreateCmd (); + cl.cmds[cmdNum] = CL_CreateCmd(); } /* @@ -986,6 +1014,11 @@ void CL_InitInput( void ) { Cmd_AddCommand ("+block", IN_Button8Down);//manual blocking Cmd_AddCommand ("-block", IN_Button8Up); #endif + Cmd_AddCommand ("+saber_throw", IN_Button9Down); + Cmd_AddCommand ("-saber_throw", IN_Button9Up); + + Cmd_AddCommand ("+force_repulse", IN_Button10Down); + Cmd_AddCommand ("-force_repulse", IN_Button10Up); Cmd_AddCommand ("+button0", IN_Button0Down); Cmd_AddCommand ("-button0", IN_Button0Up); diff --git a/code/client/cl_input_hotswap.cpp b/code/client/cl_input_hotswap.cpp deleted file mode 100644 index e2384103f3..0000000000 --- a/code/client/cl_input_hotswap.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/* - TODO: finalize item support - - 1) Make ItemSelectUp() work. - 2) Change cg.itemSelect to whatever var is used to store selected item. - 3) Make sure commands in itemCommands work in both multi & single player. -*/ - -#ifdef _JK2MP -#include "../../codemp/client/client.h" -#include "../../codemp/cgame/cg_local.h" -#else -#include "client.h" -#include "../cgame/cg_local.h" -#endif - -#include "cl_input_hotswap.h" - - -#ifdef _JK2MP -#define FORCESELECTTIME forceSelectTime -#define FORCESELECT forceSelect -#define INVSELECTTIME invenSelectTime -#define INVSELECT itemSelect -#define REGISTERSOUND S_RegisterSound -#define STARTSOUND S_StartLocalSound -#define WEAPONBINDSTR "weaponclean" -#else -#define FORCESELECTTIME forcepowerSelectTime -#define FORCESELECT forcepowerSelect -#define INVSELECTTIME inventorySelectTime -#define INVSELECT inventorySelect -#define REGISTERSOUND cgi_S_RegisterSound -#define STARTSOUND cgi_S_StartLocalSound -#define WEAPONBINDSTR "weapon" -#endif - -#define BIND_TIME 3000 //number of milliseconds button is held before binding -#define EXEC_TIME 500 //max ms button can be held to execute in bind mode - - -#ifdef _JK2MP -const char *itemCommands[HI_NUM_HOLDABLE] = { - NULL, //HI_NONE - "use_seeker\n", - "use_field\n", - "use_bacta\n", - "use_bactabig\n", - "use_electrobinoculars\n", - "use_sentry\n", - "use_jetpack\n", - NULL, //ammo dispenser - NULL, //health dispenser - "use_eweb\n", - "use_cloak\n", -}; -#else -const char *itemCommands[INV_MAX] = { - "use_electrobinoculars\n", - "use_bacta\n", - "use_seeker\n", - "use_goggles\n", - "use_sentry\n", - NULL, //goodie key - NULL, //security key -}; -#endif - - - -HotSwapManager::HotSwapManager(int uniqueID) : - uniqueID(uniqueID), - forceBound(false) -{ - Reset(); -} - - -char *HotSwapManager::GetBinding(void) -{ - char buf[64]; - - sprintf(buf, "hotswap%d", uniqueID); - cvar_t *cvar = Cvar_Get(buf, "", CVAR_ARCHIVE); - - if(cvar && cvar->string[0] != 0) { - return cvar->string; - } else { - return NULL; - } -} - - -void HotSwapManager::Bind(void) -{ - forceBound = false; - - if(WeaponSelectUp()) { - HotSwapBind(uniqueID, HOTSWAP_CAT_WEAPON, cg.weaponSelect); - } else if(ForceSelectUp()) { - forceBound = true; - HotSwapBind(uniqueID, HOTSWAP_CAT_FORCE, -#ifdef _JK2MP - cg.FORCESELECT -#else - showPowers[cg.FORCESELECT] -#endif - ); - } else if(ItemSelectUp()) { - HotSwapBind(uniqueID, HOTSWAP_CAT_ITEM, cg.INVSELECT); - } else{ - assert(0); - } - - noExec = true; - noBind = true; - STARTSOUND(REGISTERSOUND("sound/interface/update"), 0); -} - - -bool HotSwapManager::ForceSelectUp(void) -{ - return cg.FORCESELECTTIME != 0 && - (cg.FORCESELECTTIME + WEAPON_SELECT_TIME >= cg.time); -} - - -bool HotSwapManager::WeaponSelectUp(void) -{ - return cg.weaponSelectTime != 0 && - (cg.weaponSelectTime + WEAPON_SELECT_TIME >= cg.time); -} - - -bool HotSwapManager::ItemSelectUp(void) -{ - return cg.INVSELECTTIME != 0 && - (cg.INVSELECTTIME + WEAPON_SELECT_TIME >= cg.time); -} - - -bool HotSwapManager::HUDInBindState(void) -{ - return ForceSelectUp() || WeaponSelectUp() || ItemSelectUp(); -} - - -void HotSwapManager::Update(void) -{ - if(down) { - //Increment bindTime only if HUD is in select mode. - if(HUDInBindState()) { - bindTime += cls.frametime; - } else { - - //Clear bind time. - bindTime = 0; - - //If a force power is bound, want to execute whenever the button - //is down to handle powers which can be held. - if(forceBound) { - Execute(); - } - } - downTime += cls.frametime; - } - - //Down long enough, bind button. - if(!noBind && bindTime >= BIND_TIME) { - Bind(); - } -} - - -void HotSwapManager::Execute(void) -{ - char *binding = GetBinding(); - if(binding && !noExec) { - if(!forceBound) { - noExec = true; - } - Cbuf_ExecuteText(EXEC_APPEND, binding); - } -} - - -void HotSwapManager::SetDown(void) -{ - //Set the down flag. - down = true; - - //Execute the bind if the HUD isn't up. - if(!HUDInBindState()) { - Execute(); - } -} - - -void HotSwapManager::SetUp(void) -{ - //Execute the bind if the button was held down for long enough. - if(downTime <= EXEC_TIME) { - Execute(); - } - - Reset(); -} - - -void HotSwapManager::Reset(void) -{ - down = false; - downTime = 0; - bindTime = 0; - noExec = false; - noBind = false; -} - - -static void HotSwapBind(const char *uniqueID, const char *value) -{ - Cvar_Set(uniqueID, value); -} - - -void HotSwapBind(int buttonID, int category, int value) -{ - char uniqueID[64]; - sprintf(uniqueID, "hotswap%d", buttonID); - - switch(category) { - case HOTSWAP_CAT_WEAPON: - HotSwapBind(uniqueID, va("%s %d\n", WEAPONBINDSTR, value)); - break; - case HOTSWAP_CAT_ITEM: - assert(itemCommands[value]); - HotSwapBind(uniqueID, itemCommands[value]); - break; - case HOTSWAP_CAT_FORCE: - HotSwapBind(uniqueID, va("useGivenForce %d\n", value)); - break; - default: - assert(0); - } -} - diff --git a/code/client/cl_input_hotswap.h b/code/client/cl_input_hotswap.h deleted file mode 100644 index 174456e7bb..0000000000 --- a/code/client/cl_input_hotswap.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef __CL_INPUT_HOTSWAP_H -#define __CL_INPUT_HOTSWAP_H - - -#define HOTSWAP_ID_WHITE 0 -#define HOTSWAP_ID_BLACK 1 - -#define HOTSWAP_CAT_WEAPON 0 -#define HOTSWAP_CAT_ITEM 1 -#define HOTSWAP_CAT_FORCE 2 - - -class HotSwapManager -{ -private: - bool down; //Is the button down? - bool noExec; //Don't execute the button's bind. - bool noBind; //Don't bind the button. - bool forceBound;//Is a force power currently bound? - int downTime; //How long the button has been held down. - int bindTime; //How long the button has been down with the selection up. - int uniqueID; //Unique ID for this button. - - //Return the binding for the button, or NULL if none. - char *GetBinding(void); - - //Returns true if the weapon/force/item select screen is up. - bool HUDInBindState(void); - - //Returns true if the weapon/force/item select screen is up. - bool ForceSelectUp(void); - bool WeaponSelectUp(void); - bool ItemSelectUp(void); - - //Binds the button based on the current HUD selection. - void Bind(void); - - //Execute the current bind, if there is one. - void Execute(void); - - //Reset the object to the default state. - void Reset(void); - -public: - HotSwapManager(int uniqueID); - - //Call every frame. Uses cg.frametime to increment timers. - void Update(void); - - //Set the button down or up. - void SetDown(void); - void SetUp(void); - - //Returns true if the button is currently down. - bool ButtonDown(void) { return down; } -}; - - -//External bind function for sharing with UI. -extern void HotSwapBind(int buttonID, int category, int value); - - - -#endif diff --git a/code/client/cl_keys.cpp b/code/client/cl_keys.cpp index 7f9d42b9de..b9b36739e2 100644 --- a/code/client/cl_keys.cpp +++ b/code/client/cl_keys.cpp @@ -571,7 +571,7 @@ void Field_KeyDownEvent( field_t *edit, int key ) { if ( key == A_INSERT ) { - kg.key_overstrikeMode = !kg.key_overstrikeMode; + kg.key_overstrikeMode = (qboolean)!kg.key_overstrikeMode; return; } } @@ -1173,10 +1173,10 @@ void CL_ParseBinding( int key, qboolean down, unsigned time ) Q_strncpyz( buf, kg.keys[keynames[key].upper].binding, sizeof( buf ) ); // run all bind commands if console, ui, etc aren't reading keys - allCommands = ( Key_GetCatcher( ) == 0 ); + allCommands = (qboolean)( Key_GetCatcher( ) == 0 ); // allow button up commands if in game even if key catcher is set - allowUpCmds = ( cls.state != CA_DISCONNECTED ); + allowUpCmds = (qboolean)( cls.state != CA_DISCONNECTED ); while( 1 ) { @@ -1224,7 +1224,7 @@ void CL_KeyDownEvent( int key, unsigned time ) kg.keys[keynames[key].upper].repeats++; if( kg.keys[keynames[key].upper].repeats == 1 ) { kg.keyDownCount++; - kg.anykeydown = true; + kg.anykeydown = qtrue; } if ( cl_allowAltEnter->integer && kg.keys[A_ALT].down && key == A_ENTER ) @@ -1243,7 +1243,7 @@ void CL_KeyDownEvent( int key, unsigned time ) // keys can still be used for bound actions if ( ( cls.state == CA_CINEMATIC || CL_IsRunningInGameCinematic()) && !Key_GetCatcher() ) { - SCR_StopCinematic(true); + SCR_StopCinematic(qtrue); return; // key = A_ESCAPE; } @@ -1297,7 +1297,7 @@ void CL_KeyUpEvent( int key, unsigned time ) kg.keyDownCount--; if (kg.keyDownCount <= 0) { - kg.anykeydown = 0; + kg.anykeydown = qfalse; kg.keyDownCount = 0; } @@ -1339,10 +1339,6 @@ Normal keyboard characters, already shifted / capslocked / etc =================== */ void CL_CharEvent( int key ) { - // the console key should never be used as a char - if ( key == '`' || key == '~' ) - return; - // delete is not a printable character and is otherwise handled by Field_KeyDownEvent if ( key == 127 ) return; diff --git a/code/client/cl_main.cpp b/code/client/cl_main.cpp index 6ab334d913..8a4cfdcf70 100644 --- a/code/client/cl_main.cpp +++ b/code/client/cl_main.cpp @@ -32,6 +32,7 @@ along with this program; if not, see . #include "../ghoul2/G2.h" #include "qcommon/stringed_ingame.h" #include "sys/sys_loadlib.h" +#include "qcommon/ojk_saved_game.h" #define RETRANSMIT_TIMEOUT 3000 // time between connection packet retransmits @@ -76,6 +77,7 @@ cvar_t *cl_allowAltEnter; cvar_t *cl_inGameVideo; cvar_t *cl_consoleKeys; +cvar_t *cl_consoleUseScanCode; clientActive_t cl; clientConnection_t clc; @@ -1072,7 +1074,8 @@ static CMiniHeap *GetG2VertSpaceServer( void ) { #ifdef JK2_MODE #define DEFAULT_RENDER_LIBRARY "rdjosp-vanilla" #else -#define DEFAULT_RENDER_LIBRARY "rdsp-vanilla" +//#define DEFAULT_RENDER_LIBRARY "rdsp-vanilla-dp" +#define DEFAULT_RENDER_LIBRARY "rdcustomsp-vanilla" #endif void CL_InitRef( void ) { @@ -1142,7 +1145,6 @@ void CL_InitRef( void ) { RIT(FS_Write); RIT(FS_WriteFile); RIT(Hunk_ClearToMark); - RIT(SG_Append); RIT(SND_RegisterAudio_LevelLoadEnd); //RIT(SV_PointContents); RIT(SV_Trace); @@ -1181,6 +1183,8 @@ void CL_InitRef( void ) { rit.SV_PointContents = SV_PointContents; + rit.saved_game = &ojk::SavedGame::get_instance(); + ret = GetRefAPI( REF_API_VERSION, &rit ); if ( !ret ) { @@ -1271,7 +1275,8 @@ void CL_Init( void ) { m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE); // ~ and `, as keys and characters - cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60", CVAR_ARCHIVE); + cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60 0xb2", CVAR_ARCHIVE); + cl_consoleUseScanCode = Cvar_Get( "cl_consoleUseScanCode", "1", CVAR_ARCHIVE ); // userinfo #ifdef JK2_MODE diff --git a/code/client/cl_mp3.cpp b/code/client/cl_mp3.cpp index 37a6a6973c..c397a494f4 100644 --- a/code/client/cl_mp3.cpp +++ b/code/client/cl_mp3.cpp @@ -42,7 +42,7 @@ qboolean MP3_IsValid( const char *psLocalFilename, void *pvData, int iDataLen, q Com_Printf(va(S_COLOR_RED"%s(%s)\n",psError, psLocalFilename)); } - return !psError; + return (qboolean)(!psError); } @@ -168,7 +168,7 @@ qboolean MP3_FakeUpWAVInfo( const char *psLocalFilename, void *pvData, int iData // samples = iUnpackedDataLength / width; - return !psError; + return (qboolean)(!psError); } @@ -232,7 +232,7 @@ qboolean MP3_ReadSpecialTagInfo(byte *pbLoadedFile, int iLoadedFileLen, *ppTAG = pTAG; } - return (pTAG && !qbError); + return (qboolean)(pTAG && !qbError); } diff --git a/code/client/cl_mp3.h b/code/client/cl_mp3.h index 032343146f..ccdab28d2e 100644 --- a/code/client/cl_mp3.h +++ b/code/client/cl_mp3.h @@ -58,7 +58,7 @@ extern const char sKEY_UNCOMP[]; void MP3_InitCvars ( void ); qboolean MP3_IsValid ( const char *psLocalFilename, void *pvData, int iDataLen, qboolean bStereoDesired = qfalse ); int MP3_GetUnpackedSize ( const char *psLocalFilename, void *pvData, int iDataLen, qboolean qbIgnoreID3Tag = qfalse, qboolean bStereoDesired = qfalse ); -qboolean MP3_UnpackRawPCM ( const char *psLocalFilename, void *pvData, int iDataLen, byte *pbUnpackBuffer, qboolean bStereoDesired = qfalse ); +int MP3_UnpackRawPCM ( const char *psLocalFilename, void *pvData, int iDataLen, byte *pbUnpackBuffer, qboolean bStereoDesired = qfalse ); qboolean MP3Stream_InitPlayingTimeFields( LP_MP3STREAM lpMP3Stream, const char *psLocalFilename, void *pvData, int iDataLen, qboolean bStereoDesired = qfalse); float MP3Stream_GetPlayingTimeInSeconds( LP_MP3STREAM lpMP3Stream ); float MP3Stream_GetRemainingTimeInSeconds( LP_MP3STREAM lpMP3Stream ); diff --git a/code/client/cl_scrn.cpp b/code/client/cl_scrn.cpp index 6a797181c6..0e03a1f0b6 100644 --- a/code/client/cl_scrn.cpp +++ b/code/client/cl_scrn.cpp @@ -582,7 +582,7 @@ void SCR_SetScreenshot(const byte *pbData, int w, int h) } -#ifdef JK2_MODE +//#ifdef JK2_MODE // This is just a client-side wrapper for the function RE_TempRawImage_ReadFromFile() in the renderer code... // @@ -597,6 +597,6 @@ void SCR_TempRawImage_CleanUp() { re.TempRawImage_CleanUp(); } -#endif +//#endif diff --git a/code/client/cl_ui.cpp b/code/client/cl_ui.cpp index 980fe57b4f..61017e9f51 100644 --- a/code/client/cl_ui.cpp +++ b/code/client/cl_ui.cpp @@ -32,9 +32,9 @@ along with this program; if not, see . intptr_t CL_UISystemCalls( intptr_t *args ); //prototypes -#ifdef JK2_MODE +//#ifdef JK2_MODE extern qboolean SG_GetSaveImage( const char *psPathlessBaseName, void *pvAddress ); -#endif +//#endif extern int SG_GetSaveGameComment(const char *psPathlessBaseName, char *sComment, char *sMapName); extern qboolean SG_GameAllowedToSaveHere(qboolean inCamera); extern void SG_StoreSaveGameComment(const char *sComment); @@ -273,6 +273,12 @@ void CL_InitUI( void ) { uii.R_SetColor = re.SetColor; uii.R_DrawStretchPic = re.DrawStretchPic; uii.UpdateScreen = SCR_UpdateScreen; + + uii.PrecacheScreenshot = SCR_PrecacheScreenshot; + +#ifdef JK2_MODE + uii.PrecacheScreenshot = SCR_PrecacheScreenshot; +#endif #ifdef JK2_MODE uii.PrecacheScreenshot = SCR_PrecacheScreenshot; @@ -309,7 +315,7 @@ void CL_InitUI( void ) { uii.Milliseconds = Sys_Milliseconds2; - UI_Init(UI_API_VERSION, &uii, (cls.state > CA_DISCONNECTED && cls.state <= CA_ACTIVE)); + UI_Init(UI_API_VERSION, &uii, (qboolean)(cls.state > CA_DISCONNECTED && cls.state <= CA_ACTIVE)); // uie->UI_Init( UI_API_VERSION, &uii ); diff --git a/code/client/client.h b/code/client/client.h index 9eefc002a6..f9ffdf2351 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -278,6 +278,7 @@ extern cvar_t *m_filter; extern cvar_t *cl_activeAction; extern cvar_t *cl_consoleKeys; +extern cvar_t *cl_consoleUseScanCode; //================================================= @@ -313,9 +314,6 @@ extern kbutton_t in_speed; void CL_InitInput (void); void CL_SendCmd (void); void CL_ClearState (void); -void CL_ReadPackets (void); -void CL_UpdateHotSwap(void); -bool CL_ExtendSelectTime(void); void CL_WritePacket( void ); void IN_CenterView (void); @@ -374,9 +372,9 @@ void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, void SCR_DrawBigChar( int x, int y, int ch ); void SCR_DrawSmallChar( int x, int y, int ch ); -#ifdef JK2_MODE +//#ifdef JK2_MODE void SCR_PrecacheScreenshot(); -#endif +//#endif // // cl_cin.c diff --git a/code/client/client.h.BACKUP.2896.h b/code/client/client.h.BACKUP.2896.h new file mode 100644 index 0000000000..a47a70e68e --- /dev/null +++ b/code/client/client.h.BACKUP.2896.h @@ -0,0 +1,425 @@ +/* +=========================================================================== +Copyright (C) 1999 - 2005, Id Software, Inc. +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +// client.h -- primary header for client +#pragma once + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +extern cvar_t *cl_consoleKeys; + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +<<<<<<< HEAD +void SCR_PrecacheScreenshot(); +======= +#ifdef JK2_MODE +void SCR_PrecacheScreenshot(); +#endif +>>>>>>> 38e3edc40da09b9c20f585a3b939ff493be8bf50 + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +qboolean CL_InitCGameVM( void *gameLibrary ); +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); diff --git a/code/client/client.h.BACKUP.7476.h b/code/client/client.h.BACKUP.7476.h new file mode 100644 index 0000000000..a47a70e68e --- /dev/null +++ b/code/client/client.h.BACKUP.7476.h @@ -0,0 +1,425 @@ +/* +=========================================================================== +Copyright (C) 1999 - 2005, Id Software, Inc. +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +// client.h -- primary header for client +#pragma once + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +extern cvar_t *cl_consoleKeys; + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +<<<<<<< HEAD +void SCR_PrecacheScreenshot(); +======= +#ifdef JK2_MODE +void SCR_PrecacheScreenshot(); +#endif +>>>>>>> 38e3edc40da09b9c20f585a3b939ff493be8bf50 + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +qboolean CL_InitCGameVM( void *gameLibrary ); +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); diff --git a/code/client/client.h.BACKUP.8932.h b/code/client/client.h.BACKUP.8932.h new file mode 100644 index 0000000000..a47a70e68e --- /dev/null +++ b/code/client/client.h.BACKUP.8932.h @@ -0,0 +1,425 @@ +/* +=========================================================================== +Copyright (C) 1999 - 2005, Id Software, Inc. +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +// client.h -- primary header for client +#pragma once + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +extern cvar_t *cl_consoleKeys; + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +<<<<<<< HEAD +void SCR_PrecacheScreenshot(); +======= +#ifdef JK2_MODE +void SCR_PrecacheScreenshot(); +#endif +>>>>>>> 38e3edc40da09b9c20f585a3b939ff493be8bf50 + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +qboolean CL_InitCGameVM( void *gameLibrary ); +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); diff --git a/code/client/client.h.BASE.2896.h b/code/client/client.h.BASE.2896.h new file mode 100644 index 0000000000..89cf670b37 --- /dev/null +++ b/code/client/client.h.BASE.2896.h @@ -0,0 +1,417 @@ +/* +This file is part of Jedi Academy. + + Jedi Academy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Jedi Academy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Jedi Academy. If not, see . +*/ +// Copyright 2001-2013 Raven Software + +// client.h -- primary header for client +#pragma once +#ifndef __CLIENT_H__ +#define __CLIENT_H__ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +#ifndef _WIN32 +extern cvar_t *cl_consoleKeys; +#endif + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); + +#endif //__CLIENT_H__ diff --git a/code/client/client.h.BASE.7476.h b/code/client/client.h.BASE.7476.h new file mode 100644 index 0000000000..89cf670b37 --- /dev/null +++ b/code/client/client.h.BASE.7476.h @@ -0,0 +1,417 @@ +/* +This file is part of Jedi Academy. + + Jedi Academy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Jedi Academy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Jedi Academy. If not, see . +*/ +// Copyright 2001-2013 Raven Software + +// client.h -- primary header for client +#pragma once +#ifndef __CLIENT_H__ +#define __CLIENT_H__ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +#ifndef _WIN32 +extern cvar_t *cl_consoleKeys; +#endif + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); + +#endif //__CLIENT_H__ diff --git a/code/client/client.h.BASE.8932.h b/code/client/client.h.BASE.8932.h new file mode 100644 index 0000000000..89cf670b37 --- /dev/null +++ b/code/client/client.h.BASE.8932.h @@ -0,0 +1,417 @@ +/* +This file is part of Jedi Academy. + + Jedi Academy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Jedi Academy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Jedi Academy. If not, see . +*/ +// Copyright 2001-2013 Raven Software + +// client.h -- primary header for client +#pragma once +#ifndef __CLIENT_H__ +#define __CLIENT_H__ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +#ifndef _WIN32 +extern cvar_t *cl_consoleKeys; +#endif + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); + +#endif //__CLIENT_H__ diff --git a/code/client/client.h.LOCAL.2896.h b/code/client/client.h.LOCAL.2896.h new file mode 100644 index 0000000000..5e8ea6628a --- /dev/null +++ b/code/client/client.h.LOCAL.2896.h @@ -0,0 +1,419 @@ +/* +This file is part of Jedi Academy. + + Jedi Academy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Jedi Academy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Jedi Academy. If not, see . +*/ +// Copyright 2001-2013 Raven Software + +// client.h -- primary header for client +#pragma once +#ifndef __CLIENT_H__ +#define __CLIENT_H__ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +#ifndef _WIN32 +extern cvar_t *cl_consoleKeys; +#endif + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +void SCR_PrecacheScreenshot(); + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); + +#endif //__CLIENT_H__ diff --git a/code/client/client.h.LOCAL.7476.h b/code/client/client.h.LOCAL.7476.h new file mode 100644 index 0000000000..5e8ea6628a --- /dev/null +++ b/code/client/client.h.LOCAL.7476.h @@ -0,0 +1,419 @@ +/* +This file is part of Jedi Academy. + + Jedi Academy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Jedi Academy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Jedi Academy. If not, see . +*/ +// Copyright 2001-2013 Raven Software + +// client.h -- primary header for client +#pragma once +#ifndef __CLIENT_H__ +#define __CLIENT_H__ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +#ifndef _WIN32 +extern cvar_t *cl_consoleKeys; +#endif + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +void SCR_PrecacheScreenshot(); + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); + +#endif //__CLIENT_H__ diff --git a/code/client/client.h.LOCAL.8932.h b/code/client/client.h.LOCAL.8932.h new file mode 100644 index 0000000000..5e8ea6628a --- /dev/null +++ b/code/client/client.h.LOCAL.8932.h @@ -0,0 +1,419 @@ +/* +This file is part of Jedi Academy. + + Jedi Academy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Jedi Academy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Jedi Academy. If not, see . +*/ +// Copyright 2001-2013 Raven Software + +// client.h -- primary header for client +#pragma once +#ifndef __CLIENT_H__ +#define __CLIENT_H__ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +#ifndef _WIN32 +extern cvar_t *cl_consoleKeys; +#endif + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +void SCR_PrecacheScreenshot(); + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); + +#endif //__CLIENT_H__ diff --git a/code/client/client.h.REMOTE.2896.h b/code/client/client.h.REMOTE.2896.h new file mode 100644 index 0000000000..d50be2ddcb --- /dev/null +++ b/code/client/client.h.REMOTE.2896.h @@ -0,0 +1,421 @@ +/* +=========================================================================== +Copyright (C) 1999 - 2005, Id Software, Inc. +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +// client.h -- primary header for client +#pragma once + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +extern cvar_t *cl_consoleKeys; + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +#ifdef JK2_MODE +void SCR_PrecacheScreenshot(); +#endif + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +qboolean CL_InitCGameVM( void *gameLibrary ); +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); diff --git a/code/client/client.h.REMOTE.7476.h b/code/client/client.h.REMOTE.7476.h new file mode 100644 index 0000000000..d50be2ddcb --- /dev/null +++ b/code/client/client.h.REMOTE.7476.h @@ -0,0 +1,421 @@ +/* +=========================================================================== +Copyright (C) 1999 - 2005, Id Software, Inc. +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +// client.h -- primary header for client +#pragma once + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +extern cvar_t *cl_consoleKeys; + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +#ifdef JK2_MODE +void SCR_PrecacheScreenshot(); +#endif + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +qboolean CL_InitCGameVM( void *gameLibrary ); +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); diff --git a/code/client/client.h.REMOTE.8932.h b/code/client/client.h.REMOTE.8932.h new file mode 100644 index 0000000000..d50be2ddcb --- /dev/null +++ b/code/client/client.h.REMOTE.8932.h @@ -0,0 +1,421 @@ +/* +=========================================================================== +Copyright (C) 1999 - 2005, Id Software, Inc. +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +// client.h -- primary header for client +#pragma once + +#include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" +#include "../rd-common/tr_public.h" +#include "keys.h" +#include "snd_public.h" +#include "../cgame/cg_public.h" + +// snapshots are a view of the server at a given time +typedef struct { + qboolean valid; // cleared if delta parsing was invalid + int snapFlags; // rate delayed and dropped commands + + int serverTime; // server time the message is valid for (in msec) + + int messageNum; // copied from netchan->incoming_sequence + int deltaNum; // messageNum the delta is from + int ping; // time from when cmdNum-1 was sent to time packet was reeceived + byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits + + int cmdNum; // the next cmdNum the server is expecting + playerState_t ps; // complete information about the current player at this time + + int numEntities; // all of the entities that need to be presented + int parseEntitiesNum; // at the time of this snapshot + + int serverCommandNum; // execute all commands up to this before + // making the snapshot current +} clSnapshot_t; + + + +/* +============================================================================= + +the clientActive_t structure is wiped completely at every +new gamestate_t, potentially several times during an established connection + +============================================================================= +*/ + +// the parseEntities array must be large enough to hold PACKET_BACKUP frames of +// entities, so that when a delta compressed message arives from the server +// it can be un-deltad from the original +#define MAX_PARSE_ENTITIES 512 + +extern int g_console_field_width; + +typedef struct { + int timeoutcount; + + clSnapshot_t frame; // latest received from server + + int serverTime; + int oldServerTime; // to prevent time from flowing bakcwards + int oldFrameServerTime; // to check tournament restarts + int serverTimeDelta; // cl.serverTime = cls.realtime + cl.serverTimeDelta + // this value changes as net lag varies + qboolean extrapolatedSnapshot; // set if any cgame frame has been forced to extrapolate + // cleared when CL_AdjustTimeDelta looks at it + qboolean newSnapshots; // set on parse, cleared when CL_AdjustTimeDelta looks at it + + gameState_t gameState; // configstrings + char mapname[MAX_QPATH]; // extracted from CS_SERVERINFO + + int parseEntitiesNum; // index (not anded off) into cl_parse_entities[] + + int mouseDx[2], mouseDy[2]; // added to by mouse events + int mouseIndex; + int joystickAxis[MAX_JOYSTICK_AXIS]; // set by joystick events + + int cgameUserCmdValue; // current weapon to add to usercmd_t + float cgameSensitivity; + + // cmds[cmdNumber] is the predicted command, [cmdNumber-1] is the last + // properly generated command + usercmd_t cmds[CMD_BACKUP]; // each mesage will send several old cmds + int cmdNumber; // incremented each frame, because multiple + // frames may need to be packed into a single packet + + int packetTime[PACKET_BACKUP]; // cls.realtime sent, for calculating pings + int packetCmdNumber[PACKET_BACKUP]; // cmdNumber when packet was sent + + // the client maintains its own idea of view angles, which are + // sent to the server each frame. It is cleared to 0 upon entering each level. + // the server sends a delta each frame which is added to the locally + // tracked view angles to account for standing on rotating objects, + // and teleport direction changes + vec3_t viewangles; + + // these are just parsed out of the configstrings for convenience + int serverId; + + // non-gameserver infornamtion + int cinematictime; // cls.realtime for first cinematic frame (FIXME: NO LONGER USED!, but I wasn't sure if I could remove it because of struct sizes assumed elsewhere? -Ste) + + // big stuff at end of structure so most offsets are 15 bits or less + clSnapshot_t frames[PACKET_BACKUP]; + + entityState_t parseEntities[MAX_PARSE_ENTITIES]; + + //DJC added - making force powers in single player work like those in + //multiplayer. This makes hot swapping code more portable. + qboolean gcmdSendValue; + byte gcmdValue; +} clientActive_t; + +extern clientActive_t cl; + +/* +============================================================================= + +the clientConnection_t structure is wiped when disconnecting from a server, +either to go to a full screen console, or connect to a different server + +A connection can be to either a server through the network layer, +or just a streaming cinematic. + +============================================================================= +*/ + + +typedef struct { + int lastPacketSentTime; // for retransmits + int lastPacketTime; + char servername[MAX_OSPATH]; // name of server from original connect + netadr_t serverAddress; + int connectTime; // for connection retransmits + int connectPacketCount; // for display on connection dialog + + int challenge; // from the server to use for connecting + + int reliableSequence; + int reliableAcknowledge; + char *reliableCommands[MAX_RELIABLE_COMMANDS]; + + // reliable messages received from server + int serverCommandSequence; + char *serverCommands[MAX_RELIABLE_COMMANDS]; + + // big stuff at end of structure so most offsets are 15 bits or less + netchan_t netchan; +} clientConnection_t; + +extern clientConnection_t clc; + +/* +================================================================== + +the clientStatic_t structure is never wiped, and is used even when +no client connection is active at all + +================================================================== +*/ + +typedef struct { + connstate_t state; // connection status + + char servername[MAX_OSPATH]; // name of server from original connect (used by reconnect) + + // when the server clears the hunk, all of these must be restarted + qboolean rendererStarted; + qboolean soundStarted; + qboolean soundRegistered; + qboolean uiStarted; + qboolean cgameStarted; + + int framecount; + int frametime; // msec since last frame + float frametimeFraction; // fraction of a msec since last frame + + int realtime; // ignores pause + float realtimeFraction; // fraction of a msec accumulated + int realFrametime; // ignoring pause, so console always works + + // update server info + char updateInfoString[MAX_INFO_STRING]; + + // rendering info + glconfig_t glconfig; + qhandle_t charSetShader; + qhandle_t whiteShader; + qhandle_t consoleShader; +} clientStatic_t; + +#define CON_TEXTSIZE 0x30000 //was 32768 +#define NUM_CON_TIMES 4 + +typedef struct { + qboolean initialized; + + short text[CON_TEXTSIZE]; + int current; // line where next message will be printed + int x; // offset in current line for next print + int display; // bottom of console displays this line + + int linewidth; // characters across screen + int totallines; // total lines in console scrollback + + float xadjust; // for wide aspect screens + float yadjust; + + float displayFrac; // aproaches finalFrac at scr_conspeed + float finalFrac; // 0.0 to 1.0 lines of console to display + + int vislines; // in scanlines + + int times[NUM_CON_TIMES]; // cls.realtime time the line was generated + // for transparent notify lines + vec4_t color; +} console_t; + +extern clientStatic_t cls; + +//============================================================================= + +extern refexport_t re; // interface to refresh .dll + + +// +// cvars +// +extern cvar_t *cl_nodelta; +extern cvar_t *cl_debugMove; +extern cvar_t *cl_noprint; +extern cvar_t *cl_timegraph; +extern cvar_t *cl_packetdup; +extern cvar_t *cl_shownet; +extern cvar_t *cl_timeNudge; +extern cvar_t *cl_showTimeDelta; + +extern cvar_t *cl_yawspeed; +extern cvar_t *cl_pitchspeed; +extern cvar_t *cl_run; +extern cvar_t *cl_anglespeedkey; + +extern cvar_t *cl_sensitivity; +extern cvar_t *cl_freelook; + +extern cvar_t *cl_mouseAccel; +extern cvar_t *cl_showMouseRate; + +extern cvar_t *cl_inGameVideo; + +extern cvar_t *m_pitch; +extern cvar_t *m_yaw; +extern cvar_t *m_forward; +extern cvar_t *m_side; +extern cvar_t *m_filter; + +extern cvar_t *cl_activeAction; + +extern cvar_t *cl_consoleKeys; + +//================================================= + +// +// cl_main +// + +void CL_Init (void); + +void CL_AddReliableCommand( const char *cmd ); + +void CL_Disconnect_f (void); +void CL_Vid_Restart_f( void ); +void CL_Snd_Restart_f (void); + +qboolean CL_CheckPaused(void); + +// +// cl_input +// +typedef struct { + int down[2]; // key nums holding it down + unsigned downtime; // msec timestamp + unsigned msec; // msec down this frame if both a down and up happened + qboolean active; // current state + qboolean wasPressed; // set when down, not cleared when up +} kbutton_t; + +extern kbutton_t in_mlook, in_klook; +extern kbutton_t in_strafe; +extern kbutton_t in_speed; + +void CL_InitInput (void); +void CL_SendCmd (void); +void CL_ClearState (void); +void CL_ReadPackets (void); +void CL_UpdateHotSwap(void); +bool CL_ExtendSelectTime(void); + +void CL_WritePacket( void ); +void IN_CenterView (void); + +float CL_KeyState (kbutton_t *key); +const char *Key_KeynumToString( int keynum/*, qboolean bTranslate*/ ); //note: translate is only called for menu display not configs + +// +// cl_parse.c +// +extern int cl_connectedToCheatServer; + +void CL_SystemInfoChanged( void ); +void CL_ParseServerMessage( msg_t *msg ); + +//==================================================================== + +// +// console +// +void Con_DrawCharacter (int cx, int line, int num); + +void Con_CheckResize (void); +void Con_Init (void); +void Con_Clear_f (void); +void Con_ToggleConsole_f (void); +void Con_DrawNotify (void); +void Con_ClearNotify (void); +void Con_RunConsole (void); +void Con_DrawConsole (void); +void Con_PageUp( void ); +void Con_PageDown( void ); +void Con_Top( void ); +void Con_Bottom( void ); +void Con_Close( void ); + + +// +// cl_scrn.c +// +void SCR_Init (void); +void SCR_UpdateScreen (void); + +void SCR_DebugGraph (float value, int color); + +int SCR_GetBigStringWidth( const char *str ); // returns in virtual 640x480 coordinates + +void SCR_FillRect( float x, float y, float width, float height, + const float *color ); +void SCR_DrawPic( float x, float y, float width, float height, qhandle_t hShader ); +void SCR_DrawNamedPic( float x, float y, float width, float height, const char *picname ); + +void SCR_DrawBigString( int x, int y, const char *s, float alpha, qboolean noColorEscape ); // draws a string with embedded color control characters with fade +void SCR_DrawBigStringColor( int x, int y, const char *s, vec4_t color, qboolean noColorEscape ); // ignores embedded color control characters +void SCR_DrawSmallStringExt( int x, int y, const char *string, float *setColor, qboolean forceColor, qboolean noColorEscape ); +void SCR_DrawBigChar( int x, int y, int ch ); +void SCR_DrawSmallChar( int x, int y, int ch ); + +#ifdef JK2_MODE +void SCR_PrecacheScreenshot(); +#endif + +// +// cl_cin.c +// +void CL_PlayCinematic_f( void ); +void CL_PlayInGameCinematic_f(void); +qboolean CL_CheckPendingCinematic(void); +qboolean CL_IsRunningInGameCinematic(void); +qboolean CL_InGameCinematicOnStandBy(void); +void SCR_DrawCinematic (void); +void SCR_RunCinematic (void); +void SCR_StopCinematic( qboolean bAllowRefusal = qfalse ); + +int CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */); +e_status CIN_StopCinematic(int handle); +e_status CIN_RunCinematic (int handle); +void CIN_DrawCinematic (int handle); +void CIN_SetExtents (int handle, int x, int y, int w, int h); +void CIN_SetLooping (int handle, qboolean loop); +void CIN_UploadCinematic(int handle); +void CIN_CloseAllVideos(void); + +// +// cl_cgame.c +// +qboolean CL_InitCGameVM( void *gameLibrary ); +void CL_InitCGame( void ); +void CL_ShutdownCGame( void ); +qboolean CL_GameCommand( void ); +void CL_CGameRendering( stereoFrame_t stereo ); +void CL_SetCGameTime( void ); +void CL_FirstSnapshot( void ); + + +// +// cl_ui.c +// +void CL_InitUI( void ); +void CL_ShutdownUI( void ); +void CL_GenericMenu_f(void); +void CL_DataPad_f(void); +void CL_EndScreenDissolve_f(void); +int Key_GetCatcher( void ); +void Key_SetCatcher( int catcher ); diff --git a/code/client/snd_ambient.cpp b/code/client/snd_ambient.cpp index 4c56abbfae..66143fbe75 100644 --- a/code/client/snd_ambient.cpp +++ b/code/client/snd_ambient.cpp @@ -112,7 +112,7 @@ void CSetGroup::Free( void ) { std::vector::iterator ai; - for ( ai = m_ambientSets->begin(); ai != m_ambientSets->end(); ai++ ) + for ( ai = m_ambientSets->begin(); ai != m_ambientSets->end(); ++ai ) { Z_Free ( (*ai) ); } diff --git a/code/client/snd_dma.cpp b/code/client/snd_dma.cpp index 12dac7087c..48ae0057a9 100644 --- a/code/client/snd_dma.cpp +++ b/code/client/snd_dma.cpp @@ -201,8 +201,8 @@ typedef struct soundChannel_t entchan; // For Open AL - bool bProcessed; - bool bRelative; + qboolean bProcessed; + qboolean bRelative; } loopSound_t; #define MAX_LOOP_SOUNDS 64 @@ -1859,7 +1859,7 @@ void S_ClearLoopingSounds( void ) if (s_UseOpenAL) { for (int i = 0; i < MAX_LOOP_SOUNDS; i++) - loopSounds[i].bProcessed = false; + loopSounds[i].bProcessed = qfalse; } #endif numLoopSounds = 0; @@ -1892,7 +1892,7 @@ void S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocit } sfx = &s_knownSfx[ sfxHandle ]; - if (sfx->bInMemory == qfalse){ + if (!sfx->bInMemory){ S_memoryLoad(sfx); } SND_TouchSFX(sfx); @@ -3282,7 +3282,7 @@ void UpdateLoopingSounds() alSourcefv(s_channels[i].alSource, AL_POSITION, pos); alSourcei(s_channels[i].alSource, AL_SOURCE_RELATIVE, AL_TRUE); - loop->bRelative = true; + loop->bRelative = qtrue; } // Make sure Gain is set correctly @@ -3293,13 +3293,13 @@ void UpdateLoopingSounds() } ch->bProcessed = true; - loop->bProcessed = true; + loop->bProcessed = qtrue; } else if ((loop->bProcessed == false) && (ch->thesfx == loop->sfx) && (!memcmp(ch->origin, loop->origin, sizeof(ch->origin)))) { // Match ! ch->bProcessed = true; - loop->bProcessed = true; + loop->bProcessed = qtrue; // Make sure Gain is set correctly if (ch->master_vol != loop->volume) @@ -3344,7 +3344,7 @@ void UpdateLoopingSounds() UpdateEAXBuffer(ch); ch->bProcessed = true; - loop->bProcessed = true; + loop->bProcessed = qtrue; break; } } @@ -3373,7 +3373,7 @@ void UpdateLoopingSounds() { loop = &loopSounds[j]; - if (loop->bProcessed == false) + if (!loop->bProcessed) { ch = S_PickChannel(0,0); @@ -3388,7 +3388,7 @@ void UpdateLoopingSounds() && (loop->origin[2] == listener_pos[1]) ) { // Assume that this sound is head relative - loop->bRelative = true; + loop->bRelative = qtrue; ch->origin[0] = 0.f; ch->origin[1] = 0.f; ch->origin[2] = 0.f; @@ -3398,7 +3398,7 @@ void UpdateLoopingSounds() ch->origin[0] = loop->origin[0]; ch->origin[1] = loop->origin[1]; ch->origin[2] = loop->origin[2]; - loop->bRelative = false; + loop->bRelative = qfalse; } ch->fixed_origin = loop->bRelative; @@ -3858,7 +3858,13 @@ void S_SoundList_f( void ) { for (sfx=s_knownSfx, i=0 ; ibDefaultSound && !sfx->pMP3StreamHeader && sfx->pSoundData && (Z_Size(sfx->pSoundData) > cv_MP3overhead->integer); + qboolean bMP3DumpOverride = (qboolean)( + bShouldBeMP3 && + cv_MP3overhead && + !sfx->bDefaultSound && + !sfx->pMP3StreamHeader && + sfx->pSoundData && + (Z_Size(sfx->pSoundData) > cv_MP3overhead->integer)); if (bMP3DumpOverride || (!bShouldBeMP3 && (!bWavOnly || sfx->eSoundCompressionMethod == ct_16))) { @@ -4527,8 +4533,12 @@ void S_StartBackgroundTrack( const char *intro, const char *loop, qboolean bCall loop = intro; } - Q_strncpyz(gsIntroMusic,intro, sizeof(gsIntroMusic)); - Q_strncpyz(gsLoopMusic, loop, sizeof(gsLoopMusic)); + if ( intro != gsIntroMusic ) { + Q_strncpyz( gsIntroMusic, intro, sizeof(gsIntroMusic) ); + } + if ( loop != gsLoopMusic ) { + Q_strncpyz( gsLoopMusic, loop, sizeof(gsLoopMusic) ); + } char sNameIntro[MAX_QPATH]; char sNameLoop [MAX_QPATH]; @@ -5011,7 +5021,7 @@ static void S_UpdateBackgroundTrack( void ) // standard / non-dynamic one-track music... // const char *psCommand = S_Music_GetRequestedState(); // special check just for "silence" case... - qboolean bShouldBeSilent = (psCommand && !Q_stricmp(psCommand,"silence")); + qboolean bShouldBeSilent = (qboolean)(psCommand && !Q_stricmp(psCommand,"silence")); float fDesiredVolume = bShouldBeSilent ? 0.0f : s_musicVolume->value; // // internal to this code is a volume-smoother... @@ -5269,11 +5279,11 @@ qboolean SND_RegisterAudio_LevelLoadEnd(qboolean bDeleteEverythingNotUsedThisLev if (bDeleteEverythingNotUsedThisLevel) { - bDeleteThis = (sfx->iLastLevelUsedOn != re.RegisterMedia_GetLevel()); + bDeleteThis = (qboolean)(sfx->iLastLevelUsedOn != re.RegisterMedia_GetLevel()); } else { - bDeleteThis = (sfx->iLastLevelUsedOn < re.RegisterMedia_GetLevel()); + bDeleteThis = (qboolean)(sfx->iLastLevelUsedOn < re.RegisterMedia_GetLevel()); } if (bDeleteThis) diff --git a/code/client/snd_mem.cpp b/code/client/snd_mem.cpp index d31fafc1e9..821c8424b8 100644 --- a/code/client/snd_mem.cpp +++ b/code/client/snd_mem.cpp @@ -794,7 +794,7 @@ static qboolean S_LoadSound_Actual( sfx_t *sfx ) { // Create space for lipsync data (4 lip sync values per streaming AL buffer) if (strstr(sfx->sSoundName, "chars") ) - sfx->lipSyncData = (char *)Z_Malloc(16, TAG_SND_RAWDATA, false); + sfx->lipSyncData = (char *)Z_Malloc(16, TAG_SND_RAWDATA, qfalse); else sfx->lipSyncData = NULL; } @@ -853,7 +853,7 @@ static qboolean S_LoadSound_Actual( sfx_t *sfx ) { if (strstr(sfx->sSoundName, "chars")) { - sfx->lipSyncData = (char *)Z_Malloc((sfx->iSoundLengthInSamples / 1000) + 1, TAG_SND_RAWDATA, false); + sfx->lipSyncData = (char *)Z_Malloc((sfx->iSoundLengthInSamples / 1000) + 1, TAG_SND_RAWDATA, qfalse); S_PreProcessLipSync(sfx); } else @@ -926,7 +926,7 @@ static qboolean S_LoadSound_Actual( sfx_t *sfx ) { if ((strstr(sfx->sSoundName, "chars")) || (strstr(sfx->sSoundName, "CHARS"))) { - sfx->lipSyncData = (char *)Z_Malloc((sfx->iSoundLengthInSamples / 1000) + 1, TAG_SND_RAWDATA, false); + sfx->lipSyncData = (char *)Z_Malloc((sfx->iSoundLengthInSamples / 1000) + 1, TAG_SND_RAWDATA, qfalse); S_PreProcessLipSync(sfx); } else diff --git a/code/client/snd_music.cpp b/code/client/snd_music.cpp index 5e982ad1d8..1d9596d387 100644 --- a/code/client/snd_music.cpp +++ b/code/client/snd_music.cpp @@ -30,6 +30,7 @@ along with this program; if not, see . #include "../qcommon/sstring.h" #include #include +#include #include "snd_local.h" #include "cl_mp3.h" @@ -41,29 +42,25 @@ along with this program; if not, see . extern qboolean S_FileExists( const char *psFilename ); -#define sKEY_MUSICFILES "musicfiles" -#define sKEY_ENTRY "entry" -#define sKEY_EXIT "exit" -#define sKEY_MARKER "marker" -#define sKEY_TIME "time" -#define sKEY_NEXTFILE "nextfile" -#define sKEY_NEXTMARK "nextmark" -#define sKEY_LEVELMUSIC "levelmusic" -#define sKEY_EXPLORE "explore" -#define sKEY_ACTION "action" -#define sKEY_BOSS "boss" -#define sKEY_DEATH "death" -#define sKEY_USES "uses" -#define sKEY_USEBOSS "useboss" +#define sKEY_MUSICFILES CSTRING_VIEW( "musicfiles" ) +#define sKEY_ENTRY CSTRING_VIEW( "entry" ) +#define sKEY_EXIT CSTRING_VIEW( "exit" ) +#define sKEY_MARKER CSTRING_VIEW( "marker" ) +#define sKEY_TIME CSTRING_VIEW( "time" ) +#define sKEY_NEXTFILE CSTRING_VIEW( "nextfile" ) +#define sKEY_NEXTMARK CSTRING_VIEW( "nextmark" ) +#define sKEY_LEVELMUSIC CSTRING_VIEW( "levelmusic" ) +#define sKEY_EXPLORE CSTRING_VIEW( "explore" ) +#define sKEY_ACTION CSTRING_VIEW( "action" ) +#define sKEY_BOSS CSTRING_VIEW( "boss" ) +#define sKEY_DEATH CSTRING_VIEW( "death" ) +#define sKEY_USES CSTRING_VIEW( "uses" ) +#define sKEY_USEBOSS CSTRING_VIEW( "useboss" ) #define sKEY_PLACEHOLDER "placeholder" // ignore these #define sFILENAME_DMS "ext_data/dms.dat" - -#define MUSIC_PARSE_ERROR(_string) Music_Parse_Error(_string) // only use during parse, not run-time use, and bear in mid that data is zapped after error message, so exit any loops immediately -#define MUSIC_PARSE_WARNING(_string) Music_Parse_Warning(_string) - typedef struct { sstring_t sNextFile; @@ -113,22 +110,49 @@ void Music_Free(void) MusicData = NULL; } +namespace detail +{ + static void build_string( std::ostream& stream ) + { + } + + template< typename T, typename... Tail > + static void build_string( std::ostream& stream, const T& head, Tail... tail ) + { + stream << head; + build_string( stream, tail... ); + } +} + +template< typename... Tail > +static std::string build_string( Tail... tail ) +{ + std::ostringstream os; + detail::build_string( os, tail... ); + return os.str(); +} + // some sort of error in the music data... +// only use during parse, not run-time use, and bear in mid that data is zapped after error message, so exit any loops immediately // -static void Music_Parse_Error(const char *psError) +static void Music_Parse_Error( gsl::czstring filename, const std::string& error ) { - Com_Printf(S_COLOR_RED "Error parsing music data ( in \"%s\" ):\n%s\n",sFILENAME_DMS,psError); + std::string message = build_string( + S_COLOR_RED "Error parsing music data (in \"", filename, "\"):\n", + error , "\n" + ); + Com_Printf( "%s", message.c_str() ); MusicData->clear(); } // something to just mention if interested... // -static void Music_Parse_Warning(const char *psError) +static void Music_Parse_Warning( const std::string& error ) { extern cvar_t *s_debugdynamic; - if (s_debugdynamic && s_debugdynamic->integer) + if( s_debugdynamic && s_debugdynamic->integer ) { - Com_Printf(S_COLOR_YELLOW "%s", psError); + Com_Printf( S_COLOR_YELLOW "%s", error.c_str() ); } } @@ -180,225 +204,159 @@ const char *Music_BaseStateToString( MusicState_e eMusicState, qboolean bDebugPr return NULL; } -static qboolean Music_ParseMusic(CGenericParser2 &Parser, MusicData_t *MusicData, CGPGroup *pgMusicFiles, const char *psMusicName, const char *psMusicNameKey, MusicState_e eMusicState) +static qboolean Music_ParseMusic( gsl::czstring filename, const CGenericParser2& Parser, MusicData_t* MusicData, const CGPGroup& pgMusicFiles, const gsl::cstring_view& psMusicName, const gsl::cstring_view& psMusicNameKey, MusicState_e eMusicState ) { - qboolean bReturn = qfalse; + bool bReturn = false; MusicFile_t MusicFile; - CGPGroup *pgMusicFile = pgMusicFiles->FindSubGroup(psMusicName); - if (pgMusicFile) + const CGPGroup* const pgMusicFile = pgMusicFiles.FindSubGroup( psMusicName ); + if( pgMusicFile ) { // read subgroups... // - qboolean bEntryFound = qfalse; - qboolean bExitFound = qfalse; + bool bEntryFound = false; + bool bExitFound = false; // // (read entry points first, so I can check exit points aren't too close in time) // - CGPGroup *pEntryGroup = pgMusicFile->FindSubGroup(sKEY_ENTRY); - if (pEntryGroup) + const CGPGroup* pEntryGroup = pgMusicFile->FindSubGroup( sKEY_ENTRY ); + if( pEntryGroup ) { // read entry points... // - for (CGPValue *pValue = pEntryGroup->GetPairs(); pValue; pValue = pValue->GetNext()) + for( auto& prop : pEntryGroup->GetProperties() ) { - const char *psKey = pValue->GetName(); - const char *psValue = pValue->GetTopValue(); - - //if (!strncmp(psKey,sKEY_MARKER,strlen(sKEY_MARKER))) // for now, assume anything is a marker + //if( Q::substr( prop.GetName(), 0, sKEY_MARKER.size() ) == sKEY_MARKER ) // for now, assume anything is a marker { - MusicFile.MusicEntryTimes[psKey] = atof(psValue); - bEntryFound = qtrue; // harmless to keep setting + MusicFile.MusicEntryTimes[ prop.GetName() ] = Q::svtoi( prop.GetTopValue() ); + bEntryFound = true; } } } - for (CGPGroup *pGroup = pgMusicFile->GetSubGroups(); pGroup; pGroup = pGroup->GetNext()) + for( auto& group : pgMusicFile->GetSubGroups() ) { - const char *psGroupName = pGroup->GetName(); + auto& groupName = group.GetName(); - if (!strcmp(psGroupName,sKEY_ENTRY)) + if( groupName == sKEY_ENTRY ) { // skip entry points, I've already read them in above // } - else - if (!strcmp(psGroupName,sKEY_EXIT)) + else if( groupName == sKEY_EXIT ) { int iThisExitPointIndex = MusicFile.MusicExitPoints.size(); // must eval this first, so unaffected by push_back etc // // read this set of exit points... // MusicExitPoint_t MusicExitPoint; - for (CGPValue *pValue = pGroup->GetPairs(); pValue; pValue = pValue->GetNext()) + for( auto& prop : group.GetProperties() ) { - const char *psKey = pValue->GetName(); - const char *psValue = pValue->GetTopValue(); + auto& key = prop.GetName(); + auto& value = prop.GetTopValue(); - if (!strcmp(psKey,sKEY_NEXTFILE)) + if( key == sKEY_NEXTFILE ) { - MusicExitPoint.sNextFile = psValue; - bExitFound = qtrue; // harmless to keep setting + MusicExitPoint.sNextFile = value; + bExitFound = true; // harmless to keep setting } - else - if (!strcmp(psKey,sKEY_NEXTMARK)) + else if( key == sKEY_NEXTMARK ) { - MusicExitPoint.sNextMark = psValue; + MusicExitPoint.sNextMark = value; } - else - if (!strncmp(psKey,sKEY_TIME,strlen(sKEY_TIME))) + else if( Q::substr( key, 0, sKEY_TIME.size() ) == sKEY_TIME ) { MusicExitTime_t MusicExitTime; - MusicExitTime.fTime = atof(psValue); - MusicExitTime.iExitPoint= iThisExitPointIndex; + MusicExitTime.fTime = Q::svtof( value ); + MusicExitTime.iExitPoint = iThisExitPointIndex; // new check, don't keep this this exit point if it's within 1.5 seconds either way of an entry point... // - qboolean bTooCloseToEntryPoint = qfalse; - for (MusicEntryTimes_t::iterator itEntryTimes = MusicFile.MusicEntryTimes.begin(); itEntryTimes != MusicFile.MusicEntryTimes.end(); ++itEntryTimes) + bool bTooCloseToEntryPoint = false; + for( auto& item : MusicFile.MusicEntryTimes ) { - float fThisEntryTime = (*itEntryTimes).second; + float fThisEntryTime = item.second; - if (Q_fabs(fThisEntryTime - MusicExitTime.fTime) < 1.5f) + if( Q_fabs( fThisEntryTime - MusicExitTime.fTime ) < 1.5f ) { -// bTooCloseToEntryPoint = qtrue; // not sure about this, ignore for now + // bTooCloseToEntryPoint = true; // not sure about this, ignore for now break; } } - if (!bTooCloseToEntryPoint) + if( !bTooCloseToEntryPoint ) { - MusicFile.MusicExitTimes.push_back(MusicExitTime); + MusicFile.MusicExitTimes.push_back( MusicExitTime ); } } } - MusicFile.MusicExitPoints.push_back(MusicExitPoint); + MusicFile.MusicExitPoints.push_back( MusicExitPoint ); int iNumExitPoints = MusicFile.MusicExitPoints.size(); // error checking... // - switch (eMusicState) + switch( eMusicState ) { - case eBGRNDTRACK_EXPLORE: - if (iNumExitPoints > iMAX_EXPLORE_TRANSITIONS) - { - MUSIC_PARSE_ERROR( va("\"%s\" has > %d %s transitions defined!\n",psMusicName,iMAX_EXPLORE_TRANSITIONS,psMusicNameKey) ); - return qfalse; - } - break; + case eBGRNDTRACK_EXPLORE: + if( iNumExitPoints > iMAX_EXPLORE_TRANSITIONS ) + { + Music_Parse_Error( filename, build_string( "\"", psMusicName, "\" has > ", iMAX_EXPLORE_TRANSITIONS, " ", psMusicNameKey, " transitions defined!\n" ) ); + return qfalse; + } + break; - case eBGRNDTRACK_ACTION: - if (iNumExitPoints > iMAX_ACTION_TRANSITIONS) - { - MUSIC_PARSE_ERROR( va("\"%s\" has > %d %s transitions defined!\n",psMusicName,iMAX_ACTION_TRANSITIONS,psMusicNameKey) ); - return qfalse; - } - break; + case eBGRNDTRACK_ACTION: + if( iNumExitPoints > iMAX_ACTION_TRANSITIONS ) + { + Music_Parse_Error( filename, build_string( "\"", psMusicName, "\" has > ", iMAX_ACTION_TRANSITIONS, " ", psMusicNameKey, " transitions defined!\n" ) ); + return qfalse; + } + break; - case eBGRNDTRACK_BOSS: - case eBGRNDTRACK_DEATH: + case eBGRNDTRACK_BOSS: + case eBGRNDTRACK_DEATH: - MUSIC_PARSE_ERROR( va("\"%s\" has %s transitions defined, this is not allowed!\n",psMusicName,psMusicNameKey) ); - break; - default: - break; + Music_Parse_Error( filename, build_string( "\"", psMusicName, "\" has ", psMusicNameKey, " transitions defined, this is not allowed!\n" ) ); + return qfalse; + default: + break; } } } // for now, assume everything was ok unless some obvious things are missing... // - bReturn = qtrue; + bReturn = true; - if (eMusicState != eBGRNDTRACK_BOSS && eMusicState != eBGRNDTRACK_DEATH) // boss & death pieces can omit entry/exit stuff + // boss & death pieces can omit entry/exit stuff + if( eMusicState != eBGRNDTRACK_BOSS && eMusicState != eBGRNDTRACK_DEATH ) { - if (!bEntryFound) + if( !bEntryFound ) { - MUSIC_PARSE_ERROR(va("Unable to find subgroup \"%s\" in group \"%s\"\n",sKEY_ENTRY,psMusicName)); - bReturn = qfalse; + Music_Parse_Error( filename, build_string( "Unable to find subgroup \"", sKEY_ENTRY, "\" in group \"", psMusicName, "\"\n" ) ); + bReturn = false; } - if (!bExitFound) + if( !bExitFound ) { - MUSIC_PARSE_ERROR(va("Unable to find subgroup \"%s\" in group \"%s\"\n",sKEY_EXIT,psMusicName)); - bReturn = qfalse; + Music_Parse_Error( filename, build_string( "Unable to find subgroup \"", sKEY_EXIT, "\" in group \"", psMusicName, "\"\n" ) ); + bReturn = false; } } } else { - MUSIC_PARSE_ERROR(va("Unable to find musicfiles entry \"%s\"\n",psMusicName)); + Music_Parse_Error( filename, build_string( "Unable to find musicfiles entry \"", psMusicName, "\"\n" ) ); } - if (bReturn) + if( bReturn ) { - MusicFile.sFileNameBase = psMusicName; - (*MusicData)[ psMusicNameKey ] = MusicFile; - } - - return bReturn; -} - - - - -// I only need this because GP2 can't cope with trailing whitespace (for !@#$%^'s sake!!!!)... -// -// (output buffer will always be just '\n' seperated, regardless of possible "\r\n" pairs) -// -// (remember to Z_Free() the returned char * when done with it!!!) -// -static char *StripTrailingWhiteSpaceOnEveryLine(char *pText) -{ - std::string strNewText; - - while (*pText) - { - char sOneLine[1024]; // BTO: was 16k - - // find end of line... - // - size_t pos = 0; - while (pText[pos] != '\0' && pText[pos] != '\r' && (pos < sizeof(sOneLine)-1)) - { - pos++; - } - - strncpy(sOneLine, pText, pos); - sOneLine[pos]='\0'; - pText += pos; - while (*pText == '\n' || *pText == '\r') pText++; - - // trim trailing... - // - qboolean bTrimmed = qfalse; - do - { - bTrimmed = qfalse; - int iStrLen = strlen(sOneLine); - - if (iStrLen) - { - if (sOneLine[iStrLen-1] == '\t' || sOneLine[iStrLen-1] == ' ') - { - sOneLine[iStrLen-1] = '\0'; - bTrimmed = qtrue; - } - } - } - while (bTrimmed); - - strNewText += sOneLine; - strNewText += "\n"; + MusicFile.sFileNameBase = psMusicName; + ( *MusicData )[ psMusicNameKey ] = MusicFile; } - char *pNewText = (char *) Z_Malloc( strlen(strNewText.c_str())+1, - TAG_TEMP_WORKSPACE, - qfalse); - strcpy(pNewText, strNewText.c_str()); - return pNewText; + return (qboolean)bReturn; } - // called from SV_SpawnServer, but before map load and music start etc. // // This just initialises the Lucas music structs so the background music player can interrogate them... @@ -409,7 +367,7 @@ void Music_SetLevelName(const char *psLevelName) gsLevelNameFromServer = psLevelName; } -static qboolean Music_ParseLeveldata(const char *psLevelName) +static qboolean Music_ParseLeveldata( gsl::czstring psLevelName ) { qboolean bReturn = qfalse; @@ -429,6 +387,7 @@ static qboolean Music_ParseLeveldata(const char *psLevelName) MusicData->clear(); + // shorten level name to MAX_QPATH so sstring's assignment assertion is satisfied. char sLevelName[MAX_QPATH]; Q_strncpyz(sLevelName,psLevelName,sizeof(sLevelName)); @@ -436,192 +395,166 @@ static qboolean Music_ParseLeveldata(const char *psLevelName) gsLevelNameForCompare = sLevelName; // harmless to init here even if we fail to parse dms.dat file gsLevelNameForBossLoad = sLevelName; // harmless to init here even if we fail to parse dms.dat file - char *pText = NULL; - /*int iTotalBytesLoaded = */FS_ReadFile(sFILENAME_DMS, (void **)&pText ); - if (pText) + gsl::czstring filename = sFILENAME_DMS; + CGenericParser2 Parser; + if( !Parser.Parse( filename ) ) { - char *psStrippedText = StripTrailingWhiteSpaceOnEveryLine(pText); - CGenericParser2 Parser; - char *psDataPtr = psStrippedText; // because ptr gets advanced, so we supply a clone that GP can alter - if (Parser.Parse(&psDataPtr, true)) + Music_Parse_Error( filename, "Error using GP to parse file\n" ); + } + else + { + const CGPGroup& pFileGroup = Parser.GetBaseParseGroup(); + const CGPGroup* pgMusicFiles = pFileGroup.FindSubGroup( sKEY_MUSICFILES ); + if( !pgMusicFiles ) + { + Music_Parse_Error(filename, build_string( "Unable to find subgroup \"", sKEY_MUSICFILES ,"\"\n" ) ); + } + else { - CGPGroup *pFileGroup = Parser.GetBaseParseGroup(); - if (pFileGroup) + const CGPGroup* pgLevelMusic = pFileGroup.FindSubGroup( sKEY_LEVELMUSIC ); + + if( !pgLevelMusic ) { - CGPGroup *pgMusicFiles = pFileGroup->FindSubGroup(sKEY_MUSICFILES); - if (pgMusicFiles) + Music_Parse_Error( filename, build_string( "Unable to find subgroup \"", sKEY_MUSICFILES, "\"\n" ) ); + } + else + { + const CGPGroup *pgThisLevelMusic = nullptr; + // + // check for new USE keyword... + // + int steps = 0; + gsl::cstring_view searchName{ &sLevelName[ 0 ], &sLevelName[ strlen( &sLevelName[ 0 ] ) ] }; + + const int sanityLimit = 10; + while( !searchName.empty() && steps < sanityLimit ) { - CGPGroup *pgLevelMusic = pFileGroup->FindSubGroup(sKEY_LEVELMUSIC); + gsLevelNameForLoad = searchName; + gsLevelNameForBossLoad = gsLevelNameForLoad; + pgThisLevelMusic = pgLevelMusic->FindSubGroup( searchName ); - if (pgLevelMusic) + if( pgThisLevelMusic ) { - CGPGroup *pgThisLevelMusic = NULL; - // - // check for new USE keyword... - // - int iSanityLimit = 0; - sstring_t sSearchName(sLevelName); - - while (sSearchName.c_str()[0] && iSanityLimit < 10) + const CGPProperty* pValue = pgThisLevelMusic->FindProperty( sKEY_USES ); + if( pValue ) { - gsLevelNameForLoad = sSearchName; - gsLevelNameForBossLoad = sSearchName; - pgThisLevelMusic = pgLevelMusic->FindSubGroup(sSearchName.c_str()); - - if (pgThisLevelMusic) - { - CGPValue *pValue = pgThisLevelMusic->FindPair(sKEY_USES); - if (pValue) - { - // re-search using the USE param... - // - sSearchName = pValue->GetTopValue(); - iSanityLimit++; -// Com_DPrintf("Using \"%s\"\n",sSearchName.c_str()); - } - else - { - // no new USE keyword found... - // - sSearchName = ""; - } - } - else - { - // level entry not found... - // - break; - } + // re-search using the USE param... + // + searchName = pValue->GetTopValue(); + steps++; + // Com_DPrintf("Using \"%s\"\n",sSearchName.c_str()); } - - // now go ahead and use the final music set we've decided on... - // - if (pgThisLevelMusic && iSanityLimit < 10) + else { - // these are optional fields, so see which ones we find... + // no new USE keyword found... // - const char *psName_Explore = NULL; - const char *psName_Action = NULL; - const char *psName_Boss = NULL; - //const char *psName_Death = NULL; - // - const char *psName_UseBoss = NULL; + searchName = {}; + } + } + else + { + // level entry not found... + // + break; + } + } - for (CGPValue *pValue = pgThisLevelMusic->GetPairs(); pValue; pValue = pValue->GetNext()) - { - const char *psKey = pValue->GetName(); - const char *psValue = pValue->GetTopValue(); + // now go ahead and use the final music set we've decided on... + // + if( !pgThisLevelMusic || steps >= sanityLimit ) + { + Music_Parse_Warning( build_string( "Unable to find entry for \"", sLevelName, "\" in \"", filename, "\"\n" ) ); + } + else + { + // these are optional fields, so see which ones we find... + // + gsl::cstring_view psName_Explore; + gsl::cstring_view psName_Action; + gsl::cstring_view psName_Boss; + gsl::cstring_view psName_UseBoss; - if (Q_stricmp(psValue,sKEY_PLACEHOLDER)) // ignore "placeholder" items - { - if (!Q_stricmp(psKey,sKEY_EXPLORE)) - { - psName_Explore = psValue; - } - else - if (!Q_stricmp(psKey,sKEY_ACTION)) - { - psName_Action = psValue; - } - else - if (!Q_stricmp(psKey,sKEY_USEBOSS)) - { - psName_UseBoss = psValue; - } - else - if (!Q_stricmp(psKey,sKEY_BOSS)) - { - psName_Boss = psValue; - } - /*else - if (!Q_stricmp(psKey,sKEY_DEATH)) - { - psName_Death = psValue; - }*/ - } - } + for( auto& prop : pgThisLevelMusic->GetProperties() ) + { + auto& key = prop.GetName(); + auto& value = prop.GetTopValue(); - bReturn = qtrue; // defualt to ON now, so I can turn it off if "useboss" fails + if( Q::stricmp( value, sKEY_PLACEHOLDER ) == Q::Ordering::EQ ) + { + // ignore "placeholder" items + continue; + } - if (psName_UseBoss) - { - CGPGroup *pgLevelMusicOfBoss = pgLevelMusic->FindSubGroup(psName_UseBoss); - if (pgLevelMusicOfBoss) - { - CGPValue *pValueBoss = pgLevelMusicOfBoss->FindPair(sKEY_BOSS); - if (pValueBoss) - { - psName_Boss = pValueBoss->GetTopValue(); - gsLevelNameForBossLoad = psName_UseBoss; - } - else - { - MUSIC_PARSE_ERROR(va("'useboss' \"%s\" has no \"boss\" entry!\n",psName_UseBoss)); - bReturn = qfalse; - } - } - else - { - MUSIC_PARSE_ERROR(va("Unable to find 'useboss' entry \"%s\"\n",psName_UseBoss)); - bReturn = qfalse; - } - } + if( Q::stricmp( key, sKEY_EXPLORE ) == Q::Ordering::EQ ) + { + psName_Explore = value; + } + else if( Q::stricmp( key, sKEY_ACTION ) == Q::Ordering::EQ ) + { + psName_Action = value; + } + else if( Q::stricmp( key, sKEY_USEBOSS ) == Q::Ordering::EQ ) + { + psName_UseBoss = value; + } + else if( Q::stricmp( key, sKEY_BOSS ) == Q::Ordering::EQ ) + { + psName_Boss = value; + } + } + bReturn = qtrue; // defualt to ON now, so I can turn it off if "useboss" fails - // done this way in case I want to conditionally pass any bools depending on music type... - // - if (bReturn && psName_Explore) - { - bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Explore, sKEY_EXPLORE, eBGRNDTRACK_EXPLORE); - } - if (bReturn && psName_Action) - { - bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Action, sKEY_ACTION, eBGRNDTRACK_ACTION); - } - if (bReturn && psName_Boss) + if( !psName_UseBoss.empty() ) + { + const CGPGroup *pgLevelMusicOfBoss = pgLevelMusic->FindSubGroup( psName_UseBoss ); + if( !pgLevelMusicOfBoss ) + { + Music_Parse_Error( filename, build_string( "Unable to find 'useboss' entry \"", psName_UseBoss, "\"\n", psName_UseBoss ) ); + bReturn = qfalse; + } + else + { + const CGPProperty *pValueBoss = pgLevelMusicOfBoss->FindProperty( sKEY_BOSS ); + if( pValueBoss ) { - bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Boss, sKEY_BOSS, eBGRNDTRACK_BOSS); + Music_Parse_Error( filename, build_string( "'useboss' \"", psName_UseBoss, "\" has no \"boss\" entry!\n" ) ); + bReturn = qfalse; } - if (bReturn /*&& psName_Death*/) // LAST MINUTE HACK!!, always force in some death music!!!! + else { - //bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Death, sKEY_DEATH, eBGRNDTRACK_DEATH); - - MusicFile_t m; - m.sFileNameBase = "death_music"; - (*MusicData)[ sKEY_DEATH ] = m; + psName_Boss = pValueBoss->GetTopValue(); + gsLevelNameForBossLoad = psName_UseBoss; } } - else - { - MUSIC_PARSE_WARNING(va("Unable to find entry for \"%s\" in \"%s\"\n",sLevelName,sFILENAME_DMS)); - } } - else + + + // done this way in case I want to conditionally pass any bools depending on music type... + // + if( bReturn && psName_Explore ) { - MUSIC_PARSE_ERROR(va("Unable to find subgroup \"%s\"\n",sKEY_LEVELMUSIC)); + bReturn = Music_ParseMusic( filename, Parser, MusicData, *pgMusicFiles, psName_Explore, sKEY_EXPLORE, eBGRNDTRACK_EXPLORE ); + } + if( bReturn && psName_Action ) + { + bReturn = Music_ParseMusic( filename, Parser, MusicData, *pgMusicFiles, psName_Action, sKEY_ACTION, eBGRNDTRACK_ACTION ); + } + if( bReturn && psName_Boss ) + { + bReturn = Music_ParseMusic( filename, Parser, MusicData, *pgMusicFiles, psName_Boss, sKEY_BOSS, eBGRNDTRACK_BOSS ); + } + if( bReturn /*&& psName_Death*/ ) // LAST MINUTE HACK!!, always force in some death music!!!! + { + //bReturn = Music_ParseMusic(Parser, MusicData, pgMusicFiles, psName_Death, sKEY_DEATH, eBGRNDTRACK_DEATH); + + MusicFile_t m; + m.sFileNameBase = "death_music"; + ( *MusicData )[ sKEY_DEATH ] = m; } } - else - { - MUSIC_PARSE_ERROR(va("Unable to find subgroup \"%s\"\n",sKEY_MUSICFILES)); - } - } - else - { - MUSIC_PARSE_ERROR( "Error calling GP2.GetBaseParseGroup()\n" ); } } - else - { - MUSIC_PARSE_ERROR( "Error using GP to parse file\n" ); - } - - Z_Free(psStrippedText); - FS_FreeFile( pText ); - } - else - { - MUSIC_PARSE_ERROR( "Unable to even read main file\n" ); // file name specified in error message } if (bReturn) @@ -647,7 +580,7 @@ static qboolean Music_ParseLeveldata(const char *psLevelName) const char *psMusicFileName = Music_BuildFileName( MusicFile.sFileNameBase.c_str(), eMusicState ); if (!S_FileExists( psMusicFileName )) { - MUSIC_PARSE_ERROR(va("Music file \"%s\" not found!\n",psMusicFileName)); + Music_Parse_Error( filename, build_string( "Music file \"", psMusicFileName, "\" not found!\n" ) ); return qfalse; // have to return, because music data destroyed now } @@ -660,7 +593,7 @@ static qboolean Music_ParseLeveldata(const char *psLevelName) const char *psTransitionFileName = Music_BuildFileName( MusicExitPoint.sNextFile.c_str(), eMusicState ); if (!S_FileExists( psTransitionFileName )) { - MUSIC_PARSE_ERROR(va("Transition file \"%s\" (entry \"%s\" ) not found!\n",psTransitionFileName, MusicExitPoint.sNextFile.c_str())); + Music_Parse_Error( filename, build_string( "Transition file \"", psTransitionFileName, "\" (entry \"", MusicExitPoint.sNextFile.c_str(), "\" ) not found!\n" ) ); return qfalse; // have to return, because music data destroyed now } @@ -680,13 +613,13 @@ static qboolean Music_ParseLeveldata(const char *psLevelName) if (!MusicFile_Explore.MusicEntryTimes.count(psNextMark)) { - MUSIC_PARSE_ERROR( va("Unable to find entry point \"%s\" in description for \"%s\"\n",psNextMark,MusicFile_Explore.sFileNameBase.c_str()) ); + Music_Parse_Error( filename, build_string( "Unable to find entry point \"", psNextMark, "\" in description for \"", MusicFile_Explore.sFileNameBase.c_str(), "\"\n" ) ); return qfalse; // have to return, because music data destroyed now } } else { - MUSIC_PARSE_ERROR( va("Unable to find %s piece to match \"%s\"\n", Music_BaseStateToString(eBGRNDTRACK_EXPLORE), MusicFile.sFileNameBase.c_str() ) ); + Music_Parse_Error( filename, build_string( "Unable to find ", Music_BaseStateToString( eBGRNDTRACK_EXPLORE ), " piece to match \"", MusicFile.sFileNameBase.c_str(), "\"\n" ) ); return qfalse; // have to return, because music data destroyed now } } @@ -694,51 +627,6 @@ static qboolean Music_ParseLeveldata(const char *psLevelName) } } -#ifdef _DEBUG -/* - // dump the whole thing out to prove it was read in ok... - // - if (bReturn) - { - for (MusicData_t::iterator itMusicData = MusicData->begin(); itMusicData != MusicData->end(); ++itMusicData) - { - const char *psMusicState = (*itMusicData).first.c_str(); - MusicFile_t &MusicFile = (*itMusicData).second; - - OutputDebugString(va("Music State: \"%s\", File: \"%s\"\n",psMusicState, MusicFile.sFileNameBase.c_str())); - - // entry times... - // - for (MusicEntryTimes_t::iterator itEntryTimes = MusicFile.MusicEntryTimes.begin(); itEntryTimes != MusicFile.MusicEntryTimes.end(); ++itEntryTimes) - { - const char *psMarkerName = (*itEntryTimes).first.c_str(); - float fEntryTime = (*itEntryTimes).second; - - OutputDebugString(va("Entry time for \"%s\": %f\n", psMarkerName, fEntryTime)); - } - - // exit points... - // - for (int i=0; i= eBGRNDTRACK_FIRSTTRANSITION && - eMusicState <= eBGRNDTRACK_LASTTRANSITION - ); + return (qboolean)(eMusicState >= eBGRNDTRACK_FIRSTTRANSITION && + eMusicState <= eBGRNDTRACK_LASTTRANSITION); } diff --git a/code/game/AI_Animal.cpp b/code/game/AI_Animal.cpp index 4626ff4d1f..965347df00 100644 --- a/code/game/AI_Animal.cpp +++ b/code/game/AI_Animal.cpp @@ -198,8 +198,8 @@ void NPC_BSAnimal_Default( void ) bool EvadeThreat = (level.timeinvestigateSoundDebounceTime); - bool CharmedDocile = (level.timeconfusionTime); - bool CharmedApproach = (level.timecharmedTime); + bool CharmedDocile = (level.timeconfusionTime)||(level.timeinsanityTime); + bool CharmedApproach = (level.timecharmedTime)||(level.timedarkCharmedTime); diff --git a/code/game/AI_AssassinDroid.cpp b/code/game/AI_AssassinDroid.cpp index 26368d9463..8057f89b84 100644 --- a/code/game/AI_AssassinDroid.cpp +++ b/code/game/AI_AssassinDroid.cpp @@ -172,7 +172,7 @@ void BubbleShield_Update() // If We Have Enough Armor And Are Not Shooting Right Now, Kick The Shield On //---------------------------------------------------------------------------- - if (NPC->client->ps.stats[STAT_ARMOR]>100 && TIMER_Done(NPC, "ShieldsDown")) + if (NPC->client->ps.stats[STAT_ARMOR]>100 && TIMER_Done(NPC, "ShieldsDown")) //now they can shoot while shield is up, had to edit npc_combat or something - Dusty { // Check On Timers To Raise And Lower Shields //-------------------------------------------- diff --git a/code/game/AI_BobaFett.cpp b/code/game/AI_BobaFett.cpp index 0b50e99e4f..e2abfdab08 100644 --- a/code/game/AI_BobaFett.cpp +++ b/code/game/AI_BobaFett.cpp @@ -34,6 +34,7 @@ along with this program; if not, see . //////////////////////////////////////////////////////////////////////////////////////// #include "b_local.h" #include "../Ravl/CVec.h" +#include "../cgame/cg_local.h" //////////////////////////////////////////////////////////////////////////////////////// @@ -102,6 +103,12 @@ extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pus extern void CG_DrawEdge( vec3_t start, vec3_t end, int type ); +extern qboolean heavyWeap(int); +extern qboolean blasterWeap(int); + +extern int ChooseBestWeapon(gentity_t* ent); +extern int ChooseWeaponRandom(gentity_t *ent, int wpnGroup); + //////////////////////////////////////////////////////////////////////////////////////// // External Data //////////////////////////////////////////////////////////////////////////////////////// @@ -221,6 +228,7 @@ enum EBobaTacticsState //////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////// +extern void Saber_SithSwordPrecache( void ); void Boba_Precache( void ) { G_SoundIndex( "sound/chars/boba/bf_blast-off.wav" ); @@ -241,6 +249,9 @@ void Boba_Precache( void ) BobaHadDeathScript = false; BobaActive = true; BobaFootStepCount = 0; + + RegisterItem( FindItemForWeapon( WP_EMPLACED_GUN )); //precache the weapon + Saber_SithSwordPrecache(); } //////////////////////////////////////////////////////////////////////////////////////// @@ -261,8 +272,8 @@ void Boba_DustFallNear(const vec3_t origin, int dustcount) VectorCopy(origin, testStartPos); for (int i=0; iclient->NPC_class != CLASS_BOBAFETT ) + if (self->client->NPC_class != CLASS_BOBAFETT && self->client->NPC_class != CLASS_MANDA && self->client->NPC_class != CLASS_COMMANDO) { return qfalse; } @@ -369,7 +380,8 @@ qboolean Boba_StopKnockdown( gentity_t *self, gentity_t *pusher, const vec3_t pu //////////////////////////////////////////////////////////////////////////////////////// qboolean Boba_Flying( gentity_t *self ) { - assert(self && self->client && self->client->NPC_class==CLASS_BOBAFETT);//self->NPC && + assert(self && self->client && (self->client->NPC_class == CLASS_BOBAFETT || self->client->NPC_class == CLASS_MANDA + || self->client->NPC_class == CLASS_COMMANDO));//self->NPC && return ((qboolean)(self->client->moveType==MT_FLYSWIM)); } @@ -378,7 +390,7 @@ qboolean Boba_Flying( gentity_t *self ) //////////////////////////////////////////////////////////////////////////////////////// bool Boba_CanSeeEnemy( gentity_t *self ) { - assert(self && self->NPC && self->client && self->client->NPC_class==CLASS_BOBAFETT); + assert(self && self->NPC && self->client && (self->client->NPC_class == CLASS_BOBAFETT || self->client->NPC_class == CLASS_MANDA || self->client->NPC_class == CLASS_COMMANDO)); return ((level.time - self->NPC->enemyLastSeenTime)<1000); } @@ -557,6 +569,11 @@ void Boba_StartFlameThrower( gentity_t *self ) //////////////////////////////////////////////////////////////////////////////////////// void Boba_DoFlameThrower( gentity_t *self ) { + if (self->client->NPC_class == CLASS_COMMANDO) + { + return; + } + if ( self->s.number < MAX_CLIENTS ) { if ( self->client ) @@ -584,6 +601,219 @@ void Boba_DoFlameThrower( gentity_t *self ) } } +#include "AI_BobaFett.h" + +wristWeapon_t missileStates[4] = { + {BOBA_MISSILE_ROCKET, FP_FIRST, WP_ROCKET_LAUNCHER, qfalse, 1, BOBA_FLAMEDURATION, 150, BOTH_FORCELIGHTNING_HOLD, qfalse, qtrue, qtrue}, + {BOBA_MISSILE_LASER, FP_GRIP, WP_EMPLACED_GUN, qfalse, 5, BOBA_FLAMEDURATION, 150, BOTH_FORCELIGHTNING_HOLD, qfalse, qtrue, qtrue}, + {BOBA_MISSILE_DART, FP_FIRST, WP_DISRUPTOR, qfalse, 1, 1500, 200, BOTH_PULL_IMPALE_STAB, qfalse, qfalse, qtrue}, + {BOBA_MISSILE_VIBROBLADE, FP_DRAIN, WP_MELEE, qfalse, 1, 1000, 100, BOTH_PULL_IMPALE_STAB, qfalse, qfalse, qfalse} +}; + +void Boba_VibrobladePunch( gentity_t *self ) +{ + int dummyForcePower = FP_DRAIN; + int addTime, oldWeapon; + if ( !self->client->ps.forcePowerDuration[dummyForcePower] ) + { + NPC_SetAnim( self, SETANIM_TORSO, BOTH_PULL_IMPALE_STAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + self->client->ps.torsoAnimTimer = 1000; + self->client->ps.forcePowerDuration[dummyForcePower] = 1; + G_SoundOnEnt( self, CHAN_WEAPON, va( "sound/weapons/sword/swing%d.wav", Q_irand( 1, 4 ) ) ); + } + + if ( self->client->ps.torsoAnimTimer > 900 ) + { + return; + } + + if ( self->client->ps.weaponTime > 0 ) + { + return; + } + + if ( self->client->ps.torsoAnimTimer < 50 ) + { + self->client->ps.forcePowerDuration[dummyForcePower] = 0; + self->client->ps.torsoAnimTimer = 0; + return; + } + + mdxaBone_t boltMatrix; + vec3_t muzzlePoint; + vec3_t muzzleDir; + gi.G2API_GetBoltMatrix( self->ghoul2, self->playerModel, self->handRBolt, &boltMatrix, self->currentAngles, self->currentOrigin, (cg.time?cg.time:level.time), + NULL, self->s.modelScale ); + // work the matrix axis stuff into the original axis and origins used. + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, muzzlePoint ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, muzzleDir ); + + VectorCopy(muzzlePoint, self->client->renderInfo.muzzlePoint); + VectorCopy(muzzleDir, self->client->renderInfo.muzzleDir); + + oldWeapon = self->s.weapon; + self->s.weapon = WP_MELEE; + + addTime = weaponData[WP_MELEE].fireTime; + self->client->ps.weaponTime += self->client->ps.torsoAnimTimer; + + FireWeapon(self, qfalse); + + self->s.weapon = oldWeapon; + +} + +void Boba_FireWristMissile( gentity_t *self, int whichMissile ) +{ + int addTime, oldWeapon; + qboolean altFire; + int dummyForcePower; + static int shotsFired = 0;//only 5 shots allowed for wristlaser; only 1 for missile launcher or dart + + if ( whichMissile == BOBA_MISSILE_VIBROBLADE ) + { + Boba_VibrobladePunch( self ); + return; + } + + if ( self->s.number >= MAX_CLIENTS ) + { + return; + } + + if ( !self->client ) + { + return; + } + + if ( self->health <= 0 ) + { + return; + } + + if ( self->client->ps.leanofs ) + { + return; + } + + if ( cg.zoomMode || in_camera ) + { + return; + } + + dummyForcePower = missileStates[whichMissile].dummyForcePower; + + if ( !self->client->ps.forcePowerDuration[dummyForcePower] ) + { + NPC_SetAnim( self, SETANIM_TORSO, missileStates[whichMissile].fireAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + self->client->ps.torsoAnimTimer = missileStates[whichMissile].animTimer; + self->client->ps.forcePowerDuration[dummyForcePower] = 1; + shotsFired = 0; + } + + if ( self->client->ps.torsoAnimTimer > missileStates[whichMissile].animTimer - missileStates[whichMissile].animDelay ) + { + return; + } + + if ( self->client->ps.weaponTime > 0 ) + { + return; + } + + if ( missileStates[whichMissile].hold ) + { + if ( self->client->ps.torsoAnimTimer < 150 ) + { + self->client->ps.forcePowerDuration[dummyForcePower] = 150; + return; + } + } + else + { + if ( self->client->ps.torsoAnimTimer < 50 ) + { + self->client->ps.forcePowerDuration[dummyForcePower] = 0; + self->client->ps.torsoAnimTimer = 0; + return; + } + } + + if ( shotsFired >= missileStates[whichMissile].maxShots ) + { + vec3_t ORIGIN = {0, 0, 0}; + G_PlayEffect( G_EffectIndex("repeater/muzzle_smoke"), self->playerModel, missileStates[whichMissile].leftBolt ? self->genericBolt3 : self->handRBolt, self->s.number, ORIGIN); + //play smoke + return; + } + + oldWeapon = self->s.weapon; + + self->s.weapon = missileStates[whichMissile].whichWeapon; + altFire = missileStates[whichMissile].altFire; + if (missileStates[whichMissile].fullyCharged) + { + self->client->ps.weaponChargeTime = 0; + } + + addTime = weaponData[self->s.weapon].fireTime; + self->client->ps.weaponTime += addTime; + self->client->ps.lastShotTime = level.time; + + const weaponData_t *wData = NULL; + const char *effect = NULL; + + wData = &weaponData[self->s.weapon]; + + // Try and get a default muzzle so we have one to fall back on + if ( wData->mMuzzleEffect[0] ) + { + effect = &wData->mMuzzleEffect[0]; + } + + if ( altFire ) + { + // We're alt-firing, so see if we need to override with a custom alt-fire effect + if ( wData->mAltMuzzleEffect[0] ) + { + effect = &wData->mAltMuzzleEffect[0]; + } + } + + if ( effect ) + { + + mdxaBone_t boltMatrix; + vec3_t muzzlePoint; + vec3_t muzzleDir; + gi.G2API_GetBoltMatrix( self->ghoul2, self->playerModel, missileStates[whichMissile].leftBolt ? self->genericBolt3 : self->handRBolt, &boltMatrix, self->currentAngles, self->currentOrigin, (cg.time?cg.time:level.time), + NULL, self->s.modelScale ); + // work the matrix axis stuff into the original axis and origins used. + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, muzzlePoint ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, muzzleDir ); + + VectorCopy(muzzlePoint, self->client->renderInfo.muzzlePoint); + VectorCopy(muzzleDir, self->client->renderInfo.muzzleDir); + + G_PlayEffect( effect, muzzlePoint, muzzleDir ); + } + + FireWeapon(self, altFire); + shotsFired++; + + self->s.weapon = oldWeapon; + +} + +void Boba_EndWristMissile( gentity_t *self, int whichMissile ) +{ + if (missileStates[whichMissile].hold) + { + self->client->ps.forcePowerDuration[missileStates[whichMissile].dummyForcePower] = 0; + self->client->ps.torsoAnimTimer = 0; + } +} + //////////////////////////////////////////////////////////////////////////////////////// // //////////////////////////////////////////////////////////////////////////////////////// @@ -648,56 +878,57 @@ void Boba_Fire() //------------------------------------------------------------------------ if (ucmd.buttons&BUTTON_ATTACK) { - switch (NPC->s.weapon) + if (heavyWeap(NPC->s.weapon)) { - case WP_ROCKET_LAUNCHER: - TIMER_Set( NPC, "nextAttackDelay", Q_irand(1000, 2000)); + if (NPC->s.weapon == WP_ROCKET_LAUNCHER) + TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 2000)); // Occasionally Shoot A Homing Missile //------------------------------------- - if (!Q_irand(0,3)) + if (!Q_irand(0, 3)) { ucmd.buttons &= ~BUTTON_ATTACK; - ucmd.buttons |= BUTTON_ALT_ATTACK; - NPC->client->fireDelay = Q_irand( 1000, 3000 ); + ucmd.buttons |= BUTTON_ALT_ATTACK; + if (NPC->s.weapon == WP_ROCKET_LAUNCHER) + NPC->client->fireDelay = Q_irand(1000, 3000); } - break; - - case WP_DISRUPTOR: + + } + else if (NPC->s.weapon == WP_DISRUPTOR) + { TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 4000)); // Occasionally Alt-Fire //----------------------- - if (!Q_irand(0,3)) + if (!Q_irand(0, 3)) { ucmd.buttons &= ~BUTTON_ATTACK; - ucmd.buttons |= BUTTON_ALT_ATTACK; - NPC->client->fireDelay = Q_irand( 1000, 3000 ); + ucmd.buttons |= BUTTON_ALT_ATTACK; + NPC->client->fireDelay = Q_irand(1000, 3000); } - break; - - case WP_BLASTER: - + } + else + {//blaster-type weapon if (TIMER_Done(NPC, "nextBlasterAltFireDecide")) { - if (Q_irand(0, (NPC->count*2)+3)>2) + if (Q_irand(0, (NPC->count * 2) + 3)>2) { - TIMER_Set(NPC, "nextBlasterAltFireDecide", Q_irand(3000, 8000)); + TIMER_Set(NPC, "nextBlasterAltFireDecide", Q_irand(3000, 8000)); if (!(NPCInfo->scriptFlags&SCF_ALT_FIRE)) { Boba_Printf("ALT FIRE On"); NPCInfo->scriptFlags |= SCF_ALT_FIRE; - NPC_ChangeWeapon(WP_BLASTER); // Update Delay Timers + NPC_ChangeWeapon(NPC->s.weapon); // Update Delay Timers } } else { TIMER_Set(NPC, "nextBlasterAltFireDecide", Q_irand(2000, 5000)); - if ( (NPCInfo->scriptFlags&SCF_ALT_FIRE)) + if ((NPCInfo->scriptFlags&SCF_ALT_FIRE)) { Boba_Printf("ALT FIRE Off"); - NPCInfo->scriptFlags &=~SCF_ALT_FIRE; - NPC_ChangeWeapon(WP_BLASTER); // Update Delay Timers + NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; + NPC_ChangeWeapon(NPC->s.weapon); // Update Delay Timers } } } @@ -707,9 +938,8 @@ void Boba_Fire() if (NPCInfo->scriptFlags&SCF_ALT_FIRE) { ucmd.buttons &= ~BUTTON_ATTACK; - ucmd.buttons |= BUTTON_ALT_ATTACK; + ucmd.buttons |= BUTTON_ALT_ATTACK; } - break; } } } @@ -718,15 +948,24 @@ void Boba_Fire() //////////////////////////////////////////////////////////////////////////////////////// // Call this function to see if Fett should fire his current weapon //////////////////////////////////////////////////////////////////////////////////////// -void Boba_FireDecide( void ) +//helper function +qboolean isBobaClass(int className) +{ + if (NPC->client->NPC_class == CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_MANDA || NPC->client->NPC_class == CLASS_COMMANDO) + return qtrue; + + return qfalse; +} + +void Boba_FireDecide(void) { // Any Reason Not To Shoot? //-------------------------- if (!NPC || // Only NPCs !NPC->client || // Only Clients - NPC->client->NPC_class!=CLASS_BOBAFETT || // Only Boba + !isBobaClass(NPC->client->NPC_class) || // Only Boba !NPC->enemy || // Only If There Is An Enemy - NPC->s.weapon==WP_NONE || // Only If Using A Valid Weapon + NPC->s.weapon == WP_NONE || // Only If Using A Valid Weapon !TIMER_Done(NPC, "nextAttackDelay") || // Only If Ready To Shoot Again !Boba_CanSeeEnemy(NPC) // Only If Enemy Recently Seen ) @@ -736,24 +975,16 @@ void Boba_FireDecide( void ) // Now Check Weapon Specific Parameters To See If We Should Shoot Or Not //----------------------------------------------------------------------- - switch (NPC->s.weapon) + if (heavyWeap(NPC->s.weapon)) { - case WP_ROCKET_LAUNCHER: - if (Distance(NPC->currentOrigin, NPC->enemy->currentOrigin)>400.0f) + if (Distance(NPC->currentOrigin, NPC->enemy->currentOrigin) > 400.0f) { Boba_Fire(); } - break; - - case WP_DISRUPTOR: - // TODO: Add Conditions Here - Boba_Fire(); - break; - - case WP_BLASTER: - // TODO: Add Conditions Here + } + else + { Boba_Fire(); - break; } } @@ -804,7 +1035,7 @@ void Boba_TacticsSelect() { // If It's Been Long Enough Since Our Last Flame Blast, Try To Torch The Enemy //----------------------------------------------------------------------------- - if (TIMER_Done(NPC, "nextFlameDelay")) + if (TIMER_Done(NPC, "nextFlameDelay") && (NPC->client->NPC_class == CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_MANDA)) { nextState = BTS_FLAMETHROW; } @@ -847,7 +1078,7 @@ void Boba_TacticsSelect() } - if (SnipePointsNear && TIMER_Done(NPC, "Boba_NoSniperTime")) + if (SnipePointsNear && TIMER_Done(NPC, "Boba_NoSniperTime") && HaveWeapon(NPC, WP_DISRUPTOR)) { TIMER_Set(NPC, "Boba_NoSniperTime", 120000); // Don't snipe again for a while TIMER_Set(NPC, "Boba_TacticsSelect", Q_irand(35000, 45000));// More patience here @@ -864,42 +1095,50 @@ void Boba_TacticsSelect() } } - - // The Next State Has Been Selected, Now Change Weapon If Necessary //------------------------------------------------------------------ - if (nextState!=NPCInfo->localState) + NPCInfo->localState = nextState; + int weapon = 0; + + switch (NPCInfo->localState) { - NPCInfo->localState = nextState; - switch (NPCInfo->localState) + case BTS_SNIPER: + if (HaveWeapon(NPC, WP_DISRUPTOR)) { - case BTS_FLAMETHROW: - Boba_Printf("NEW TACTIC: Flame Thrower"); - Boba_ChangeWeapon(WP_NONE); - Boba_DoFlameThrower(NPC); + Boba_Printf("NEW TACTIC: Sniper"); + Boba_ChangeWeapon(WP_DISRUPTOR); break; - - case BTS_RIFLE: + } + + case BTS_RIFLE: + weapon = ChooseWeaponRandom(NPC, WEAPS_BLASTER); + if (weapon) + { Boba_Printf("NEW TACTIC: Rifle"); - Boba_ChangeWeapon(WP_BLASTER); + Boba_ChangeWeapon(weapon); break; + } //if no blasters overflow to next case - case BTS_MISSILE: + case BTS_MISSILE: + weapon = ChooseWeaponRandom(NPC, WEAPS_HEAVY); + if (weapon) + { Boba_Printf("NEW TACTIC: Rocket Launcher"); - Boba_ChangeWeapon(WP_ROCKET_LAUNCHER); + Boba_ChangeWeapon(weapon); break; + } //if no heavy weaps overflow to next case - case BTS_SNIPER: - Boba_Printf("NEW TACTIC: Sniper"); - Boba_ChangeWeapon(WP_DISRUPTOR); - break; + case BTS_FLAMETHROW: //kinda stuck at this point if doesn't have flamethrower + Boba_Printf("NEW TACTIC: Flame Thrower"); + Boba_ChangeWeapon(WP_NONE); + Boba_DoFlameThrower(NPC); + break; - case BTS_AMBUSHWAIT: - Boba_Printf("NEW TACTIC: Ambush"); - Boba_ChangeWeapon(WP_NONE); - break; - } - } + case BTS_AMBUSHWAIT: + Boba_Printf("NEW TACTIC: Ambush"); + Boba_ChangeWeapon(WP_NONE); + break; + } } @@ -1111,7 +1350,9 @@ void Boba_Update() //--------------------------------------------- if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE && NPC->client->ps.forceJumpZStart - && !Q_irand( 0, 10 ) ) + && !Q_irand( 0, 10 ) + && NPC->client->NPC_class != CLASS_COMMANDO + && NPC->client->NPC_class != CLASS_MANDA) {//take off Boba_FlyStart( NPC ); } @@ -1191,9 +1432,9 @@ bool Boba_Flee() case 1: Boba_Printf("SPOOK: Footsteps"); - testDirection[0] = (random() * 0.5f) - 1.0f; + testDirection[0] = (Q_flrand(0.0f, 1.0f) * 0.5f) - 1.0f; testDirection[0] += (testDirection[0]>0.0f)?(0.5f):(-0.5f); - testDirection[1] = (random() * 0.5f) - 1.0f; + testDirection[1] = (Q_flrand(0.0f, 1.0f) * 0.5f) - 1.0f; testDirection[1] += (testDirection[1]>0.0f)?(0.5f):(-0.5f); testDirection[2] = 1.0f; VectorMA(NPC->enemy->currentOrigin, 400.0f, testDirection, BobaFootStepLoc); diff --git a/code/game/AI_BobaFett.h b/code/game/AI_BobaFett.h new file mode 100644 index 0000000000..6c2dc02dde --- /dev/null +++ b/code/game/AI_BobaFett.h @@ -0,0 +1,21 @@ +#define BOBA_MISSILE_ROCKET 0 +#define BOBA_MISSILE_LASER 1 +#define BOBA_MISSILE_DART 2 +#define BOBA_MISSILE_VIBROBLADE 3 + +extern void Boba_FireWristMissile( gentity_t *self, int whichMissile ); +extern void Boba_EndWristMissile( gentity_t *self, int whichMissile ); + +typedef struct wristWeapon_s { + int theMissile; + int dummyForcePower; + int whichWeapon; + qboolean altFire; + int maxShots; + int animTimer; + int animDelay; + int fireAnim; + qboolean fullyCharged; + qboolean leftBolt; + qboolean hold; +} wristWeapon_t; diff --git a/code/game/AI_Civilian.cpp b/code/game/AI_Civilian.cpp index b2ba49fc6e..8795782c35 100644 --- a/code/game/AI_Civilian.cpp +++ b/code/game/AI_Civilian.cpp @@ -24,7 +24,7 @@ along with this program; if not, see . #include "b_local.h" #include "Q3_Interface.h" -extern qboolean NPC_CheckSurrender( void ); +extern qboolean NPC_CheckSurrender( qboolean noEscape = qfalse ); extern void NPC_BehaviorSet_Default( int bState ); void NPC_BSCivilian_Default( int bState ) diff --git a/code/game/AI_Default.cpp b/code/game/AI_Default.cpp index 58e4a013d1..368cf10832 100644 --- a/code/game/AI_Default.cpp +++ b/code/game/AI_Default.cpp @@ -116,8 +116,8 @@ qboolean NPC_StandTrackAndShoot (gentity_t *NPC, qboolean canDuck) { if ( NPC->health < 20 ) { - // if( NPC->svFlags&SVF_HEALING || random() ) - if( random() ) + // if( NPC->svFlags&SVF_HEALING || Q_flrand(0.0f, 1.0f) ) + if( Q_flrand(0.0f, 1.0f) ) { duck_ok = qtrue; } @@ -127,12 +127,6 @@ qboolean NPC_StandTrackAndShoot (gentity_t *NPC, qboolean canDuck) // if ( NPC->svFlags&SVF_HEALING ) // {//Medic is on the way, get down! // duck_ok = qtrue; -// } - // no more borg -/// if ( NPC->client->playerTeam!= TEAM_BORG ) -// {//Borg don't care if they're about to die - //attack_scale will be a max of .66 -// attack_scale = NPC->health/60; // } } } @@ -211,11 +205,16 @@ void NPC_BSStandGuard (void) //FIXME: Use Snapshot info if ( NPC->enemy == NULL ) {//Possible to pick one up by being shot - if( random() < 0.5 ) + if( Q_flrand(0.0f, 1.0f) < 0.5 ) { if(NPC->client->enemyTeam) { - gentity_t *newenemy = NPC_PickEnemy(NPC, NPC->client->enemyTeam, (NPC->cantHitEnemyCounter < 10), (NPC->client->enemyTeam == TEAM_PLAYER), qtrue); + gentity_t *newenemy = NPC_PickEnemy( + NPC, NPC->client->enemyTeam, + (qboolean)(NPC->cantHitEnemyCounter < 10), + (qboolean)(NPC->client->enemyTeam == TEAM_PLAYER), + qtrue); + //only checks for vis if couldn't hit last enemy if(newenemy) { @@ -255,7 +254,7 @@ void NPC_BSHuntAndKill( void ) visibility_t oEVis; int curAnim; - NPC_CheckEnemy( NPCInfo->tempBehavior != BS_HUNT_AND_KILL, qfalse );//don't find new enemy if this is tempbehav + NPC_CheckEnemy( (qboolean)(NPCInfo->tempBehavior != BS_HUNT_AND_KILL), qfalse );//don't find new enemy if this is tempbehav if ( NPC->enemy ) { @@ -544,7 +543,6 @@ void NPC_BSPointShoot (qboolean shoot) switch( NPC->client->ps.weapon ) { case WP_NONE: -// case WP_TRICORDER: case WP_MELEE: case WP_TUSKEN_STAFF: case WP_SABER: @@ -754,7 +752,7 @@ void NPC_BSDefault( void ) } } //look for a new enemy if don't have one and are allowed to look, validate current enemy if have one - NPC_CheckEnemy( (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES), qfalse ); + NPC_CheckEnemy( (qboolean)((NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) != 0), qfalse ); if ( !NPC->enemy ) {//still don't have an enemy if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) ) diff --git a/code/game/AI_Droid.cpp b/code/game/AI_Droid.cpp index 3b018d42c7..8fab6ecac9 100644 --- a/code/game/AI_Droid.cpp +++ b/code/game/AI_Droid.cpp @@ -299,7 +299,7 @@ void NPC_Droid_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, co pain_chance = NPC_GetPainChance( self, damage ); // Put it in pain - if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || random() < pain_chance ) // Spin around in pain? Demp2 always does this + if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || Q_flrand(0.0f, 1.0f) < pain_chance ) // Spin around in pain? Demp2 always does this { // Health is between 0-30 or was hit by a DEMP2 so pop his head if ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) @@ -365,7 +365,7 @@ void NPC_Droid_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, co pain_chance = NPC_GetPainChance( self, damage ); - if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || random() < pain_chance ) // Spin around in pain? Demp2 always does this + if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT || Q_flrand(0.0f, 1.0f) < pain_chance ) // Spin around in pain? Demp2 always does this { anim = self->client->ps.legsAnim; @@ -562,7 +562,7 @@ void NPC_BSDroid_Default( void ) else if ( NPCInfo->localState == LSTATE_DROP ) { NPC_UpdateAngles( qtrue, qtrue ); - ucmd.upmove = crandom() * 64; + ucmd.upmove = Q_flrand(-1.0f, 1.0f) * 64; } else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES ) { diff --git a/code/game/AI_GalakMech.cpp b/code/game/AI_GalakMech.cpp index cb9816747e..021df9284e 100644 --- a/code/game/AI_GalakMech.cpp +++ b/code/game/AI_GalakMech.cpp @@ -19,748 +19,1254 @@ You should have received a copy of the GNU General Public License along with this program; if not, see . =========================================================================== */ - -//////////////////////////////////////////////////////////////////////////////////////// -// RAVEN SOFTWARE - STAR WARS: JK III -// (c) 2002 Activision -// -// April 3, 2003 - This file has been commandeered for use by AI vehicle pilots. -// -//////////////////////////////////////////////////////////////////////////////////////// - - -//////////////////////////////////////////////////////////////////////////////////////// -// Includes -//////////////////////////////////////////////////////////////////////////////////////// #include "b_local.h" +#include "g_nav.h" #include "anims.h" #include "g_navigator.h" -#include "g_vehicles.h" +#include "../cgame/cg_local.h" #include "g_functions.h" -#if !defined(RATL_VECTOR_VS_INC) - #include "../Ratl/vector_vs.h" -#endif - - -//////////////////////////////////////////////////////////////////////////////////////// -// Defines -//////////////////////////////////////////////////////////////////////////////////////// -#define MAX_VEHICLES_REGISTERED 100 - -#define ATTACK_FWD 0.95f -#define ATTACK_SIDE 0.20f -#define AIM_SIDE 0.60f -#define FUTURE_PRED_DIST 20.0f -#define FUTURE_SIDE_DIST 60.0f -#define ATTACK_FLANK_SLOWING 1000.0f -#define RAM_DIST 150.0f -#define MIN_STAY_VIEWABLE_TIME 20000 - - -//////////////////////////////////////////////////////////////////////////////////////// -// Externs -//////////////////////////////////////////////////////////////////////////////////////// -extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent ); +extern qboolean G_StandardHumanoid( gentity_t *ent ); +extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime ); +extern qboolean Q3_TaskIDPending( gentity_t *ent, taskID_t taskType ); +extern void NPC_AimAdjust( int change ); +extern qboolean WP_LobFire( gentity_t *self, vec3_t start, vec3_t target, vec3_t mins, vec3_t maxs, int clipmask, + vec3_t velocity, qboolean tracePath, int ignoreEntNum, int enemyNum, + float minSpeed, float maxSpeed, float idealSpeed, qboolean mustHit ); +extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); extern void G_SoundAtSpot( vec3_t org, int soundIndex, qboolean broadcast ); -extern void CG_DrawEdge( vec3_t start, vec3_t end, int type ); - - - -trace_t mPilotViewTrace; -int mPilotViewTraceCount; -int mActivePilotCount; -ratl::vector_vs mRegistered; - - - -//////////////////////////////////////////////////////////////////////////////////////// -// -//////////////////////////////////////////////////////////////////////////////////////// -void Pilot_Reset(void) +extern void G_SoundOnEnt (gentity_t *ent, soundChannel_t channel, const char *soundPath); +extern qboolean PM_CrouchAnim( int anim ); +//extern void NPC_Mark1_Part_Explode(gentity_t *self,int bolt); + +#define ARMOR_EFFECT_TIME 500 +#define MELEE_DIST_SQUARED 6400//80*80 +#define MIN_LOB_DIST_SQUARED 65536//256*256 +#define MAX_LOB_DIST_SQUARED 200704//448*448 +#define REPEATER_ALT_SIZE 3 // half of bbox size +#define GENERATOR_HEALTH 25 +#define TURN_ON 0x00000000 +#define TURN_OFF 0x00000100 +#define GALAK_SHIELD_HEALTH 500 + +static vec3_t shieldMins = {-60, -60, -24 }; +static vec3_t shieldMaxs = {60, 60, 80}; + +extern qboolean NPC_CheckPlayerTeamStealth( void ); + +static qboolean enemyLOS; +static qboolean enemyCS; +static qboolean hitAlly; +static qboolean faceEnemy; +static qboolean AImove; +static qboolean shoot; +static float enemyDist; +static vec3_t impactPos; + +void NPC_GalakMech_Precache( void ) { - mPilotViewTraceCount = 0; - mActivePilotCount = 0; - mRegistered.clear(); + G_SoundIndex( "sound/weapons/galak/skewerhit.wav" ); + G_SoundIndex( "sound/weapons/galak/lasercharge.wav" ); + G_SoundIndex( "sound/weapons/galak/lasercutting.wav" ); + G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ); + + G_EffectIndex( "galak/trace_beam" ); + G_EffectIndex( "galak/beam_warmup" ); +// G_EffectIndex( "small_chunks"); + G_EffectIndex( "env/med_explode2"); + G_EffectIndex( "env/small_explode2"); + G_EffectIndex( "galak/explode"); + G_EffectIndex( "blaster/smoke_bolton"); +// G_EffectIndex( "env/exp_trail_comp"); } -//////////////////////////////////////////////////////////////////////////////////////// -// -//////////////////////////////////////////////////////////////////////////////////////// -int Pilot_ActivePilotCount() +void NPC_GalakMech_Init( gentity_t *ent ) { - return mActivePilotCount; + if (ent->NPC->behaviorState != BS_CINEMATIC) + { + ent->client->ps.stats[STAT_ARMOR] = GALAK_SHIELD_HEALTH; + ent->NPC->investigateCount = ent->NPC->investigateDebounceTime = 0; + ent->flags |= FL_SHIELDED;//reflect normal shots + ent->client->ps.powerups[PW_GALAK_SHIELD] = Q3_INFINITE;//temp, for effect + ent->fx_time = level.time; + VectorSet( ent->mins, -60, -60, -24 ); + VectorSet( ent->maxs, 60, 60, 80 ); + ent->flags |= FL_NO_KNOCKBACK;//don't get pushed + TIMER_Set( ent, "attackDelay", 0 ); //FIXME: Slant for difficulty levels + TIMER_Set( ent, "flee", 0 ); + TIMER_Set( ent, "smackTime", 0 ); + TIMER_Set( ent, "beamDelay", 0 ); + TIMER_Set( ent, "noLob", 0 ); + TIMER_Set( ent, "noRapid", 0 ); + TIMER_Set( ent, "talkDebounce", 0 ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_shield_off", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_galakface_off", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_galakhead_off", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_eyes_mouth_off", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_collar_off", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_galaktorso_off", TURN_OFF ); + } + else + { +// gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "helmet", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_shield_off", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_galakface_off", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_galakhead_off", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_eyes_mouth_off", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_collar_off", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_galaktorso_off", TURN_ON ); + } + } -//////////////////////////////////////////////////////////////////////////////////////// -// -//////////////////////////////////////////////////////////////////////////////////////// -void Pilot_Update(void) +//----------------------------------------------------------------- +static void GM_CreateExplosion( gentity_t *self, const int boltID, qboolean doSmall = qfalse ) { - mActivePilotCount = 0; - mRegistered.clear(); - for (int i=0; i=0 ) { - if (g_entities[i].inuse && - g_entities[i].client && - g_entities[i].NPC && - g_entities[i].NPC->greetEnt && - g_entities[i].NPC->greetEnt->owner==(&g_entities[i]) - ) + mdxaBone_t boltMatrix; + vec3_t org, dir; + + gi.G2API_GetBoltMatrix( self->ghoul2, self->playerModel, + boltID, + &boltMatrix, self->currentAngles, self->currentOrigin, level.time, + NULL, self->s.modelScale ); + + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, org ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, dir ); + + if ( doSmall ) { - mActivePilotCount++; - } - if ( g_entities[i].inuse && - g_entities[i].client && - g_entities[i].m_pVehicle && - !g_entities[i].owner && - g_entities[i].health>0 && - g_entities[i].m_pVehicle->m_pVehicleInfo->type==VH_SPEEDER && - !mRegistered.full()) + G_PlayEffect( "env/small_explode2", org, dir ); + } + else { - mRegistered.push_back(&g_entities[i]); + G_PlayEffect( "env/med_explode2", org, dir ); } - } +} +/* +------------------------- +GM_Dying +------------------------- +*/ - if (player && - player->inuse && - TIMER_Done(player, "FlybySoundArchitectureDebounce")) - { - TIMER_Set(player, "FlybySoundArchitectureDebounce", 300); - - Vehicle_t* pVeh = G_IsRidingVehicle(player); - - if (pVeh && - (pVeh->m_pVehicleInfo->soundFlyBy || pVeh->m_pVehicleInfo->soundFlyBy2) && - //fabsf(pVeh->m_pParentEntity->currentAngles[2])<15.0f && - VectorLength(pVeh->m_pParentEntity->client->ps.velocity)>500.0f) +void GM_Dying( gentity_t *self ) +{ + if ( level.time - self->s.time < 4000 ) + {//FIXME: need a real effect + self->s.powerups |= ( 1 << PW_SHOCKED ); + self->client->ps.powerups[PW_SHOCKED] = level.time + 1000; + if ( TIMER_Done( self, "dyingExplosion" ) ) { - vec3_t projectedPosition; - vec3_t projectedDirection; - vec3_t projectedRight; - vec3_t anglesNoRoll; - - VectorCopy(pVeh->m_pParentEntity->currentAngles, anglesNoRoll); - anglesNoRoll[2] = 0; - AngleVectors(anglesNoRoll, projectedDirection, projectedRight, 0); - - VectorMA(player->currentOrigin, 1.2f, pVeh->m_pParentEntity->client->ps.velocity, projectedPosition); - VectorMA(projectedPosition, Q_flrand(-200.0f, 200.0f), projectedRight, projectedPosition); - - gi.trace(&mPilotViewTrace, - player->currentOrigin, - 0, - 0, - projectedPosition, - player->s.number, - MASK_SHOT, (EG2_Collision)0, 0); - - if ((mPilotViewTrace.allsolid==qfalse) && - (mPilotViewTrace.startsolid==qfalse) && - (mPilotViewTrace.fraction<0.99f) && - (mPilotViewTrace.plane.normal[2]<0.5f) && - (DotProduct(projectedDirection, mPilotViewTrace.plane.normal)<-0.5f) - ) + int newBolt; + switch ( Q_irand( 1, 14 ) ) { - // CG_DrawEdge(player->currentOrigin, mPilotViewTrace.endpos, EDGE_IMPACT_POSSIBLE); - TIMER_Set(player, "FlybySoundArchitectureDebounce", Q_irand(1000, 2000)); - - int soundFlyBy = pVeh->m_pVehicleInfo->soundFlyBy; - if (pVeh->m_pVehicleInfo->soundFlyBy2 && (!soundFlyBy || !Q_irand(0,1))) - { - soundFlyBy = pVeh->m_pVehicleInfo->soundFlyBy2; + // Find place to generate explosion + case 1: + if (!gi.G2API_GetSurfaceRenderStatus( &self->ghoul2[self->playerModel], "r_hand" )) + {//r_hand still there + GM_CreateExplosion( self, self->handRBolt, qtrue ); + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "r_hand", TURN_OFF ); } - G_SoundAtSpot(mPilotViewTrace.endpos, soundFlyBy, qtrue); - } - else - { - // CG_DrawEdge(player->currentOrigin, mPilotViewTrace.endpos, EDGE_IMPACT_SAFE); + else if (!gi.G2API_GetSurfaceRenderStatus( &self->ghoul2[self->playerModel], "r_arm_middle" )) + {//r_arm_middle still there + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*r_arm_elbow" ); + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "r_arm_middle", TURN_OFF ); + } + break; + case 2: + //FIXME: do only once? + if (!gi.G2API_GetSurfaceRenderStatus( &self->ghoul2[self->playerModel], "l_hand" )) + {//l_hand still there + GM_CreateExplosion( self, self->handLBolt ); + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "l_hand", TURN_OFF ); + } + else if (!gi.G2API_GetSurfaceRenderStatus( &self->ghoul2[self->playerModel], "l_arm_wrist" )) + {//l_arm_wrist still there + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*l_arm_cap_l_hand" ); + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "l_arm_wrist", TURN_OFF ); + } + else if (!gi.G2API_GetSurfaceRenderStatus( &self->ghoul2[self->playerModel], "l_arm_middle" )) + {//l_arm_middle still there + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*l_arm_cap_l_hand" ); + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "l_arm_middle", TURN_OFF ); + } + else if (!gi.G2API_GetSurfaceRenderStatus( &self->ghoul2[self->playerModel], "l_arm_augment" )) + {//l_arm_augment still there + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*l_arm_elbow" ); + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "l_arm_augment", TURN_OFF ); + } + break; + case 3: + case 4: + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*hip_fr" ); + GM_CreateExplosion( self, newBolt ); + break; + case 5: + case 6: + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*shldr_l" ); + GM_CreateExplosion( self, newBolt ); + break; + case 7: + case 8: + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*uchest_r" ); + GM_CreateExplosion( self, newBolt ); + break; + case 9: + case 10: + GM_CreateExplosion( self, self->headBolt ); + break; + case 11: + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*l_leg_knee" ); + GM_CreateExplosion( self, newBolt, qtrue ); + break; + case 12: + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*r_leg_knee" ); + GM_CreateExplosion( self, newBolt, qtrue ); + break; + case 13: + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*l_leg_foot" ); + GM_CreateExplosion( self, newBolt, qtrue ); + break; + case 14: + newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*r_leg_foot" ); + GM_CreateExplosion( self, newBolt, qtrue ); + break; } + + TIMER_Set( self, "dyingExplosion", Q_irand( 300, 1100 ) ); } } + else + {//one final, huge explosion + G_PlayEffect( "galak/explode", self->currentOrigin ); +// G_PlayEffect( "small_chunks", self->currentOrigin ); +// G_PlayEffect( "env/exp_trail_comp", self->currentOrigin, self->currentAngles ); + self->nextthink = level.time + FRAMETIME; + self->e_ThinkFunc = thinkF_G_FreeEntity; + } } -//////////////////////////////////////////////////////////////////////////////////////// -// -//////////////////////////////////////////////////////////////////////////////////////// -bool Pilot_AnyVehiclesRegistered() -{ - return (!mRegistered.empty()); -} - - - - - -//////////////////////////////////////////////////////////////////////////////////////// -// Vehicle Registration -// -// Any vehicles that can be ridden by NPCs should be registered here -// -//////////////////////////////////////////////////////////////////////////////////////// -void Vehicle_Register(gentity_t *ent) -{ -} - +/* +------------------------- +NPC_GM_Pain +------------------------- +*/ -//////////////////////////////////////////////////////////////////////////////////////// -// Vehicle Remove From The List Of Valid -//////////////////////////////////////////////////////////////////////////////////////// -void Vehicle_Remove(gentity_t *ent) +extern void NPC_SetPainEvent( gentity_t *self ); +void NPC_GM_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc ) { -} + if ( self->client->ps.powerups[PW_GALAK_SHIELD] == 0 ) + {//shield is currently down + //FIXME: allow for radius damage? + if ( (hitLoc==HL_GENERIC1) && (self->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH) ) + { + int newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*antenna_base" ); + if ( newBolt != -1 ) + { + GM_CreateExplosion( self, newBolt ); + } -//////////////////////////////////////////////////////////////////////////////////////// -// Vehicle_Find -// -// Will look through all registered vehicles and choose the closest one that the given -// entity can get to. -// -//////////////////////////////////////////////////////////////////////////////////////// -gentity_t* Vehicle_Find(gentity_t *ent) -{ - gentity_t* closest = 0; - float closestDist = 0; - float curDist = 0; + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_shield_off", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_antenna", TURN_OFF ); + gi.G2API_SetSurfaceOnOff( &self->ghoul2[self->playerModel], "torso_antenna_base_cap_off", TURN_ON ); + self->client->ps.powerups[PW_GALAK_SHIELD] = 0;//temp, for effect + self->client->ps.stats[STAT_ARMOR] = 0;//no more armor + self->NPC->investigateDebounceTime = 0;//stop recharging + NPC_SetAnim( self, SETANIM_BOTH, BOTH_ALERT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + TIMER_Set( self, "attackDelay", self->client->ps.torsoAnimTimer ); + G_AddEvent( self, Q_irand( EV_DEATH1, EV_DEATH3 ), self->health ); + } + } + else + {//store the point for shield impact + if ( point ) + { + VectorCopy( point, self->pos4 ); + self->client->poisonTime = level.time; + } + } - for (int i=0; iowner) + if ( !self->lockCount && !self->client->ps.torsoAnimTimer ) + {//don't interrupt laser sweep attack or other special attacks/moves + if ( self->count < 4 && self->health > 100 && hitLoc != HL_GENERIC1 ) { - curDist = Distance(mRegistered[i]->currentOrigin, ent->currentOrigin); - if (curDist<1000 && (!closest || curDistdelay < level.time ) { - if (NAV::InSameRegion(ent, mRegistered[i])) + int speech; + switch( self->count ) { - closest = mRegistered[i]; - closestDist = curDist; + default: + case 0: + speech = EV_PUSHED1; + break; + case 1: + speech = EV_PUSHED2; + break; + case 2: + speech = EV_PUSHED3; + break; + case 3: + speech = EV_DETECTED1; + break; } + self->count++; + self->NPC->blockedSpeechDebounceTime = 0; + G_AddVoiceEvent( self, speech, Q_irand( 3000, 5000 ) ); + self->delay = level.time + Q_irand( 5000, 7000 ); } } + else + { + NPC_Pain( self, inflictor, attacker, point, damage, mod, hitLoc ); + } } - - return closest; -} - - - -void Pilot_Update_Enemy(); -void Pilot_Steer_Vehicle(); -void Pilot_Goto_Vehicle(); - - - - - - -//////////////////////////////////////////////////////////////////////////////////////// -// Pilot_MasterUpdate() - Master think function for Pilot NPCs -// -// Will return true if the character is either driving a vehicle or on his way to get -// onto one. -//////////////////////////////////////////////////////////////////////////////////////// -bool Pilot_MasterUpdate() -{ - if (!NPC->enemy) + else if ( hitLoc == HL_GENERIC1 ) { - // If Still On A Vehicle, Jump Off - //--------------------------------- - if (NPCInfo->greetEnt) - { - ucmd.upmove = 127; + NPC_SetPainEvent( self ); + self->s.powerups |= ( 1 << PW_SHOCKED ); + self->client->ps.powerups[PW_SHOCKED] = level.time + Q_irand( 500, 2500 ); + } - if (NPCInfo->greetEnt && NPCInfo->greetEnt->m_pVehicle && level.timeconfusionTime) + if ( inflictor && inflictor->lastEnemy == self ) + {//He force-pushed my own lobfires back at me + if ( mod == MOD_REPEATER_ALT && !Q_irand( 0, 2 ) ) + { + if ( TIMER_Done( self, "noRapid" ) ) { - Vehicle_t* pVeh = NPCInfo->greetEnt->m_pVehicle; - if (!(pVeh->m_ulFlags&VEH_OUTOFCONTROL)) - { - gentity_t* parent = pVeh->m_pParentEntity; - float CurSpeed = VectorLength(parent->client->ps.velocity); - pVeh->m_pVehicleInfo->StartDeathDelay(pVeh, 10000); - pVeh->m_ulFlags |= (VEH_OUTOFCONTROL); - VectorScale(parent->client->ps.velocity, 1.25f, parent->pos3); - if (CurSpeedm_pVehicleInfo->speedMax) - { - VectorNormalize(parent->pos3); - if (fabsf(parent->pos3[2])<0.25f) - { - VectorScale(parent->pos3, (pVeh->m_pVehicleInfo->speedMax * 1.25f), parent->pos3); - } - else - { - VectorScale(parent->client->ps.velocity, 1.25f, parent->pos3); - } - } - } + self->NPC->scriptFlags &= ~SCF_ALT_FIRE; + self->alt_fire = qfalse; + TIMER_Set( self, "noLob", Q_irand( 2000, 6000 ) ); } - - if (NPCInfo->greetEnt->owner==NPC) - { - return true; + else + {//hopefully this will make us fire the laser + TIMER_Set( self, "noLob", Q_irand( 1000, 2000 ) ); } - NPCInfo->greetEnt = 0; - } - - // Otherwise Nothing To See Here - //------------------------------- - return false; - } - - - // If We Already Have A Target Vehicle, Make Sure It Is Still Valid - //------------------------------------------------------------------ - if (NPCInfo->greetEnt) - { - if (!NPCInfo->greetEnt->inuse || - !NPCInfo->greetEnt->m_pVehicle || - !NPCInfo->greetEnt->m_pVehicle->m_pVehicleInfo) - { - NPCInfo->greetEnt = Vehicle_Find(NPC); } - else + else if ( mod == MOD_REPEATER && !Q_irand( 0, 5 ) ) { - if (NPCInfo->greetEnt->owner && NPCInfo->greetEnt->owner!=NPC) + if ( TIMER_Done( self, "noLob" ) ) { - NPCInfo->greetEnt = Vehicle_Find(NPC); + self->NPC->scriptFlags |= SCF_ALT_FIRE; + self->alt_fire = qtrue; + TIMER_Set( self, "noRapid", Q_irand( 2000, 6000 ) ); + } + else + {//hopefully this will make us fire the laser + TIMER_Set( self, "noRapid", Q_irand( 1000, 2000 ) ); } } } +} - // If We Have An Enemy, Try To Find A Vehicle Nearby - //--------------------------------------------------- - else - { - NPCInfo->greetEnt = Vehicle_Find(NPC); +/* +------------------------- +GM_HoldPosition +------------------------- +*/ + +static void GM_HoldPosition( void ) +{ + NPC_FreeCombatPoint( NPCInfo->combatPoint, qtrue ); + if ( !Q3_TaskIDPending( NPC, TID_MOVE_NAV ) ) + {//don't have a script waiting for me to get to my point, okay to stop trying and stand + NPCInfo->goalEntity = NULL; } +} - // If No Vehicle Available, Continue As Usual - //-------------------------------------------- - if (!NPCInfo->greetEnt) - { - return false; +/* +------------------------- +GM_Move +------------------------- +*/ +static qboolean GM_Move( void ) +{ + NPCInfo->combatMove = qtrue;//always move straight toward our goal + + qboolean moved = NPC_MoveToGoal( qtrue ); +// navInfo_t info; + + //Get the move info +// NAV_GetLastMove( info ); + + //FIXME: if we bump into another one of our guys and can't get around him, just stop! + //If we hit our target, then stop and fire! +// if ( frameNavInfo.flags & NIF_COLLISION ) +// { +// if ( info.blocker == NPC->enemy ) +// { +// GM_HoldPosition(); +// } +// } + + //If our move failed, then reset + if ( moved == qfalse ) + {//FIXME: if we're going to a combat point, need to pick a different one + if ( !Q3_TaskIDPending( NPC, TID_MOVE_NAV ) ) + {//can't transfer movegoal or stop when a script we're running is waiting to complete + GM_HoldPosition(); + } } + return moved; +} +/* +------------------------- +NPC_BSGM_Patrol +------------------------- +*/ - if (NPCInfo->greetEnt->owner==NPC) +void NPC_BSGM_Patrol( void ) +{ + if ( NPC_CheckPlayerTeamStealth() ) { - Pilot_Steer_Vehicle(); + NPC_UpdateAngles( qtrue, qtrue ); + return; } - else + + //If we have somewhere to go, then do that + if ( UpdateGoal() ) { - Pilot_Goto_Vehicle(); + ucmd.buttons |= BUTTON_WALKING; + NPC_MoveToGoal( qtrue ); } - Pilot_Update_Enemy(); - return true; + NPC_UpdateAngles( qtrue, qtrue ); } +/* +------------------------- +GM_CheckMoveState +------------------------- +*/ +extern qboolean NAV_HitNavGoal( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t dest, int radius, qboolean flying ); +static void GM_CheckMoveState( void ) +{ + if ( Q3_TaskIDPending( NPC, TID_MOVE_NAV ) ) + {//moving toward a goal that a script is waiting on, so don't stop for anything! + AImove = qtrue; + } + //See if we're moving towards a goal, not the enemy + if ( ( NPCInfo->goalEntity != NPC->enemy ) && ( NPCInfo->goalEntity != NULL ) ) + { + //Did we make it? + if ( NAV_HitNavGoal( NPC->currentOrigin, NPC->mins, NPC->maxs, NPCInfo->goalEntity->currentOrigin, 16, qfalse ) || + ( !Q3_TaskIDPending( NPC, TID_MOVE_NAV ) && enemyLOS && enemyDist <= 10000 ) ) + {//either hit our navgoal or our navgoal was not a crucial (scripted) one (maybe a combat point) and we're scouting and found our enemy + NPC_ReachedGoal(); + //don't attack right away + TIMER_Set( NPC, "attackDelay", Q_irand( 250, 500 ) ); //FIXME: Slant for difficulty levels + return; + } + } +} +/* +------------------------- +GM_CheckFireState +------------------------- +*/ - -//////////////////////////////////////////////////////////////////////////////////////// -// -//////////////////////////////////////////////////////////////////////////////////////// -void Pilot_Update_Enemy() +static void GM_CheckFireState( void ) { - if (!TIMER_Exists(NPC, "PilotRemoveTime")) - { - TIMER_Set(NPC, "PilotRemoveTime", MIN_STAY_VIEWABLE_TIME); + if ( enemyCS ) + {//if have a clear shot, always try + return; } - if (TIMER_Done(NPC, "NextPilotCheckEnemyTime")) + if ( !VectorCompare( NPC->client->ps.velocity, vec3_origin ) ) + {//if moving at all, don't do this + return; + } + + //See if we should continue to fire on their last position + if ( !hitAlly && NPCInfo->enemyLastSeenTime > 0 ) { - TIMER_Set(NPC, "NextPilotCheckEnemyTime", Q_irand(1000,2000)); - if (NPC->enemy && Distance(NPC->currentOrigin, NPC->enemy->currentOrigin)>1000.0f) + if ( level.time - NPCInfo->enemyLastSeenTime < 10000 ) { - mPilotViewTraceCount ++; - gi.trace(&mPilotViewTrace, - NPC->currentOrigin, - 0, - 0, - NPC->enemy->currentOrigin, - NPC->s.number, - MASK_SHOT, - (EG2_Collision)0, 0); - - if ((mPilotViewTrace.allsolid==qfalse) && - (mPilotViewTrace.startsolid==qfalse ) && - ((mPilotViewTrace.entityNum==NPC->enemy->s.number)||(mPilotViewTrace.entityNum==NPC->enemy->s.m_iVehicleNum))) + if ( !Q_irand( 0, 10 ) ) { - TIMER_Set(NPC, "PilotRemoveTime", MIN_STAY_VIEWABLE_TIME); + //Fire on the last known position + vec3_t muzzle, dir, angles; + qboolean tooClose = qfalse; + qboolean tooFar = qfalse; + + CalcEntitySpot( NPC, SPOT_HEAD, muzzle ); + if ( VectorCompare( impactPos, vec3_origin ) ) + {//never checked ShotEntity this frame, so must do a trace... + trace_t tr; + //vec3_t mins = {-2,-2,-2}, maxs = {2,2,2}; + vec3_t forward, end; + AngleVectors( NPC->client->ps.viewangles, forward, NULL, NULL ); + VectorMA( muzzle, 8192, forward, end ); + gi.trace( &tr, muzzle, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT, G2_NOCOLLIDE, 0 ); + VectorCopy( tr.endpos, impactPos ); + } + + //see if impact would be too close to me + float distThreshold = 16384/*128*128*/;//default + if ( NPC->s.weapon == WP_REPEATER ) + { + if ( NPCInfo->scriptFlags&SCF_ALT_FIRE ) + { + distThreshold = 65536/*256*256*/; + } + } + + float dist = DistanceSquared( impactPos, muzzle ); + + if ( dist < distThreshold ) + {//impact would be too close to me + tooClose = qtrue; + } + else if ( level.time - NPCInfo->enemyLastSeenTime > 5000 ) + {//we've haven't seen them in the last 5 seconds + //see if it's too far from where he is + distThreshold = 65536/*256*256*/;//default + if ( NPC->s.weapon == WP_REPEATER ) + { + if ( NPCInfo->scriptFlags&SCF_ALT_FIRE ) + { + distThreshold = 262144/*512*512*/; + } + } + dist = DistanceSquared( impactPos, NPCInfo->enemyLastSeenLocation ); + if ( dist > distThreshold ) + {//impact would be too far from enemy + tooFar = qtrue; + } + } + + if ( !tooClose && !tooFar ) + {//okay too shoot at last pos + VectorSubtract( NPCInfo->enemyLastSeenLocation, muzzle, dir ); + VectorNormalize( dir ); + vectoangles( dir, angles ); + + NPCInfo->desiredYaw = angles[YAW]; + NPCInfo->desiredPitch = angles[PITCH]; + + shoot = qtrue; + faceEnemy = qfalse; + return; + } } } - else - { - TIMER_Set(NPC, "PilotRemoveTime", MIN_STAY_VIEWABLE_TIME); - } } +} - if (TIMER_Done(NPC, "PilotRemoveTime")) - { - if (NPCInfo->greetEnt->owner==NPC) - { - NPCInfo->greetEnt->e_ThinkFunc = thinkF_G_FreeEntity; - NPCInfo->greetEnt->nextthink = level.time; - } - NPC->e_ThinkFunc = thinkF_G_FreeEntity; - NPC->nextthink = level.time; +void NPC_GM_StartLaser( void ) +{ + if ( !NPC->lockCount ) + {//haven't already started a laser attack + //warm up for the beam attack + NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_RAISEWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + TIMER_Set( NPC, "beamDelay", NPC->client->ps.torsoAnimTimer ); + TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoAnimTimer+3000 ); + NPC->lockCount = 1; + //turn on warmup effect + G_PlayEffect( "galak/beam_warmup", NPC->s.number ); + G_SoundOnEnt( NPC, CHAN_AUTO, "sound/weapons/galak/lasercharge.wav" ); } } +void GM_StartGloat( void ) +{ + NPC->wait = 0; + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_galakface_off", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_galakhead_off", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_eyes_mouth_off", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_collar_off", TURN_ON ); + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_galaktorso_off", TURN_ON ); + NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND2TO1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + NPC->client->ps.legsAnimTimer += 500; + NPC->client->ps.torsoAnimTimer += 500; +} +/* +------------------------- +NPC_BSGM_Attack +------------------------- +*/ -//////////////////////////////////////////////////////////////////////////////////////// -// -//////////////////////////////////////////////////////////////////////////////////////// -void Pilot_Goto_Vehicle() +void NPC_BSGM_Attack( void ) { - STEER::Activate(NPC); + //Don't do anything if we're hurt + if ( NPC->painDebounceTime > level.time ) { - if (STEER::Reached(NPC, NPCInfo->greetEnt, 80.0f)) + NPC_UpdateAngles( qtrue, qtrue ); + return; + } + + //FIXME: if killed enemy, use victory anim + if ( NPC->enemy && NPC->enemy->health <= 0 + && !NPC->enemy->s.number ) + {//my enemy is dead + if ( NPC->client->ps.torsoAnim == BOTH_STAND2TO1 ) { - NPC_Use(NPCInfo->greetEnt, NPC, NPC); + if ( NPC->client->ps.torsoAnimTimer <= 500 ) + { + G_AddVoiceEvent( NPC, Q_irand( EV_VICTORY1, EV_VICTORY3 ), 3000 ); + NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + NPC->client->ps.legsAnimTimer += 500; + NPC->client->ps.torsoAnimTimer += 500; + } } - else if (NAV::OnNeighboringPoints(NPC, NPCInfo->greetEnt)) + else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1START ) { - STEER::Persue(NPC, NPCInfo->greetEnt, 50.0f, 0.0f, 30.0f, 0.0f, true); + if ( NPC->client->ps.torsoAnimTimer <= 500 ) + { + NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STARTGESTURE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + NPC->client->ps.legsAnimTimer += 500; + NPC->client->ps.torsoAnimTimer += 500; + } } - else + else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STARTGESTURE ) + { + if ( NPC->client->ps.torsoAnimTimer <= 500 ) + { + NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + NPC->client->ps.legsAnimTimer += 500; + NPC->client->ps.torsoAnimTimer += 500; + } + } + else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STOP ) + { + if ( NPC->client->ps.torsoAnimTimer <= 500 ) + { + NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + NPC->client->ps.legsAnimTimer = -1; + NPC->client->ps.torsoAnimTimer = -1; + } + } + else if ( NPC->wait ) { - if (!NAV::GoTo(NPC, NPCInfo->greetEnt)) + if ( TIMER_Done( NPC, "gloatTime" ) ) { - STEER::Stop(NPC); + GM_StartGloat(); + } + else if ( DistanceHorizontalSquared( NPC->client->renderInfo.eyePoint, NPC->enemy->currentOrigin ) > 4096 && (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )//64 squared + { + NPCInfo->goalEntity = NPC->enemy; + GM_Move(); + } + else + {//got there + GM_StartGloat(); } } + NPC_FaceEnemy( qtrue ); + NPC_UpdateAngles( qtrue, qtrue ); + return; } - STEER::AvoidCollisions(NPC); - STEER::DeActivate(NPC, &ucmd); - NPC_UpdateAngles(qtrue, qtrue); -} - -extern bool VEH_StartStrafeRam(Vehicle_t *pVeh, bool Right); - -//////////////////////////////////////////////////////////////////////////////////////// -// -//////////////////////////////////////////////////////////////////////////////////////// -void Pilot_Steer_Vehicle() -{ - if (!NPC->enemy || !NPC->enemy->client) + //If we don't have an enemy, just idle + if ( NPC_CheckEnemyExt() == qfalse || !NPC->enemy ) { + NPC->enemy = NULL; + NPC_BSGM_Patrol(); return; } + enemyLOS = enemyCS = qfalse; + AImove = qtrue; + faceEnemy = qfalse; + shoot = qfalse; + hitAlly = qfalse; + VectorClear( impactPos ); + enemyDist = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ); - - - - -// SETUP -//======= - // Setup Actor Data - //------------------ - CVec3 ActorPos(NPC->currentOrigin); - CVec3 ActorAngles(NPC->currentAngles); - ActorAngles[2] = 0; - Vehicle_t* ActorVeh = NPCInfo->greetEnt->m_pVehicle; - bool ActorInTurbo = (ActorVeh->m_iTurboTime>level.time); - float ActorSpeed = (ActorVeh)?(VectorLength(ActorVeh->m_pParentEntity->client->ps.velocity)):(NPC->client->ps.speed); - - - // If my vehicle is spinning out of control, just hold on, we're going to die!!!!! - //--------------------------------------------------------------------------------- - if (ActorVeh && (ActorVeh->m_ulFlags & VEH_OUTOFCONTROL)) + if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 || + NPC->client->ps.torsoAnim == BOTH_ATTACK5 ) { - if (NPC->client->ps.weapon!=WP_NONE) - { - NPC_ChangeWeapon(WP_NONE); + shoot = qfalse; + if ( TIMER_Done( NPC, "smackTime" ) && !NPCInfo->blockedDebounceTime ) + {//time to smack + //recheck enemyDist and InFront + if ( enemyDist < MELEE_DIST_SQUARED && InFront( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 0.3f ) ) + { + vec3_t smackDir; + VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, smackDir ); + smackDir[2] += 30; + VectorNormalize( smackDir ); + //hurt them + G_Sound( NPC->enemy, G_SoundIndex( "sound/weapons/galak/skewerhit.wav" ) ); + G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->currentOrigin, (g_spskill->integer+1)*Q_irand( 5, 10), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); + if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 ) + {//smackdown + int knockAnim = BOTH_KNOCKDOWN1; + if ( PM_CrouchAnim( NPC->enemy->client->ps.legsAnim ) ) + {//knockdown from crouch + knockAnim = BOTH_KNOCKDOWN4; + } + //throw them + smackDir[2] = 1; + VectorNormalize( smackDir ); + G_Throw( NPC->enemy, smackDir, 50 ); + NPC_SetAnim( NPC->enemy, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else + {//uppercut + //throw them + G_Throw( NPC->enemy, smackDir, 100 ); + //make them backflip + NPC_SetAnim( NPC->enemy, SETANIM_BOTH, BOTH_KNOCKDOWN5, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + //done with the damage + NPCInfo->blockedDebounceTime = 1; + } } - ucmd.buttons &=~BUTTON_ATTACK; - ucmd.buttons &=~BUTTON_ALT_ATTACK; - return; } - - CVec3 ActorDirection; - AngleVectors(ActorAngles.v, ActorDirection.v, 0, 0); - - CVec3 ActorFuturePos(ActorPos); - ActorFuturePos.ScaleAdd(ActorDirection, FUTURE_PRED_DIST); - - bool ActorDoTurbo = false; - bool ActorAccelerate = false; - bool ActorAimAtTarget= true; - float ActorYawOffset = 0.0f; - - - // Setup Enemy Data - //------------------ - CVec3 EnemyPos(NPC->enemy->currentOrigin); - CVec3 EnemyAngles(NPC->enemy->currentAngles); - EnemyAngles[2] = 0; - Vehicle_t* EnemyVeh = (NPC->enemy->s.m_iVehicleNum)?(g_entities[NPC->enemy->s.m_iVehicleNum].m_pVehicle):(0); - bool EnemyInTurbo = (EnemyVeh && EnemyVeh->m_iTurboTime>level.time); - float EnemySpeed = (EnemyVeh)?(EnemyVeh->m_pParentEntity->client->ps.speed):(NPC->enemy->resultspeed); - bool EnemySlideBreak = (EnemyVeh && (EnemyVeh->m_ulFlags&VEH_SLIDEBREAKING || EnemyVeh->m_ulFlags&VEH_STRAFERAM)); - bool EnemyDead = (NPC->enemy->health<=0); - - bool ActorFlank = (NPCInfo->lastAvoidSteerSideDebouncer>level.time && EnemyVeh && EnemySpeed>10.0f); - - CVec3 EnemyDirection; - CVec3 EnemyRight; - AngleVectors(EnemyAngles.v, EnemyDirection.v, EnemyRight.v, 0); - - CVec3 EnemyFuturePos(EnemyPos); - EnemyFuturePos.ScaleAdd(EnemyDirection, FUTURE_PRED_DIST); - - ESide EnemySide = ActorPos.LRTest(EnemyPos, EnemyFuturePos); - CVec3 EnemyFlankPos(EnemyFuturePos); - EnemyFlankPos.ScaleAdd(EnemyRight, (EnemySide==Side_Right)?(FUTURE_SIDE_DIST):(-FUTURE_SIDE_DIST)); - - // Debug Draw Enemy Data - //----------------------- - if (false) - { - CG_DrawEdge(EnemyPos.v, EnemyFuturePos.v, EDGE_IMPACT_SAFE); - CG_DrawEdge(EnemyFuturePos.v, EnemyFlankPos.v, EDGE_IMPACT_SAFE); + else if ( NPC->lockCount ) //already shooting laser + {//sometimes use the laser beam attack, but only after he's taken down our generator + shoot = qfalse; + if ( NPC->lockCount == 1 ) + {//charging up + if ( TIMER_Done( NPC, "beamDelay" ) ) + {//time to start the beam + int laserAnim; + if ( Q_irand( 0, 1 ) ) + { + laserAnim = BOTH_ATTACK2; + } + else + { + laserAnim = BOTH_ATTACK7; + } + NPC_SetAnim( NPC, SETANIM_BOTH, laserAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoAnimTimer + Q_irand( 1000, 3000 ) ); + //turn on beam effect + NPC->lockCount = 2; + G_PlayEffect( "galak/trace_beam", NPC->s.number ); + NPC->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" ); + if ( !NPCInfo->coverTarg ) + {//for moving looping sound at end of trace + NPCInfo->coverTarg = G_Spawn(); + if ( NPCInfo->coverTarg ) + { + G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint ); + NPCInfo->coverTarg->svFlags |= SVF_BROADCAST; + NPCInfo->coverTarg->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" ); + } + } + } + } + else + {//in the actual attack now + if ( !NPC->client->ps.torsoAnimTimer ) + {//attack done! + NPC->lockCount = 0; + G_FreeEntity( NPCInfo->coverTarg ); + NPC->s.loopSound = 0; + NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_DROPWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoAnimTimer ); + } + else + {//attack still going + //do the trace and damage + trace_t trace; + vec3_t end, mins={-3,-3,-3}, maxs={3,3,3}; + VectorMA( NPC->client->renderInfo.muzzlePoint, 1024, NPC->client->renderInfo.muzzleDir, end ); + gi.trace( &trace, NPC->client->renderInfo.muzzlePoint, mins, maxs, end, NPC->s.number, MASK_SHOT, G2_NOCOLLIDE, 0 ); + if ( trace.allsolid || trace.startsolid ) + {//oops, in a wall + if ( NPCInfo->coverTarg ) + { + G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint ); + } + } + else + {//clear + if ( trace.fraction < 1.0f ) + {//hit something + gentity_t *traceEnt = &g_entities[trace.entityNum]; + if ( traceEnt && traceEnt->takedamage ) + {//damage it + G_SoundAtSpot( trace.endpos, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ), qfalse ); + G_Damage( traceEnt, NPC, NPC, NPC->client->renderInfo.muzzleDir, trace.endpos, 10, 0, MOD_ENERGY ); + } + } + if ( NPCInfo->coverTarg ) + { + G_SetOrigin( NPCInfo->coverTarg, trace.endpos ); + } + if ( !Q_irand( 0, 5 ) ) + { + G_SoundAtSpot( trace.endpos, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ), qfalse ); + } + } + } + } + } + else + {//Okay, we're not in a special attack, see if we should switch weapons or start a special attack + /* + if ( NPC->s.weapon == WP_REPEATER + && !(NPCInfo->scriptFlags & SCF_ALT_FIRE)//using rapid-fire + && NPC->enemy->s.weapon == WP_SABER //enemy using saber + && NPC->client && (NPC->client->ps.saberEventFlags&SEF_DEFLECTED) + && !Q_irand( 0, 50 ) ) + {//he's deflecting my shots, switch to the laser or the lob fire for a while + TIMER_Set( NPC, "noRapid", Q_irand( 2000, 6000 ) ); + NPCInfo->scriptFlags |= SCF_ALT_FIRE; + NPC->alt_fire = qtrue; + if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH && (Q_irand( 0, 1 )||enemyDist < MAX_LOB_DIST_SQUARED) ) + {//shield down, use laser + NPC_GM_StartLaser(); + } + } + else*/ + if ( !NPC->client->ps.powerups[PW_GALAK_SHIELD] + && enemyDist < MELEE_DIST_SQUARED + && InFront( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 0.3f ) + && G_StandardHumanoid( NPC->enemy ) )//within 80 and in front + {//our shield is down, and enemy within 80, if very close, use melee attack to slap away + if ( TIMER_Done( NPC, "attackDelay" ) ) + { + //animate me + int swingAnim; + if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH ) + {//generator down, use random melee + swingAnim = Q_irand( BOTH_ATTACK4, BOTH_ATTACK5 );//smackdown or uppercut + } + else + {//always knock-away + swingAnim = BOTH_ATTACK5;//uppercut + } + //FIXME: swing sound + NPC_SetAnim( NPC, SETANIM_BOTH, swingAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoAnimTimer + Q_irand( 1000, 3000 ) ); + //delay the hurt until the proper point in the anim + TIMER_Set( NPC, "smackTime", 600 ); + NPCInfo->blockedDebounceTime = 0; + //FIXME: say something? + } + } + else if ( !NPC->lockCount && NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH + && TIMER_Done( NPC, "attackDelay" ) + && InFront( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 0.3f ) + && ((!Q_irand( 0, 10*(2-g_spskill->integer))&& enemyDist > MIN_LOB_DIST_SQUARED&& enemyDist < MAX_LOB_DIST_SQUARED) + ||(!TIMER_Done( NPC, "noLob" )&&!TIMER_Done( NPC, "noRapid" ))) + && NPC->enemy->s.weapon != WP_TURRET ) + {//sometimes use the laser beam attack, but only after he's taken down our generator + shoot = qfalse; + NPC_GM_StartLaser(); + } + else if ( enemyDist < MIN_LOB_DIST_SQUARED + && (NPC->enemy->s.weapon != WP_TURRET || Q_stricmp( "PAS", NPC->enemy->classname )) + && TIMER_Done( NPC, "noRapid" ) )//256 + {//enemy within 256 + if ( (NPC->client->ps.weapon == WP_REPEATER) && (NPCInfo->scriptFlags & SCF_ALT_FIRE) ) + {//shooting an explosive, but enemy too close, switch to primary fire + NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; + NPC->alt_fire = qfalse; + //FIXME: use weap raise & lower anims + NPC_ChangeWeapon( WP_REPEATER ); + } + } + else if ( (enemyDist > MAX_LOB_DIST_SQUARED || (NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname ))) + && TIMER_Done( NPC, "noLob" ) )//448 + {//enemy more than 448 away and we are ready to try lob fire again + if ( (NPC->client->ps.weapon == WP_REPEATER) && !(NPCInfo->scriptFlags & SCF_ALT_FIRE) ) + {//enemy far enough away to use lobby explosives + NPCInfo->scriptFlags |= SCF_ALT_FIRE; + NPC->alt_fire = qtrue; + //FIXME: use weap raise & lower anims + NPC_ChangeWeapon( WP_REPEATER ); + } + } } - - // Setup Move And Aim Directions - //------------------------------- - CVec3 MoveDirection((ActorFlank)?(EnemyFlankPos):(EnemyFuturePos)); - MoveDirection -= ActorPos; - float MoveDistance = MoveDirection.SafeNorm(); - float MoveAccuracy = MoveDirection.Dot(ActorDirection); - - CVec3 AimDirection(EnemyPos); - AimDirection -= ActorPos; - float AimDistance = AimDirection.SafeNorm(); - float AimAccuracy = AimDirection.Dot(ActorDirection); - - - - if (!ActorFlank && TIMER_Done(NPC, "FlankAttackCheck")) + //can we see our target? + if ( NPC_ClearLOS( NPC->enemy ) ) { - TIMER_Set(NPC, "FlankAttackCheck", Q_irand(1000, 3000)); - if (MoveDistance<4000 && Q_irand(0, 1)==0) + NPCInfo->enemyLastSeenTime = level.time;//used here for aim debouncing, not always a clear LOS + enemyLOS = qtrue; + + if ( NPC->client->ps.weapon == WP_NONE ) { - NPCInfo->lastAvoidSteerSideDebouncer = level.time + Q_irand(8000, 14000); + enemyCS = qfalse;//not true, but should stop us from firing + NPC_AimAdjust( -1 );//adjust aim worse longer we have no weapon + } + else + {//can we shoot our target? + if ( ((NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist < MIN_LOB_DIST_SQUARED )//256 + { + enemyCS = qfalse;//not true, but should stop us from firing + hitAlly = qtrue;//us! + //FIXME: if too close, run away! + } + else + { + int hit = NPC_ShotEntity( NPC->enemy, impactPos ); + gentity_t *hitEnt = &g_entities[hit]; + if ( hit == NPC->enemy->s.number + || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) + || ( hitEnt && hitEnt->takedamage ) ) + {//can hit enemy or will hit glass or other breakable, so shoot anyway + enemyCS = qtrue; + NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy + VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation ); + } + else + {//Hmm, have to get around this bastard + NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy + if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam ) + {//would hit an ally, don't fire!!! + hitAlly = qtrue; + } + else + {//Check and see where our shot *would* hit... if it's not close to the enemy (within 256?), then don't fire + } + } + } } } - - - - // Fly By Sounds - //--------------- - if ((ActorVeh->m_pVehicleInfo->soundFlyBy || ActorVeh->m_pVehicleInfo->soundFlyBy2) && - EnemyVeh && - MoveDistance<800 && - ActorSpeed>500.0f && - TIMER_Done(NPC, "FlybySoundDebouncer") - ) + else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) ) { - if (EnemySpeed<100.0f || (ActorDirection.Dot(EnemyDirection)*(MoveDistance/800.0f))<-0.5f) + if ( TIMER_Done( NPC, "talkDebounce" ) && !Q_irand( 0, 10 ) ) { - TIMER_Set(NPC, "FlybySoundDebouncer", 2000); - int soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy; - if (ActorVeh->m_pVehicleInfo->soundFlyBy2 && (!soundFlyBy || !Q_irand(0,1))) + if ( NPCInfo->enemyCheckDebounceTime < 8 ) { - soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy2; + int speech = -1; + switch( NPCInfo->enemyCheckDebounceTime ) + { + case 0: + case 1: + case 2: + speech = EV_CHASE1 + NPCInfo->enemyCheckDebounceTime; + break; + case 3: + case 4: + case 5: + speech = EV_COVER1 + NPCInfo->enemyCheckDebounceTime-3; + break; + case 6: + case 7: + speech = EV_ESCAPING1 + NPCInfo->enemyCheckDebounceTime-6; + break; + } + NPCInfo->enemyCheckDebounceTime++; + if ( speech != -1 ) + { + G_AddVoiceEvent( NPC, speech, Q_irand( 3000, 5000 ) ); + TIMER_Set( NPC, "talkDebounce", Q_irand( 5000, 7000 ) ); + } } - G_Sound(ActorVeh->m_pParentEntity, soundFlyBy); } - } + NPCInfo->enemyLastSeenTime = level.time; + int hit = NPC_ShotEntity( NPC->enemy, impactPos ); + gentity_t *hitEnt = &g_entities[hit]; + if ( hit == NPC->enemy->s.number + || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) + || ( hitEnt && hitEnt->takedamage ) ) + {//can hit enemy or will hit glass or other breakable, so shoot anyway + enemyCS = qtrue; + } + else + { + faceEnemy = qtrue; + NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy + } + } -// FLY PAST BEHAVIOR -//=================== - if (EnemySlideBreak || !TIMER_Done(NPC, "MinHoldDirectionTime")) + if ( enemyLOS ) + { + faceEnemy = qtrue; + } + else { - if (TIMER_Done(NPC, "MinHoldDirectionTime")) + if ( !NPCInfo->goalEntity ) { - TIMER_Set(NPC, "MinHoldDirectionTime", 500); // Hold For At Least 500 ms + NPCInfo->goalEntity = NPC->enemy; + } + if ( NPCInfo->goalEntity == NPC->enemy ) + {//for now, always chase the enemy + AImove = qtrue; } - ActorAccelerate = true; // Go - ActorAimAtTarget = false; // Don't Alter Our Aim Direction - ucmd.buttons &=~BUTTON_VEH_SPEED; // Let Normal Vehicle Controls Go } - - -// FLANKING BEHAVIOR -//=================== - else if (ActorFlank) + if ( enemyCS ) { - ActorAccelerate = true; - ActorDoTurbo = (MoveDistance>2500 || EnemyInTurbo); - ucmd.buttons |= BUTTON_VEH_SPEED; // Tells PMove to use the ps.speed we calculate here, not the one from g_vehicles.c - + shoot = qtrue; + //NPCInfo->enemyCheckDebounceTime = level.time;//actually used here as a last actual LOS + } + else + { + if ( !NPCInfo->goalEntity ) + { + NPCInfo->goalEntity = NPC->enemy; + } + if ( NPCInfo->goalEntity == NPC->enemy ) + {//for now, always chase the enemy + AImove = qtrue; + } + } - // For Flanking, We Calculate The Speed By Hand, Rather Than Using Pure Accelerate / No Accelerate Functionality - //--------------------------------------------------------------------------------------------------------------- - NPC->client->ps.speed = ActorVeh->m_pVehicleInfo->speedMax * ((ActorInTurbo)?(1.35f):(1.15f)); + //Check for movement to take care of + GM_CheckMoveState(); + //See if we should override shooting decision with any special considerations + GM_CheckFireState(); - // If In Slowing Distance, Scale Down The Speed As We Approach Our Move Target - //----------------------------------------------------------------------------- - if (MoveDistanceclient->ps.speed *= (MoveDistance/ATTACK_FLANK_SLOWING); - NPC->client->ps.speed += EnemySpeed; - - // Match Enemy Speed - //------------------- - if (NPC->client->ps.speed<5.0f && EnemySpeed<5.0f) - { - NPC->client->ps.speed = EnemySpeed; + if ( NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE) && shoot && TIMER_Done( NPC, "attackDelay" ) ) + { + vec3_t muzzle; + vec3_t angles; + vec3_t target; + vec3_t velocity = {0,0,0}; + vec3_t mins = {-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE}, maxs = {REPEATER_ALT_SIZE,REPEATER_ALT_SIZE,REPEATER_ALT_SIZE}; + + CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); + + VectorCopy( NPC->enemy->currentOrigin, target ); + + target[0] += Q_flrand( -5, 5 )+(Q_flrand(-1.0f, 1.0f)*(6-NPCInfo->currentAim)*2); + target[1] += Q_flrand( -5, 5 )+(Q_flrand(-1.0f, 1.0f)*(6-NPCInfo->currentAim)*2); + target[2] += Q_flrand( -5, 5 )+(Q_flrand(-1.0f, 1.0f)*(6-NPCInfo->currentAim)*2); + + //Find the desired angles + qboolean clearshot = WP_LobFire( NPC, muzzle, target, mins, maxs, MASK_SHOT|CONTENTS_LIGHTSABER, + velocity, qtrue, NPC->s.number, NPC->enemy->s.number, + 300, 1100, 1500, qtrue ); + if ( VectorCompare( vec3_origin, velocity ) || (!clearshot&&enemyLOS&&enemyCS) ) + {//no clear lob shot and no lob shot that will hit something breakable + if ( enemyLOS && enemyCS && TIMER_Done( NPC, "noRapid" ) ) + {//have a clear straight shot, so switch to primary + NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; + NPC->alt_fire = qfalse; + NPC_ChangeWeapon( WP_REPEATER ); + //keep this weap for a bit + TIMER_Set( NPC, "noLob", Q_irand( 500, 1000 ) ); } - - // Extra Slow Down When Out In Front - //----------------------------------- - if (MoveAccuracy<0.0f) + else { - NPC->client->ps.speed *= (MoveAccuracy + 1.0f); + shoot = qfalse; } + } + else + { + vectoangles( velocity, angles ); + NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); + NPCInfo->desiredPitch = AngleNormalize360( angles[PITCH] ); - MoveDirection *= (MoveDistance/ATTACK_FLANK_SLOWING); - EnemyDirection *= 1.0f - (MoveDistance/ATTACK_FLANK_SLOWING); - MoveDirection += EnemyDirection; - - if (TIMER_Done(NPC, "RamCheck")) - { - TIMER_Set(NPC, "RamCheck", Q_irand(1000, 3000)); - if (MoveDistanceclient->hiddenDir ); + NPC->client->hiddenDist = VectorNormalize ( NPC->client->hiddenDir ); } } + else if ( faceEnemy ) + {//face the enemy + NPC_FaceEnemy( qtrue ); + } - -// NORMAL CHASE BEHAVIOR -//======================= - else + if ( !TIMER_Done( NPC, "standTime" ) ) { - if (!EnemyVeh && AimAccuracy>0.99f && MoveDistance<500 && !EnemyDead) + AImove = qfalse; + } + if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) ) + {//not supposed to chase my enemies + if ( NPCInfo->goalEntity == NPC->enemy ) + {//goal is my entity, so don't move + AImove = qfalse; + } + } + + if ( AImove && !NPC->lockCount ) + {//move toward goal + if ( NPCInfo->goalEntity + && NPC->client->ps.legsAnim != BOTH_ALERT1 + && NPC->client->ps.legsAnim != BOTH_ATTACK2 + && NPC->client->ps.legsAnim != BOTH_ATTACK4 + && NPC->client->ps.legsAnim != BOTH_ATTACK5 + && NPC->client->ps.legsAnim != BOTH_ATTACK7 ) { - ActorAccelerate = true; - ActorDoTurbo = false; + AImove = GM_Move(); } else { - ActorAccelerate = ((MoveDistance>500 && EnemySpeed>20.0f) || MoveDistance>1000); - ActorDoTurbo = (MoveDistance>3000 && EnemySpeed>20.0f); + AImove = qfalse; } - ucmd.buttons &=~BUTTON_VEH_SPEED; } + if ( !TIMER_Done( NPC, "flee" ) ) + {//running away + faceEnemy = qfalse; + } + //FIXME: check scf_face_move_dir here? - -// APPLY RESULTS -//======================= - // Decide Turbo - //-------------- - if (ActorDoTurbo || ActorInTurbo) - { - ucmd.buttons |= BUTTON_ALT_ATTACK; + if ( !faceEnemy ) + {//we want to face in the dir we're running + if ( !AImove ) + {//if we haven't moved, we should look in the direction we last looked? + VectorCopy( NPC->client->ps.viewangles, NPCInfo->lastPathAngles ); + } + if ( AImove ) + {//don't run away and shoot + NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW]; + NPCInfo->desiredPitch = 0; + shoot = qfalse; + } } - else + NPC_UpdateAngles( qtrue, qtrue ); + + if ( NPCInfo->scriptFlags & SCF_DONT_FIRE ) { - ucmd.buttons &=~BUTTON_ALT_ATTACK; + shoot = qfalse; } - // Decide Acceleration - //--------------------- - ucmd.forwardmove = (ActorAccelerate)?(127):(0); - - - - // Decide To Shoot - //----------------- - ucmd.buttons &=~BUTTON_ATTACK; - ucmd.rightmove = 0; - if (AimDistance<2000 && !EnemyDead) + if ( NPC->enemy && NPC->enemy->enemy ) { - // If Doing A Ram Attack - //----------------------- - if (ActorYawOffset!=0) + if ( NPC->enemy->s.weapon == WP_SABER && NPC->enemy->enemy->s.weapon == WP_SABER ) + {//don't shoot at an enemy jedi who is fighting another jedi, for fear of injuring one or causing rogue blaster deflections (a la Obi Wan/Vader duel at end of ANH) + shoot = qfalse; + } + } + //FIXME: don't shoot right away! + if ( shoot ) + {//try to shoot if it's time + if ( TIMER_Done( NPC, "attackDelay" ) ) { - if (NPC->client->ps.weapon!=WP_NONE) + if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here { - NPC_ChangeWeapon(WP_NONE); + WeaponThink( qtrue ); } - ucmd.buttons &=~BUTTON_ATTACK; } - else if (AimAccuracy>ATTACK_FWD) - { - if (NPC->client->ps.weapon!=WP_NONE) + } + + //also: + if ( NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname ) ) + {//crush turrets + if ( G_BoundsOverlap( NPC->absmin, NPC->absmax, NPC->enemy->absmin, NPC->enemy->absmax ) ) + {//have to do this test because placed turrets are not solid to NPCs (so they don't obstruct navigation) + if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 ) { - NPC_ChangeWeapon(WP_NONE); + NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME; + G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_ELECTROCUTE ); + } + else + { + G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); } - ucmd.buttons |= BUTTON_ATTACK; } - else if (AimAccuracy-AIM_SIDE) - { - if (NPC->client->ps.weapon!=WP_BLASTER) + } + else if ( NPCInfo->touchedByPlayer != NULL && NPCInfo->touchedByPlayer == NPC->enemy ) + {//touched enemy + if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 ) + {//zap him! + //animate me + NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK6, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoAnimTimer ); + TIMER_Set( NPC, "standTime", NPC->client->ps.legsAnimTimer ); + //FIXME: debounce this? + NPCInfo->touchedByPlayer = NULL; + //FIXME: some shield effect? + NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME; + vec3_t smackDir; + VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, smackDir ); + smackDir[2] += 30; + VectorNormalize( smackDir ); + G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->currentOrigin, (g_spskill->integer+1)*Q_irand( 5, 10), DAMAGE_NO_KNOCKBACK, MOD_ELECTROCUTE ); + //throw them + G_Throw( NPC->enemy, smackDir, 100 ); + NPC->enemy->s.powerups |= ( 1 << PW_SHOCKED ); + if ( NPC->enemy->client ) { - NPC_ChangeWeapon(WP_BLASTER); + NPC->enemy->client->ps.powerups[PW_SHOCKED] = level.time + 1000; } + //stop any attacks + ucmd.buttons = 0; + } + } - if (AimAccuracy-ATTACK_SIDE) + if ( NPCInfo->movementSpeech < 3 && NPCInfo->blockedSpeechDebounceTime <= level.time ) + { + if ( NPC->enemy && NPC->enemy->health > 0 && NPC->enemy->painDebounceTime > level.time ) + { + if ( NPC->enemy->health < 50 && NPCInfo->movementSpeech == 2 ) { - //if (!TIMER_Done(NPC, "RiderAltAttack")) - //{ - // ucmd.buttons |= BUTTON_ALT_ATTACK; - //} - //else - //{ - ucmd.buttons |= BUTTON_ATTACK; - - /* if (TIMER_Done(NPC, "RiderAltAttackCheck")) - { - TIMER_Set(NPC, "RiderAltAttackCheck", Q_irand(1000, 3000)); - if (Q_irand(0, 2)==0) - { - TIMER_Set(NPC, "RiderAltAttack", 300); - } - }*/ - //} - WeaponThink(true); + G_AddVoiceEvent( NPC, EV_ANGER2, Q_irand( 2000, 4000 ) ); + NPCInfo->movementSpeech = 3; + } + else if ( NPC->enemy->health < 75 && NPCInfo->movementSpeech == 1 ) + { + G_AddVoiceEvent( NPC, EV_ANGER1, Q_irand( 2000, 4000 ) ); + NPCInfo->movementSpeech = 2; + } + else if ( NPC->enemy->health < 100 && NPCInfo->movementSpeech == 0 ) + { + G_AddVoiceEvent( NPC, EV_ANGER3, Q_irand( 2000, 4000 ) ); + NPCInfo->movementSpeech = 1; } - ucmd.rightmove = (EnemySide==Side_Left)?( 127):(-127); } - else - { - if (NPC->client->ps.weapon!=WP_NONE) + } +} + +void NPC_BSGM_Default( void ) +{ + if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON ) + { + WeaponThink( qtrue ); + } + + if ( NPC->client->ps.stats[STAT_ARMOR] <= 0 ) + {//armor gone + if ( !NPCInfo->investigateDebounceTime ) + {//start regenerating the armor + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_OFF ); + NPC->flags &= ~FL_SHIELDED;//no more reflections + VectorSet( NPC->mins, -20, -20, -24 ); + VectorSet( NPC->maxs, 20, 20, 64 ); + NPC->client->crouchheight = NPC->client->standheight = 64; + if ( NPC->locationDamage[HL_GENERIC1] < GENERATOR_HEALTH ) + {//still have the generator bolt-on + if ( NPCInfo->investigateCount < 12 ) + { + NPCInfo->investigateCount++; + } + NPCInfo->investigateDebounceTime = level.time + (NPCInfo->investigateCount * 5000); + } + } + else if ( NPCInfo->investigateDebounceTime < level.time ) + {//armor regenerated, turn shield back on + //do a trace and make sure we can turn this back on? + trace_t tr; + gi.trace( &tr, NPC->currentOrigin, shieldMins, shieldMaxs, NPC->currentOrigin, NPC->s.number, NPC->clipmask, G2_NOCOLLIDE, 0 ); + if ( !tr.startsolid ) { - NPC_ChangeWeapon(WP_NONE); + VectorCopy( shieldMins, NPC->mins ); + VectorCopy( shieldMaxs, NPC->maxs ); + NPC->client->crouchheight = NPC->client->standheight = shieldMaxs[2]; + NPC->client->ps.stats[STAT_ARMOR] = GALAK_SHIELD_HEALTH; + NPCInfo->investigateDebounceTime = 0; + NPC->flags |= FL_SHIELDED;//reflect normal shots + NPC->fx_time = level.time; + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_ON ); } } } + if ( NPC->client->ps.stats[STAT_ARMOR] > 0 ) + {//armor present + NPC->client->ps.powerups[PW_GALAK_SHIELD] = Q3_INFINITE;//temp, for effect + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_ON ); + } else { - if (NPC->client->ps.weapon!=WP_NONE) - { - NPC_ChangeWeapon(WP_NONE); - } + gi.G2API_SetSurfaceOnOff( &NPC->ghoul2[NPC->playerModel], "torso_shield_off", TURN_OFF ); } - - // Aim At Target - //--------------- - if (ActorAimAtTarget) - { - MoveDirection.VecToAng(); - NPCInfo->desiredPitch = AngleNormalize360(MoveDirection[PITCH]); - NPCInfo->desiredYaw = AngleNormalize360(MoveDirection[YAW] + ActorYawOffset); + if( !NPC->enemy ) + {//don't have an enemy, look for one + NPC_BSGM_Patrol(); + } + else //if ( NPC->enemy ) + {//have an enemy + NPC_BSGM_Attack(); } - NPC_UpdateAngles(qtrue, qtrue); } - diff --git a/code/game/AI_Grenadier.cpp b/code/game/AI_Grenadier.cpp index 6932d0913d..077ccc47f1 100644 --- a/code/game/AI_Grenadier.cpp +++ b/code/game/AI_Grenadier.cpp @@ -48,6 +48,8 @@ extern qboolean FlyingCreature( gentity_t *ent ); #define REALIZE_THRESHOLD 0.6f #define CAUTIOUS_THRESHOLD ( REALIZE_THRESHOLD * 0.75 ) +#define MELEE_CHANCE 1800 + qboolean NPC_CheckPlayerTeamStealth( void ); static qboolean enemyLOS; @@ -205,7 +207,7 @@ NPC_BSGrenadier_Patrol void NPC_BSGrenadier_Patrol( void ) {//FIXME: pick up on bodies of dead buddies? - if ( NPCInfo->confusionTime < level.time ) + if ( (NPCInfo->confusionTimeinsanityTimescriptFlags&SCF_LOOK_FOR_ENEMIES ) @@ -510,9 +512,16 @@ void NPC_BSGrenadier_Attack( void ) shoot = qfalse; enemyDist = DistanceSquared( NPC->enemy->currentOrigin, NPC->currentOrigin ); + qboolean enemyUsingSaber = qfalse; + if ((NPC->enemy->client && NPC->enemy->client->ps.weapon == WP_SABER && NPC->enemy->client->ps.SaberActive())) + { + enemyUsingSaber = qtrue; + } + //See if we should switch to melee attack - if ( enemyDist < 16384 && (!NPC->enemy->client||NPC->enemy->client->ps.weapon != WP_SABER||(!NPC->enemy->client->ps.SaberActive())) )//128 - {//enemy is close and not using saber + if ( (enemyDist < 16384 && !enemyUsingSaber) + || (enemyDist < 64*64 && !Q_irand(0, MELEE_CHANCE) && TIMER_Done(NPC, "sleepTime"))) + {//enemy is close and not using saber or very close and random chance and we weren't just using melee if ( NPC->client->ps.weapon == WP_THERMAL ) {//grenadier trace_t trace; @@ -520,20 +529,41 @@ void NPC_BSGrenadier_Attack( void ) if ( !trace.allsolid && !trace.startsolid && (trace.fraction == 1.0 || trace.entityNum == NPC->enemy->s.number ) ) {//I can get right to him //reset fire-timing variables - NPC_ChangeWeapon( WP_MELEE ); - if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )//NPCInfo->behaviorState == BS_STAND_AND_SHOOT ) - {//FIXME: should we be overriding scriptFlags? - NPCInfo->scriptFlags |= SCF_CHASE_ENEMIES;//NPCInfo->behaviorState = BS_HUNT_AND_KILL; - } + if (NPCInfo->aiFlags&NPCAI_HEAVY_MELEE) + { + if (enemyUsingSaber) + { + TIMER_Set(NPC, "sleepTime", Q_irand(1000, 2500));//keep using melee for a short while + NPC_ChangeWeapon(WP_MELEE); + if (!(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES))//NPCInfo->behaviorState == BS_STAND_AND_SHOOT ) + {//FIXME: should we be overriding scriptFlags? + NPCInfo->scriptFlags |= SCF_CHASE_ENEMIES;//NPCInfo->behaviorState = BS_HUNT_AND_KILL; + } + } + else + {//enemy not using saber, just go for it + NPC_ChangeWeapon(WP_MELEE); + if (!(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES))//NPCInfo->behaviorState == BS_STAND_AND_SHOOT ) + {//FIXME: should we be overriding scriptFlags? + NPCInfo->scriptFlags |= SCF_CHASE_ENEMIES;//NPCInfo->behaviorState = BS_HUNT_AND_KILL; + } + } + + } } } } - else if ( enemyDist > 65536 || (NPC->enemy->client && NPC->enemy->client->ps.weapon == WP_SABER && NPC->enemy->client->ps.SaberActive()) )//256 - {//enemy is far or using saber + else if (enemyDist > 65536 || (enemyUsingSaber && TIMER_Done(NPC, "sleepTime")))//256 + {//enemy is far or using saber and sleep time is done if ( NPC->client->ps.weapon == WP_MELEE && (NPC->client->ps.stats[STAT_WEAPONS]&(1<NPC->combatPoint!=-1) { - NPC_FreeCombatPoint(mActors[actorIndex]->NPC->combatPoint, false); + NPC_FreeCombatPoint(mActors[actorIndex]->NPC->combatPoint, qfalse); mActors[actorIndex]->NPC->combatPoint = -1; } @@ -1120,7 +1120,7 @@ void Troop_Initialize() //////////////////////////////////////////////////////////////////////////////////////// void Troop_Update() { - for (TTroopPool::iterator i=mTroops.begin(); i!=mTroops.end(); i++) + for (TTroopPool::iterator i=mTroops.begin(); i!=mTroops.end(); ++i) { i->Update(); } @@ -1141,7 +1141,7 @@ void Trooper_UpdateTroop(gentity_t* actor) TTroopPool::iterator closestTroop = mTroops.end(); trace_t trace; - for (TTroopPool::iterator iTroop=mTroops.begin(); iTroop!=mTroops.end(); iTroop++) + for (TTroopPool::iterator iTroop=mTroops.begin(); iTroop!=mTroops.end(); ++iTroop) { if (iTroop->Team()==actor->client->playerTeam) { @@ -1196,7 +1196,7 @@ void Trooper_UpdateTroop(gentity_t* actor) float closestDist = 0; TTroopPool::iterator closestTroop = mTroops.end(); - for (TTroopPool::iterator iTroop=mTroops.begin(); iTroop!=mTroops.end(); iTroop++) + for (TTroopPool::iterator iTroop=mTroops.begin(); iTroop!=mTroops.end(); ++iTroop) { curDist = iTroop->DistanceSq(actor); if ((curDistenemy;//FIXME: what about NPC->lastEnemy? NPC->enemy = NULL; - gentity_t *newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse ); + gentity_t *newEnemy = NPC_CheckEnemy( (qboolean)((NPCInfo->confusionTimeinsanityTimeenemy = sav_enemy; if ( newEnemy && newEnemy != sav_enemy ) {//picked up a new enemy! diff --git a/code/game/AI_ImperialProbe.cpp b/code/game/AI_ImperialProbe.cpp index b9d81e6a43..25620b374b 100644 --- a/code/game/AI_ImperialProbe.cpp +++ b/code/game/AI_ImperialProbe.cpp @@ -224,7 +224,7 @@ void ImperialProbe_Strafe( void ) // Set the strafe start time so we can do a controlled roll NPC->fx_time = level.time; - NPCInfo->standTime = level.time + 3000 + random() * 500; + NPCInfo->standTime = level.time + 3000 + Q_flrand(0.0f, 1.0f) * 500; } } @@ -495,7 +495,7 @@ void NPC_Probe_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, co { pain_chance = NPC_GetPainChance( self, damage ); - if ( random() < pain_chance ) // Spin around in pain? + if ( Q_flrand(0.0f, 1.0f) < pain_chance ) // Spin around in pain? { NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE); } diff --git a/code/game/AI_Interrogator.cpp b/code/game/AI_Interrogator.cpp index 6ad508dbc8..f3bd826dd3 100644 --- a/code/game/AI_Interrogator.cpp +++ b/code/game/AI_Interrogator.cpp @@ -67,8 +67,8 @@ void Interrogator_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacke */ { self->client->moveType = MT_WALK; - self->client->ps.velocity[0] = Q_irand( -10, -20 ); - self->client->ps.velocity[1] = Q_irand( -10, -20 ); + self->client->ps.velocity[0] = Q_irand( -20, -10 ); + self->client->ps.velocity[1] = Q_irand( -20, -10 ); self->client->ps.velocity[2] = -100; } //self->takedamage = qfalse; @@ -289,7 +289,7 @@ void Interrogator_Strafe( void ) // Set the strafe start time NPC->fx_time = level.time; - NPCInfo->standTime = level.time + 3000 + random() * 500; + NPCInfo->standTime = level.time + 3000 + Q_flrand(0.0f, 1.0f) * 500; } } @@ -471,4 +471,4 @@ void NPC_BSInterrogator_Default( void ) Interrogator_Idle(); } -} \ No newline at end of file +} diff --git a/code/game/AI_Jedi.cpp b/code/game/AI_Jedi.cpp index 61ab411948..ded08f2dbc 100644 --- a/code/game/AI_Jedi.cpp +++ b/code/game/AI_Jedi.cpp @@ -48,6 +48,8 @@ extern void ForceRage( gentity_t *self ); extern void ForceProtect( gentity_t *self ); extern void ForceAbsorb( gentity_t *self ); extern qboolean ForceDrain2( gentity_t *self ); +extern void ForceStasis( gentity_t *self ); +extern void ForceDestruction( gentity_t *self ); extern int WP_MissileBlockForBlock( int saberBlock ); extern qboolean WP_ForcePowerUsable( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); extern qboolean WP_ForcePowerAvailable( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); @@ -96,7 +98,10 @@ extern qboolean G_ClearLineOfSight(const vec3_t point1, const vec3_t point2, int extern cvar_t *g_saberRealisticCombat; extern cvar_t *d_slowmodeath; extern cvar_t *g_saberNewControlScheme; +extern cvar_t *g_forceNewPowers; +extern cvar_t *g_playerCheatPowers; extern int parryDebounce[]; +//extern int forcePowerNeeded[]; //FIXME doesn't point to anything //Locals static void Jedi_Aggression( gentity_t *self, int change ); @@ -104,7 +109,11 @@ qboolean Jedi_WaitingAmbush( gentity_t *self ); void Tavion_SithSwordRecharge( void ); qboolean Rosh_BeingHealed( gentity_t *self ); -static qboolean enemy_in_striking_range = qfalse; +//for approximating the distance of an enemy Jedi +static qboolean enemy_in_range = qfalse; +static qboolean enemy_near = qfalse; +static qboolean enemy_approaching = qfalse; + static int jediSpeechDebounceTime[TEAM_NUM_TEAMS];//used to stop several jedi from speaking all at once void NPC_CultistDestroyer_Precache( void ) @@ -155,7 +164,7 @@ void Jedi_ClearTimers( gentity_t *ent ) } qboolean Jedi_CultistDestroyer( gentity_t *self ) -{ +{ if ( !self || !self->client ) { return qfalse; @@ -200,9 +209,9 @@ void NPC_Jedi_PlayConfusionSound( gentity_t *self ) { if ( self->health > 0 ) { - if ( self->client - && ( self->client->NPC_class == CLASS_ALORA - || self->client->NPC_class == CLASS_TAVION + if ( self->client + && ( self->client->NPC_class == CLASS_ALORA + || self->client->NPC_class == CLASS_TAVION || self->client->NPC_class == CLASS_DESANN ) ) { G_AddVoiceEvent( self, Q_irand( EV_CONFUSE1, EV_CONFUSE3 ), 2000 ); @@ -323,7 +332,7 @@ void NPC_TavionSithSword_Precache( void ) void Tavion_ScepterDamage( void ) { - if ( !NPC->ghoul2.size() + if ( !NPC->ghoul2.size() || NPC->weaponModel[1] <= 0 ) { return; @@ -340,7 +349,7 @@ void Tavion_ScepterDamage( void ) vec3_t tip, dir, base, angles={0,NPC->currentAngles[YAW],0}; trace_t trace; - gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->weaponModel[1], + gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->weaponModel[1], NPC->genericBolt1, &boltMatrix, angles, NPC->currentOrigin, time, NULL, NPC->s.modelScale ); @@ -364,7 +373,7 @@ void Tavion_ScepterDamage( void ) G_PlayEffect( G_EffectIndex( "scepter/impact.efx" ), trace.endpos, trace.plane.normal ); } - if ( traceEnt->takedamage + if ( traceEnt->takedamage && trace.entityNum != lastHit && (!traceEnt->client || traceEnt == NPC->enemy || traceEnt->client->NPC_class != NPC->client->NPC_class) ) {//smack @@ -398,7 +407,7 @@ void Tavion_ScepterDamage( void ) void Tavion_ScepterSlam( void ) { - if ( !NPC->ghoul2.size() + if ( !NPC->ghoul2.size() || NPC->weaponModel[1] <= 0 ) { return; @@ -418,7 +427,7 @@ void Tavion_ScepterSlam( void ) int i; vec3_t mins, maxs, entDir; - gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->weaponModel[1], + gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->weaponModel[1], boltIndex, &boltMatrix, angles, NPC->currentOrigin, (cg.time?cg.time:level.time), NULL, NPC->s.modelScale ); @@ -428,7 +437,7 @@ void Tavion_ScepterSlam( void ) gi.trace( &trace, handle, vec3_origin, vec3_origin, bottom, NPC->s.number, MASK_SHOT, G2_RETURNONHIT, 10 ); G_PlayEffect( G_EffectIndex( "scepter/slam.efx" ), trace.endpos, trace.plane.normal ); - + //Setup the bbox to search in for ( i = 0; i < 3; i++ ) { @@ -445,7 +454,7 @@ void Tavion_ScepterSlam( void ) { continue; } - + if ( (radiusEnts[i]->flags&FL_NO_KNOCKBACK) ) {//don't throw them back continue; @@ -455,7 +464,7 @@ void Tavion_ScepterSlam( void ) {//Skip myself continue; } - + if ( radiusEnts[i]->client == NULL ) {//must be a client if ( G_EntIsBreakable( radiusEnts[i]->s.number, NPC ) ) @@ -465,12 +474,12 @@ void Tavion_ScepterSlam( void ) continue; } - if ( (radiusEnts[i]->client->ps.eFlags&EF_HELD_BY_RANCOR) + if ( (radiusEnts[i]->client->ps.eFlags&EF_HELD_BY_RANCOR) || (radiusEnts[i]->client->ps.eFlags&EF_HELD_BY_WAMPA) ) {//can't be one being held continue; } - + VectorSubtract( radiusEnts[i]->currentOrigin, trace.endpos, entDir ); dist = VectorNormalize( entDir ); if ( dist <= radius ) @@ -483,7 +492,7 @@ void Tavion_ScepterSlam( void ) && radiusEnts[i]->client->NPC_class != CLASS_RANCOR && radiusEnts[i]->client->NPC_class != CLASS_ATST ) { - float throwStr = 0.0f; + float throwStr = 0.0f; if ( g_spskill->integer > 1 ) { throwStr = 10.0f+((radius-dist)/2.0f); @@ -547,9 +556,9 @@ void Tavion_StartScepterSlam( void ) void Tavion_SithSwordRecharge( void ) { - if ( NPC->client->ps.torsoAnim != BOTH_TAVION_SWORDPOWER - && NPC->count - && TIMER_Done( NPC, "rechargeDebounce" ) + if ( NPC->client->ps.torsoAnim != BOTH_TAVION_SWORDPOWER + && NPC->count + && TIMER_Done( NPC, "rechargeDebounce" ) && NPC->weaponModel[0] != -1 ) { NPC->s.loopSound = G_SoundIndex( "sound/weapons/scepter/recharge.wav" ); @@ -606,13 +615,13 @@ void Jedi_Decloak( gentity_t *self ) void Jedi_CheckCloak( void ) { - if ( NPC - && NPC->client + if ( NPC + && NPC->client && NPC->client->NPC_class == CLASS_SHADOWTROOPER && Q_stricmpn("shadowtrooper", NPC->NPC_type, 13 ) == 0 ) { if ( NPC->client->ps.SaberActive() || - NPC->health <= 0 || + NPC->health <= 0 || NPC->client->ps.saberInFlight || (NPC->client->ps.eFlags&EF_FORCE_GRIPPED) || (NPC->client->ps.eFlags&EF_FORCE_DRAINED) || @@ -620,10 +629,10 @@ void Jedi_CheckCloak( void ) {//can't be cloaked if saber is on, or dead or saber in flight or taking pain or being gripped Jedi_Decloak( NPC ); } - else if ( NPC->health > 0 - && !NPC->client->ps.saberInFlight - && !(NPC->client->ps.eFlags&EF_FORCE_GRIPPED) - && !(NPC->client->ps.eFlags&EF_FORCE_DRAINED) + else if ( NPC->health > 0 + && !NPC->client->ps.saberInFlight + && !(NPC->client->ps.eFlags&EF_FORCE_GRIPPED) + && !(NPC->client->ps.eFlags&EF_FORCE_DRAINED) && NPC->painDebounceTime < level.time ) {//still alive, have saber in hand, not taking pain and not being gripped Jedi_Cloak( NPC ); @@ -640,7 +649,7 @@ static void Jedi_Aggression( gentity_t *self, int change ) int upper_threshold, lower_threshold; self->NPC->stats.aggression += change; - + //FIXME: base this on initial NPC stats if ( self->client->playerTeam == TEAM_PLAYER ) {//good guys are less aggressive @@ -679,7 +688,7 @@ static void Jedi_AggressionErosion( int amt ) TIMER_Set( NPC, "roamTime", Q_irand( 2000, 5000 ) ); Jedi_Aggression( NPC, amt ); } - + if ( NPCInfo->stats.aggression < 4 || (NPCInfo->stats.aggression < 6&&NPC->client->NPC_class == CLASS_DESANN)) {//turn off the saber WP_DeactivateSaber( NPC ); @@ -753,14 +762,14 @@ SPEAKING static qboolean Jedi_BattleTaunt( void ) { - if ( TIMER_Done( NPC, "chatter" ) - && !Q_irand( 0, 3 ) - && NPCInfo->blockedSpeechDebounceTime < level.time + if ( TIMER_Done( NPC, "chatter" ) + && !Q_irand( 0, 3 ) + && NPCInfo->blockedSpeechDebounceTime < level.time && jediSpeechDebounceTime[NPC->client->playerTeam] < level.time ) { int event = -1; - if ( NPC->enemy - && NPC->enemy->client + if ( NPC->enemy + && NPC->enemy->client && (NPC->enemy->client->NPC_class == CLASS_RANCOR || NPC->enemy->client->NPC_class == CLASS_WAMPA || NPC->enemy->client->NPC_class == CLASS_SAND_CREATURE) ) @@ -769,7 +778,7 @@ static qboolean Jedi_BattleTaunt( void ) } else { - if ( NPC->client->playerTeam == TEAM_PLAYER + if ( NPC->client->playerTeam == TEAM_PLAYER && NPC->enemy && NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_JEDI ) {//a jedi fighting a jedi - training if ( NPC->client->NPC_class == CLASS_JEDI && NPCInfo->rank == RANK_COMMANDER ) @@ -817,7 +826,7 @@ static qboolean Jedi_ClearPathToSpot( vec3_t dest, int impactEntNum ) //Offset the step height VectorSet( mins, NPC->mins[0], NPC->mins[1], NPC->mins[2] + STEPSIZE ); - + gi.trace( &trace, NPC->currentOrigin, mins, NPC->maxs, dest, NPC->s.number, NPC->clipmask, (EG2_Collision)0, 0 ); //Do a simple check @@ -838,7 +847,7 @@ static qboolean Jedi_ClearPathToSpot( vec3_t dest, int impactEntNum ) } } - //otherwise, clear path in a straight line. + //otherwise, clear path in a straight line. //Now at intervals of my size, go along the trace and trace down STEPSIZE to make sure there is a solid floor. VectorSubtract( dest, NPC->currentOrigin, dir ); dist = VectorNormalize( dir ); @@ -967,7 +976,7 @@ qboolean NPC_MoveDirClear( int forwardmove, int rightmove, qboolean reset ) if ( trace.fraction < 1.0 ) {//Not going off a cliff - //FIXME: what if plane.normal is sloped? We'll slide off, not land... plus this doesn't account for slide-movement... + //FIXME: what if plane.normal is sloped? We'll slide off, not land... plus this doesn't account for slide-movement... //gi.Printf( "%d walk off cliff okay will hit entnum %d at dropdist of %4.2f\n", level.time, trace.entityNum, (trace.fraction*bottom_max) ); return qtrue; } @@ -992,7 +1001,7 @@ static void Jedi_HoldPosition( void ) { //NPCInfo->squadState = SQUAD_STAND_AND_SHOOT; NPCInfo->goalEntity = NULL; - + /* if ( TIMER_Done( NPC, "stand" ) ) { @@ -1052,10 +1061,10 @@ static qboolean Jedi_Hunt( void ) if (NPC->client && NPC->client->NPC_class==CLASS_BOBAFETT) { NPCInfo->goalEntity = NPC->enemy; - }*/ + }*/ // NPC_SetMoveGoal(NPC, NPC->enemy->currentOrigin, 40.0f, false, 0, NPC->enemy); - NPCInfo->goalEntity = NPC->enemy; - NPCInfo->goalRadius = 40.0f; + NPCInfo->goalEntity = NPC->enemy; + NPCInfo->goalRadius = 40.0f; //Jedi_Move( NPC->enemy, qfalse ); if ( NPC_MoveToGoal( qfalse ) ) @@ -1087,7 +1096,7 @@ static qboolean Jedi_Track( void ) */ static void Jedi_StartBackOff( void ) -{ +{ TIMER_Set( NPC, "roamTime", -level.time ); TIMER_Set( NPC, "strafeLeft", -level.time ); TIMER_Set( NPC, "strafeRight", -level.time ); @@ -1098,7 +1107,7 @@ static void Jedi_StartBackOff( void ) TIMER_Set( NPC, "moveleft", -level.time ); TIMER_Set( NPC, "movecenter", -level.time ); TIMER_Set( NPC, "moveback", 1000 ); - ucmd.forwardmove = -127; + ucmd.forwardmove = -127; ucmd.rightmove = 0; ucmd.upmove = 0; if ( d_JediAI->integer ) @@ -1120,7 +1129,7 @@ static qboolean Jedi_Retreat( void ) {//don't actually move return qfalse; } - //FIXME: when retreating, we should probably see if we can retreat + //FIXME: when retreating, we should probably see if we can retreat //in the direction we want. If not...? Evade? //gi.Printf( "Retreating\n" ); return Jedi_Move( NPC->enemy, qtrue ); @@ -1138,14 +1147,14 @@ static qboolean Jedi_Advance( void ) } //gi.Printf( "Advancing\n" ); return Jedi_Move( NPC->enemy, qfalse ); - + //TIMER_Set( NPC, "roamTime", Q_irand( 2000, 4000 ) ); //TIMER_Set( NPC, "attackDelay", Q_irand( 250, 500 ) ); //TIMER_Set( NPC, "duck", 0 ); } static void Jedi_AdjustSaberAnimLevel( gentity_t *self, int newLevel ) -{ +{ if ( !self || !self->client ) { return; @@ -1180,7 +1189,7 @@ static void Jedi_AdjustSaberAnimLevel( gentity_t *self, int newLevel ) self->client->ps.saberAnimLevel = SS_FAST; return; } - if ( self->NPC->rank == RANK_CREWMAN + if ( self->NPC->rank == RANK_CREWMAN || self->NPC->rank == RANK_ENSIGN ) {//acrobat & force-users always use medium attacks self->client->ps.saberAnimLevel = SS_MEDIUM; @@ -1232,7 +1241,7 @@ static void Jedi_AdjustSaberAnimLevel( gentity_t *self, int newLevel ) static void Jedi_CheckDecreaseSaberAnimLevel( void ) { - if ( !NPC->client->ps.weaponTime && !(ucmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS)) ) + if ( !NPC->client->ps.weaponTime && !(ucmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS|BUTTON_SABERTHROW)) ) {//not attacking if ( TIMER_Done( NPC, "saberLevelDebounce" ) && !Q_irand( 0, 10 ) ) { @@ -1247,8 +1256,13 @@ static void Jedi_CheckDecreaseSaberAnimLevel( void ) } } -static qboolean Jedi_DecideKick( void ) -{ +static qboolean Jedi_DecideKick( int enemy_dist ) +{ + if ( enemy_dist > 0 + || (enemy_dist > 128 && NPC->client->NPC_class == CLASS_REBORN && !Q_irand(0,3))) //reborn like to show off a little + { + return qfalse; + } if ( PM_InKnockDown( &NPC->client->ps ) ) { return qfalse; @@ -1286,7 +1300,7 @@ static qboolean Jedi_DecideKick( void ) { return qfalse; } - else if ( NPC->client->ps.dualSabers + else if ( NPC->client->ps.dualSabers && (NPC->client->ps.saber[1].saberFlags&SFL_NO_KICKS) ) { return qfalse; @@ -1318,8 +1332,9 @@ void Kyle_TryGrab( void ) qboolean Kyle_CanDoGrab( void ) { - if ( NPC->client->NPC_class == CLASS_KYLE && (NPC->spawnflags&1) ) - {//Boss Kyle + if ( (NPC->client->NPC_class == CLASS_KYLE && (NPC->spawnflags&1)) + || (NPCInfo->stats.saberMeleeKatas && NPC->s.weapon == WP_SABER) || (NPCInfo->stats.meleeKatas && NPC->s.weapon == WP_MELEE)) + {//Boss Kyle or NPC with special fields if ( NPC->enemy && NPC->enemy->client ) {//have a valid enemy if ( TIMER_Done( NPC, "grabEnemyDebounce" ) ) @@ -1358,7 +1373,7 @@ static void Jedi_CombatDistance( int enemy_dist ) ucmd.buttons &= ~BUTTON_WALKING; return; } - if ( enemy_dist < 128 + if ( enemy_dist < 128 && NPC->enemy && NPC->enemy->client && (NPC->enemy->client->ps.torsoAnim == BOTH_SPINATTACK6 @@ -1380,7 +1395,7 @@ static void Jedi_CombatDistance( int enemy_dist ) TIMER_Set( NPC, "gripping", -level.time ); TIMER_Set( NPC, "attackDelay", Q_irand( 0, 1000 ) ); } - + if ( NPC->client->ps.forcePowersActive&(1<client->ps.forcePowerLevel[FP_DRAIN] > FORCE_LEVEL_1 ) {//when draining, don't move @@ -1392,7 +1407,7 @@ static void Jedi_CombatDistance( int enemy_dist ) TIMER_Set( NPC, "attackDelay", Q_irand( 0, 1000 ) ); } - if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + if (NPC->client->NPC_class == CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_MANDA || NPC->client->NPC_class == CLASS_COMMANDO) { if ( !TIMER_Done( NPC, "flameTime" ) ) { @@ -1456,7 +1471,7 @@ static void Jedi_CombatDistance( int enemy_dist ) && !(NPC->client->ps.forcePowersActive&(1 << FP_SPEED)) && !(NPC->client->ps.saberEventFlags&SEF_INWATER) )//saber not in water {//hold it out there - ucmd.buttons |= BUTTON_ALT_ATTACK; + ucmd.buttons |= BUTTON_SABERTHROW; //FIXME: time limit? } } @@ -1498,9 +1513,9 @@ static void Jedi_CombatDistance( int enemy_dist ) TIMER_Set( NPC, "strafeLeft", -1 ); TIMER_Set( NPC, "strafeRight", -1 ); } - else if ( NPC->enemy->client - && NPC->enemy->s.weapon == WP_SABER - && NPC->enemy->client->ps.saberLockTime > level.time + else if ( NPC->enemy->client + && NPC->enemy->s.weapon == WP_SABER + && NPC->enemy->client->ps.saberLockTime > level.time && NPC->client->ps.saberLockTime < level.time ) {//enemy is in a saberLock and we are not if ( enemy_dist < 64 ) @@ -1508,8 +1523,8 @@ static void Jedi_CombatDistance( int enemy_dist ) Jedi_Retreat(); } } - else if ( NPC->enemy->s.weapon == WP_TURRET - && !Q_stricmp( "PAS", NPC->enemy->classname ) + else if ( NPC->enemy->s.weapon == WP_TURRET + && !Q_stricmp( "PAS", NPC->enemy->classname ) && NPC->enemy->s.apos.trType == TR_STATIONARY ) { if ( enemy_dist > forcePushPullRadius[FORCE_LEVEL_1] - 16 ) @@ -1535,7 +1550,7 @@ static void Jedi_CombatDistance( int enemy_dist ) } } } - else if ( enemy_dist <= 64 + else if ( enemy_dist <= 64 && ((NPCInfo->scriptFlags&SCF_DONT_FIRE)||(!Q_stricmp("Yoda",NPC->NPC_type)&&!Q_irand(0,10))) ) {//can't use saber and they're in striking range if ( !Q_irand( 0, 5 ) && InFront( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 0.2f ) ) @@ -1551,12 +1566,16 @@ static void Jedi_CombatDistance( int enemy_dist ) } else { - if ( Jedi_DecideKick() ) + if ( Jedi_DecideKick(enemy_dist) ) {//let's try a kick if ( G_PickAutoMultiKick( NPC, qfalse, qtrue ) != LS_NONE || (G_CanKickEntity(NPC, NPC->enemy ) && G_PickAutoKick( NPC, NPC->enemy, qtrue ) != LS_NONE ) ) {//kicked! - TIMER_Set( NPC, "kickDebounce", Q_irand( 3000, 10000 ) ); + int kickFreqIncrease = NPC->NPC->stats.meleeKicks - 1; + if (kickFreqIncrease > 0) + TIMER_Set(NPC, "kickDebounce", Q_irand(3000, 10000 - kickFreqIncrease*1000)); + else + TIMER_Set(NPC, "kickDebounce", (Q_irand(3000, 10000))); return; } } @@ -1565,11 +1584,11 @@ static void Jedi_CombatDistance( int enemy_dist ) } Jedi_Retreat(); } - else if ( enemy_dist <= 64 + else if ( enemy_dist <= 64 && NPC->max_health - NPC->health > NPC->max_health*0.25f//lost over 1/4 of our health && NPC->client->ps.forcePowersKnown&(1<enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 0.2f ) ) { TIMER_Set( NPC, "draining", 3000 ); @@ -1585,18 +1604,22 @@ static void Jedi_CombatDistance( int enemy_dist ) Kyle_TryGrab(); return; } - else if ( NPC->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SCEPTER] && !Q_irand( 0, 20 ) ) { Tavion_StartScepterSlam(); return; } - if ( Jedi_DecideKick() ) + if ( Jedi_DecideKick(enemy_dist) ) {//let's try a kick if ( G_PickAutoMultiKick( NPC, qfalse, qtrue ) != LS_NONE || (G_CanKickEntity(NPC, NPC->enemy ) && G_PickAutoKick( NPC, NPC->enemy, qtrue ) != LS_NONE ) ) {//kicked! - TIMER_Set( NPC, "kickDebounce", Q_irand( 3000, 10000 ) ); + int kickFreqIncrease = NPC->NPC->stats.meleeKicks - 1; + if (kickFreqIncrease > 0) + TIMER_Set(NPC, "kickDebounce", Q_irand(3000, 10000 - kickFreqIncrease * 1000)); + else + TIMER_Set(NPC, "kickDebounce", (Q_irand(3000, 10000))); return; } } @@ -1613,18 +1636,22 @@ static void Jedi_CombatDistance( int enemy_dist ) Kyle_TryGrab(); return; } - else if ( NPC->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SCEPTER] && !Q_irand( 0, 20 ) ) { Tavion_StartScepterSlam(); return; } - if ( Jedi_DecideKick() ) + if ( Jedi_DecideKick(enemy_dist) ) {//let's try a kick if ( G_PickAutoMultiKick( NPC, qfalse, qtrue ) != LS_NONE || (G_CanKickEntity(NPC, NPC->enemy ) && G_PickAutoKick( NPC, NPC->enemy, qtrue ) != LS_NONE ) ) {//kicked! - TIMER_Set( NPC, "kickDebounce", Q_irand( 3000, 10000 ) ); + int kickFreqIncrease = NPC->NPC->stats.meleeKicks - 1; + if (kickFreqIncrease > 0) + TIMER_Set(NPC, "kickDebounce", Q_irand(3000, 10000 - kickFreqIncrease * 1000)); + else + TIMER_Set(NPC, "kickDebounce", (Q_irand(3000, 10000))); return; } } @@ -1634,53 +1661,53 @@ static void Jedi_CombatDistance( int enemy_dist ) else if ( enemy_dist > 256 ) {//we're way out of range qboolean usedForce = qfalse; - if ( NPCInfo->stats.aggression < Q_irand( 0, 20 ) - && NPC->health < NPC->max_health*0.75f + if ( NPCInfo->stats.aggression < Q_irand( 0, 20 ) + && NPC->health < NPC->max_health*0.75f && !Q_irand( 0, 2 ) ) { - if ( NPC->enemy + if ( NPC->enemy && NPC->enemy->s.number < MAX_CLIENTS && NPC->client->NPC_class!=CLASS_KYLE && ((NPCInfo->aiFlags&NPCAI_BOSS_CHARACTER) - || NPC->client->NPC_class==CLASS_SHADOWTROOPER) + || NPC->client->NPC_class==CLASS_SHADOWTROOPER) && Q_irand(0, 3-g_spskill->integer) ) {//hmm, bosses should do this less against the player } - else if ( NPC->client->ps.saber[0].type == SABER_SITH_SWORD + else if ( NPC->client->ps.saber[0].type == SABER_SITH_SWORD && NPC->weaponModel[0] != -1 ) { Tavion_SithSwordRecharge(); usedForce = qtrue; } - else if ( (NPC->client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1< 384 ) @@ -1721,7 +1748,7 @@ static void Jedi_CombatDistance( int enemy_dist ) //FIXME: enemy_dist calc needs to include all blade lengths, and include distance from hand to start of blade.... else if ( enemy_dist > 50 )//FIXME: not hardcoded- base on our reach (modelScale?) and saberLengthMax {//we're out of striking range and we are allowed to attack - //first, check some tactical force power decisions + //first, check some tactical force power decisions if ( NPC->enemy && NPC->enemy->client && (NPC->enemy->client->ps.eFlags&EF_FORCE_GRIPPED) ) {//They're being gripped, rush them! if ( NPC->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE ) @@ -1735,11 +1762,11 @@ static void Jedi_CombatDistance( int enemy_dist ) } } if ( (NPCInfo->rank >= RANK_LT_JG||WP_ForcePowerUsable( NPC, FP_SABERTHROW, 0 )) - && !Q_irand( 0, 5 ) + && !Q_irand( 0, 5 ) && !(NPC->client->ps.forcePowersActive&(1 << FP_SPEED)) && !(NPC->client->ps.saberEventFlags&SEF_INWATER) )//saber not in water {//throw saber - ucmd.buttons |= BUTTON_ALT_ATTACK; + ucmd.buttons |= BUTTON_SABERTHROW; } } else if ( NPC->enemy && NPC->enemy->client && //valid enemy @@ -1793,7 +1820,7 @@ static void Jedi_CombatDistance( int enemy_dist ) { ForceThrow( NPC, qtrue ); } - else if ( NPC->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SCEPTER] && !Q_irand( 0, 20 ) ) { Tavion_StartScepterBeam(); @@ -1806,14 +1833,14 @@ static void Jedi_CombatDistance( int enemy_dist ) { chanceScale = 4; } - else if ( NPC->enemy + else if ( NPC->enemy && NPC->enemy->s.number < MAX_CLIENTS && ((NPCInfo->aiFlags&NPCAI_BOSS_CHARACTER) || NPC->client->NPC_class==CLASS_SHADOWTROOPER) ) {//hmm, bosses do this less against player - chanceScale = 8 - g_spskill->integer*2; + chanceScale = 8 - g_spskill->integer*2; } - else if ( NPC->client->NPC_class == CLASS_DESANN + else if ( NPC->client->NPC_class == CLASS_DESANN || !Q_stricmp("Yoda",NPC->NPC_type) ) //|| (NPC->client->NPC_class == CLASS_CULTIST && NPC->client->ps.weapon == WP_NONE) )//force-only cultists use force a lot { @@ -1827,9 +1854,9 @@ static void Jedi_CombatDistance( int enemy_dist ) { chanceScale = 5; } - if ( chanceScale + if ( chanceScale && (enemy_dist > Q_irand( 100, 200 ) || (NPCInfo->scriptFlags&SCF_DONT_FIRE) || (!Q_stricmp("Yoda",NPC->NPC_type)&&!Q_irand(0,3)) ) - && enemy_dist < 500 + && enemy_dist < 500 && (Q_irand( 0, chanceScale*10 )<5 || (NPC->enemy->client && NPC->enemy->client->ps.weapon != WP_SABER && !Q_irand( 0, chanceScale ) ) ) ) {//else, randomly try some kind of attack every now and then //FIXME: Cultist fencers don't have any of these fancy powers @@ -1839,8 +1866,8 @@ static void Jedi_CombatDistance( int enemy_dist ) /* || WP_ForcePowerUsable( NPC, FP_PULL, 0 ) || WP_ForcePowerUsable( NPC, FP_LIGHTNING, 0 ) - || WP_ForcePowerUsable( NPC, FP_DRAIN, 0 ) - || WP_ForcePowerUsable( NPC, FP_GRIP, 0 ) + || WP_ForcePowerUsable( NPC, FP_DRAIN, 0 ) + || WP_ForcePowerUsable( NPC, FP_GRIP, 0 ) || WP_ForcePowerUsable( NPC, FP_SABERTHROW, 0 ) */ ) @@ -1858,7 +1885,7 @@ static void Jedi_CombatDistance( int enemy_dist ) ucmd.buttons |= BUTTON_ATTACK; } } - else if ( WP_ForcePowerUsable( NPC, FP_LIGHTNING, 0 ) + else if ( WP_ForcePowerUsable( NPC, FP_LIGHTNING, 0 ) && (((NPCInfo->scriptFlags&SCF_DONT_FIRE)&&Q_stricmp("cultist_lightning",NPC->NPC_type)) || Q_irand( 0, 1 )) ) { ForceLightning( NPC ); @@ -1871,7 +1898,7 @@ static void Jedi_CombatDistance( int enemy_dist ) } else if ( NPC->health < NPC->max_health * 0.75f && Q_irand( FORCE_LEVEL_0, NPC->client->ps.forcePowerLevel[FP_DRAIN] ) > FORCE_LEVEL_1 - && WP_ForcePowerUsable( NPC, FP_DRAIN, 0 ) + && WP_ForcePowerUsable( NPC, FP_DRAIN, 0 ) && (((NPCInfo->scriptFlags&SCF_DONT_FIRE)&&Q_stricmp("cultist_drain",NPC->NPC_type)) || Q_irand( 0, 1 )) ) { ForceDrain2( NPC ); @@ -1901,13 +1928,23 @@ static void Jedi_CombatDistance( int enemy_dist ) TIMER_Set( NPC, "gripping", 3000 ); TIMER_Set( NPC, "attackDelay", 3000 ); } + else if ( WP_ForcePowerUsable( NPC, FP_STASIS, 0 ) && Q_irand(0, 1) && NPC->enemy && NPC->enemy->client && NPC->enemy->client->ps.stasisTime < level.time) + { + ForceStasis( NPC ); + TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime ); + } + else if ( WP_ForcePowerUsable( NPC, FP_DESTRUCTION, 0 ) && Q_irand(0, 1)) + { + ForceDestruction( NPC ); + TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime ); + } else { - if ( WP_ForcePowerUsable( NPC, FP_SABERTHROW, 0 ) + if ( WP_ForcePowerUsable( NPC, FP_SABERTHROW, 0 ) && !(NPC->client->ps.forcePowersActive&(1 << FP_SPEED)) && !(NPC->client->ps.saberEventFlags&SEF_INWATER) )//saber not in water {//throw saber - ucmd.buttons |= BUTTON_ALT_ATTACK; + ucmd.buttons |= BUTTON_SABERTHROW; } } } @@ -1917,7 +1954,7 @@ static void Jedi_CombatDistance( int enemy_dist ) && !(NPC->client->ps.forcePowersActive&(1 << FP_SPEED)) && !(NPC->client->ps.saberEventFlags&SEF_INWATER) )//saber not in water {//throw saber - ucmd.buttons |= BUTTON_ALT_ATTACK; + ucmd.buttons |= BUTTON_SABERTHROW; } } } @@ -1944,7 +1981,7 @@ static void Jedi_CombatDistance( int enemy_dist ) } else {//we're not close enough to attack, but not far enough away to be safe - if ( !Q_irand( 0, 30 ) + if ( !Q_irand( 0, 30 ) && Kyle_CanDoGrab() ) { Kyle_TryGrab(); @@ -1952,12 +1989,16 @@ static void Jedi_CombatDistance( int enemy_dist ) } if ( NPCInfo->stats.aggression < 4 ) {//back off and defend - if ( Jedi_DecideKick() ) + if ( Jedi_DecideKick(enemy_dist) ) {//let's try a kick if ( G_PickAutoMultiKick( NPC, qfalse, qtrue ) != LS_NONE || (G_CanKickEntity(NPC, NPC->enemy ) && G_PickAutoKick( NPC, NPC->enemy, qtrue ) != LS_NONE ) ) {//kicked! - TIMER_Set( NPC, "kickDebounce", Q_irand( 3000, 10000 ) ); + int kickFreqIncrease = NPC->NPC->stats.meleeKicks - 1; + if (kickFreqIncrease > 0) + TIMER_Set(NPC, "kickDebounce", Q_irand(3000, 10000 - kickFreqIncrease * 1000)); + else + TIMER_Set(NPC, "kickDebounce", (Q_irand(3000, 10000))); return; } } @@ -1982,12 +2023,14 @@ static void Jedi_CombatDistance( int enemy_dist ) //Move forward and back? } } - //if really really mad, rage! + //if really really mad, rage! But less likely if low HP, or if the enemy is far away. if ( NPCInfo->stats.aggression > Q_irand( 5, 15 ) - && NPC->health < NPC->max_health*0.75f - && !Q_irand( 0, 2 ) ) + && ( NPC->health > NPC->max_health*0.5f || !Q_irand(0, 3) ) //if weak, only 25% chance + && !( NPC->health < NPC->max_health*0.25f ) //never if less than 1/4 HP, "too weak" + && !Q_irand( 0, 2 ) + && enemy_dist < 284 ) //can't be really far away { - if ( (NPC->client->ps.forcePowersKnown&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1<s.weapon == WP_SABER ) + {//should be a slow strafe if we are a saber-wielder TIMER_Set( NPC, "walking", strafeTime ); } return qtrue; @@ -2092,12 +2135,18 @@ FIXME: possibly let player do this too? qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, int hitLoc ) { int dodgeAnim = -1; + qboolean playerNoDrain = false; + qboolean playerPerfectDodge = false; if ( !self || !self->client || self->health <= 0 ) { return qfalse; } + if (self->NPC && self->NPC->stats.restrictJediPowers && self->client->ps.forcePowerLevel[FP_SPEED] < 2) { + return qfalse; + } + if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE ) {//can't dodge in mid-air return qfalse; @@ -2131,18 +2180,60 @@ qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, in } else {//the player - if ( !(self->client->ps.forcePowersActive&(1<integer && !g_playerCheatPowers->integer) + { + if (self->client->ps.forcePowerLevel[FP_SPEED] < 2) + { + return qfalse; + } + + if (self->client->ps.forcePowersActive&(1 << FP_SPEED)) + { + return qfalse; //you're already moving so fast, player should just dodge manually + } + + if (self->client->ps.forcePowerLevel[FP_SPEED] == 2 + && !(self->client->ps.forcePower >= S2_AUTODODGE_FP)) + {//make sure we have adequate force power + return qfalse; + } + else if (self->client->ps.forcePowerLevel[FP_SPEED] == 3 + && !(self->client->ps.forcePower >= S3_AUTODODGE_FP)) + {//make sure we have adequate force power + return qfalse; + } + + if (self->client->ps.forcePowersActive&(1 << FP_SEE)) { + if (!(self->client->ps.forcePowerLevel[FP_SPEED] == 3 + && self->client->ps.forcePowerLevel[FP_SEE] == 3)) { + //we have both Sense 3 and Speed 3 so no drain + playerNoDrain = qtrue; + playerPerfectDodge = qtrue; + } + } + + if (!playerPerfectDodge && Q_irand(1, 10) > self->client->ps.forcePowerLevel[FP_SPEED]) //auto-dodge + {//more likely to fail on lower force speed level return qfalse; } + } - //check force speed power level to determine if I should be able to dodge it - if ( Q_irand( 1, 10 ) > self->client->ps.forcePowerLevel[FP_SPEED] ) - {//more likely to fail on lower force speed level - return qfalse; + else if (!g_playerCheatPowers->integer) + { //old force power version + if (!(self->client->ps.forcePowersActive&(1 << FP_SPEED))) + {//not already in speed + if (!WP_ForcePowerUsable(self, FP_SPEED, 0)) + {//make sure we have it and have enough force power + return qfalse; + } + } + //check force speed power level to determine if I should be able to dodge it + if (Q_irand(1, 10) > self->client->ps.forcePowerLevel[FP_SPEED]) + {//more likely to fail on lower force speed level + return qfalse; + } } + } if ( hitLoc == HL_NONE ) @@ -2185,25 +2276,26 @@ qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, in { G_SetEnemy( self, shooter ); } - if ( self->NPC + if ( self->NPC && ((self->NPC->scriptFlags&SCF_NO_ACROBATICS) || PM_InKnockDown( &self->client->ps ) ) ) { return qfalse; } - if ( self->client + if ( self->client && (self->client->ps.forceRageRecoveryTime > level.time || (self->client->ps.forcePowersActive&(1<client->NPC_class == CLASS_BOBAFETT && !Q_irand(0,1)) + if ((self->client->NPC_class == CLASS_BOBAFETT || self->client->NPC_class == CLASS_MANDA || self->client->NPC_class == CLASS_COMMANDO) && !Q_irand(0,1)) { return qfalse; // half the time he dodges } - if ( self->client->NPC_class == CLASS_BOBAFETT + if ( self->client->NPC_class == CLASS_BOBAFETT || (self->client->NPC_class == CLASS_REBORN && self->s.weapon != WP_SABER) - || self->client->NPC_class == CLASS_ROCKETTROOPER ) + || self->client->NPC_class == CLASS_ROCKETTROOPER + || self->client->NPC_class == CLASS_MANDA) { self->client->ps.forceJumpCharge = 280;//FIXME: calc this intelligently? } @@ -2259,7 +2351,7 @@ qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, in {//player if ( (self->client->ps.forcePowersActive&(1<client->ps.torsoAnim ) + if ( PM_DodgeAnim( self->client->ps.torsoAnim ) && !PM_DodgeHoldAnim( self->client->ps.torsoAnim ) ) {//already in a dodge //use the hold pose, don't start it all over again @@ -2280,7 +2372,7 @@ qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, in self->client->ps.legsAnimTimer = self->client->ps.torsoAnimTimer; } - if ( self->s.number ) + if (self->s.number || (!self->s.number && g_playerCheatPowers->integer)) {//NPC //maybe force them to stop moving in this case? self->client->ps.pm_time = self->client->ps.torsoAnimTimer + Q_irand( 100, 1000 ); @@ -2293,7 +2385,28 @@ qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, in } else {//player - ForceSpeed( self, 500 ); + if (g_forceNewPowers->integer) + {//this version is more complex + if (!(self->client->ps.forcePowersActive&(1 << FP_SPEED))) + {//not in speed + if (playerNoDrain) { + ForceSpeed(self, 500, 0); + } + else if (self->client->ps.forcePowerLevel[FP_SPEED] == 3) + { + ForceSpeed(self, 500, S3_AUTODODGE_FP); + } + else + { + ForceSpeed(self, 500 /*, S2_AUTODODGE_FP*/); + } + } + } + else + {//base JA version much simpler + ForceSpeed(self, 500); + } + } WP_ForcePowerStop( self, FP_GRIP ); @@ -2318,11 +2431,13 @@ evasionType_t Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdi } if ( self->client ) { - if ( self->client->NPC_class == CLASS_BOBAFETT ) + if ((self->client->NPC_class == CLASS_BOBAFETT + || self->client->NPC_class == CLASS_MANDA + || self->client->NPC_class == CLASS_COMMANDO)) {//boba can't flip return EVASION_NONE; } - if ( self->client->ps.forceRageRecoveryTime > level.time + if ( self->client->ps.forceRageRecoveryTime > level.time || (self->client->ps.forcePowersActive&(1<client->NPC_class != CLASS_DESANN //desann doesn't do these kind of frilly acrobatics - && (self->NPC->rank == RANK_CREWMAN || self->NPC->rank >= RANK_LT) - && Q_irand( 0, 1 ) + && (self->NPC->rank == RANK_CREWMAN || self->NPC->rank >= RANK_LT) + && Q_irand( 0, 1 ) && !PM_InRoll( &self->client->ps ) && !PM_InKnockDown( &self->client->ps ) - && !PM_SaberInSpecialAttack( self->client->ps.torsoAnim ) ) + && !PM_SaberInSpecialAttack( self->client->ps.torsoAnim ) + && self->NPC->stats.move > 3) { vec3_t fwd, right, traceto, mins = {self->mins[0],self->mins[1],self->mins[2]+STEPSIZE}, maxs = {self->maxs[0],self->maxs[1],24}, fwdAngles = {0, self->client->ps.viewangles[YAW], 0}; trace_t trace; @@ -2415,12 +2531,16 @@ evasionType_t Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdi { allowCartWheels = qfalse; } - else if ( self->client->ps.dualSabers + else if ( self->client->ps.dualSabers && (self->client->ps.saber[1].saberFlags&SFL_NO_CARTWHEELS) ) { allowCartWheels = qfalse; } } + if (self->client->ps.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_1 && self->s.weapon != WP_SABER) + { //can't do no-handed cartwheels without a bit of force jumping ability or teh moves + allowCartWheels = qfalse; + } if ( PM_SaberInAttack( self->client->ps.saberMove ) || PM_SaberInStart( self->client->ps.saberMove ) ) @@ -2471,8 +2591,10 @@ evasionType_t Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdi self->client->ps.velocity[2] = 200; self->client->ps.forceJumpZStart = self->currentOrigin[2];//so we don't take damage if we land at same height self->client->ps.pm_flags |= PMF_JUMPING; - if ( self->client->NPC_class == CLASS_BOBAFETT - || (self->client->NPC_class == CLASS_REBORN && self->s.weapon != WP_SABER) ) + if (self->client->NPC_class == CLASS_BOBAFETT + || self->client->NPC_class == CLASS_MANDA + || self->client->NPC_class == CLASS_COMMANDO + || (self->client->NPC_class == CLASS_REBORN && self->s.weapon != WP_SABER) ) { G_AddEvent( self, EV_JUMP, 0 ); } @@ -2514,7 +2636,7 @@ evasionType_t Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdi { allowWallFlips = qfalse; } - else if ( self->client->ps.dualSabers + else if ( self->client->ps.dualSabers && (self->client->ps.saber[1].saberFlags&SFL_NO_WALL_FLIPS) ) { allowWallFlips = qfalse; @@ -2546,7 +2668,9 @@ evasionType_t Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdi NPC_SetAnim( self, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); self->client->ps.forceJumpZStart = self->currentOrigin[2];//so we don't take damage if we land at same height self->client->ps.pm_flags |= (PMF_JUMPING|PMF_SLOW_MO_FALL); - if ( self->client->NPC_class == CLASS_BOBAFETT + if (self->client->NPC_class == CLASS_BOBAFETT + || self->client->NPC_class == CLASS_MANDA + || self->client->NPC_class == CLASS_COMMANDO || (self->client->NPC_class == CLASS_REBORN && self->s.weapon != WP_SABER)) { G_AddEvent( self, EV_JUMP, 0 ); @@ -2558,7 +2682,7 @@ evasionType_t Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdi return EVASION_OTHER; } } - else + else {//boxed in on both sides if ( DotProduct( self->client->ps.velocity, fwd ) < 0 ) {//moving backwards @@ -2596,7 +2720,7 @@ evasionType_t Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdi { allowWallRuns = qfalse; } - else if ( self->client->ps.dualSabers + else if ( self->client->ps.dualSabers && (self->client->ps.saber[1].saberFlags&SFL_NO_WALL_RUNS) ) { allowWallRuns = qfalse; @@ -2623,7 +2747,9 @@ evasionType_t Jedi_CheckFlipEvasions( gentity_t *self, float rightdot, float zdi NPC_SetAnim( self, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); self->client->ps.forceJumpZStart = self->currentOrigin[2];//so we don't take damage if we land at same height self->client->ps.pm_flags |= (PMF_JUMPING|PMF_SLOW_MO_FALL); - if ( self->client->NPC_class == CLASS_BOBAFETT + if (self->client->NPC_class == CLASS_BOBAFETT + || self->client->NPC_class == CLASS_MANDA + || self->client->NPC_class == CLASS_COMMANDO || (self->client->NPC_class == CLASS_REBORN && self->s.weapon != WP_SABER)) { G_AddEvent( self, EV_JUMP, 0 ); @@ -2649,13 +2775,13 @@ int Jedi_ReCalcParryTime( gentity_t *self, evasionType_t evasionType ) return 0; } if ( !self->s.number ) - {//player + {//player return parryDebounce[self->client->ps.forcePowerLevel[FP_SABER_DEFENSE]]; } else if ( self->NPC ) { /* - if ( !g_saberRealisticCombat->integer + if ( !g_saberRealisticCombat->integer && ( g_spskill->integer == 2 || (g_spskill->integer == 1 && (self->client->NPC_class == CLASS_TAVION||self->client->NPC_class == CLASS_ALORA) ) ) ) { if ( (self->client->NPC_class == CLASS_TAVION||self->client->NPC_class == CLASS_ALORA) ) @@ -2739,7 +2865,7 @@ int Jedi_ReCalcParryTime( gentity_t *self, evasionType_t evasionType ) } } - if ( self->client->NPC_class == CLASS_ALORA + if ( self->client->NPC_class == CLASS_ALORA || self->client->NPC_class == CLASS_SHADOWTROOPER || self->client->NPC_class == CLASS_TAVION ) {//Tavion & Alora are faster @@ -2790,7 +2916,7 @@ int Jedi_ReCalcParryTime( gentity_t *self, evasionType_t evasionType ) baseTime += 300;//400;//100; } } - + return baseTime; } } @@ -2811,14 +2937,14 @@ qboolean Jedi_QuickReactions( gentity_t *self ) qboolean Jedi_SaberBusy( gentity_t *self ) { - if ( self->client->ps.torsoAnimTimer > 300 - && ( (PM_SaberInAttack( self->client->ps.saberMove )&&self->client->ps.saberAnimLevel==SS_STRONG) - || PM_SpinningSaberAnim( self->client->ps.torsoAnim ) - || PM_SaberInSpecialAttack( self->client->ps.torsoAnim ) - //|| PM_SaberInBounce( self->client->ps.saberMove ) - || PM_SaberInBrokenParry( self->client->ps.saberMove ) - //|| PM_SaberInDeflect( self->client->ps.saberMove ) - || PM_FlippingAnim( self->client->ps.torsoAnim ) + if ( self->client->ps.torsoAnimTimer > 300 + && ( (PM_SaberInAttack( self->client->ps.saberMove )&&self->client->ps.saberAnimLevel==SS_STRONG) + || PM_SpinningSaberAnim( self->client->ps.torsoAnim ) + || PM_SaberInSpecialAttack( self->client->ps.torsoAnim ) + //|| PM_SaberInBounce( self->client->ps.saberMove ) + || PM_SaberInBrokenParry( self->client->ps.saberMove ) + //|| PM_SaberInDeflect( self->client->ps.saberMove ) + || PM_FlippingAnim( self->client->ps.torsoAnim ) || PM_RollingAnim( self->client->ps.torsoAnim ) ) ) {//my saber is not in a parrying position return qtrue; @@ -2855,16 +2981,16 @@ qboolean Jedi_InNoAIAnim( gentity_t *self ) case BOTH_BUTTERFLY_RIGHT: case BOTH_BUTTERFLY_FL1: case BOTH_BUTTERFLY_FR1: - case BOTH_FLIP_F: - case BOTH_FLIP_B: - case BOTH_FLIP_L: - case BOTH_FLIP_R: - case BOTH_DODGE_FL: - case BOTH_DODGE_FR: - case BOTH_DODGE_BL: - case BOTH_DODGE_BR: - case BOTH_DODGE_L: - case BOTH_DODGE_R: + case BOTH_FLIP_F: + case BOTH_FLIP_B: + case BOTH_FLIP_L: + case BOTH_FLIP_R: + case BOTH_DODGE_FL: + case BOTH_DODGE_FR: + case BOTH_DODGE_BL: + case BOTH_DODGE_BR: + case BOTH_DODGE_L: + case BOTH_DODGE_R: case BOTH_DODGE_HOLD_FL: case BOTH_DODGE_HOLD_FR: case BOTH_DODGE_HOLD_BL: @@ -2894,10 +3020,10 @@ qboolean Jedi_InNoAIAnim( gentity_t *self ) void Jedi_CheckJumpEvasionSafety( gentity_t *self, usercmd_t *cmd, evasionType_t evasionType ) { if ( evasionType != EVASION_OTHER//not a FlipEvasion, which does it's own safety checks - && NPC->client->ps.groundEntityNum != ENTITYNUM_NONE ) + && NPC->client->ps.groundEntityNum != ENTITYNUM_NONE ) {//on terra firma right now - if ( NPC->client->ps.velocity[2] > 0 - || NPC->client->ps.forceJumpCharge + if ( NPC->client->ps.velocity[2] > 0 + || NPC->client->ps.forceJumpCharge || cmd->upmove > 0 ) {//going to jump if ( !NAV_MoveDirSafe( NPC, cmd, NPC->client->ps.speed*10.0f ) ) @@ -2960,7 +3086,7 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc return EVASION_NONE; } - if ( PM_LockedAnim( self->client->ps.torsoAnim ) + if ( PM_LockedAnim( self->client->ps.torsoAnim ) && self->client->ps.torsoAnimTimer ) {//Never interrupt these... return EVASION_NONE; @@ -3000,7 +3126,7 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc } } else - { + { if ( incoming->s.weapon == WP_SABER ) {//flying lightsaber, face it! //FIXME: for this to actually work, we'd need to call update angles too? @@ -3020,10 +3146,12 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc rightdot = DotProduct(right, diff);// + Q_flrand(-0.10f,0.10f); //totalHeight = self->client->renderInfo.eyePoint[2] - self->absmin[2]; zdiff = hitloc[2] - self->client->renderInfo.eyePoint[2];// + Q_irand(-6,6); - + qboolean doDodge = qfalse; qboolean alwaysDodgeOrRoll = qfalse; - if ( self->client->NPC_class == CLASS_BOBAFETT ) + if (self->client->NPC_class == CLASS_BOBAFETT + || self->client->NPC_class == CLASS_MANDA + || self->client->NPC_class == CLASS_COMMANDO) { saberBusy = qtrue; doDodge = qtrue; @@ -3037,12 +3165,14 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc alwaysDodgeOrRoll = qtrue; } //see if we can dodge if need-be - if ( (dist>16&&(Q_irand( 0, 2 )||saberBusy)) - || self->client->ps.saberInFlight - || !self->client->ps.SaberActive() + if ( (dist>16&&(Q_irand( 0, 2 )||saberBusy)) + || self->client->ps.saberInFlight + || !self->client->ps.SaberActive() || (self->client->NPC_class == CLASS_REBORN && self->s.weapon != WP_SABER) ) {//either it will miss by a bit (and 25% chance) OR our saber is not in-hand OR saber is off - if ( self->NPC && (self->NPC->rank == RANK_CREWMAN || self->NPC->rank >= RANK_LT_JG) ) + if ( self->NPC + && (self->NPC->rank == RANK_CREWMAN || self->NPC->rank >= RANK_LT_JG) + && self->NPC->stats.move > 3) {//acrobat or fencer or above if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE &&//on the ground !(self->client->ps.pm_flags&PMF_DUCKED)&&cmd->upmove>=0&&TIMER_Done( self, "duck" )//not ducking @@ -3064,10 +3194,11 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc } qboolean doRoll = qfalse; - if ( ( self->client->NPC_class == CLASS_BOBAFETT //boba fett + if ((self->client->NPC_class == CLASS_BOBAFETT || self->client->NPC_class == CLASS_MANDA || self->client->NPC_class == CLASS_COMMANDO //boba fett || (self->client->NPC_class == CLASS_REBORN && self->s.weapon != WP_SABER) //non-saber reborn (cultist) ) - && !Q_irand( 0, 2 ) + //&& self->NPC->stats.move >= 3 + && !Q_irand( 0, 2 ) ) { doRoll = qtrue; @@ -3087,8 +3218,8 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc { if ( incoming || !saberBusy || alwaysDodgeOrRoll ) { - if ( rightdot > 12 - || (rightdot > 3 && zdiff < 5) + if ( rightdot > 12 + || (rightdot > 3 && zdiff < 5) || (!incoming&&fabs(hitdir[2])<0.25f) )//was normalized, 0.3 {//coming from right if ( doDodge ) @@ -3135,8 +3266,8 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc gi.Printf( "UR block\n" ); } } - else if ( rightdot < -12 - || (rightdot < -3 && zdiff < 5) + else if ( rightdot < -12 + || (rightdot < -3 && zdiff < 5) || (!incoming&&fabs(hitdir[2])<0.25f) )//was normalized, -0.3 {//coming from left if ( doDodge ) @@ -3242,7 +3373,7 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc TIMER_Start( self, "strafeLeft", Q_irand( 500, 1500 ) ); TIMER_Set( self, "strafeRight", 0 ); } - else + else { dodgeAnim = BOTH_DODGE_L; } @@ -3273,7 +3404,7 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc TIMER_Start( self, "strafeLeft", Q_irand( 500, 1500 ) ); TIMER_Set( self, "strafeRight", 0 ); } - else + else { dodgeAnim = BOTH_DODGE_R; } @@ -3348,16 +3479,16 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc } } } - else + else {//gotta jump! if ( self->NPC && (self->NPC->rank == RANK_CREWMAN || self->NPC->rank > RANK_LT_JG ) && (!Q_irand( 0, 10 ) || (!Q_irand( 0, 2 ) && (cmd->forwardmove || cmd->rightmove))) ) {//superjump //FIXME: check the jump, if can't, then block - if ( self->NPC - && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) + if ( self->NPC + && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) && self->client->ps.forceRageRecoveryTime < level.time - && !(self->client->ps.forcePowersActive&(1<client->ps.forcePowersActive&(1<client->ps ) ) { self->client->ps.forceJumpCharge = 320;//FIXME: calc this intelligently @@ -3371,12 +3502,12 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc else {//normal jump //FIXME: check the jump, if can't, then block - if ( self->NPC - && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) + if ( self->NPC + && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) && self->client->ps.forceRageRecoveryTime < level.time && !(self->client->ps.forcePowersActive&(1<client->NPC_class == CLASS_BOBAFETT + if ( (self->client->NPC_class == CLASS_BOBAFETT || self->client->NPC_class == CLASS_MANDA || self->client->NPC_class == CLASS_COMMANDO || (self->client->NPC_class == CLASS_REBORN && self->s.weapon != WP_SABER) ) && !Q_irand( 0, 1 ) ) @@ -3411,22 +3542,22 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc gi.Printf( "jump + " ); } } - if ( self->client->NPC_class == CLASS_ALORA + if ( self->client->NPC_class == CLASS_ALORA || self->client->NPC_class == CLASS_SHADOWTROOPER || self->client->NPC_class == CLASS_TAVION ) { - if ( !incoming - && self->client->ps.groundEntityNum < ENTITYNUM_NONE + if ( !incoming + && self->client->ps.groundEntityNum < ENTITYNUM_NONE && !Q_irand( 0, 2 ) ) { if ( !PM_SaberInAttack( self->client->ps.saberMove ) - && !PM_SaberInStart( self->client->ps.saberMove ) + && !PM_SaberInStart( self->client->ps.saberMove ) && !PM_InRoll( &self->client->ps ) && !PM_InKnockDown( &self->client->ps ) && !PM_SaberInSpecialAttack( self->client->ps.torsoAnim ) ) {//do the butterfly! int butterflyAnim; - if ( self->client->NPC_class == CLASS_ALORA + if ( self->client->NPC_class == CLASS_ALORA && !Q_irand( 0, 2 ) ) { butterflyAnim = BOTH_ALORA_SPIN; @@ -3536,10 +3667,10 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc (!Q_irand( 0, 10 ) || (!Q_irand( 0, 2 ) && (cmd->forwardmove || cmd->rightmove))) ) {//superjump //FIXME: check the jump, if can't, then block - if ( self->NPC - && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) + if ( self->NPC + && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) && self->client->ps.forceRageRecoveryTime < level.time - && !(self->client->ps.forcePowersActive&(1<client->ps.forcePowersActive&(1<client->ps ) ) { self->client->ps.forceJumpCharge = 320;//FIXME: calc this intelligently @@ -3553,8 +3684,8 @@ evasionType_t Jedi_SaberBlockGo( gentity_t *self, usercmd_t *cmd, vec3_t pHitloc else {//normal jump //FIXME: check the jump, if can't, then block - if ( self->NPC - && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) + if ( self->NPC + && !(self->NPC->scriptFlags&SCF_NO_ACROBATICS) && self->client->ps.forceRageRecoveryTime < level.time && !(self->client->ps.forcePowersActive&(1<client ) { @@ -3659,7 +3790,7 @@ static evasionType_t Jedi_CheckEvadeSpecialAttacks( void ) } if ( !NPC->enemy - || NPC->enemy->health <= 0 + || NPC->enemy->health <= 0 || !NPC->enemy->client ) {//don't keep blocking him once he's dead (or if not a client) return EVASION_NONE; @@ -3681,12 +3812,12 @@ static evasionType_t Jedi_CheckEvadeSpecialAttacks( void ) if ( (NPCInfo->aiFlags&NPCAI_BOSS_CHARACTER) || NPC->client->NPC_class == CLASS_SHADOWTROOPER || NPC->client->NPC_class == CLASS_ALORA - || Q_irand( 0, NPCInfo->rank ) > RANK_LT_JG ) + || Q_irand( 0, NPCInfo->rank ) > RANK_LT_JG ) {//see if we should back off if ( InFront( NPC->currentOrigin, NPC->enemy->currentOrigin, NPC->enemy->currentAngles ) ) {//facing me float minSafeDistSq = (NPC->maxs[0]*1.5f+NPC->enemy->maxs[0]*1.5f+NPC->enemy->client->ps.SaberLength()+24.0f); - minSafeDistSq *= minSafeDistSq; + minSafeDistSq *= minSafeDistSq; if ( DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ) < minSafeDistSq ) {//back off! Jedi_StartBackOff(); @@ -3714,7 +3845,7 @@ static evasionType_t Jedi_CheckEvadeSpecialAttacks( void ) float distSq = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ); if ( distSq < minSafeDistSq ) {//evade! - qboolean doJump = ( NPC->enemy->client->ps.torsoAnim == BOTH_ROLL_STAB || distSq < 3000.0f );//not much time left, just jump! + qboolean doJump = (qboolean)( NPC->enemy->client->ps.torsoAnim == BOTH_ROLL_STAB || distSq < 3000.0f );//not much time left, just jump! if ( (NPCInfo->scriptFlags&SCF_NO_ACROBATICS) || !doJump ) {//roll? @@ -3838,7 +3969,7 @@ static evasionType_t Jedi_CheckEvadeSpecialAttacks( void ) extern int WPDEBUG_SaberColor( saber_colors_t saberColor ); static qboolean Jedi_SaberBlock( void ) { - vec3_t hitloc, saberTipOld, saberTip, top, bottom, axisPoint, saberPoint, dir;//saberBase, + vec3_t hitloc, saberTipOld, saberTip, top, bottom, axisPoint, saberPoint, dir;//saberBase, vec3_t pointDir, baseDir, tipDir, saberHitPoint, saberMins={-4,-4,-4}, saberMaxs={4,4,4}; float pointDist, baseDirPerc; float dist, bestDist = Q3_INFINITE; @@ -3863,7 +3994,7 @@ static qboolean Jedi_SaberBlock( void ) {//can't move the saber to another position yet return qfalse; } - + /* if ( NPCInfo->rank < RANK_LT_JG && Q_irand( 0, (2 - g_spskill->integer) ) ) {//lower rank reborn have a random chance of not doing it at all @@ -3918,7 +4049,7 @@ static qboolean Jedi_SaberBlock( void ) VectorCopy( tr.endpos, hitloc ); } */ - + //FIXME: need to check against both sabers/blades now!...? for ( saberNum = 0; saberNum < MAX_SABERS; saberNum++ ) @@ -3969,12 +4100,12 @@ static qboolean Jedi_SaberBlock( void ) } dist = bestDist; - + if ( d_JediAI->integer ) { Com_Printf( S_COLOR_GREEN"enemy saber dist: %4.2f\n", dist ); } - + //now use the closest blade for my evasion check VectorMA( NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].muzzlePointOld, NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].length, NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].muzzleDirOld, saberTipOld ); VectorMA( NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].muzzlePoint, NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].length, NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].muzzleDir, saberTip ); @@ -3987,7 +4118,7 @@ static qboolean Jedi_SaberBlock( void ) dist = ShortestLineSegBewteen2LineSegs( NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].muzzlePoint, saberTip, bottom, top, saberPoint, axisPoint ); VectorSubtract( saberPoint, NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].muzzlePoint, pointDir ); pointDist = VectorLength( pointDir ); - + if ( NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].length <= 0 ) { baseDirPerc = 0.5f; @@ -4037,13 +4168,13 @@ static qboolean Jedi_SaberBlock( void ) { VectorCopy( tr.endpos, hitloc ); } - + if ( d_JediAI->integer ) { G_DebugLine( saberPoint, hitloc, FRAMETIME, WPDEBUG_SaberColor( NPC->enemy->client->ps.saber[closestSaberNum].blade[closestBladeNum].color ), qtrue ); } - //FIXME: if saber is off and/or we have force speed and want to be really cocky, + //FIXME: if saber is off and/or we have force speed and want to be really cocky, // and the swing misses by some amount, we can use the dodges here... :) evasionType_t evasionType; if ( (evasionType=Jedi_SaberBlockGo( NPC, &ucmd, hitloc, dir, NULL, dist )) != EVASION_NONE ) @@ -4066,7 +4197,7 @@ static qboolean Jedi_SaberBlock( void ) //determine how long to hold this anim if ( TIMER_Done( NPC, "parryTime" ) ) { - if ( NPC->client->NPC_class == CLASS_TAVION + if ( NPC->client->NPC_class == CLASS_TAVION || NPC->client->NPC_class == CLASS_SHADOWTROOPER || NPC->client->NPC_class == CLASS_ALORA ) { @@ -4101,7 +4232,7 @@ static qboolean Jedi_SaberBlock( void ) { if ( Jedi_CheckEvadeSpecialAttacks() != EVASION_NONE ) {//got a new evasion! - //see if it's okay to jump + //see if it's okay to jump Jedi_CheckJumpEvasionSafety( NPC, &ucmd, evasionType ); } } @@ -4118,10 +4249,16 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en { vec3_t dirEnemy2Me; int evasionChance = 30;//only step aside 30% if he's moving at me but not attacking + if (NPC->s.weapon != WP_SABER) + {//be more wary of approaching enemy even if he's not attacking + evasionChance = 40; + } + qboolean enemy_attacking = qfalse; qboolean throwing_saber = qfalse; qboolean shooting_lightning = qfalse; + if ( !NPC->enemy->client ) { return; @@ -4167,8 +4304,7 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en else if ( Jedi_CheckEvadeSpecialAttacks() != EVASION_NONE ) { return; - } - + } VectorSubtract( NPC->currentOrigin, NPC->enemy->currentOrigin, dirEnemy2Me ); VectorNormalize( dirEnemy2Me ); @@ -4189,9 +4325,13 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en if ( NPC->enemy->client->ps.saberInFlight && NPC->enemy->client->ps.saberEntityNum != ENTITYNUM_NONE && NPC->enemy->client->ps.saberEntityState != SES_RETURNING ) - {//enemy is shooting lightning + {//enemy is saber throwing enemy_attacking = qtrue; throwing_saber = qtrue; + if (NPC->s.weapon != WP_SABER) + {//I can't block it so have to dodge + evasionChance = 90; + } } //FIXME: this needs to take skill and rank(reborn type) into account much more @@ -4234,11 +4374,13 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en } else */if ( NPC->client->ps.weaponTime || NPC->client->ps.saberInFlight - || NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA + || NPC->client->NPC_class == CLASS_COMMANDO || (NPC->client->NPC_class == CLASS_REBORN && NPC->s.weapon != WP_SABER) || NPC->client->NPC_class == CLASS_ROCKETTROOPER ) {//I'm attacking or recovering from a parry, can only try to strafe/jump right now - if ( Q_irand( 0, 10 ) < NPCInfo->stats.aggression ) + if ( (Q_irand( 0, 10 ) < NPCInfo->stats.aggression && NPC->s.weapon == WP_SABER)) { return; } @@ -4304,7 +4446,7 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en } else if ( enemy_dist < 56 ) {//he's very close, maybe we should be more inclined to block or throw - whichDefense = Q_irand( NPCInfo->stats.aggression, 12 ); + whichDefense = Q_irand( Q_min(NPCInfo->stats.aggression, 12), 12 ); } else { @@ -4329,13 +4471,17 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en case 3: //use jedi force push? or kick? //FIXME: try to do this if health low or enemy back to a cliff? - if ( Jedi_DecideKick()//let's try a kick + if ( Jedi_DecideKick(enemy_dist)//let's try a kick && ( G_PickAutoMultiKick( NPC, qfalse, qtrue ) != LS_NONE || (G_CanKickEntity(NPC, NPC->enemy )&&G_PickAutoKick( NPC, NPC->enemy, qtrue )!=LS_NONE) ) ) {//kicked - TIMER_Set( NPC, "kickDebounce", Q_irand( 3000, 10000 ) ); + int kickFreqIncrease = NPC->NPC->stats.meleeKicks - 1; + if (kickFreqIncrease > 0) + TIMER_Set(NPC, "kickDebounce", Q_irand(3000, 10000 - kickFreqIncrease * 1000)); + else + TIMER_Set(NPC, "kickDebounce", (Q_irand(3000, 10000))); } else if ( (NPCInfo->rank == RANK_ENSIGN || NPCInfo->rank > RANK_LT_JG) && TIMER_Done( NPC, "parryTime" ) ) {//FIXME: check forcePushRadius[NPC->client->ps.forcePowerLevel[FP_PUSH]] @@ -4361,16 +4507,22 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en if ( !Q_irand( 0, 5 ) || !Jedi_Strafe( 300, 1000, 0, 1000, qfalse ) ) {//certain chance they will pick an alternative evasion //if couldn't strafe, try a different kind of evasion... - if ( Jedi_DecideKick() && G_CanKickEntity(NPC, NPC->enemy ) && G_PickAutoKick( NPC, NPC->enemy, qtrue ) != LS_NONE ) + if ((NPC->s.weapon == WP_SABER || /*NPCInfo->stats.meleeKicks*/ NPC->s.weapon == WP_MELEE) && Jedi_DecideKick(enemy_dist) && G_CanKickEntity(NPC, NPC->enemy) && G_PickAutoKick(NPC, NPC->enemy, qtrue) != LS_NONE) {//kicked! - TIMER_Set( NPC, "kickDebounce", Q_irand( 3000, 10000 ) ); + int kickFreqIncrease = NPC->NPC->stats.meleeKicks - 1; + if (kickFreqIncrease > 0) + TIMER_Set(NPC, "kickDebounce", Q_irand(3000, 10000 - kickFreqIncrease * 1000)); + else + TIMER_Set(NPC, "kickDebounce", (Q_irand(3000, 10000))); } else if ( shooting_lightning || throwing_saber || enemy_dist < 80 ) { //FIXME: force-jump+forward - jump over the guy! - if ( shooting_lightning || (!Q_irand( 0, 2 ) && NPCInfo->stats.aggression < 4 && TIMER_Done( NPC, "parryTime" ) ) ) - { - if ( (NPCInfo->rank == RANK_ENSIGN || NPCInfo->rank > RANK_LT_JG) && !shooting_lightning && Q_irand( 0, 2 ) ) + if ( shooting_lightning || (!Q_irand( 0, 2 ) + && (NPCInfo->stats.aggression < 4 || NPC->s.weapon != WP_SABER || NPC->client->ps.saberInFlight) + && TIMER_Done( NPC, "parryTime" ) ) ) + { + if ( (NPCInfo->rank == RANK_ENSIGN || NPCInfo->rank > RANK_LT_JG) && !shooting_lightning && Q_irand( 0, 2 ) && (NPC->client->ps.forcePowerLevel[FP_PUSH] > 0 || !(NPCInfo->stats.restrictJediPowers)) ) {//FIXME: check forcePushRadius[NPC->client->ps.forcePowerLevel[FP_PUSH]] ForceThrow( NPC, qfalse ); } @@ -4378,7 +4530,9 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en && !(NPCInfo->scriptFlags&SCF_NO_ACROBATICS) && NPC->client->ps.forceRageRecoveryTime < level.time && !(NPC->client->ps.forcePowersActive&(1<client->ps ) ) + && !PM_InKnockDown( &NPC->client->ps ) + && (NPC->client->ps.forcePowerLevel[FP_LEVITATION] > 0 + || !(NPCInfo->stats.restrictJediPowers))) {//FIXME: make this a function call? //FIXME: check for clearance, safety of landing spot? NPC->client->ps.forceJumpCharge = 480; @@ -4405,9 +4559,24 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en } } } - else if ( enemy_attacking ) + else if (enemy_attacking || (enemy_approaching && (NPC->s.weapon != WP_SABER || NPC->client->ps.saberInFlight))) { - Jedi_SaberBlock(); + if (!enemy_attacking && enemy_approaching && (NPC->s.weapon != WP_SABER || NPC->client->ps.saberInFlight)) + { + /* + if (Jedi_DecideKick(enemy_dist) && G_CanKickEntity(NPC, NPC->enemy) && G_PickAutoKick(NPC, NPC->enemy, qtrue) != LS_NONE) + {//kicked! + TIMER_Set(NPC, "kickDebounce", Q_irand(3000, 10000)); + }*/ + + //if enemy hasn't slashed yet try just backing away + Jedi_StartBackOff(); + } + else + { + Jedi_SaberBlock(); + } + } } } @@ -4426,6 +4595,8 @@ static void Jedi_EvasionSaber( vec3_t enemy_movedir, float enemy_dist, vec3_t en {//FIXME: make this a function call? //FIXME: check for clearance, safety of landing spot? if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA + || NPC->client->NPC_class == CLASS_COMMANDO || (NPC->client->NPC_class == CLASS_REBORN && NPC->s.weapon != WP_SABER) || NPC->client->NPC_class == CLASS_ROCKETTROOPER ) { @@ -4484,14 +4655,14 @@ gentity_t *Jedi_FindEnemyInCone( gentity_t *self, gentity_t *fallback, float min AngleVectors( self->client->ps.viewangles, forward, NULL, NULL ); - for ( e = 0 ; e < 3 ; e++ ) + for ( e = 0 ; e < 3 ; e++ ) { mins[e] = self->currentOrigin[e] - 1024; maxs[e] = self->currentOrigin[e] + 1024; } numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for ( e = 0 ; e < numListedEntities ; e++ ) + for ( e = 0 ; e < numListedEntities ; e++ ) { check = entityList[e]; if ( check == self ) @@ -4577,23 +4748,26 @@ static void Jedi_SetEnemyInfo( vec3_t enemy_dest, vec3_t enemy_dir, float *enemy // two enemies, use lightning spread when fighting multiple enemies, etc. // Also, when kill one, check rest of group instead of walking up to victim. } + //init this to false - enemy_in_striking_range = qfalse; + enemy_in_range = qfalse; + enemy_near = qfalse; + enemy_approaching = qfalse; + if ( *enemy_dist <= 0.0f ) { - enemy_in_striking_range = qtrue; + enemy_in_range = qtrue; } else {//if he's too far away, see if he's at least facing us or coming towards us if ( *enemy_dist <= 32.0f ) - {//has to be facing us - vec3_t eAngles = {0,NPC->currentAngles[YAW],0}; - if ( InFOV( NPC->currentOrigin, NPC->enemy->currentOrigin, eAngles, 30, 90 ) ) - {//in striking range - enemy_in_striking_range = qtrue; - } + { + enemy_in_range = qtrue; + } + if (*enemy_dist < 64.0f) { + enemy_near = qtrue; } - if ( *enemy_dist >= 64.0f ) + else if ( *enemy_dist >= 64.0f || enemy_near ) {//we have to be approaching each other float vDot = 1.0f; if ( !VectorCompare( NPC->client->ps.velocity, vec3_origin ) ) @@ -4614,9 +4788,10 @@ static void Jedi_SetEnemyInfo( vec3_t enemy_dest, vec3_t enemy_dir, float *enemy {//neither of us is moving, below check will fail, so just return return; } + if ( vDot >= *enemy_dist ) {//moving towards each other - enemy_in_striking_range = qtrue; + enemy_approaching = qtrue; } } } @@ -4657,7 +4832,7 @@ static void Jedi_FaceEnemy( qboolean doPitch ) CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_eyes ); - if ( NPC->client->NPC_class == CLASS_BOBAFETT + if ((NPC->client->NPC_class == CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_MANDA) && TIMER_Done( NPC, "flameTime" ) && NPC->s.weapon != WP_NONE && NPC->s.weapon != WP_DISRUPTOR @@ -4681,8 +4856,8 @@ static void Jedi_FaceEnemy( qboolean doPitch ) } //Find the desired angles - if ( !NPC->client->ps.saberInFlight - && (NPC->client->ps.legsAnim == BOTH_A2_STABBACK1 + if ( !NPC->client->ps.saberInFlight + && (NPC->client->ps.legsAnim == BOTH_A2_STABBACK1 || NPC->client->ps.legsAnim == BOTH_CROUCHATTACKBACK1 || NPC->client->ps.legsAnim == BOTH_ATTACK_BACK || NPC->client->ps.legsAnim == BOTH_A7_KICK_B ) @@ -4696,8 +4871,8 @@ static void Jedi_FaceEnemy( qboolean doPitch ) else if ( NPC->client->ps.legsAnim == BOTH_A7_KICK_L ) {//keep enemy to left } - else if ( NPC->client->ps.legsAnim == BOTH_A7_KICK_RL - || NPC->client->ps.legsAnim == BOTH_A7_KICK_BF + else if ( NPC->client->ps.legsAnim == BOTH_A7_KICK_RL + || NPC->client->ps.legsAnim == BOTH_A7_KICK_BF || NPC->client->ps.legsAnim == BOTH_A7_KICK_S ) {//??? } @@ -4893,7 +5068,7 @@ static void Jedi_DebounceDirectionChanges( void ) } } else if ( !TIMER_Done( NPC, "moveright" ) ) - {//NOTE: edge checking should stop me if this is bad... + {//NOTE: edge checking should stop me if this is bad... ucmd.rightmove = 127; VectorClear( NPC->client->ps.moveDir ); } @@ -5092,9 +5267,9 @@ static void Jedi_CombatTimersUpdate( int enemy_dist ) { gi.Printf( "(%d) HIT: agg %d\n", level.time, NPCInfo->stats.aggression ); } - if ( !Q_irand( 0, 3 ) - && NPCInfo->blockedSpeechDebounceTime < level.time - && jediSpeechDebounceTime[NPC->client->playerTeam] < level.time + if ( !Q_irand( 0, 3 ) + && NPCInfo->blockedSpeechDebounceTime < level.time + && jediSpeechDebounceTime[NPC->client->playerTeam] < level.time && NPC->painDebounceTime < level.time - 1000 ) { G_AddVoiceEvent( NPC, Q_irand( EV_GLOAT1, EV_GLOAT3 ), 3000 ); @@ -5183,12 +5358,12 @@ static void Jedi_CombatIdle( int enemy_dist ) {//don't do this idle stuff if throwing saber return; } - if ( NPC->client->ps.forcePowersActive&(1<client->ps.forcePowersActive&(1<client->ps.forceRageRecoveryTime > level.time ) {//never taunt while raging or recovering from rage return; } - if ( NPC->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SCEPTER] ) {//never taunt when holding scepter return; } @@ -5211,11 +5386,13 @@ static void Jedi_CombatIdle( int enemy_dist ) if ( TIMER_Done( NPC, "chatter" ) ) {//FIXME: add more taunt behaviors //FIXME: sometimes he turns it off, then turns it right back on again??? - if ( enemy_dist > 200 + if ( enemy_dist > 200 && NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA + && NPC->client->NPC_class != CLASS_COMMANDO && (NPC->client->NPC_class != CLASS_REBORN || NPC->s.weapon == WP_SABER) && NPC->client->NPC_class != CLASS_ROCKETTROOPER - && NPC->client->ps.SaberActive() + && NPC->client->ps.SaberActive() && !Q_irand( 0, 5 ) ) {//taunt even more, turn off the saber //FIXME: don't do this if health low? @@ -5276,9 +5453,9 @@ static qboolean Jedi_AttackDecide( int enemy_dist ) } return qfalse; } - if ( NPC->enemy->client - && NPC->enemy->s.weapon == WP_SABER - && NPC->enemy->client->ps.saberLockTime > level.time + if ( NPC->enemy->client + && NPC->enemy->s.weapon == WP_SABER + && NPC->enemy->client->ps.saberLockTime > level.time && NPC->client->ps.saberLockTime < level.time ) {//enemy is in a saberLock and we are not return qfalse; @@ -5291,7 +5468,7 @@ static qboolean Jedi_AttackDecide( int enemy_dist ) {//desann and luke chance = 20; } - else if ( NPC->client->NPC_class == CLASS_TAVION + else if ( NPC->client->NPC_class == CLASS_TAVION || NPC->client->NPC_class == CLASS_ALORA ) {//tavion chance = 10; @@ -5300,7 +5477,7 @@ static qboolean Jedi_AttackDecide( int enemy_dist ) {//shadowtrooper chance = 5; } - else if ( NPC->client->NPC_class == CLASS_REBORN && NPCInfo->rank == RANK_LT_JG ) + else if ( NPC->client->NPC_class == CLASS_REBORN && NPCInfo->rank == RANK_LT_JG ) {//fencer chance = 5; } @@ -5321,7 +5498,7 @@ static qboolean Jedi_AttackDecide( int enemy_dist ) } } - if ( NPC->client->NPC_class == CLASS_TAVION || + if ( NPC->client->NPC_class == CLASS_TAVION || NPC->client->NPC_class == CLASS_ALORA || NPC->client->NPC_class == CLASS_SHADOWTROOPER || ( NPC->client->NPC_class == CLASS_REBORN && NPCInfo->rank == RANK_LT_JG ) || @@ -5340,7 +5517,7 @@ static qboolean Jedi_AttackDecide( int enemy_dist ) } //try to hit them if we can - if ( !enemy_in_striking_range ) + if ( !enemy_in_range ) { return qfalse; } @@ -5350,19 +5527,46 @@ static qboolean Jedi_AttackDecide( int enemy_dist ) return qfalse; } - if ( (NPCInfo->scriptFlags&SCF_DONT_FIRE) ) - {//not allowed to attack + + if (NPC->s.weapon == WP_MELEE + && enemy_dist < -20 + && ucmd.forwardmove >= 0 //if moving backwards punch tends to miss + && (NPC->client->ps.groundEntityNum != ENTITYNUM_NONE + && NPC->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE)) //both on ground + { + if (!(NPC->client->ps.forcePowerDebounce[FP_PUSH] > level.time || NPC->client->ps.forcePowerDebounce[FP_PULL] > level.time + || NPC->client->ps.forcePowerDebounce[FP_DRAIN] > level.time || NPC->client->ps.forcePowerDebounce[FP_LIGHTNING] > level.time)) + {//not in the middle of pushing, pulling, draining, or zapping + if ((level.time - NPC->client->ps.forcePowerDebounce[FP_PUSH] < 1000 + || level.time - NPC->client->ps.forcePowerDebounce[FP_PUSH] < 1000)) + {//we already tried to push/pull and aren't using another force power + if ((NPCInfo->scriptFlags&SCF_DONT_FIRE && NPCInfo->stats.rareFire)) + {//melee cultists punch *rarely* + if (!Q_irand(0, 6) - NPCInfo->stats.aggression) + { + TIMER_Set(NPC, "parryTime", 4000); //single punches only, don't try again for a while + } + } + else + { + //no delays or anything, punch normally + } + } + } + } + else { //no punching return qfalse; } - + if ( !(ucmd.buttons&BUTTON_ATTACK) && !(ucmd.buttons&BUTTON_ALT_ATTACK) - && !(ucmd.buttons&BUTTON_FORCE_FOCUS) ) + && !(ucmd.buttons&BUTTON_FORCE_FOCUS) + && !(ucmd.buttons&BUTTON_SABERTHROW)) {//not already attacking //Try to attack WeaponThink( qtrue ); } - + //FIXME: Maybe try to push enemy off a ledge? //close enough to step forward @@ -5473,10 +5677,10 @@ static void Jedi_CheckEnemyMovement( float enemy_dist ) TIMER_Set( NPC, "movecenter", Q_irand( 500, 1000 ) ); } } - else if ( NPC->enemy->client->ps.legsAnim == BOTH_WALL_FLIP_BACK1 - || NPC->enemy->client->ps.legsAnim == BOTH_WALL_FLIP_RIGHT - || NPC->enemy->client->ps.legsAnim == BOTH_WALL_FLIP_LEFT - || NPC->enemy->client->ps.legsAnim == BOTH_WALL_RUN_LEFT_FLIP + else if ( NPC->enemy->client->ps.legsAnim == BOTH_WALL_FLIP_BACK1 + || NPC->enemy->client->ps.legsAnim == BOTH_WALL_FLIP_RIGHT + || NPC->enemy->client->ps.legsAnim == BOTH_WALL_FLIP_LEFT + || NPC->enemy->client->ps.legsAnim == BOTH_WALL_RUN_LEFT_FLIP || NPC->enemy->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT_FLIP ) {//he's flipping off a wall if ( NPC->enemy->client->ps.groundEntityNum == ENTITYNUM_NONE ) @@ -5592,7 +5796,7 @@ static void Jedi_CheckJumps( void ) { return; } - + //NOTE: for now, we clear ucmd.forwardmove & ucmd.rightmove while in air to avoid jumps going awry... if ( !jumpVel[0] && !jumpVel[1] )//FIXME: && !ucmd.forwardmove && !ucmd.rightmove? {//we assume a jump straight up is safe @@ -5611,7 +5815,7 @@ static void Jedi_CheckJumps( void ) tr.trType = TR_GRAVITY; tr.trTime = level.time; VectorCopy( NPC->currentOrigin, lastPos ); - + //This may be kind of wasteful, especially on long throws... use larger steps? Divide the travelTime into a certain hard number of slices? Trace just to apex and down? for ( elapsedTime = 500; elapsedTime <= 4000; elapsedTime += 500 ) { @@ -5689,7 +5893,7 @@ void RT_CheckJump( void ) if ( NPC->enemy ) { //FIXME: debounce this? - if ( TIMER_Done( NPC, "roamTime" ) + if ( TIMER_Done( NPC, "roamTime" ) && Q_irand( 0, 9 ) ) {//okay to try to find another spot to be int cpFlags = (CP_CLEAR|CP_HAS_ROUTE);//must have a clear shot at enemy @@ -5704,20 +5908,20 @@ void RT_CheckJump( void ) cpFlags |= CP_RETREAT; } int sendFlags = cpFlags; - int cp = NPC_FindCombatPointRetry( NPC->currentOrigin, + int cp = NPC_FindCombatPointRetry( NPC->currentOrigin, + NPC->currentOrigin, NPC->currentOrigin, - NPC->currentOrigin, - &sendFlags, - 256, + &sendFlags, + 256, NPCInfo->lastFailedCombatPoint ); if ( cp == -1 ) {//try again, no route needed since we can rocket-jump to it! cpFlags &= ~CP_HAS_ROUTE; - cp = NPC_FindCombatPointRetry( NPC->currentOrigin, - NPC->currentOrigin, + cp = NPC_FindCombatPointRetry( NPC->currentOrigin, + NPC->currentOrigin, NPC->currentOrigin, &cpFlags, - 256, + 256, NPCInfo->lastFailedCombatPoint ); } if ( cp != -1 ) @@ -5766,7 +5970,7 @@ void RT_CheckJump( void ) //If we can't get straight at him if ( !Jedi_ClearPathToSpot( jumpPos, jumpEntNum ) ) {//hunt him down - if ( (NPC_ClearLOS( NPC->enemy )||NPCInfo->enemyLastSeenTime>level.time-500) + if ( (NPC_ClearLOS( NPC->enemy )||NPCInfo->enemyLastSeenTime>level.time-500) && InFOV( jumpPos, NPC->currentOrigin, NPC->client->ps.viewangles, 20, 60 ) ) { if ( NPC_TryJump( jumpPos ) ) // Rocket Trooper @@ -5793,7 +5997,7 @@ void RT_CheckJump( void ) } } //////////////////////////////////////////////////////////////////////////////////////// -// +// //////////////////////////////////////////////////////////////////////////////////////// static void Jedi_Combat( void ) { @@ -5818,7 +6022,7 @@ static void Jedi_Combat( void ) if ( !Jedi_ClearPathToSpot( enemy_dest, NPC->enemy->s.number ) ) {//hunt him down //gi.Printf( "No Clear Path\n" ); - if ( (NPC_ClearLOS( NPC->enemy )||NPCInfo->enemyLastSeenTime>level.time-500) && NPC_FaceEnemy( qtrue ) )//( NPCInfo->rank == RANK_CREWMAN || NPCInfo->rank > RANK_LT_JG ) && + if ( (NPC_ClearLOS( NPC->enemy )||NPCInfo->enemyLastSeenTime>level.time-500) && NPC_FaceEnemy( qtrue ) )//( NPCInfo->rank == RANK_CREWMAN || NPCInfo->rank > RANK_LT_JG ) && { //try to jump to him? /* @@ -5845,12 +6049,15 @@ static void Jedi_Combat( void ) } } */ - //FIXME: about every 1 second calc a velocity, - //run a loop of traces with evaluate trajectory + //FIXME: about every 1 second calc a velocity, + //run a loop of traces with evaluate trajectory //for gravity with my size, see if it makes it... //this will also catch misacalculations that send you off ledges! //gi.Printf( "Considering Jump\n" ); - if (NPC->client && NPC->client->NPC_class==CLASS_BOBAFETT) + if (NPC->client && + (NPC->client->NPC_class==CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA + || NPC->client->NPC_class == CLASS_COMMANDO)) { Boba_FireDecide(); } @@ -5876,7 +6083,10 @@ static void Jedi_Combat( void ) G_AddVoiceEvent( NPC, Q_irand( EV_JLOST1, EV_JLOST3 ), 3000 ); jediSpeechDebounceTime[NPC->client->playerTeam] = NPCInfo->blockedSpeechDebounceTime = level.time + 3000; } - if (NPC->client && NPC->client->NPC_class==CLASS_BOBAFETT) + if (NPC->client && + (NPC->client->NPC_class==CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA + || NPC->client->NPC_class == CLASS_COMMANDO)) { Boba_FireDecide(); } @@ -5917,7 +6127,9 @@ static void Jedi_Combat( void ) } //if ( !enemy_lost ) - if (NPC->client->NPC_class != CLASS_BOBAFETT) + if (NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA + && NPC->client->NPC_class != CLASS_COMMANDO) { //Update our seen enemy position if ( !NPC->enemy->client || ( NPC->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE && NPC->client->ps.groundEntityNum != ENTITYNUM_NONE ) ) @@ -5933,7 +6145,7 @@ static void Jedi_Combat( void ) Jedi_FaceEnemy( qtrue ); } NPC_UpdateAngles( qtrue, qtrue ); - + //Check for evasion if ( TIMER_Done( NPC, "parryTime" ) ) {//finished parrying @@ -5974,7 +6186,9 @@ static void Jedi_Combat( void ) else { } - if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA + || NPC->client->NPC_class == CLASS_COMMANDO) { Boba_FireDecide(); } @@ -6021,7 +6235,7 @@ NPC_Jedi_Pain ------------------------- */ -void NPC_Jedi_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc ) +void NPC_Jedi_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc ) { //FIXME: base the actual aggression add/subtract on health? //FIXME: don't do this more than once per frame? @@ -6045,7 +6259,7 @@ void NPC_Jedi_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, con {//ouch... maybe switch up which saber power level we're using Jedi_AdjustSaberAnimLevel( self, Q_irand( SS_FAST, SS_STRONG ) ); } - if ( !Q_irand( 0, 1 ) )//damage > 20 || self->health < 40 || + if ( !Q_irand( 0, 1 ) )//damage > 20 || self->health < 40 || { //Com_Printf( "(%d) drop agg - hit by saber\n", level.time ); Jedi_Aggression( self, -1 ); @@ -6066,7 +6280,7 @@ void NPC_Jedi_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, con AngleVectors( fwdangles, NULL, right, NULL ); float rightdot = DotProduct(right, diff); float zdiff = point[2] - self->client->renderInfo.eyePoint[2]; - + gi.Printf( "(%d) saber hit at height %4.2f, zdiff: %4.2f, rightdot: %4.2f\n", level.time, point[2]-self->absmin[2],zdiff,rightdot); } } @@ -6102,8 +6316,8 @@ void NPC_Jedi_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, con } //check special defenses - if ( other - && other->client + if ( other + && other->client && !OnSameTeam( self, other )) {//hit by a client //FIXME: delay this until *after* the pain anim? @@ -6111,7 +6325,7 @@ void NPC_Jedi_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, con || mod == MOD_FORCE_LIGHTNING || mod == MOD_FORCE_DRAIN ) {//see if we should turn on absorb - if ( (self->client->ps.forcePowersKnown&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1<s.number >= MAX_CLIENTS //enemy is an NPC @@ -6129,21 +6343,21 @@ void NPC_Jedi_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, con } else if ( damage > Q_irand( 5, 20 ) ) {//respectable amount of normal damage - if ( (self->client->ps.forcePowersKnown&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1<s.number >= MAX_CLIENTS //enemy is an NPC || Q_irand( 0, g_spskill->integer+1 ) )//enemy is player { if ( Q_irand( 0, self->NPC->rank ) > RANK_ENSIGN ) - { + { if ( !Q_irand( 0, 1 ) ) { if ( other->s.number < MAX_CLIENTS && ((self->NPC->aiFlags&NPCAI_BOSS_CHARACTER) - || self->client->NPC_class==CLASS_SHADOWTROOPER) - && Q_irand(0, 6-g_spskill->integer) ) - { + || self->client->NPC_class==CLASS_SHADOWTROOPER) + && Q_irand(0, 6-g_spskill->integer) ) + { } else { @@ -6162,8 +6376,8 @@ qboolean Jedi_CheckDanger( void ) int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue ); if ( level.alertEvents[alertEvent].level >= AEL_DANGER ) {//run away! - if ( !level.alertEvents[alertEvent].owner - || !level.alertEvents[alertEvent].owner->client + if ( !level.alertEvents[alertEvent].owner + || !level.alertEvents[alertEvent].owner->client || (level.alertEvents[alertEvent].owner!=NPC&&level.alertEvents[alertEvent].owner->client->playerTeam!=NPC->client->playerTeam) ) {//no owner return qfalse; @@ -6251,6 +6465,8 @@ void Jedi_Ambush( gentity_t *self ) NPC_SetAnim( self, SETANIM_BOTH, BOTH_CEILING_DROP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); self->client->ps.weaponTime = NPC->client->ps.torsoAnimTimer; if ( self->client->NPC_class != CLASS_BOBAFETT + && self->client->NPC_class != CLASS_MANDA + && self->client->NPC_class != CLASS_COMMANDO && self->client->NPC_class != CLASS_ROCKETTROOPER ) { self->client->ps.SaberActivate(); @@ -6367,7 +6583,9 @@ static void Jedi_Patrol( void ) G_SetEnemy( NPC, best_enemy ); NPCInfo->stats.aggression = 3; } - else if ( NPC->client->NPC_class != CLASS_BOBAFETT ) + else if ( NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA + && NPC->client->NPC_class != CLASS_COMMANDO) {//the player, toy with him //get progressively more interested over time if ( TIMER_Done( NPC, "watchTime" ) ) @@ -6472,14 +6690,14 @@ void NPC_BSJedi_FollowLeader( void ) //Com_Printf( "(%d) drop agg - no enemy (follow)\n", level.time ); Jedi_AggressionErosion(-1); } - + //did we drop our saber? If so, go after it! if ( NPC->client->ps.saberInFlight ) {//saber is not in hand if ( NPC->client->ps.saberEntityNum < ENTITYNUM_NONE && NPC->client->ps.saberEntityNum > 0 )//player is 0 {// if ( g_entities[NPC->client->ps.saberEntityNum].s.pos.trType == TR_STATIONARY ) - {//fell to the ground, try to pick it up... + {//fell to the ground, try to pick it up... if ( Jedi_CanPullBackSaber( NPC ) ) { //FIXME: if it's on the ground and we just pulled it back to us, should we @@ -6508,8 +6726,8 @@ void NPC_BSJedi_FollowLeader( void ) NPC_BSFollowLeader(); - if (!NPC->enemy && - NPC->health < NPC->max_health && + if (!NPC->enemy && + NPC->health < NPC->max_health && (NPC->client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1<integer + if ( (g_saberNewControlScheme->integer && !(ucmd.buttons&BUTTON_FORCE_FOCUS) ) - ||(!g_saberNewControlScheme->integer + ||(!g_saberNewControlScheme->integer && !(ucmd.buttons&BUTTON_ALT_ATTACK) ) ) {//not already going to do a kata move somehow if ( NPC->client->ps.groundEntityNum != ENTITYNUM_NONE ) @@ -6555,17 +6773,22 @@ qboolean Jedi_CheckKataAttack( void ) if ( Q_irand( 0, g_spskill->integer+1 ) //50% chance on easy, 66% on medium, 75% on hard && !Q_irand( 0, 9 ) )//10% chance overall {//base on skill level - ucmd.upmove = 0; - VectorClear( NPC->client->ps.moveDir ); - if ( g_saberNewControlScheme->integer ) - { - ucmd.buttons |= BUTTON_FORCE_FOCUS; - } - else - { - ucmd.buttons |= BUTTON_ALT_ATTACK; + if ( enemy_in_range //enemy is in saber range + || (enemy_near && enemy_approaching) //is moving into range + || (NPC->client->NPC_class == CLASS_REBORN && !enemy_near && !enemy_approaching && !enemy_in_range) ) + {//reborn like to show off a little... + ucmd.upmove = 0; + VectorClear( NPC->client->ps.moveDir ); + if ( g_saberNewControlScheme->integer ) + { + ucmd.buttons |= BUTTON_FORCE_FOCUS; + } + else + { + ucmd.buttons |= BUTTON_ALT_ATTACK; + } + return qtrue; } - return qtrue; } } } @@ -6618,8 +6841,8 @@ static void Jedi_Attack( void ) { //FIXME: maybe kick out of saberlock? //maybe if I'm losing I should try to force-push out of it? Very rarely, though... - if ( NPC->client->ps.forcePowerLevel[FP_PUSH] > FORCE_LEVEL_2 - && NPC->client->ps.saberLockTime < level.time + 5000 + if ( NPC->client->ps.forcePowerLevel[FP_PUSH] > FORCE_LEVEL_2 + && NPC->client->ps.saberLockTime < level.time + 5000 && !Q_irand( 0, 10 )) { ForceThrow( NPC, qfalse ); @@ -6628,7 +6851,7 @@ static void Jedi_Attack( void ) else { float chance; - + if ( NPC->client->NPC_class == CLASS_DESANN || !Q_stricmp("Yoda",NPC->NPC_type) ) { if ( g_spskill->integer ) @@ -6640,7 +6863,7 @@ static void Jedi_Attack( void ) chance = 3.0f;//he pushes *hard* } } - else if ( NPC->client->NPC_class == CLASS_TAVION + else if ( NPC->client->NPC_class == CLASS_TAVION || NPC->client->NPC_class == CLASS_SHADOWTROOPER || NPC->client->NPC_class == CLASS_ALORA || (NPC->client->NPC_class == CLASS_KYLE&&(NPC->spawnflags&1)) ) @@ -6664,8 +6887,8 @@ static void Jedi_Attack( void ) } } if ( (NPCInfo->aiFlags&NPCAI_BOSS_CHARACTER) ) - { - chance += Q_irand(0,2); + { + chance += Q_irand(0,2); } else if ( (NPCInfo->aiFlags&NPCAI_SUBBOSS_CHARACTER) ) { @@ -6705,20 +6928,45 @@ static void Jedi_Attack( void ) return; } } + else { + if (NPC->enemy && NPC->enemy->health > 0) + { + if (NPC->enemy->s.weapon == WP_SABER) + {//be sure to continue evasion even if can't pull back saber yet + vec3_t enemy_dir, enemy_movedir, enemy_dest; + float enemy_dist, enemy_movespeed; + Jedi_SetEnemyInfo(enemy_dest, enemy_dir, &enemy_dist, enemy_movedir, &enemy_movespeed, 300); + Jedi_EvasionSaber(enemy_movedir, enemy_dist, enemy_dir); + } + } + } } - } + + if (NPC->enemy && NPC->enemy->health > 0) + { + if (NPC->enemy->s.weapon == WP_SABER) + {//be sure to continue evasion even if can't pull back saber yet + vec3_t enemy_dir, enemy_movedir, enemy_dest; + float enemy_dist, enemy_movespeed; + Jedi_SetEnemyInfo(enemy_dest, enemy_dir, &enemy_dist, enemy_movedir, &enemy_movespeed, 300); + Jedi_EvasionSaber(enemy_movedir, enemy_dist, enemy_dir); + } + } + } } //see if our enemy was killed by us, gloat and turn off saber after cool down. //FIXME: don't do this if we have other enemies to fight...? if ( NPC->enemy ) { - if ( NPC->enemy->health <= 0 - && NPC->enemy->enemy == NPC + if ( NPC->enemy->health <= 0 + && NPC->enemy->enemy == NPC && (NPC->client->playerTeam != TEAM_PLAYER||(NPC->client->NPC_class==CLASS_KYLE&&(NPC->spawnflags&1)&&NPC->enemy==player)) )//good guys don't gloat (unless it's Kyle having just killed his student {//my enemy is dead and I killed him NPCInfo->enemyCheckDebounceTime = 0;//keep looking for others if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA + || NPC->client->NPC_class == CLASS_COMMANDO || (NPC->client->NPC_class == CLASS_REBORN && NPC->s.weapon != WP_SABER) || NPC->client->NPC_class == CLASS_ROCKETTROOPER ) { @@ -6787,12 +7035,12 @@ static void Jedi_Attack( void ) {//got there if ( NPC->health < NPC->max_health ) { - if ( NPC->client->ps.saber[0].type == SABER_SITH_SWORD + if ( NPC->client->ps.saber[0].type == SABER_SITH_SWORD && NPC->weaponModel[0] != -1 ) { Tavion_SithSwordRecharge(); } - else if ( (NPC->client->ps.forcePowersKnown&(1<client->ps.forcePowersKnown&(1<client->ps.forcePowersActive&(1<enemy && NPC->enemy->NPC - && NPC->enemy->NPC->charmedTime > level.time ) + && (NPC->enemy->NPC->charmedTime > level.time || NPC->enemy->NPC->darkCharmedTime > level.time) ) {//my enemy was charmed if ( OnSameTeam( NPC, NPC->enemy ) ) {//has been charmed to be on my team @@ -6837,8 +7085,8 @@ static void Jedi_Attack( void ) && NPC->client->enemyTeam == TEAM_PLAYER && NPC->enemy && NPC->enemy->client - && NPC->enemy->client->playerTeam != NPC->client->enemyTeam - && OnSameTeam( NPC, NPC->enemy ) + && NPC->enemy->client->playerTeam != NPC->client->enemyTeam + && OnSameTeam( NPC, NPC->enemy ) && !(NPC->svFlags&SVF_LOCKEDENEMY) ) {//an evil jedi somehow got another evil NPC as an enemy, they were probably charmed and it's run out now if ( !NPC_ValidEnemy( NPC->enemy ) ) @@ -6867,7 +7115,7 @@ static void Jedi_Attack( void ) //Track the player and kill them if possible Jedi_Combat(); - if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) + if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) || ((NPC->client->ps.forcePowersActive&(1<client->ps.forcePowerLevel[FP_HEAL]client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA + && NPC->client->NPC_class != CLASS_COMMANDO && (NPC->client->NPC_class != CLASS_REBORN || NPC->s.weapon == WP_SABER) && NPC->client->NPC_class != CLASS_ROCKETTROOPER ) { @@ -6904,11 +7154,11 @@ static void Jedi_Attack( void ) } } - if( (NPCInfo->scriptFlags&SCF_DONT_FIRE) //not allowed to attack - || ((NPC->client->ps.forcePowersActive&(1<client->ps.forcePowerLevel[FP_HEAL]scriptFlags&SCF_DONT_FIRE) //not allowed to attack... well maybe rarely + ((NPC->client->ps.forcePowersActive&(1<client->ps.forcePowerLevel[FP_HEAL]client->ps.saberEventFlags&SEF_INWATER)&&!NPC->client->ps.saberInFlight) )//saber in water { - ucmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS); + ucmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS|BUTTON_SABERTHROW); } if ( (NPCInfo->scriptFlags&SCF_NO_ACROBATICS) ) @@ -6918,6 +7168,8 @@ static void Jedi_Attack( void ) } if ( NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA + && NPC->client->NPC_class != CLASS_COMMANDO && (NPC->client->NPC_class != CLASS_REBORN || NPC->s.weapon == WP_SABER) && NPC->client->NPC_class != CLASS_ROCKETTROOPER ) { @@ -6926,8 +7178,8 @@ static void Jedi_Attack( void ) if ( ucmd.buttons & BUTTON_ATTACK && NPC->client->playerTeam == TEAM_ENEMY ) { - if ( Q_irand( 0, NPC->client->ps.saberAnimLevel ) > 0 - && Q_irand( 0, NPC->max_health+10 ) > NPC->health + if ( Q_irand( 0, NPC->client->ps.saberAnimLevel ) > 0 + && Q_irand( 0, NPC->max_health+10 ) > NPC->health && !Q_irand( 0, 3 )) {//the more we're hurt and the stronger the attack we're using, the more likely we are to make a anger noise when we swing G_AddVoiceEvent( NPC, Q_irand( EV_COMBAT1, EV_COMBAT3 ), 1000 ); @@ -6941,19 +7193,21 @@ static void Jedi_Attack( void ) } else {//check other special combat behavior - if ( NPC->client->NPC_class != CLASS_BOBAFETT + if ( NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA + && NPC->client->NPC_class != CLASS_COMMANDO && (NPC->client->NPC_class != CLASS_REBORN || NPC->s.weapon == WP_SABER) && NPC->client->NPC_class != CLASS_ROCKETTROOPER ) { - if ( NPC->client->NPC_class == CLASS_TAVION + if ( NPC->client->NPC_class == CLASS_TAVION || NPC->client->NPC_class == CLASS_SHADOWTROOPER || NPC->client->NPC_class == CLASS_ALORA || (g_spskill->integer && ( NPC->client->NPC_class == CLASS_DESANN || NPCInfo->rank >= Q_irand( RANK_CREWMAN, RANK_CAPTAIN )))) {//Tavion will kick in force speed if the player does... - if ( NPC->enemy - && !NPC->enemy->s.number - && NPC->enemy->client - && (NPC->enemy->client->ps.forcePowersActive & (1<enemy + && !NPC->enemy->s.number + && NPC->enemy->client + && (NPC->enemy->client->ps.forcePowersActive & (1<client->ps.forcePowersActive & (1<client->NPC_class == CLASS_ALORA ) { - if ( (ucmd.buttons&BUTTON_ALT_ATTACK) ) + if ( (ucmd.buttons&BUTTON_SABERTHROW) ) {//chance of doing a special dual saber throw if ( NPC->client->ps.saberAnimLevel == SS_DUAL && !NPC->client->ps.saberInFlight ) @@ -6994,7 +7248,7 @@ static void Jedi_Attack( void ) } } else if ( NPC->enemy - && ucmd.forwardmove > 0 + && ucmd.forwardmove > 0 && fabs((float)ucmd.rightmove) < 32 && !(ucmd.buttons&BUTTON_WALKING) && !(ucmd.buttons&BUTTON_ATTACK) @@ -7039,9 +7293,9 @@ static void Jedi_Attack( void ) qboolean Rosh_BeingHealed( gentity_t *self ) { if ( self - && self->NPC + && self->NPC && self->client - && (self->NPC->aiFlags&NPCAI_ROSH) + && (self->NPC->aiFlags&NPCAI_ROSH) && (self->flags&FL_UNDYING) && ( self->health == 1 //need healing || self->client->ps.powerups[PW_INVINCIBLE] > level.time ) )//being healed @@ -7054,12 +7308,12 @@ qboolean Rosh_BeingHealed( gentity_t *self ) qboolean Rosh_TwinPresent( gentity_t *self ) { gentity_t *foundTwin = G_Find( NULL, FOFS(NPC_type), "DKothos" ); - if ( !foundTwin + if ( !foundTwin || foundTwin->health < 0 ) { foundTwin = G_Find( NULL, FOFS(NPC_type), "VKothos" ); } - if ( !foundTwin + if ( !foundTwin || foundTwin->health < 0 ) {//oh well, both twins are dead... return qfalse; @@ -7070,12 +7324,12 @@ qboolean Rosh_TwinPresent( gentity_t *self ) qboolean Rosh_TwinNearBy( gentity_t *self ) { gentity_t *foundTwin = G_Find( NULL, FOFS(NPC_type), "DKothos" ); - if ( !foundTwin + if ( !foundTwin || foundTwin->health < 0 ) { foundTwin = G_Find( NULL, FOFS(NPC_type), "VKothos" ); } - if ( !foundTwin + if ( !foundTwin || foundTwin->health < 0 ) {//oh well, both twins are dead... return qfalse; @@ -7083,7 +7337,7 @@ qboolean Rosh_TwinNearBy( gentity_t *self ) if ( self->client && foundTwin->client ) { - if ( Distance( self->currentOrigin, foundTwin->currentOrigin ) <= 512.0f + if ( Distance( self->currentOrigin, foundTwin->currentOrigin ) <= 512.0f && G_ClearLineOfSight( self->client->renderInfo.eyePoint, foundTwin->client->renderInfo.eyePoint, foundTwin->s.number, MASK_OPAQUE ) ) { //make them look charge me for a bit while I do this @@ -7106,7 +7360,7 @@ qboolean Kothos_HealRosh( void ) //NPC_FaceEntity( NPC->client->leader, qtrue ); NPC_SetAnim( NPC, SETANIM_TORSO, BOTH_FORCE_2HANDEDLIGHTNING_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); NPC->client->ps.torsoAnimTimer = 1000; - + //FIXME: unique effect and sound //NPC->client->ps.eFlags |= EF_POWERING_ROSH; if ( NPC->ghoul2.size() ) @@ -7114,7 +7368,7 @@ qboolean Kothos_HealRosh( void ) mdxaBone_t boltMatrix; vec3_t fxOrg, fxDir, angles={0,NPC->currentAngles[YAW],0}; - gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->playerModel, + gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->playerModel, (Q_irand(0,1)?NPC->handLBolt:NPC->handRBolt), &boltMatrix, angles, NPC->currentOrigin, (cg.time?cg.time:level.time), NULL, NPC->s.modelScale ); @@ -7133,7 +7387,7 @@ qboolean Kothos_HealRosh( void ) NPC->client->leader->health += Q_irand( 1+g_spskill->integer*2, 4+g_spskill->integer*3 );//from 1-5 to 4-10 if ( NPC->client->leader->client ) { - if ( NPC->client->leader->client->ps.legsAnim == BOTH_FORCEHEAL_START + if ( NPC->client->leader->client->ps.legsAnim == BOTH_FORCEHEAL_START && NPC->client->leader->health >= NPC->client->leader->max_health ) {//let him get up now NPC_SetAnim( NPC->client->leader, SETANIM_BOTH, BOTH_FORCEHEAL_STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); @@ -7174,7 +7428,7 @@ void Kothos_PowerRosh( void ) if ( NPC->client && NPC->client->leader ) { - if ( Distance( NPC->client->leader->currentOrigin, NPC->currentOrigin ) <= 512.0f + if ( Distance( NPC->client->leader->currentOrigin, NPC->currentOrigin ) <= 512.0f && G_ClearLineOfSight( NPC->client->leader->client->renderInfo.eyePoint, NPC->client->renderInfo.eyePoint, NPC->s.number, MASK_OPAQUE ) ) { NPC_FaceEntity( NPC->client->leader, qtrue ); @@ -7230,8 +7484,8 @@ float Twins_DangerDist( void ) qboolean Jedi_InSpecialMove( void ) { if ( NPC->client->ps.torsoAnim == BOTH_KYLE_PA_1 - || NPC->client->ps.torsoAnim == BOTH_KYLE_PA_2 - || NPC->client->ps.torsoAnim == BOTH_KYLE_PA_3 + || NPC->client->ps.torsoAnim == BOTH_KYLE_PA_2 + || NPC->client->ps.torsoAnim == BOTH_KYLE_PA_3 || NPC->client->ps.torsoAnim == BOTH_PLAYER_PA_1 || NPC->client->ps.torsoAnim == BOTH_PLAYER_PA_2 || NPC->client->ps.torsoAnim == BOTH_PLAYER_PA_3 @@ -7278,7 +7532,7 @@ qboolean Jedi_InSpecialMove( void ) if ( NPC->client->ps.torsoAnim == BOTH_TAVION_SWORDPOWER ) { - NPC->health += Q_irand( 1, 2 ); + NPC->health += Q_irand( 1, 2 ); if ( NPC->health > NPC->max_health ) { NPC->health = NPC->max_health; @@ -7354,12 +7608,12 @@ qboolean Jedi_InSpecialMove( void ) } else if ( NPC->client->ps.torsoAnim == BOTH_TAVION_SCEPTERGROUND ) { - if ( NPC->client->ps.torsoAnimTimer <= 1200 - && !NPC->count ) + if ( NPC->client->ps.torsoAnimTimer <= 1200 + && !NPC->count ) { Tavion_ScepterSlam(); NPC->count = 1; - } + } NPC_UpdateAngles( qtrue, qtrue ); return qtrue; } @@ -7417,8 +7671,8 @@ qboolean Jedi_InSpecialMove( void ) } /* - if ( !helpingRosh - && !TIMER_Done( NPC->client->leader, "chargeMeUp" ) + if ( !helpingRosh + && !TIMER_Done( NPC->client->leader, "chargeMeUp" ) && NPC->client->leader->health > 0) { Kothos_PowerRosh(); @@ -7442,7 +7696,7 @@ qboolean Jedi_InSpecialMove( void ) //NPC_UpdateAngles( qtrue, qtrue ); if ( TIMER_Done( NPC, "attackDelay" ) ) { - if ( NPC->painDebounceTime > level.time + if ( NPC->painDebounceTime > level.time || (NPC->health < 100 && Q_irand(-20, (g_spskill->integer+1)*10) > 0 ) || !Q_irand( 0, 80-(g_spskill->integer*20) ) ) { @@ -7455,7 +7709,7 @@ qboolean Jedi_InSpecialMove( void ) case 3: ForceThrow( NPC, qfalse, qfalse ); NPC->client->ps.weaponTime = Q_irand( 1000, 3000 )+(2-g_spskill->integer)*1000; - if ( NPC->painDebounceTime <= level.time + if ( NPC->painDebounceTime <= level.time && NPC->health >= 100 ) { TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime ); @@ -7466,7 +7720,7 @@ qboolean Jedi_InSpecialMove( void ) ForceDrain2( NPC ); NPC->client->ps.weaponTime = Q_irand( 3000, 6000 )+(2-g_spskill->integer)*2000; TIMER_Set( NPC, "draining", NPC->client->ps.weaponTime ); - if ( NPC->painDebounceTime <= level.time + if ( NPC->painDebounceTime <= level.time && NPC->health >= 100 ) { TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime ); @@ -7478,7 +7732,7 @@ qboolean Jedi_InSpecialMove( void ) { NPC->client->ps.weaponTime = Q_irand( 3000, 6000 )+(2-g_spskill->integer)*2000; TIMER_Set( NPC, "gripping", 3000 ); - if ( NPC->painDebounceTime <= level.time + if ( NPC->painDebounceTime <= level.time && NPC->health >= 100 ) { TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime ); @@ -7494,7 +7748,7 @@ qboolean Jedi_InSpecialMove( void ) NPC->client->ps.weaponTime = Q_irand( 3000, 6000 )+(2-g_spskill->integer)*2000; TIMER_Set( NPC, "holdLightning", NPC->client->ps.weaponTime ); } - if ( NPC->painDebounceTime <= level.time + if ( NPC->painDebounceTime <= level.time && NPC->health >= 100 ) { TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime ); @@ -7515,7 +7769,7 @@ qboolean Jedi_InSpecialMove( void ) NPC->flags &= ~FL_LOCK_PLAYER_WEAPONS; } } - else if ( !G_ClearLOS( NPC, NPC->client->leader ) + else if ( !G_ClearLOS( NPC, NPC->client->leader ) || DistanceSquared( NPC->currentOrigin, NPC->client->leader->currentOrigin ) > (512*512) ) {//can't see Rosh or too far away, catch up with him if ( !TIMER_Done( NPC, "attackDelay" ) ) @@ -7556,7 +7810,7 @@ qboolean Jedi_InSpecialMove( void ) { if ( !NPC->client->ps.weaponTime ) {//not attacking - if ( NPC->client->ps.legsAnim != BOTH_FORCEHEAL_START + if ( NPC->client->ps.legsAnim != BOTH_FORCEHEAL_START && NPC->client->ps.legsAnim != BOTH_FORCEHEAL_STOP ) {//get down and wait for Vil or Dasariah to help us //FIXME: sound? @@ -7611,6 +7865,8 @@ void NPC_BSJedi_Default( void ) if( !NPC->enemy ) {//don't have an enemy, look for one if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA + || NPC->client->NPC_class == CLASS_COMMANDO || (NPC->client->NPC_class == CLASS_REBORN && NPC->s.weapon != WP_SABER) || NPC->client->NPC_class == CLASS_ROCKETTROOPER ) { @@ -7628,7 +7884,7 @@ void NPC_BSJedi_Default( void ) Jedi_Ambush( NPC ); } - if ( Jedi_CultistDestroyer( NPC ) + if ( Jedi_CultistDestroyer( NPC ) && !NPCInfo->charmedTime ) {//destroyer //permanent effect @@ -7647,7 +7903,7 @@ void NPC_BSJedi_Default( void ) //FIXME: build a list of all local enemies (since we have to find best anyway) for other AI factors- like when to use group attacks, determine when to change tactics, when surrounded, when blocked by another in the enemy group, etc. Should we build this group list or let the enemies maintain their own list and we just access it? gentity_t *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy? NPC->enemy = NULL; - gentity_t *newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse ); + gentity_t *newEnemy = NPC_CheckEnemy( (qboolean)((NPCInfo->confusionTimeinsanityTimeenemy = sav_enemy; if ( newEnemy && newEnemy != sav_enemy ) {//picked up a new enemy! @@ -7657,10 +7913,10 @@ void NPC_BSJedi_Default( void ) NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 1000, 3000 ); } } - if ( NPC->client->ps.saber[0].type == SABER_SITH_SWORD + if ( NPC->client->ps.saber[0].type == SABER_SITH_SWORD && NPC->weaponModel[0] != -1 ) { - if ( NPC->health < 100 + if ( NPC->health < 100 && !Q_irand( 0, 20 ) ) { Tavion_SithSwordRecharge(); diff --git a/code/game/AI_MineMonster.cpp b/code/game/AI_MineMonster.cpp index b3f6fae466..02ad97ce8f 100644 --- a/code/game/AI_MineMonster.cpp +++ b/code/game/AI_MineMonster.cpp @@ -144,18 +144,18 @@ void MineMonster_Attack( void ) if ( !TIMER_Exists( NPC, "attacking" )) { // usually try and play a jump attack if the player somehow got above them....or just really rarely - if ( NPC->enemy && ((NPC->enemy->currentOrigin[2] - NPC->currentOrigin[2] > 10 && random() > 0.1f ) - || random() > 0.8f )) + if ( NPC->enemy && ((NPC->enemy->currentOrigin[2] - NPC->currentOrigin[2] > 10 && Q_flrand(0.0f, 1.0f) > 0.1f ) + || Q_flrand(0.0f, 1.0f) > 0.8f )) { // Going to do ATTACK4 - TIMER_Set( NPC, "attacking", 1750 + random() * 200 ); + TIMER_Set( NPC, "attacking", 1750 + Q_flrand(0.0f, 1.0f) * 200 ); NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TIMER_Set( NPC, "attack2_dmg", 950 ); // level two damage } - else if ( random() > 0.5f ) + else if ( Q_flrand(0.0f, 1.0f) > 0.5f ) { - if ( random() > 0.8f ) + if ( Q_flrand(0.0f, 1.0f) > 0.8f ) { // Going to do ATTACK3, (rare) TIMER_Set( NPC, "attacking", 850 ); @@ -227,7 +227,7 @@ void MineMonster_Combat( void ) } else { - MineMonster_Move( 1 ); + MineMonster_Move( qtrue ); } } else diff --git a/code/game/AI_Rancor.cpp b/code/game/AI_Rancor.cpp index 45f6448f79..f276e4e310 100644 --- a/code/game/AI_Rancor.cpp +++ b/code/game/AI_Rancor.cpp @@ -933,7 +933,7 @@ void Rancor_Attack( float distance, qboolean doCharge, qboolean aimAtBlockedEnti return; } - TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer + random() * 200 ); + TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer + Q_flrand(0.0f, 1.0f) * 200 ); } // Need to do delayed damage since the attack animations encapsulate multiple mini-attacks @@ -1164,7 +1164,7 @@ void Rancor_Combat( void ) } else { - Rancor_Move( 1 ); + Rancor_Move( qtrue ); } } else @@ -1571,7 +1571,7 @@ void NPC_BSRancor_Default( void ) {//breakable brush if ( !Rancor_AttackBBrush() ) {//didn't move inside that func, so call move here...? - Rancor_Move( 1 ); + Rancor_Move( qtrue ); } NPC_UpdateAngles( qtrue, qtrue ); return; @@ -1621,7 +1621,7 @@ void NPC_BSRancor_Default( void ) { gentity_t *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy? NPC->enemy = NULL; - gentity_t *newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse ); + gentity_t *newEnemy = NPC_CheckEnemy( (qboolean)((NPCInfo->confusionTimeinsanityTimeenemy = sav_enemy; if ( newEnemy && newEnemy != sav_enemy ) {//picked up a new enemy! diff --git a/code/game/AI_Remote.cpp b/code/game/AI_Remote.cpp index b18b377c2f..da712d0143 100644 --- a/code/game/AI_Remote.cpp +++ b/code/game/AI_Remote.cpp @@ -188,7 +188,7 @@ void Remote_Strafe( void ) // Set the strafe start time so we can do a controlled roll NPC->fx_time = level.time; - NPCInfo->standTime = level.time + 3000 + random() * 500; + NPCInfo->standTime = level.time + 3000 + Q_flrand(0.0f, 1.0f) * 500; } } diff --git a/code/game/AI_RocketTrooper.cpp b/code/game/AI_RocketTrooper.cpp index 70e5a57d48..69ac2f99bb 100644 --- a/code/game/AI_RocketTrooper.cpp +++ b/code/game/AI_RocketTrooper.cpp @@ -627,7 +627,7 @@ void RT_Flying_Strafe( void ) vec3_t end, right, dir; trace_t tr; - if ( random() > 0.7f + if ( Q_flrand(0.0f, 1.0f) > 0.7f || !NPC->enemy || !NPC->enemy->client ) { @@ -663,7 +663,7 @@ void RT_Flying_Strafe( void ) } } - NPCInfo->standTime = level.time + 1000 + random() * 500; + NPCInfo->standTime = level.time + 1000 + Q_flrand(0.0f, 1.0f) * 500; } } else @@ -677,7 +677,7 @@ void RT_Flying_Strafe( void ) VectorMA( NPC->enemy->currentOrigin, stDis * side, right, end ); // then add a very small bit of random in front of/behind the player action - VectorMA( end, crandom() * 25, dir, end ); + VectorMA( end, Q_flrand(-1.0f, 1.0f) * 25, dir, end ); gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID, (EG2_Collision)0, 0 ); @@ -716,7 +716,7 @@ void RT_Flying_Strafe( void ) } } - NPCInfo->standTime = level.time + 2500 + random() * 500; + NPCInfo->standTime = level.time + 2500 + Q_flrand(0.0f, 1.0f) * 500; } } } @@ -857,7 +857,7 @@ void RT_Flying_Think( void ) if ( NPC->random == 0.0f ) { // used to offset seekers around a circle so they don't occupy the same spot. This is not a fool-proof method. - NPC->random = random() * 6.3f; // roughly 2pi + NPC->random = Q_flrand(0.0f, 1.0f) * 6.3f; // roughly 2pi } if ( NPC->enemy && NPC->enemy->health && NPC->enemy->inuse ) diff --git a/code/game/AI_SaberDroid.cpp b/code/game/AI_SaberDroid.cpp index 7a6996c099..78eb11e636 100644 --- a/code/game/AI_SaberDroid.cpp +++ b/code/game/AI_SaberDroid.cpp @@ -110,7 +110,7 @@ NPC_BSSaberDroid_Patrol void NPC_BSSaberDroid_Patrol( void ) {//FIXME: pick up on bodies of dead buddies? - if ( NPCInfo->confusionTime < level.time ) + if ( (NPCInfo->confusionTimeinsanityTimescriptFlags&SCF_LOOK_FOR_ENEMIES ) diff --git a/code/game/AI_Seeker.cpp b/code/game/AI_Seeker.cpp index d6dd8bbb39..24b7422b4b 100644 --- a/code/game/AI_Seeker.cpp +++ b/code/game/AI_Seeker.cpp @@ -90,7 +90,8 @@ void Seeker_MaintainHeight( void ) dif = (NPC->enemy->currentOrigin[2] + Q_flrand( NPC->enemy->maxs[2]/2, NPC->enemy->maxs[2]+8 )) - NPC->currentOrigin[2]; float difFactor = 1.0f; - if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA) { if ( TIMER_Done( NPC, "flameTime" ) ) { @@ -108,7 +109,8 @@ void Seeker_MaintainHeight( void ) NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2; } - if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA) { NPC->client->ps.velocity[2] *= Q_flrand( 0.85f, 3.0f ); } @@ -178,7 +180,7 @@ void Seeker_Strafe( void ) vec3_t end, right, dir; trace_t tr; - if ( random() > 0.7f || !NPC->enemy || !NPC->enemy->client ) + if ( Q_flrand(0.0f, 1.0f) > 0.7f || !NPC->enemy || !NPC->enemy->client ) { // Do a regular style strafe AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL ); @@ -195,7 +197,8 @@ void Seeker_Strafe( void ) { float vel = SEEKER_STRAFE_VEL; float upPush = SEEKER_UPWARD_PUSH; - if ( NPC->client->NPC_class != CLASS_BOBAFETT ) + if ( NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA) { G_Sound( NPC, G_SoundIndex( "sound/chars/seeker/misc/hiss" )); } @@ -208,7 +211,7 @@ void Seeker_Strafe( void ) // Add a slight upward push NPC->client->ps.velocity[2] += upPush; - NPCInfo->standTime = level.time + 1000 + random() * 500; + NPCInfo->standTime = level.time + 1000 + Q_flrand(0.0f, 1.0f) * 500; } } else @@ -219,14 +222,15 @@ void Seeker_Strafe( void ) // Pick a random side side = ( rand() & 1 ) ? -1 : 1; float stDis = SEEKER_STRAFE_DIS; - if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA) { stDis *= 2.0f; } VectorMA( NPC->enemy->currentOrigin, stDis * side, right, end ); // then add a very small bit of random in front of/behind the player action - VectorMA( end, crandom() * 25, dir, end ); + VectorMA( end, Q_flrand(-1.0f, 1.0f) * 25, dir, end ); gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID, (EG2_Collision)0, 0 ); @@ -241,7 +245,8 @@ void Seeker_Strafe( void ) VectorMA( NPC->client->ps.velocity, dis, dir, NPC->client->ps.velocity ); float upPush = SEEKER_UPWARD_PUSH; - if ( NPC->client->NPC_class != CLASS_BOBAFETT ) + if ( NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA) { G_Sound( NPC, G_SoundIndex( "sound/chars/seeker/misc/hiss" )); } @@ -253,7 +258,7 @@ void Seeker_Strafe( void ) // Add a slight upward push NPC->client->ps.velocity[2] += upPush; - NPCInfo->standTime = level.time + 2500 + random() * 500; + NPCInfo->standTime = level.time + 2500 + Q_flrand(0.0f, 1.0f) * 500; } } } @@ -333,7 +338,8 @@ void Seeker_Fire( void ) //------------------------------------ void Seeker_Ranged( qboolean visible, qboolean advance ) { - if ( NPC->client->NPC_class != CLASS_BOBAFETT ) + if ( NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA) { if ( NPC->count > 0 ) { @@ -371,7 +377,8 @@ void Seeker_Attack( void ) qboolean visible = NPC_ClearLOS( NPC->enemy ); qboolean advance = (qboolean)(distance > MIN_DISTANCE_SQR); - if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA) { advance = (qboolean)(distance>(200.0f*200.0f)); } @@ -434,7 +441,7 @@ void Seeker_FindEnemy( void ) if ( best ) { // used to offset seekers around a circle so they don't occupy the same spot. This is not a fool-proof method. - NPC->random = random() * 6.3f; // roughly 2pi + NPC->random = Q_flrand(0.0f, 1.0f) * 6.3f; // roughly 2pi NPC->enemy = best; } @@ -450,7 +457,8 @@ void Seeker_FollowPlayer( void ) float minDistSqr = MIN_DISTANCE_SQR; - if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA) { if ( TIMER_Done( NPC, "flameTime" ) ) { @@ -461,7 +469,8 @@ void Seeker_FollowPlayer( void ) if ( dis < minDistSqr ) { // generally circle the player closely till we take an enemy..this is our target point - if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA) { pt[0] = g_entities[0].currentOrigin[0] + cos( level.time * 0.001f + NPC->random ) * 250; pt[1] = g_entities[0].currentOrigin[1] + sin( level.time * 0.001f + NPC->random ) * 250; @@ -486,11 +495,12 @@ void Seeker_FollowPlayer( void ) } else { - if ( NPC->client->NPC_class != CLASS_BOBAFETT ) + if ( NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA) { if ( TIMER_Done( NPC, "seekerhiss" )) { - TIMER_Set( NPC, "seekerhiss", 1000 + random() * 1000 ); + TIMER_Set( NPC, "seekerhiss", 1000 + Q_flrand(0.0f, 1.0f) * 1000 ); G_Sound( NPC, G_SoundIndex( "sound/chars/seeker/misc/hiss" )); } } @@ -517,7 +527,8 @@ void NPC_BSSeeker_Default( void ) { if ( in_camera ) { - if ( NPC->client->NPC_class != CLASS_BOBAFETT ) + if ( NPC->client->NPC_class != CLASS_BOBAFETT + && NPC->client->NPC_class != CLASS_MANDA) { // cameras make me commit suicide.... G_Damage( NPC, NPC, NPC, NULL, NULL, 999, 0, MOD_UNKNOWN ); @@ -527,12 +538,12 @@ void NPC_BSSeeker_Default( void ) if ( NPC->random == 0.0f ) { // used to offset seekers around a circle so they don't occupy the same spot. This is not a fool-proof method. - NPC->random = random() * 6.3f; // roughly 2pi + NPC->random = Q_flrand(0.0f, 1.0f) * 6.3f; // roughly 2pi } if ( NPC->enemy && NPC->enemy->health && NPC->enemy->inuse ) { - if ( NPC->client->NPC_class != CLASS_BOBAFETT + if (NPC->client->NPC_class != CLASS_BOBAFETT && NPC->client->NPC_class != CLASS_MANDA && ( NPC->enemy->s.number == 0 || ( NPC->enemy->client && NPC->enemy->client->NPC_class == CLASS_SEEKER )) ) { //hacked to never take the player as an enemy, even if the player shoots at it @@ -541,14 +552,14 @@ void NPC_BSSeeker_Default( void ) else { Seeker_Attack(); - if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + if (NPC->client->NPC_class == CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_MANDA) { Boba_FireDecide(); } return; } } - else if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + else if (NPC->client->NPC_class == CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_MANDA) { NPC_BSST_Patrol(); return; diff --git a/code/game/AI_Sentry.cpp b/code/game/AI_Sentry.cpp index 0e9abf9491..d24c1f9acd 100644 --- a/code/game/AI_Sentry.cpp +++ b/code/game/AI_Sentry.cpp @@ -377,7 +377,7 @@ void Sentry_Strafe( void ) // Set the strafe start time so we can do a controlled roll NPC->fx_time = level.time; - NPCInfo->standTime = level.time + 3000 + random() * 500; + NPCInfo->standTime = level.time + 3000 + Q_flrand(0.0f, 1.0f) * 500; } } diff --git a/code/game/AI_Sniper.cpp b/code/game/AI_Sniper.cpp index 6dae655077..0eca5ac7e7 100644 --- a/code/game/AI_Sniper.cpp +++ b/code/game/AI_Sniper.cpp @@ -214,7 +214,7 @@ void NPC_BSSniper_Patrol( void ) {//FIXME: pick up on bodies of dead buddies? NPC->count = 0; - if ( NPCInfo->confusionTime < level.time ) + if ( (NPCInfo->confusionTimeinsanityTimescriptFlags&SCF_LOOK_FOR_ENEMIES ) diff --git a/code/game/AI_Stormtrooper.cpp b/code/game/AI_Stormtrooper.cpp index dfcdf9ff38..fc39c1cb64 100644 --- a/code/game/AI_Stormtrooper.cpp +++ b/code/game/AI_Stormtrooper.cpp @@ -205,7 +205,7 @@ enum static void ST_Speech( gentity_t *self, int speechType, float failChance ) { - if ( random() < failChance ) + if ( Q_flrand(0.0f, 1.0f) < failChance ) { return; } @@ -875,7 +875,7 @@ NPC_ST_InvestigateEvent static qboolean NPC_ST_InvestigateEvent( int eventID, bool extraSuspicious ) { //If they've given themselves away, just take them as an enemy - if ( NPCInfo->confusionTime < level.time ) + if ( (NPCInfo->confusionTimeinsanityTimescriptFlags&SCF_LOOK_FOR_ENEMIES) ) { @@ -1104,7 +1104,7 @@ void NPC_BSST_Investigate( void ) WeaponThink( qtrue ); } - if ( NPCInfo->confusionTime < level.time ) + if ( (NPCInfo->confusionTimeinsanityTimescriptFlags&SCF_LOOK_FOR_ENEMIES ) { @@ -1127,7 +1127,7 @@ void NPC_BSST_Investigate( void ) //There is an event to look at if ( alertEvent >= 0 ) { - if ( NPCInfo->confusionTime < level.time ) + if ( (NPCInfo->confusionTimeinsanityTimelocalState == LSTATE_INVESTIGATE && (NPCInfo->goalEntity!=NULL) ) { //See if we're there - if ( !STEER::Reached(NPC, NPCInfo->goalEntity, 32, !!FlyingCreature(NPC)) ) + if ( !STEER::Reached(NPC, NPCInfo->goalEntity, 32, FlyingCreature(NPC) != qfalse) ) { ucmd.buttons |= BUTTON_WALKING; @@ -1239,7 +1239,7 @@ void NPC_BSST_Patrol( void ) //get group- mainly for group speech debouncing, but may use for group scouting/investigating AI, too AI_GetGroup( NPC ); - if ( NPCInfo->confusionTime < level.time ) + if ( (NPCInfo->confusionTimeinsanityTimescriptFlags&SCF_LOOK_FOR_ENEMIES ) @@ -1267,7 +1267,9 @@ void NPC_BSST_Patrol( void ) ST_Speech( NPC, SPEECH_COVER, 0 ); return; } - else if (NPC->client->NPC_class==CLASS_BOBAFETT) + else if (NPC->client->NPC_class==CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA + || NPC->client->NPC_class == CLASS_COMMANDO) { //NPCInfo->lastAlertID = level.alertEvents[eventID].ID; if ( !level.alertEvents[alertEvent].owner || @@ -1716,7 +1718,7 @@ void ST_TransferMoveGoal( gentity_t *self, gentity_t *other ) {//I must be going for a goal, give that to him instead if ( self->NPC->goalEntity == self->NPC->tempGoal ) { - NPC_SetMoveGoal( other, self->NPC->tempGoal->currentOrigin, self->NPC->goalRadius, ((self->NPC->tempGoal->svFlags&SVF_NAVGOAL)?true:false) ); + NPC_SetMoveGoal( other, self->NPC->tempGoal->currentOrigin, self->NPC->goalRadius, (qboolean)((self->NPC->tempGoal->svFlags & SVF_NAVGOAL) != 0) ); } else { @@ -2355,11 +2357,39 @@ void NPC_BSST_Attack( void ) if ( enemyDist < MIN_ROCKET_DIST_SQUARED )//128 {//enemy within 128 + /*if ( enemyDist < (64*64) ) + {//enemy is close and not using saber or very close and random chance + if (NPC->client->ps.weapon != WP_MELEE) + {//I'm not in melee + trace_t trace; + gi.trace(&trace, NPC->currentOrigin, NPC->enemy->mins, NPC->enemy->maxs, NPC->enemy->currentOrigin, NPC->s.number, NPC->enemy->clipmask, (EG2_Collision)0, 0); + if (!trace.allsolid && !trace.startsolid && (trace.fraction == 1.0 || trace.entityNum == NPC->enemy->s.number)) + {//I can get right to him + //reset fire-timing variables + NPC_ChangeWeapon(WP_MELEE); + } + } + } + else if (enemyDist > 65536 || (NPC->enemy->client && NPC->enemy->client->ps.weapon == WP_SABER && NPC->enemy->client->ps.SaberActive()))//256 + {//enemy is far or using saber + if (NPC->client->ps.weapon == WP_MELEE && (NPC->client->ps.stats[STAT_WEAPONS] & (1 << WP_THERMAL))) + {//fisticuffs, make switch to thermal if have it + //reset fire-timing variables + NPC_ChangeWeapon(WP_THERMAL); + } + }*/ //melee punch stuff if ( (NPC->client->ps.weapon == WP_FLECHETTE || NPC->client->ps.weapon == WP_REPEATER) && (NPCInfo->scriptFlags & SCF_ALT_FIRE) ) {//shooting an explosive, but enemy too close, switch to primary fire - NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; - //FIXME: we can never go back to alt-fire this way since, after this, we don't know if we were initially supposed to use alt-fire or not... + NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; + } + } + else if (enemyDist >= MIN_ROCKET_DIST_SQUARED) + { + if ((NPC->client->ps.weapon == WP_FLECHETTE || NPC->client->ps.weapon == WP_REPEATER) && + (NPCInfo->stats.altFire)) + {//shooting an explosive, enemy was too close but not anymore + NPCInfo->scriptFlags |= SCF_ALT_FIRE; } } else if ( enemyDist > 65536 )//256 squared diff --git a/code/game/AI_Tusken.cpp b/code/game/AI_Tusken.cpp index 59df126b33..6462720fdf 100644 --- a/code/game/AI_Tusken.cpp +++ b/code/game/AI_Tusken.cpp @@ -175,7 +175,7 @@ NPC_BSTusken_Patrol void NPC_BSTusken_Patrol( void ) {//FIXME: pick up on bodies of dead buddies? - if ( NPCInfo->confusionTime < level.time ) + if ( (NPCInfo->confusionTimeinsanityTimescriptFlags&SCF_LOOK_FOR_ENEMIES ) @@ -494,10 +494,10 @@ qboolean G_TuskenAttackAnimDamage( gentity_t *self ) //gi.Printf("%f\n", percentComplete); switch (self->client->ps.torsoAnim) { - case BOTH_TUSKENATTACK1: return (percentComplete>0.3 && percentComplete<0.7); - case BOTH_TUSKENATTACK2: return (percentComplete>0.3 && percentComplete<0.7); - case BOTH_TUSKENATTACK3: return (percentComplete>0.1 && percentComplete<0.5); - case BOTH_TUSKENLUNGE1: return (percentComplete>0.3 && percentComplete<0.5); + case BOTH_TUSKENATTACK1: return (qboolean)(percentComplete>0.3 && percentComplete<0.7); + case BOTH_TUSKENATTACK2: return (qboolean)(percentComplete>0.3 && percentComplete<0.7); + case BOTH_TUSKENATTACK3: return (qboolean)(percentComplete>0.1 && percentComplete<0.5); + case BOTH_TUSKENLUNGE1: return (qboolean)(percentComplete>0.3 && percentComplete<0.5); } } } diff --git a/code/game/AI_Utils.cpp b/code/game/AI_Utils.cpp index 5199a61c3b..8a9924e78f 100644 --- a/code/game/AI_Utils.cpp +++ b/code/game/AI_Utils.cpp @@ -335,6 +335,9 @@ qboolean AI_ValidateGroupMember( AIGroupInfo_t *group, gentity_t *member ) //must be aware if ( member->NPC->confusionTime > level.time ) return qfalse; + + if ( member->NPC->insanityTime > level.time ) + return qfalse; //must be allowed to join groups if ( member->NPC->scriptFlags&SCF_NO_GROUPS ) @@ -934,7 +937,7 @@ qboolean AI_RefreshGroup( AIGroupInfo_t *group ) //mark this group as not having been run this frame group->processed = qfalse; - return (group->numGroup>0); + return (qboolean)(group->numGroup>0); } void AI_UpdateGroups( void ) diff --git a/code/game/AI_Vehicle.cpp b/code/game/AI_Vehicle.cpp new file mode 100644 index 0000000000..9f0714e7dc --- /dev/null +++ b/code/game/AI_Vehicle.cpp @@ -0,0 +1,766 @@ +/* +=========================================================================== +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +//////////////////////////////////////////////////////////////////////////////////////// +// RAVEN SOFTWARE - STAR WARS: JK III +// (c) 2002 Activision +// +// April 3, 2003 - This file has been commandeered for use by AI vehicle pilots. +// +//////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////// +// Includes +//////////////////////////////////////////////////////////////////////////////////////// +#include "b_local.h" +#include "anims.h" +#include "g_navigator.h" +#include "g_vehicles.h" +#include "g_functions.h" +#if !defined(RATL_VECTOR_VS_INC) + #include "../Ratl/vector_vs.h" +#endif + + +//////////////////////////////////////////////////////////////////////////////////////// +// Defines +//////////////////////////////////////////////////////////////////////////////////////// +#define MAX_VEHICLES_REGISTERED 100 + +#define ATTACK_FWD 0.95f +#define ATTACK_SIDE 0.20f +#define AIM_SIDE 0.60f +#define FUTURE_PRED_DIST 20.0f +#define FUTURE_SIDE_DIST 60.0f +#define ATTACK_FLANK_SLOWING 1000.0f +#define RAM_DIST 150.0f +#define MIN_STAY_VIEWABLE_TIME 20000 + + + +//////////////////////////////////////////////////////////////////////////////////////// +// Externs +//////////////////////////////////////////////////////////////////////////////////////// +extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent ); +extern void G_SoundAtSpot( vec3_t org, int soundIndex, qboolean broadcast ); +extern void CG_DrawEdge( vec3_t start, vec3_t end, int type ); + + + +trace_t mPilotViewTrace; +int mPilotViewTraceCount; +int mActivePilotCount; +ratl::vector_vs mRegistered; + + + +//////////////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////////////// +void Pilot_Reset(void) +{ + mPilotViewTraceCount = 0; + mActivePilotCount = 0; + mRegistered.clear(); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////////////// +int Pilot_ActivePilotCount() +{ + return mActivePilotCount; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////////////// +void Pilot_Update(void) +{ + mActivePilotCount = 0; + mRegistered.clear(); + for (int i=0; igreetEnt && + g_entities[i].NPC->greetEnt->owner==(&g_entities[i]) + ) + { + mActivePilotCount++; + } + if ( g_entities[i].inuse && + g_entities[i].client && + g_entities[i].m_pVehicle && + !g_entities[i].owner && + g_entities[i].health>0 && + g_entities[i].m_pVehicle->m_pVehicleInfo->type==VH_SPEEDER && + !mRegistered.full()) + { + mRegistered.push_back(&g_entities[i]); + } + + } + + + if (player && + player->inuse && + TIMER_Done(player, "FlybySoundArchitectureDebounce")) + { + TIMER_Set(player, "FlybySoundArchitectureDebounce", 300); + + Vehicle_t* pVeh = G_IsRidingVehicle(player); + + if (pVeh && + (pVeh->m_pVehicleInfo->soundFlyBy || pVeh->m_pVehicleInfo->soundFlyBy2) && + //fabsf(pVeh->m_pParentEntity->currentAngles[2])<15.0f && + VectorLength(pVeh->m_pParentEntity->client->ps.velocity)>500.0f) + { + vec3_t projectedPosition; + vec3_t projectedDirection; + vec3_t projectedRight; + vec3_t anglesNoRoll; + + VectorCopy(pVeh->m_pParentEntity->currentAngles, anglesNoRoll); + anglesNoRoll[2] = 0; + AngleVectors(anglesNoRoll, projectedDirection, projectedRight, 0); + + VectorMA(player->currentOrigin, 1.2f, pVeh->m_pParentEntity->client->ps.velocity, projectedPosition); + VectorMA(projectedPosition, Q_flrand(-200.0f, 200.0f), projectedRight, projectedPosition); + + gi.trace(&mPilotViewTrace, + player->currentOrigin, + 0, + 0, + projectedPosition, + player->s.number, + MASK_SHOT, (EG2_Collision)0, 0); + + if ((mPilotViewTrace.allsolid==qfalse) && + (mPilotViewTrace.startsolid==qfalse) && + (mPilotViewTrace.fraction<0.99f) && + (mPilotViewTrace.plane.normal[2]<0.5f) && + (DotProduct(projectedDirection, mPilotViewTrace.plane.normal)<-0.5f) + ) + { + // CG_DrawEdge(player->currentOrigin, mPilotViewTrace.endpos, EDGE_IMPACT_POSSIBLE); + TIMER_Set(player, "FlybySoundArchitectureDebounce", Q_irand(1000, 2000)); + + int soundFlyBy = pVeh->m_pVehicleInfo->soundFlyBy; + if (pVeh->m_pVehicleInfo->soundFlyBy2 && (!soundFlyBy || !Q_irand(0,1))) + { + soundFlyBy = pVeh->m_pVehicleInfo->soundFlyBy2; + } + G_SoundAtSpot(mPilotViewTrace.endpos, soundFlyBy, qtrue); + } + else + { + // CG_DrawEdge(player->currentOrigin, mPilotViewTrace.endpos, EDGE_IMPACT_SAFE); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////////////// +bool Pilot_AnyVehiclesRegistered() +{ + return (!mRegistered.empty()); +} + + + + + +//////////////////////////////////////////////////////////////////////////////////////// +// Vehicle Registration +// +// Any vehicles that can be ridden by NPCs should be registered here +// +//////////////////////////////////////////////////////////////////////////////////////// +void Vehicle_Register(gentity_t *ent) +{ +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// Vehicle Remove From The List Of Valid +//////////////////////////////////////////////////////////////////////////////////////// +void Vehicle_Remove(gentity_t *ent) +{ +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Vehicle_Find +// +// Will look through all registered vehicles and choose the closest one that the given +// entity can get to. +// +//////////////////////////////////////////////////////////////////////////////////////// +gentity_t* Vehicle_Find(gentity_t *ent) +{ + gentity_t* closest = 0; + float closestDist = 0; + float curDist = 0; + + + for (int i=0; iowner) + { + curDist = Distance(mRegistered[i]->currentOrigin, ent->currentOrigin); + if (curDist<1000 && (!closest || curDistenemy) + { + // If Still On A Vehicle, Jump Off + //--------------------------------- + if (NPCInfo->greetEnt) + { + ucmd.upmove = 127; + + if (NPCInfo->greetEnt && NPCInfo->greetEnt->m_pVehicle && (level.timeconfusionTime||level.timeinsanityTime)) + { + Vehicle_t* pVeh = NPCInfo->greetEnt->m_pVehicle; + if (!(pVeh->m_ulFlags&VEH_OUTOFCONTROL)) + { + gentity_t* parent = pVeh->m_pParentEntity; + float CurSpeed = VectorLength(parent->client->ps.velocity); + pVeh->m_pVehicleInfo->StartDeathDelay(pVeh, 10000); + pVeh->m_ulFlags |= (VEH_OUTOFCONTROL); + VectorScale(parent->client->ps.velocity, 1.25f, parent->pos3); + if (CurSpeedm_pVehicleInfo->speedMax) + { + VectorNormalize(parent->pos3); + if (fabsf(parent->pos3[2])<0.25f) + { + VectorScale(parent->pos3, (pVeh->m_pVehicleInfo->speedMax * 1.25f), parent->pos3); + } + else + { + VectorScale(parent->client->ps.velocity, 1.25f, parent->pos3); + } + } + } + } + + if (NPCInfo->greetEnt->owner==NPC) + { + return true; + } + NPCInfo->greetEnt = 0; + } + + // Otherwise Nothing To See Here + //------------------------------- + return false; + } + + + // If We Already Have A Target Vehicle, Make Sure It Is Still Valid + //------------------------------------------------------------------ + if (NPCInfo->greetEnt) + { + if (!NPCInfo->greetEnt->inuse || + !NPCInfo->greetEnt->m_pVehicle || + !NPCInfo->greetEnt->m_pVehicle->m_pVehicleInfo) + { + NPCInfo->greetEnt = Vehicle_Find(NPC); + } + else + { + if (NPCInfo->greetEnt->owner && NPCInfo->greetEnt->owner!=NPC) + { + NPCInfo->greetEnt = Vehicle_Find(NPC); + } + } + } + + // If We Have An Enemy, Try To Find A Vehicle Nearby + //--------------------------------------------------- + else + { + NPCInfo->greetEnt = Vehicle_Find(NPC); + } + + // If No Vehicle Available, Continue As Usual + //-------------------------------------------- + if (!NPCInfo->greetEnt) + { + return false; + } + + + + if (NPCInfo->greetEnt->owner==NPC) + { + Pilot_Steer_Vehicle(); + } + else + { + Pilot_Goto_Vehicle(); + } + + Pilot_Update_Enemy(); + return true; +} + + + + + + +//////////////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////////////// +void Pilot_Update_Enemy() +{ + if (!TIMER_Exists(NPC, "PilotRemoveTime")) + { + TIMER_Set(NPC, "PilotRemoveTime", MIN_STAY_VIEWABLE_TIME); + } + + if (TIMER_Done(NPC, "NextPilotCheckEnemyTime")) + { + TIMER_Set(NPC, "NextPilotCheckEnemyTime", Q_irand(1000,2000)); + if (NPC->enemy && Distance(NPC->currentOrigin, NPC->enemy->currentOrigin)>1000.0f) + { + mPilotViewTraceCount ++; + gi.trace(&mPilotViewTrace, + NPC->currentOrigin, + 0, + 0, + NPC->enemy->currentOrigin, + NPC->s.number, + MASK_SHOT, + (EG2_Collision)0, 0); + + if ((mPilotViewTrace.allsolid==qfalse) && + (mPilotViewTrace.startsolid==qfalse ) && + ((mPilotViewTrace.entityNum==NPC->enemy->s.number)||(mPilotViewTrace.entityNum==NPC->enemy->s.m_iVehicleNum))) + { + TIMER_Set(NPC, "PilotRemoveTime", MIN_STAY_VIEWABLE_TIME); + } + } + else + { + TIMER_Set(NPC, "PilotRemoveTime", MIN_STAY_VIEWABLE_TIME); + } + } + + if (TIMER_Done(NPC, "PilotRemoveTime")) + { + if (NPCInfo->greetEnt->owner==NPC) + { + NPCInfo->greetEnt->e_ThinkFunc = thinkF_G_FreeEntity; + NPCInfo->greetEnt->nextthink = level.time; + } + NPC->e_ThinkFunc = thinkF_G_FreeEntity; + NPC->nextthink = level.time; + } +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////////////// +void Pilot_Goto_Vehicle() +{ + STEER::Activate(NPC); + { + if (STEER::Reached(NPC, NPCInfo->greetEnt, 80.0f)) + { + NPC_Use(NPCInfo->greetEnt, NPC, NPC); + } + else if (NAV::OnNeighboringPoints(NPC, NPCInfo->greetEnt)) + { + STEER::Persue(NPC, NPCInfo->greetEnt, 50.0f, 0.0f, 30.0f, 0.0f, true); + } + else + { + if (!NAV::GoTo(NPC, NPCInfo->greetEnt)) + { + STEER::Stop(NPC); + } + } + } + STEER::AvoidCollisions(NPC); + STEER::DeActivate(NPC, &ucmd); + NPC_UpdateAngles(qtrue, qtrue); +} + +extern bool VEH_StartStrafeRam(Vehicle_t *pVeh, bool Right); + +//////////////////////////////////////////////////////////////////////////////////////// +// +//////////////////////////////////////////////////////////////////////////////////////// +void Pilot_Steer_Vehicle() +{ + if (!NPC->enemy || !NPC->enemy->client) + { + return; + } + + + + + + +// SETUP +//======= + // Setup Actor Data + //------------------ + CVec3 ActorPos(NPC->currentOrigin); + CVec3 ActorAngles(NPC->currentAngles); + ActorAngles[2] = 0; + Vehicle_t* ActorVeh = NPCInfo->greetEnt->m_pVehicle; + bool ActorInTurbo = (ActorVeh->m_iTurboTime>level.time); + float ActorSpeed = (ActorVeh)?(VectorLength(ActorVeh->m_pParentEntity->client->ps.velocity)):(NPC->client->ps.speed); + + + // If my vehicle is spinning out of control, just hold on, we're going to die!!!!! + //--------------------------------------------------------------------------------- + if (ActorVeh && (ActorVeh->m_ulFlags & VEH_OUTOFCONTROL)) + { + if (NPC->client->ps.weapon!=WP_NONE) + { + NPC_ChangeWeapon(WP_NONE); + } + ucmd.buttons &=~BUTTON_ATTACK; + ucmd.buttons &=~BUTTON_ALT_ATTACK; + return; + } + + CVec3 ActorDirection; + AngleVectors(ActorAngles.v, ActorDirection.v, 0, 0); + + CVec3 ActorFuturePos(ActorPos); + ActorFuturePos.ScaleAdd(ActorDirection, FUTURE_PRED_DIST); + + bool ActorDoTurbo = false; + bool ActorAccelerate = false; + bool ActorAimAtTarget= true; + float ActorYawOffset = 0.0f; + + + // Setup Enemy Data + //------------------ + CVec3 EnemyPos(NPC->enemy->currentOrigin); + CVec3 EnemyAngles(NPC->enemy->currentAngles); + EnemyAngles[2] = 0; + Vehicle_t* EnemyVeh = (NPC->enemy->s.m_iVehicleNum)?(g_entities[NPC->enemy->s.m_iVehicleNum].m_pVehicle):(0); + bool EnemyInTurbo = (EnemyVeh && EnemyVeh->m_iTurboTime>level.time); + float EnemySpeed = (EnemyVeh)?(EnemyVeh->m_pParentEntity->client->ps.speed):(NPC->enemy->resultspeed); + bool EnemySlideBreak = (EnemyVeh && (EnemyVeh->m_ulFlags&VEH_SLIDEBREAKING || EnemyVeh->m_ulFlags&VEH_STRAFERAM)); + bool EnemyDead = (NPC->enemy->health<=0); + + bool ActorFlank = (NPCInfo->lastAvoidSteerSideDebouncer>level.time && EnemyVeh && EnemySpeed>10.0f); + + CVec3 EnemyDirection; + CVec3 EnemyRight; + AngleVectors(EnemyAngles.v, EnemyDirection.v, EnemyRight.v, 0); + + CVec3 EnemyFuturePos(EnemyPos); + EnemyFuturePos.ScaleAdd(EnemyDirection, FUTURE_PRED_DIST); + + ESide EnemySide = ActorPos.LRTest(EnemyPos, EnemyFuturePos); + CVec3 EnemyFlankPos(EnemyFuturePos); + EnemyFlankPos.ScaleAdd(EnemyRight, (EnemySide==Side_Right)?(FUTURE_SIDE_DIST):(-FUTURE_SIDE_DIST)); + + // Debug Draw Enemy Data + //----------------------- + if (false) + { + CG_DrawEdge(EnemyPos.v, EnemyFuturePos.v, EDGE_IMPACT_SAFE); + CG_DrawEdge(EnemyFuturePos.v, EnemyFlankPos.v, EDGE_IMPACT_SAFE); + } + + + // Setup Move And Aim Directions + //------------------------------- + CVec3 MoveDirection((ActorFlank)?(EnemyFlankPos):(EnemyFuturePos)); + MoveDirection -= ActorPos; + float MoveDistance = MoveDirection.SafeNorm(); + float MoveAccuracy = MoveDirection.Dot(ActorDirection); + + CVec3 AimDirection(EnemyPos); + AimDirection -= ActorPos; + float AimDistance = AimDirection.SafeNorm(); + float AimAccuracy = AimDirection.Dot(ActorDirection); + + + + if (!ActorFlank && TIMER_Done(NPC, "FlankAttackCheck")) + { + TIMER_Set(NPC, "FlankAttackCheck", Q_irand(1000, 3000)); + if (MoveDistance<4000 && Q_irand(0, 1)==0) + { + NPCInfo->lastAvoidSteerSideDebouncer = level.time + Q_irand(8000, 14000); + } + } + + + + // Fly By Sounds + //--------------- + if ((ActorVeh->m_pVehicleInfo->soundFlyBy || ActorVeh->m_pVehicleInfo->soundFlyBy2) && + EnemyVeh && + MoveDistance<800 && + ActorSpeed>500.0f && + TIMER_Done(NPC, "FlybySoundDebouncer") + ) + { + if (EnemySpeed<100.0f || (ActorDirection.Dot(EnemyDirection)*(MoveDistance/800.0f))<-0.5f) + { + TIMER_Set(NPC, "FlybySoundDebouncer", 2000); + int soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy; + if (ActorVeh->m_pVehicleInfo->soundFlyBy2 && (!soundFlyBy || !Q_irand(0,1))) + { + soundFlyBy = ActorVeh->m_pVehicleInfo->soundFlyBy2; + } + G_Sound(ActorVeh->m_pParentEntity, soundFlyBy); + } + } + + + +// FLY PAST BEHAVIOR +//=================== + if (EnemySlideBreak || !TIMER_Done(NPC, "MinHoldDirectionTime")) + { + if (TIMER_Done(NPC, "MinHoldDirectionTime")) + { + TIMER_Set(NPC, "MinHoldDirectionTime", 500); // Hold For At Least 500 ms + } + ActorAccelerate = true; // Go + ActorAimAtTarget = false; // Don't Alter Our Aim Direction + ucmd.buttons &=~BUTTON_VEH_SPEED; // Let Normal Vehicle Controls Go + } + + +// FLANKING BEHAVIOR +//=================== + else if (ActorFlank) + { + ActorAccelerate = true; + ActorDoTurbo = (MoveDistance>2500 || EnemyInTurbo); + ucmd.buttons |= BUTTON_VEH_SPEED; // Tells PMove to use the ps.speed we calculate here, not the one from g_vehicles.c + + + // For Flanking, We Calculate The Speed By Hand, Rather Than Using Pure Accelerate / No Accelerate Functionality + //--------------------------------------------------------------------------------------------------------------- + NPC->client->ps.speed = ActorVeh->m_pVehicleInfo->speedMax * ((ActorInTurbo)?(1.35f):(1.15f)); + + + // If In Slowing Distance, Scale Down The Speed As We Approach Our Move Target + //----------------------------------------------------------------------------- + if (MoveDistanceclient->ps.speed *= (MoveDistance/ATTACK_FLANK_SLOWING); + NPC->client->ps.speed += EnemySpeed; + + // Match Enemy Speed + //------------------- + if (NPC->client->ps.speed<5.0f && EnemySpeed<5.0f) + { + NPC->client->ps.speed = EnemySpeed; + } + + // Extra Slow Down When Out In Front + //----------------------------------- + if (MoveAccuracy<0.0f) + { + NPC->client->ps.speed *= (MoveAccuracy + 1.0f); + } + + + MoveDirection *= (MoveDistance/ATTACK_FLANK_SLOWING); + EnemyDirection *= 1.0f - (MoveDistance/ATTACK_FLANK_SLOWING); + MoveDirection += EnemyDirection; + + if (TIMER_Done(NPC, "RamCheck")) + { + TIMER_Set(NPC, "RamCheck", Q_irand(1000, 3000)); + if (MoveDistance0.99f && MoveDistance<500 && !EnemyDead) + { + ActorAccelerate = true; + ActorDoTurbo = false; + } + else + { + ActorAccelerate = ((MoveDistance>500 && EnemySpeed>20.0f) || MoveDistance>1000); + ActorDoTurbo = (MoveDistance>3000 && EnemySpeed>20.0f); + } + ucmd.buttons &=~BUTTON_VEH_SPEED; + } + + + + +// APPLY RESULTS +//======================= + // Decide Turbo + //-------------- + if (ActorDoTurbo || ActorInTurbo) + { + ucmd.buttons |= BUTTON_ALT_ATTACK; + } + else + { + ucmd.buttons &=~BUTTON_ALT_ATTACK; + } + + // Decide Acceleration + //--------------------- + ucmd.forwardmove = (ActorAccelerate)?(127):(0); + + + + // Decide To Shoot + //----------------- + ucmd.buttons &=~BUTTON_ATTACK; + ucmd.rightmove = 0; + if (AimDistance<2000 && !EnemyDead) + { + // If Doing A Ram Attack + //----------------------- + if (ActorYawOffset!=0) + { + if (NPC->client->ps.weapon!=WP_NONE) + { + NPC_ChangeWeapon(WP_NONE); + } + ucmd.buttons &=~BUTTON_ATTACK; + } + else if (AimAccuracy>ATTACK_FWD) + { + if (NPC->client->ps.weapon!=WP_NONE) + { + NPC_ChangeWeapon(WP_NONE); + } + ucmd.buttons |= BUTTON_ATTACK; + } + else if (AimAccuracy-AIM_SIDE) + { + if (NPC->client->ps.weapon!=WP_BLASTER) + { + NPC_ChangeWeapon(WP_BLASTER); + } + + if (AimAccuracy-ATTACK_SIDE) + { + //if (!TIMER_Done(NPC, "RiderAltAttack")) + //{ + // ucmd.buttons |= BUTTON_ALT_ATTACK; + //} + //else + //{ + ucmd.buttons |= BUTTON_ATTACK; + + /* if (TIMER_Done(NPC, "RiderAltAttackCheck")) + { + TIMER_Set(NPC, "RiderAltAttackCheck", Q_irand(1000, 3000)); + if (Q_irand(0, 2)==0) + { + TIMER_Set(NPC, "RiderAltAttack", 300); + } + }*/ + //} + WeaponThink(qtrue); + } + ucmd.rightmove = (EnemySide==Side_Left)?( 127):(-127); + } + else + { + if (NPC->client->ps.weapon!=WP_NONE) + { + NPC_ChangeWeapon(WP_NONE); + } + } + } + else + { + if (NPC->client->ps.weapon!=WP_NONE) + { + NPC_ChangeWeapon(WP_NONE); + } + } + + + // Aim At Target + //--------------- + if (ActorAimAtTarget) + { + MoveDirection.VecToAng(); + NPCInfo->desiredPitch = AngleNormalize360(MoveDirection[PITCH]); + NPCInfo->desiredYaw = AngleNormalize360(MoveDirection[YAW] + ActorYawOffset); + } + NPC_UpdateAngles(qtrue, qtrue); +} + diff --git a/code/game/AI_Wampa.cpp b/code/game/AI_Wampa.cpp index 6a9771aa92..90f6d20634 100644 --- a/code/game/AI_Wampa.cpp +++ b/code/game/AI_Wampa.cpp @@ -311,7 +311,7 @@ void Wampa_Attack( float distance, qboolean doCharge ) TIMER_Set( NPC, "attack_dmg", 250 ); } - TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer + random() * 200 ); + TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer + Q_flrand(0.0f, 1.0f) * 200 ); //allow us to re-evaluate our running speed/anim TIMER_Set( NPC, "runfar", -1 ); TIMER_Set( NPC, "runclose", -1 ); @@ -357,7 +357,7 @@ void Wampa_Attack( float distance, qboolean doCharge ) if ( NPC->client->ps.legsAnim == BOTH_ATTACK1 && distance > (NPC->maxs[0]+MIN_DISTANCE) ) {//okay to keep moving ucmd.buttons |= BUTTON_WALKING; - Wampa_Move( 1 ); + Wampa_Move( qtrue ); } } @@ -378,7 +378,7 @@ void Wampa_Combat( void ) NPCInfo->goalEntity = NPC->enemy; NPCInfo->goalRadius = MIN_DISTANCE;//MAX_DISTANCE; // just get us within combat range - Wampa_Move( 0 ); + Wampa_Move( qfalse ); return; } /* @@ -424,7 +424,7 @@ void Wampa_Combat( void ) } else { - Wampa_Move( 1 ); + Wampa_Move( qtrue ); } } else @@ -878,7 +878,7 @@ void NPC_BSWampa_Default( void ) { gentity_t *sav_enemy = NPC->enemy;//FIXME: what about NPC->lastEnemy? NPC->enemy = NULL; - gentity_t *newEnemy = NPC_CheckEnemy( NPCInfo->confusionTime < level.time, qfalse, qfalse ); + gentity_t *newEnemy = NPC_CheckEnemy( (qboolean)((NPCInfo->confusionTimeinsanityTimeenemy = sav_enemy; if ( newEnemy && newEnemy != sav_enemy ) {//picked up a new enemy! diff --git a/code/game/CMakeLists.txt b/code/game/CMakeLists.txt index 8f9f835f90..86bb5a4515 100644 --- a/code/game/CMakeLists.txt +++ b/code/game/CMakeLists.txt @@ -21,11 +21,12 @@ if(NOT InOpenJK) message(FATAL_ERROR "Use the top-level cmake script!") endif(NOT InOpenJK) -set(SPGameDefines ${SharedDefines}) +set(SPGameDefines ${SharedDefines} "SP_GAME") set(SPGameIncludeDirectories ${SharedDir} ${SPDir} "${SPDir}/game" + "${GSLIncludeDirectory}" ) if(WIN32) @@ -61,7 +62,9 @@ set(SPGameGameFiles "${SPDir}/game/AI_Stormtrooper.cpp" "${SPDir}/game/AI_Tusken.cpp" "${SPDir}/game/AI_Utils.cpp" + "${SPDir}/game/AI_Vehicle.cpp" "${SPDir}/game/AI_Wampa.cpp" + "${SPDir}/game/fp_repulse.cpp" "${SPDir}/game/g_active.cpp" "${SPDir}/game/g_breakable.cpp" "${SPDir}/game/g_camera.cpp" @@ -131,6 +134,7 @@ set(SPGameGameFiles "${SPDir}/game/wp_rocket_launcher.cpp" "${SPDir}/game/wp_saber.cpp" "${SPDir}/game/wp_saberLoad.cpp" + "${SPDir}/game/wp_sonic_blaster.cpp" "${SPDir}/game/wp_stun_baton.cpp" "${SPDir}/game/wp_thermal.cpp" "${SPDir}/game/wp_trip_mine.cpp" @@ -181,6 +185,7 @@ set(SPGameGameFiles "${SPDir}/game/w_local.h" "${SPDir}/game/wp_saber.h" "${SPDir}/game/g_vehicles.h" + "${SPDir}/game/jke_aiworkshop.cpp" ) source_group("game" FILES ${SPGameGameFiles}) set(SPGameFiles ${SPGameFiles} ${SPGameGameFiles}) @@ -208,13 +213,16 @@ set(SPGameCGameFiles "${SPDir}/cgame/cg_snapshot.cpp" "${SPDir}/cgame/cg_syscalls.cpp" "${SPDir}/cgame/cg_text.cpp" + "${SPDir}/cgame/cg_trueview.cpp" "${SPDir}/cgame/cg_view.cpp" "${SPDir}/cgame/cg_weapons.cpp" "${SPDir}/cgame/FX_ATSTMain.cpp" "${SPDir}/cgame/FX_Blaster.cpp" "${SPDir}/cgame/FX_Bowcaster.cpp" "${SPDir}/cgame/FX_BryarPistol.cpp" + "${SPDir}/cgame/FX_Clone.cpp" "${SPDir}/cgame/FX_Concussion.cpp" + "${SPDir}/cgame/FX_Destruction.cpp" "${SPDir}/cgame/FX_DEMP2.cpp" "${SPDir}/cgame/FX_Disruptor.cpp" "${SPDir}/cgame/FX_Emplaced.cpp" @@ -256,14 +264,27 @@ set(SPGameCommonFiles "${SPDir}/qcommon/sstring.h" "${SPDir}/qcommon/tags.h" "${SPDir}/qcommon/timing.h" - "${SPDir}/qcommon/q_math.cpp" "${SPDir}/qcommon/q_shared.cpp" "${SPDir}/qcommon/q_shared.h" - "${SPDir}/qcommon/q_platform.h" + "${SPDir}/qcommon/ojk_i_saved_game.h" + "${SPDir}/qcommon/ojk_saved_game_class_archivers.h" + "${SPDir}/qcommon/ojk_saved_game_helper.h" + "${SPDir}/qcommon/ojk_saved_game_helper_fwd.h" + "${SPDir}/qcommon/ojk_scope_guard.h" + ${SharedCommonFiles} ) source_group("common" FILES ${SPGameCommonFiles}) set(SPGameFiles ${SPGameFiles} ${SPGameCommonFiles}) +set(SPGameCommonSafeFiles + ${SharedCommonSafeFiles} + "${SharedDir}/qcommon/safe/files.h" + "${SharedDir}/qcommon/safe/files.cpp" + "${SPDir}/qcommon/safe/memory.h" + ) +source_group("common/safe" FILES ${SPGameCommonSafeFiles}) +set(SPGameFiles ${SPGameFiles} ${SPGameCommonSafeFiles}) + set(SPGameIcarusFiles "${SPDir}/icarus/BlockStream.cpp" "${SPDir}/icarus/IcarusImplementation.cpp" @@ -371,18 +392,18 @@ endif() if(WIN32) install(TARGETS ${SPGame} RUNTIME - DESTINATION "${JKAInstallDir}/OpenJK" + DESTINATION "${JKAInstallDir}/jaenhanced" COMPONENT ${JKASPClientComponent}) else(WIN32) if(MakeApplicationBundles AND BuildSPEngine) install(TARGETS ${SPGame} LIBRARY - DESTINATION "${JKAInstallDir}/${SPEngine}.app/Contents/MacOS/OpenJK" + DESTINATION "${JKAInstallDir}/${SPEngine}.app/Contents/MacOS/jaenhanced" COMPONENT ${JKASPClientComponent}) else() install(TARGETS ${SPGame} LIBRARY - DESTINATION "${JKAInstallDir}/OpenJK" + DESTINATION "${JKAInstallDir}/jaenhanced" COMPONENT ${JKASPClientComponent}) endif() endif() diff --git a/code/game/FighterNPC.cpp b/code/game/FighterNPC.cpp index 9345e40717..6919bb028f 100644 --- a/code/game/FighterNPC.cpp +++ b/code/game/FighterNPC.cpp @@ -403,7 +403,6 @@ static void ProcessMoveCommands( Vehicle_t *pVeh ) playerState_t *parentPS = &parent->client->ps; #endif -#ifdef _JK2MP if ( parentPS->hyperSpaceTime && curTime - parentPS->hyperSpaceTime < HYPERSPACE_TIME ) {//Going to Hyperspace @@ -442,7 +441,6 @@ static void ProcessMoveCommands( Vehicle_t *pVeh ) } return; } -#endif if ( pVeh->m_iDropTime >= curTime ) {//no speed, just drop @@ -451,7 +449,7 @@ static void ProcessMoveCommands( Vehicle_t *pVeh ) return; } - isLandingOrLaunching = (FighterIsLanding( pVeh, parentPS )||FighterIsLaunching( pVeh, parentPS )); + isLandingOrLaunching = (qboolean)(FighterIsLanding( pVeh, parentPS )||FighterIsLaunching( pVeh, parentPS )); // If we are hitting the ground, just allow the fighter to go up and down. if ( isLandingOrLaunching//going slow enough to start landing @@ -1228,10 +1226,9 @@ static void ProcessOrientCommands( Vehicle_t *pVeh ) #else parentPS = &parent->client->ps; riderPS = &rider->client->ps; - isDead = (parentPS->stats[STAT_HEALTH] <= 0 ); + isDead = (qboolean)(parentPS->stats[STAT_HEALTH] <= 0 ); #endif -#ifdef _JK2MP if ( parentPS->hyperSpaceTime && (curTime - parentPS->hyperSpaceTime) < HYPERSPACE_TIME ) {//Going to Hyperspace @@ -1239,7 +1236,6 @@ static void ProcessOrientCommands( Vehicle_t *pVeh ) VectorCopy( riderPS->viewangles, parentPS->viewangles ); return; } -#endif if ( pVeh->m_iDropTime >= curTime ) {//you can only YAW during this @@ -1267,7 +1263,7 @@ static void ProcessOrientCommands( Vehicle_t *pVeh ) pVeh->m_vOrientation[ROLL] = PredictedAngularDecrement(0.95f, angleTimeMod*2.0f, pVeh->m_vOrientation[ROLL]); } - isLandingOrLanded = (FighterIsLanding( pVeh, parentPS )||FighterIsLanded( pVeh, parentPS )); + isLandingOrLanded = (qboolean)(FighterIsLanding( pVeh, parentPS )||FighterIsLanded( pVeh, parentPS )); if (!isLandingOrLanded) { //don't do this stuff while landed.. I guess. I don't want ships spinning in place, looks silly. diff --git a/code/game/G_Timer.cpp b/code/game/G_Timer.cpp index 952471a4cb..5b7377c467 100644 --- a/code/game/G_Timer.cpp +++ b/code/game/G_Timer.cpp @@ -22,6 +22,7 @@ along with this program; if not, see . #include "g_local.h" #include "../Rufl/hstring.h" +#include "qcommon/ojk_saved_game_helper.h" #define MAX_GTIMERS 16384 @@ -158,6 +159,9 @@ void TIMER_Save( void ) int j; gentity_t *ent; + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + for ( j = 0, ent = &g_entities[0]; j < MAX_GENTITIES; j++, ent++ ) { unsigned char numTimers = TIMER_GetCount(j); @@ -171,7 +175,9 @@ void TIMER_Save( void ) } //Write out the timer information - gi.AppendToSaveGame(INT_ID('T','I','M','E'), (void *)&numTimers, sizeof(numTimers)); + saved_game.write_chunk( + INT_ID('T', 'I', 'M', 'E'), + numTimers); gtimer_t *p = g_timers[j]; assert ((numTimers && p) || (!numTimers && !p)); @@ -185,10 +191,16 @@ void TIMER_Save( void ) assert( length < 1024 );//This will cause problems when loading the timer if longer //Write out the id string - gi.AppendToSaveGame(INT_ID('T','M','I','D'), (void *) timerID, length); + saved_game.write_chunk( + INT_ID('T', 'M', 'I', 'D'), + timerID, + length); //Write out the timer data - gi.AppendToSaveGame(INT_ID('T','D','T','A'), (void *) &time, sizeof( time ) ); + saved_game.write_chunk( + INT_ID('T', 'D', 'T', 'A'), + time); + p = p->next; } } @@ -205,23 +217,51 @@ void TIMER_Load( void ) int j; gentity_t *ent; + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + for ( j = 0, ent = &g_entities[0]; j < MAX_GENTITIES; j++, ent++ ) { - unsigned char numTimers; + unsigned char numTimers = 0; - gi.ReadFromSaveGame( INT_ID('T','I','M','E'), (void *)&numTimers, sizeof(numTimers), NULL ); + saved_game.read_chunk( + INT_ID('T', 'I', 'M', 'E'), + numTimers); //Read back all entries for ( int i = 0; i < numTimers; i++ ) { - int time; + int time = 0; char tempBuffer[1024]; // Still ugly. Setting ourselves up for 007 AUF all over again. =) assert (sizeof(g_timers[0]->time) == sizeof(time) );//make sure we're reading the same size as we wrote //Read the id string and time - gi.ReadFromSaveGame( INT_ID('T','M','I','D'), (char *) tempBuffer, 0, NULL ); - gi.ReadFromSaveGame( INT_ID('T','D','T','A'), (void *) &time, sizeof( time ), NULL ); + saved_game.read_chunk( + INT_ID('T', 'M', 'I', 'D')); + + const char* sg_buffer_data = static_cast( + saved_game.get_buffer_data()); + + int sg_buffer_size = saved_game.get_buffer_size(); + + if (sg_buffer_size < 0 || static_cast(sg_buffer_size) >= sizeof(tempBuffer)) + { + sg_buffer_size = 0; + } + else + { + std::uninitialized_copy_n( + sg_buffer_data, + sg_buffer_size, + tempBuffer); + } + + tempBuffer[sg_buffer_size] = '\0'; + + saved_game.read_chunk( + INT_ID('T', 'D', 'T', 'A'), + time); //this is odd, we saved all the timers in the autosave, but not all the ents are spawned yet from an auto load, so skip it if (ent->inuse) @@ -334,7 +374,7 @@ qboolean TIMER_Done( gentity_t *ent, const char *identifier ) return qtrue; } - return (timer->time < level.time); + return (qboolean)(timer->time < level.time); } /* @@ -357,7 +397,7 @@ qboolean TIMER_Done2( gentity_t *ent, const char *identifier, qboolean remove ) return qfalse; } - res = (timer->time < level.time); + res = (qboolean)(timer->time < level.time); if (res && remove) { @@ -414,3 +454,21 @@ qboolean TIMER_Start( gentity_t *self, const char *identifier, int duration ) } return qfalse; } + +/* + ------------------------- + TIMER_List + ------------------------- + */ +std::vector > TIMER_List(gentity_t* ent) { + std::vector > returnValue; + gtimer_t *p = g_timers[ent->s.number]; + + while (p) { + std::pair pair = std::make_pair(p->id.c_str(), p->time - level.time); + returnValue.push_back(pair); + p = p->next; + } + + return returnValue; +} diff --git a/code/game/NPC.cpp b/code/game/NPC.cpp index e224f62b71..febcc94a73 100644 --- a/code/game/NPC.cpp +++ b/code/game/NPC.cpp @@ -48,6 +48,7 @@ extern void Mark1_dying( gentity_t *self ); extern void NPC_BSCinematic( void ); extern int GetTime ( int lastTime ); extern void G_CheckCharmed( gentity_t *self ); +extern void G_CheckInsanity( gentity_t *self ); extern qboolean Boba_Flying( gentity_t *self ); extern qboolean RT_Flying( gentity_t *self ); extern qboolean Jedi_CultistDestroyer( gentity_t *self ); @@ -86,6 +87,110 @@ static bState_t G_CurrentBState( gNPC_t *gNPC ); extern int eventClearTime; +/* +what is an NPC's preferred weapon to switch to when they pick up a new weapon, or +lose their current weapon, assuming they have more than one weapon? +*/ +int allWeaponOrder[MAX_WEAPONS] = +{ + WP_CONCUSSION, + WP_ROCKET_LAUNCHER, + WP_THERMAL, + WP_DEMP2, + WP_FLECHETTE, + WP_BOWCASTER, + WP_REPEATER, + WP_NOGHRI_STICK, + WP_TUSKEN_STAFF, + WP_BLASTER, + WP_BLASTER_PISTOL, + WP_BRYAR_PISTOL, + WP_DISRUPTOR, + WP_SABER, //probably a sword and we're probably not a Jedi, so blaster weapons are preferred + WP_TUSKEN_STAFF, + WP_STUN_BATON, + WP_MELEE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE, + WP_NONE +}; + +qboolean NPC_JediClass(int className) { + switch (className) { + case CLASS_JEDI: + case CLASS_REBORN: + case CLASS_SHADOWTROOPER: + case CLASS_ALORA: + case CLASS_DESANN: + case CLASS_LUKE: + case CLASS_KYLE: + case CLASS_TAVION: + case CLASS_MORGANKATARN: + return qtrue; + default: + return qfalse; + } +} + +qboolean NPC_JediClassGood(int className) { + switch (className) { + case CLASS_JEDI: + case CLASS_LUKE: + case CLASS_KYLE: + case CLASS_MORGANKATARN: + return qtrue; + default: + return qfalse; + } +} + +qboolean NPC_JediClassNonBoss(int className) { + switch (className) { + case CLASS_JEDI: + case CLASS_REBORN: + return qtrue; + default: + return qfalse; + } +} + +qboolean NPC_JediClassSemiBoss(int className) { + switch (className) { + case CLASS_ALORA: + case CLASS_SHADOWTROOPER: + return qtrue; + } + + return qfalse; +} + +qboolean NPC_JediClassBoss(int className) { + switch (className) { + case CLASS_DESANN: + case CLASS_LUKE: + case CLASS_KYLE: + case CLASS_TAVION: + case CLASS_MORGANKATARN: + return qtrue; + default: + return qfalse; + } +} + +extern void GM_Dying( gentity_t *self ); + void CorpsePhysics( gentity_t *self ) { // run the bot through the server like it was a real client @@ -93,6 +198,10 @@ void CorpsePhysics( gentity_t *self ) ClientThink( self->s.number, &ucmd ); VectorCopy( self->s.origin, self->s.origin2 ); + if ( self->client->NPC_class == CLASS_GALAKMECH ) + { + GM_Dying( self ); + } //FIXME: match my pitch and roll for the slope of my groundPlane if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE && !(self->flags&FL_DISINTEGRATED) ) {//on the ground @@ -319,51 +428,7 @@ int BodyRemovalPadTime( gentity_t *ent ) if ( !ent || !ent->client ) return 0; -/* - switch ( ent->client->playerTeam ) - { - case TEAM_KLINGON: // no effect, we just remove them when the player isn't looking - case TEAM_SCAVENGERS: - case TEAM_HIROGEN: - case TEAM_MALON: - case TEAM_IMPERIAL: - case TEAM_STARFLEET: - time = 10000; // 15 secs. - break; - case TEAM_BORG: - time = 2000; - break; - - case TEAM_STASIS: - return qtrue; - break; - - case TEAM_FORGE: - time = 1000; - break; - - case TEAM_BOTS: -// if (!Q_stricmp( ent->NPC_type, "mouse" )) -// { - time = 0; -// } -// else -// { -// time = 10000; -// } - break; - - case TEAM_8472: - time = 2000; - break; - - default: - // never go away - time = Q3_INFINITE; - break; - } -*/ // team no longer indicates species/race, so in this case we'd use NPC_class, but switch( ent->client->NPC_class ) { @@ -415,26 +480,6 @@ static void NPC_RemoveBodyEffect(void) if ( !NPC || !NPC->client || (NPC->s.eFlags & EF_NODRAW) ) return; -/* - switch(NPC->client->playerTeam) - { - case TEAM_STARFLEET: - //FIXME: Starfleet beam out - break; - - case TEAM_BOTS: -// VectorCopy( NPC->currentOrigin, org ); -// org[2] -= 16; -// tent = G_TempEntity( org, EV_BOT_EXPLODE ); -// tent->owner = NPC; - - break; - - default: - break; - } -*/ - // team no longer indicates species/race, so in this case we'd use NPC_class, but @@ -1043,7 +1088,7 @@ void NPC_ApplyScriptFlags (void) { if ( NPCInfo->scriptFlags & SCF_CROUCHED ) { - if ( NPCInfo->charmedTime > level.time && (ucmd.forwardmove || ucmd.rightmove) ) + if ( (NPCInfo->charmedTime > level.time || NPCInfo->darkCharmedTime > level.time) && (ucmd.forwardmove || ucmd.rightmove) ) {//ugh, if charmed and moving, ignore the crouched command } else @@ -1058,7 +1103,7 @@ void NPC_ApplyScriptFlags (void) } else if(NPCInfo->scriptFlags & SCF_WALKING) { - if ( NPCInfo->charmedTime > level.time && (ucmd.forwardmove || ucmd.rightmove) ) + if ( (NPCInfo->charmedTime > level.time || NPCInfo->darkCharmedTime > level.time) && (ucmd.forwardmove || ucmd.rightmove) ) {//ugh, if charmed and moving, ignore the walking command } else @@ -1216,8 +1261,7 @@ void NPC_HandleAIFlags (void) } void NPC_AvoidWallsAndCliffs (void) -{ -/* +{/* vec3_t forward, right, testPos, angles, mins; trace_t trace; float fwdDist, rtDist; @@ -1248,7 +1292,7 @@ void NPC_AvoidWallsAndCliffs (void) // straight into a wall or off a cliff unless we really wanted to? return; } - + VectorCopy( NPC->mins, mins ); mins[2] += STEPSIZE; angles[YAW] = NPC->client->ps.viewangles[YAW];//Add ucmd.angles[YAW]? @@ -1264,6 +1308,7 @@ void NPC_AvoidWallsAndCliffs (void) ucmd.rightmove = 0; return; } + VectorCopy(trace.endpos, testPos); testPos[2] -= 128; @@ -1278,7 +1323,7 @@ void NPC_AvoidWallsAndCliffs (void) ucmd.forwardmove = 0; ucmd.rightmove = 0; return; -*/ + */ } void NPC_CheckAttackScript(void) @@ -1912,13 +1957,14 @@ NPC_RunBehavior ------------------------- */ extern void NPC_BSEmplaced( void ); -extern qboolean NPC_CheckSurrender( void ); +extern qboolean NPC_CheckSurrender( qboolean noEscape = qfalse ); extern void NPC_BSRT_Default( void ); extern void NPC_BSCivilian_Default( int bState ); extern void NPC_BSSD_Default( void ); extern void NPC_BehaviorSet_Trooper( int bState ); extern bool NPC_IsTrooper( gentity_t *ent ); extern bool Pilot_MasterUpdate(); +extern void NPC_BSGM_Default( void ); void NPC_RunBehavior( int team, int bState ) { @@ -1944,6 +1990,7 @@ void NPC_RunBehavior( int team, int bState ) { NPC_BSEmplaced(); G_CheckCharmed( NPC ); + G_CheckInsanity( NPC ); return; } else if ( NPC->client->NPC_class == CLASS_HOWLER ) @@ -1963,11 +2010,14 @@ void NPC_RunBehavior( int team, int bState ) {//jedi NPC_BehaviorSet_Jedi( bState ); } - else if ( NPC->client->NPC_class == CLASS_REBORN && NPC->client->ps.weapon == WP_MELEE ) + else if ( (NPC->client->NPC_class == CLASS_REBORN && NPC->client->ps.weapon == WP_MELEE) + || (NPC->client->ps.weapon == WP_MELEE && (NPC->NPC->stats.meleeKicks || NPC->NPC->stats.meleeKatas))) //only jedi AI supports these right now {//force-only reborn NPC_BehaviorSet_Jedi( bState ); } - else if ( NPC->client->NPC_class == CLASS_BOBAFETT ) + else if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA + || NPC->client->NPC_class == CLASS_COMMANDO) { Boba_Update(); if (NPCInfo->surrenderTime) @@ -2000,6 +2050,7 @@ void NPC_RunBehavior( int team, int bState ) NPC_BehaviorSet_Stormtrooper( bState ); } G_CheckCharmed( NPC ); + G_CheckInsanity( NPC ); } else if ( NPC->client->NPC_class == CLASS_RANCOR ) { @@ -2013,6 +2064,7 @@ void NPC_RunBehavior( int team, int bState ) { NPC_BehaviorSet_Wampa( bState ); G_CheckCharmed( NPC ); + G_CheckInsanity( NPC ); } else if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH ) {//being forced to march @@ -2024,12 +2076,14 @@ void NPC_RunBehavior( int team, int bState ) { NPC_BehaviorSet_Sniper( bState ); G_CheckCharmed( NPC ); + G_CheckInsanity( NPC ); return; } else { NPC_BehaviorSet_Tusken( bState ); G_CheckCharmed( NPC ); + G_CheckInsanity( NPC ); return; } } @@ -2037,15 +2091,18 @@ void NPC_RunBehavior( int team, int bState ) { NPC_BehaviorSet_Tusken( bState ); G_CheckCharmed( NPC ); + G_CheckInsanity( NPC ); return; } else if ( NPC->client->ps.weapon == WP_NOGHRI_STICK ) { NPC_BehaviorSet_Stormtrooper( bState ); G_CheckCharmed( NPC ); + G_CheckInsanity( NPC ); } else { + G_CheckInsanity( NPC ); switch( team ) { @@ -2092,6 +2149,9 @@ void NPC_RunBehavior( int team, int bState ) case CLASS_MARK2: NPC_BehaviorSet_Mark2( bState ); return; + case CLASS_GALAKMECH: + NPC_BSGM_Default(); + return; default: break; } @@ -2117,6 +2177,7 @@ void NPC_RunBehavior( int team, int bState ) else { NPC_BSFlee(); + } return; } @@ -2186,7 +2247,7 @@ void NPC_RunBehavior( int team, int bState ) } else { - if ( NPCInfo->charmedTime > level.time ) + if ( (NPCInfo->charmedTime > level.time || NPCInfo->darkCharmedTime > level.time) ) { NPC_BehaviorSet_Charmed( bState ); } @@ -2225,6 +2286,8 @@ NPC Behavior state thinking =============== */ +extern qboolean NPC_JediClassGood(int className); + void NPC_ExecuteBState ( gentity_t *self)//, int msec ) { bState_t bState; @@ -2296,9 +2359,10 @@ void NPC_ExecuteBState ( gentity_t *self)//, int msec ) } else if ( NPC->client->playerTeam != TEAM_ENEMY //not an enemy && (NPC->client->playerTeam != TEAM_FREE || (NPC->client->NPC_class == CLASS_TUSKEN && Q_irand( 0, 4 )))//not a rampaging creature or I'm a tusken and I feel generous (temporarily) - && NPC->enemy->NPC - && (NPC->enemy->NPC->surrenderTime > level.time || (NPC->enemy->NPC->scriptFlags&SCF_FORCED_MARCH)) ) - {//don't shoot someone who's surrendering if you're a good guy + && NPC->enemy->NPC + && (NPC->enemy->NPC->surrenderTime > level.time || (NPC->enemy->NPC->scriptFlags&SCF_FORCED_MARCH) + || (NPC_JediClassGood(NPC->client->NPC_class) && (NPC->enemy->s.weapon == WP_NONE || (NPC->enemy->s.weapon == WP_MELEE && !NPC->enemy))))) + {//don't shoot someone who's surrendering if you're a good guy, especially if you're a Jedi ucmd.buttons &= ~BUTTON_ATTACK; ucmd.buttons &= ~BUTTON_ALT_ATTACK; } @@ -2361,6 +2425,15 @@ void NPC_ExecuteBState ( gentity_t *self)//, int msec ) NPC_ApplyRoff(); } + if (NPC->client->playerTeam != TEAM_PLAYER //not an enemy + && NPC_JediClassGood(NPC->client->NPC_class) + && NPC->enemy->NPC + && (NPC->enemy->NPC->surrenderTime > level.time || (NPC->enemy->NPC->scriptFlags&SCF_FORCED_MARCH) || NPC->enemy->s.weapon == WP_NONE || (NPC->enemy->s.weapon == WP_MELEE && !NPC->enemy))) + {//redundancy for Jedi because they like to attack anyway + ucmd.buttons &= ~BUTTON_ATTACK; + ucmd.buttons &= ~BUTTON_ALT_ATTACK; + } + // end of thinking cleanup NPCInfo->touchedByPlayer = NULL; @@ -2442,7 +2515,7 @@ void NPC_Think ( gentity_t *self)//, int msec ) VectorCopy( self->client->ps.moveDir, oldMoveDir ); VectorClear( self->client->ps.moveDir ); // see if NPC ai is frozen - if ( debugNPCFreeze->integer || (NPC->svFlags&SVF_ICARUS_FREEZE) ) + if ( debugNPCFreeze->integer || (NPC->svFlags&SVF_ICARUS_FREEZE) || (self && self->client && self->client->ps.stasisTime > level.time)) { NPC_UpdateAngles( qtrue, qtrue ); ClientThink(self->s.number, &ucmd); @@ -2694,6 +2767,11 @@ void NPC_SetAnim(gentity_t *ent,int setAnimParts,int anim,int setAnimFlags, int { return; } + + if ( ent->client->ps.stasisTime > level.time ) + { + return; + } if(ent->client) {//Players, NPCs diff --git a/code/game/NPC_behavior.cpp b/code/game/NPC_behavior.cpp index 9f51936ccd..65c4d89bc0 100644 --- a/code/game/NPC_behavior.cpp +++ b/code/game/NPC_behavior.cpp @@ -159,13 +159,13 @@ void NPC_BSAdvanceFight (void) VectorMA ( muzzle, distanceToEnemy, forward, hitspot); VectorSubtract(hitspot, enemy_org, diff); aim_off = VectorLength(diff); - if(aim_off > random() * max_aim_off)//FIXME: use aim value to allow poor aim? + if(aim_off > Q_flrand(0.0f, 1.0f) * max_aim_off)//FIXME: use aim value to allow poor aim? { attack_scale *= 0.75; //see if where we're going to shoot is too far from his head VectorSubtract(hitspot, enemy_head, diff); aim_off = VectorLength(diff); - if(aim_off > random() * max_aim_off) + if(aim_off > Q_flrand(0.0f, 1.0f) * max_aim_off) { attack_ok = qfalse; } @@ -243,7 +243,7 @@ void NPC_BSCinematic( void ) if (TIMER_Done(NPC, "NoAnimFireDelay")) { TIMER_Set(NPC, "NoAnimFireDelay", NPC_AttackDebounceForWeapon()); - FireWeapon(NPC, (NPCInfo->scriptFlags&SCF_ALT_FIRE)) ; + FireWeapon(NPC, (qboolean)((NPCInfo->scriptFlags&SCF_ALT_FIRE) != 0)); } } @@ -593,7 +593,7 @@ void NPC_BSFollowLeader_UpdateEnemy(void) { if ( !NPC->enemy ) {//no enemy, find one - NPC_CheckEnemy( NPCInfo->confusionTimeconfusionTimeinsanityTimeenemy ) {//just found one NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 ); @@ -651,7 +651,7 @@ void NPC_BSFollowLeader_UpdateEnemy(void) } else if ( NPC->client->ps.weapon && NPCInfo->enemyCheckDebounceTime < level.time ) { - NPC_CheckEnemy( (NPCInfo->confusionTimetempBehavior!=BS_FOLLOW_LEADER), qfalse );//don't find new enemy if this is tempbehav + NPC_CheckEnemy( (qboolean)(((NPCInfo->confusionTimeinsanityTimetempBehavior!=BS_FOLLOW_LEADER), qfalse );//don't find new enemy if this is tempbehav } } } @@ -1518,7 +1518,7 @@ void NPC_Surrender( void ) WP_DropWeapon( NPC, NULL ); } if ( NPCInfo->surrenderTime < level.time - 5000 ) - {//haven't surrendered for at least 6 seconds, tell them what you're doing + {//haven't surrendered for at least 5 seconds, tell them what you're doing //FIXME: need real dialogue EV_SURRENDER NPCInfo->blockedSpeechDebounceTime = 0;//make sure we say this G_AddVoiceEvent( NPC, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 3000 ); @@ -1538,6 +1538,13 @@ void NPC_Surrender( void ) NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_COWER1_STOP, SETANIM_FLAG_HOLD|SETANIM_FLAG_OVERRIDE ); NPCInfo->surrenderTime = level.time + NPC->client->ps.torsoAnimTimer; } + else + { + NPC_SetAnim(NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD | SETANIM_FLAG_OVERRIDE); + NPC->client->ps.torsoAnimTimer = 2000; + NPCInfo->surrenderTime = level.time + NPC->client->ps.torsoAnimTimer + 1000;//stay surrendered for at least 1 second + //FIXME: while surrendering, make a big sight/sound alert? Or G_AlertTeam? + } } // New To The Surrender, So Start The Animation @@ -1562,15 +1569,139 @@ void NPC_Surrender( void ) //---------------------------------------------------------- else { - NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD|SETANIM_FLAG_OVERRIDE ); - NPC->client->ps.torsoAnimTimer = Q_irand(3000, 8000); // Pretend the anim lasts longer + NPC_SetAnim(NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD | SETANIM_FLAG_OVERRIDE); + NPC->client->ps.torsoAnimTimer = 2000; } } NPCInfo->surrenderTime = level.time + NPC->client->ps.torsoAnimTimer + 1000; } } -qboolean NPC_CheckSurrender( void ) +qboolean NPC_CheckUseMelee(gentity_t* NPC, int relNumEnemies) +{ + int chance = relNumEnemies; + + if (NPC->health < 25 || (NPC->health < 0.25*NPC->max_health && NPC->enemy->client->ps.weapon == WP_SABER)) + { + chance -= 4; + } + + if (NPC->enemy->enemy && DistanceSquared(NPC->currentOrigin, NPC->enemy->currentOrigin) < 64 * 64) + {//enemy is close and distracted + chance++; + } + + if (NPC->enemy->painDebounceTime < level.time - 3000) //enemy in pain + chance++; + if (NPC->enemy->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] < level.time - 1000) //enemy busy being attacked + chance++; + if (NPC->NPC->aiFlags&NPCAI_HEAVY_MELEE || NPC->client->NPC_class == CLASS_WOOKIEE) + chance++; + + chance += NPC->NPC->stats.aggression - 3; + + return chance; +} + +extern int NPC_CheckMultipleEnemies(gentity_t *closestTo, int enemyTeam, qboolean checkVis); +extern void WP_MeleeTime(gentity_t* meleer); +qboolean NPC_CheckSurrender(qboolean noEscape = qfalse) +{ + if (!g_AIsurrender->integer + && NPC->client->NPC_class != CLASS_UGNAUGHT + && NPC->client->NPC_class != CLASS_JAWA) + {//not enabled + return qfalse; + } + if (NPC->s.weapon != WP_NONE && NPC->s.weapon != WP_MELEE) + { + return qfalse; + } + if (!Q3_TaskIDPending(NPC, TID_MOVE_NAV) //not scripted to go somewhere + && NPC->client->ps.groundEntityNum != ENTITYNUM_NONE //not in the air + && !NPC->client->ps.weaponTime && !PM_InKnockDown(&NPC->client->ps)//not firing and not on the ground + && NPC->enemy && NPC->enemy->client /*&& NPC->enemy->enemy == NPC*/ && NPC->enemy->s.weapon != WP_NONE && (NPC->enemy->s.weapon != WP_MELEE || (NPC->enemy->client->NPC_class == CLASS_RANCOR || NPC->enemy->client->NPC_class == CLASS_WAMPA))//enemy is using a weapon or is a Rancor or Wampa + && NPC->enemy->health > 20 && NPC->enemy->painDebounceTime < level.time - 3000 && NPC->enemy->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] < level.time - 1000) + {//don't surrender if scripted to run somewhere or if we're in the air or if we're busy or if we don't have an enemy or if the enemy is not mad at me or is hurt or not a threat or busy being attacked + //FIXME: even if not in a group, don't surrender if there are other enemies in the PVS and within a certain range? + + //we "might" surrender, check number of enemies + int numEnemies = NPC_CheckMultipleEnemies(NPC, NPC->client->enemyTeam, 0); + int numMyGroup = 0; + if (NPCInfo->group) numMyGroup = NPCInfo->group->numGroup; + int relNumEnemies = numEnemies - numMyGroup; + //0 means there is a 1-1 ratio, < 0 means my group is bigger by x, > 0 means my group is smaller by x + + if (numEnemies == 1 && (NPC->enemy->s.weapon == WP_MELEE || NPC->enemy->s.weapon == WP_NONE)) + {//maybe we should try to melee fight? Or at least don't officially surrender... just kind of stand there + WP_MeleeTime(NPC); + } + + if (noEscape) + {//I was fleeing but I got cornered, I don't have other options so have to give up... + //NPC_FaceEnemy(); + NPC_Surrender(); + NPC_UpdateAngles(qtrue, qtrue); + return qtrue; + } + + if ((NPCInfo->group && NPCInfo->group->numGroup > 1) + || (NPCInfo->group && NPCInfo->group->numGroup > numEnemies)) + { + //FIXME: I'm not alone... maybe less likely to surrender? + } + + if (NPC->s.weapon == WP_NONE + //NPC has a weapon + /*|| NPC->enemy == player + || (NPC->enemy->s.weapon == WP_SABER&&NPC->enemy->client&&NPC->enemy->client->ps.SaberActive()) + || (NPC->enemy->NPC && NPC->enemy->NPC->group && NPC->enemy->NPC->group->numGroup > 2)*/) + {//surrender only if have no weapon or fighting a player or jedi or if we are outnumbered at least 3 to 1 + if (NPC->enemy == player) + {//player is the guy I'm running from + if ((g_crosshairEntNum == NPC->s.number && InFOV(player, NPC, 60, 30)) + || DistanceSquared(NPC->currentOrigin, player->currentOrigin) < 256 * 256 + || ((DistanceSquared(NPC->currentOrigin, player->currentOrigin) < 512 * 512) && (InFOV(NPC, player, 60, 30) || numEnemies > 1))) + {//give up if I see player aiming at me, player is very close, or player is near and watching me/has buddies + //NPC_FaceEnemy(); + NPC_Surrender(); + NPC_UpdateAngles(qtrue, qtrue); + return qtrue; + } + } + else if (NPC->enemy) + {//running from another NPC + if (InFOV(NPC, NPC->enemy, 30, 30) || numEnemies > 1) + {//they're looking at me or have friends + //float maxDist = (128+(NPC->maxs[0]*1.5)+(NPC->enemy->maxs[0]*1.5)); //64 + float maxDist = 256; + float maxDistMultEnemies = 512; + maxDist *= maxDist; + maxDistMultEnemies *= maxDistMultEnemies; + if (DistanceSquared(NPC->currentOrigin, NPC->enemy->currentOrigin) < maxDist + || (numEnemies > 1 && DistanceSquared(NPC->currentOrigin, NPC->enemy->currentOrigin) < maxDistMultEnemies) + || NPC->painDebounceTime > level.time) + {//they're very close, or somewhat close and multiple enemies, or they just hit me + if (gi.inPVS(NPC->currentOrigin, NPC->enemy->currentOrigin)) + {//they're in the same room + //FIXME: should player-team NPCs not fire on surrendered NPCs? + //NPC_FaceEnemy(); + NPC_Surrender(); + NPC_UpdateAngles(qtrue, qtrue); + return qtrue; + } + } + } + } + } + } + + return qfalse; +} + + +/* +qboolean NPC_CheckSurrender( qboolean noEscape = qfalse ) { if ( !g_AIsurrender->integer && NPC->client->NPC_class != CLASS_UGNAUGHT @@ -1609,7 +1740,7 @@ qboolean NPC_CheckSurrender( void ) {//I'm not looking at them return qfalse; } - else if ( DistanceSquared( NPC->currentOrigin, player->currentOrigin ) < 65536/*256*256*/ ) + else if ( DistanceSquared( NPC->currentOrigin, player->currentOrigin ) < 512*512 ) {//they're not close return qfalse; } @@ -1617,52 +1748,69 @@ qboolean NPC_CheckSurrender( void ) {//they're not in the same room return qfalse; } - } + } } - if ( !NPCInfo->group || (NPCInfo->group && NPCInfo->group->numGroup <= 1) ) - {//I'm alone but I was in a group//FIXME: surrender anyway if just melee or no weap? + + //we "might" surrender, check number of enemies + int numEnemies = NPC_CheckMultipleEnemies(NPC, NPC->client->enemyTeam, 0); + int relNumEnemies = numEnemies - NPCInfo->group->numGroup; + //0 means there is a 1-1 ratio, < 0 means my group is bigger by x, > 0 means my group is smaller by x + + if (noEscape && ((NPC->enemy->s.weapon != WP_MELEE && NPC->enemy->s.weapon != WP_NONE) + || numEnemies > 1)) + {//I was fleeing but I got cornered, have to give up + //FIXME: Some enemies will try to melee fight? I dunno, might be kind of silly... + //NPC_FaceEnemy(); + NPC_Surrender(); + NPC_UpdateAngles(qtrue, qtrue); + return qtrue; + } + else if (NPC->enemy->s.weapon == WP_MELEE || NPC->enemy->s.weapon == WP_NONE) + { + //maybe we should try to melee fight? + return qfalse; + } + + if ( !NPCInfo->group || (NPCInfo->group && NPCInfo->group->numGroup <= 1) + || (NPCInfo->group && NPCInfo->group->numGroup < numEnemies) + || NPCInfo->group->) + {//I'm alone but I was in a group or my group is smaller than enemy group//FIXME: surrender anyway if just melee or no weap? if ( NPC->s.weapon == WP_NONE - //NPC has a weapon + //NPC has a weapon || NPC->enemy == player || (NPC->enemy->s.weapon == WP_SABER&&NPC->enemy->client&&NPC->enemy->client->ps.SaberActive()) - || (NPC->enemy->NPC && NPC->enemy->NPC->group && NPC->enemy->NPC->group->numGroup > 2) ) + || (NPC->enemy->NPC && NPC->enemy->NPC->group && NPC->enemy->NPC->group->numGroup > 2)) {//surrender only if have no weapon or fighting a player or jedi or if we are outnumbered at least 3 to 1 - if ( NPC->enemy == player ) - {//player is the guy I'm running from - if ( g_crosshairEntNum == NPC->s.number ) - {//give up if player is aiming at me + if (NPC->enemy == player) + {//player is the guy I'm running from + if ((g_crosshairEntNum == NPC->s.number && InFOV(player, NPC, 60, 30)) + || DistanceSquared(NPC->currentOrigin, player->currentOrigin) < 200 * 200 + || ((DistanceSquared(NPC->currentOrigin, player->currentOrigin) < 512 * 512) + && (InFOV(NPC, player, 60, 30) || relNumEnemies > 1))) + {//give up if I see player aiming at me, player is very close, or player is near and watching me/has buddies + //NPC_FaceEnemy(); NPC_Surrender(); - NPC_UpdateAngles( qtrue, qtrue ); + NPC_UpdateAngles(qtrue, qtrue); return qtrue; - } - else if ( player->s.weapon == WP_SABER ) - {//player is using saber - if ( InFOV( NPC, player, 60, 30 ) ) - {//they're looking at me - if ( DistanceSquared( NPC->currentOrigin, player->currentOrigin ) < 16384/*128*128*/ ) - {//they're close - if ( gi.inPVS( NPC->currentOrigin, player->currentOrigin ) ) - {//they're in the same room - NPC_Surrender(); - NPC_UpdateAngles( qtrue, qtrue ); - return qtrue; - } - } - } - } - } + } + } else if ( NPC->enemy ) - {//??? - //should NPC's surrender to others? - if ( InFOV( NPC, NPC->enemy, 30, 30 ) ) - {//they're looking at me - float maxDist = (64+(NPC->maxs[0]*1.5)+(NPC->enemy->maxs[0]*1.5)); + {//running from another NPC + if (InFOV(NPC, NPC->enemy, 30, 30) || relNumEnemies > 1) + {//they're looking at me or have friends + //float maxDist = (128+(NPC->maxs[0]*1.5)+(NPC->enemy->maxs[0]*1.5)); //64 + float maxDist = 128; + float maxDistMultEnemies = 256; maxDist *= maxDist; - if ( DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ) < maxDist ) - {//they're close + maxDistMultEnemies *= maxDistMultEnemies; + if ( DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ) < maxDist + || (numEnemies > 1 && DistanceSquared(NPC->currentOrigin, NPC->enemy->currentOrigin) < maxDistMultEnemies) + || NPC->painDebounceTime > level.time) + {//they're very close, or somewhat close and multiple enemies, or they just hit me if ( gi.inPVS( NPC->currentOrigin, NPC->enemy->currentOrigin ) ) {//they're in the same room //FIXME: should player-team NPCs not fire on surrendered NPCs? + //NPC_FaceEnemy(); NPC_Surrender(); NPC_UpdateAngles( qtrue, qtrue ); return qtrue; @@ -1676,6 +1824,7 @@ qboolean NPC_CheckSurrender( void ) } return qfalse; } +*/ void NPC_JawaFleeSound( void ) { @@ -1703,13 +1852,16 @@ qboolean NPC_BSFlee( void ) bool moveSuccess = false; bool inSurrender = (level.timesurrenderTime); - + if (NPCInfo->surrenderTime - level.time < 4000 && NPC_CheckSurrender()) + {//currently or just finished surrendering + return qfalse; + } // Check For Enemies And Alert Events //------------------------------------ NPC_CheckEnemy(qtrue, qfalse); NPC_CheckAlertEvents(qtrue, qtrue, -1, qfalse, AEL_DANGER, qfalse); - if (NPC->enemy && G_ClearLOS(NPC, NPC->enemy)) + if (NPC->enemy) { NPCInfo->enemyLastSeenTime = level.time; } @@ -1854,11 +2006,9 @@ qboolean NPC_BSFlee( void ) if ( TIMER_Done( NPC, "panic" ) ) { //done panicking, time to realize we're dogmeat, if we haven't been able to flee for a few seconds - if ((level.time-NPC->lastMoveTime)>3000 - && (level.time-NPCInfo->surrenderTime) > 3000 )//and haven't just finished surrendering + if ((level.time - NPC->lastMoveTime)>3000) { - NPC_FaceEnemy(); - NPC_Surrender(); + NPC_CheckSurrender(qtrue); } } diff --git a/code/game/NPC_combat.cpp b/code/game/NPC_combat.cpp index 6fc04ec7c1..642c1ec350 100644 --- a/code/game/NPC_combat.cpp +++ b/code/game/NPC_combat.cpp @@ -37,6 +37,11 @@ extern qboolean PM_DroidMelee( int npc_class ); extern int delayedShutDown; extern qboolean G_ValidEnemy( gentity_t *self, gentity_t *enemy ); +extern qboolean blasterWeap(int wp); +extern qboolean lightBlasterWeap(int wp); +extern qboolean heavyBlasterWeap(int wp); +extern qboolean heavyWeap(int wp); + void ChangeWeapon( gentity_t *ent, int newWeapon ); void G_ClearEnemy (gentity_t *self) @@ -344,6 +349,20 @@ void G_AttackDelay( gentity_t *self, gentity_t *enemy ) case WP_NOGHRI_STICK: attDelay += Q_irand( 0, 500 ); break; + case WP_E5_CARBINE: + attDelay -= Q_irand( 0, 500 ); + break; + case WP_DC15S_CARBINE: + attDelay -= Q_irand( 0, 500 ); + break; + case WP_Z6_ROTARY: + attDelay += Q_irand( 0, 500 ); + break; + case WP_DC15A_RIFLE: + break; + case WP_SONIC_BLASTER: + return; + break; /* case WP_DEMP2: break; @@ -434,6 +453,11 @@ void G_SetEnemy( gentity_t *self, gentity_t *enemy ) {//can't pick up enemies if confused return; } + + if ( self->NPC->insanityTime > level.time ) + {//can't pick up enemies if confused + return; + } #ifdef _DEBUG if ( self->s.number ) @@ -449,7 +473,7 @@ void G_SetEnemy( gentity_t *self, gentity_t *enemy ) if ( self->client && self->NPC && enemy->client && enemy->client->playerTeam == self->client->playerTeam ) {//Probably a damn script! - if ( self->NPC->charmedTime > level.time ) + if ( self->NPC->charmedTime > level.time || self->NPC->darkCharmedTime > level.time ) {//Probably a damn script! return; } @@ -508,7 +532,10 @@ void G_SetEnemy( gentity_t *self, gentity_t *enemy ) // Basically, you're first one to notice enemies if ( self->forcePushTime < level.time ) // not currently being pushed { - if ( !G_TeamEnemy( self ) && self->client->NPC_class != CLASS_BOBAFETT) + if ( !G_TeamEnemy( self ) && + self->client->NPC_class != CLASS_BOBAFETT + && self->client->NPC_class != CLASS_MANDA + && self->client->NPC_class != CLASS_COMMANDO) {//team did not have an enemy previously if ( self->NPC && self->client->playerTeam == TEAM_PLAYER @@ -604,19 +631,19 @@ void G_SetEnemy( gentity_t *self, gentity_t *enemy ) //FIXME: this is a disgusting hack that is supposed to make the Imperials start with their weapon holstered- need a better way if ( self->client->ps.weapon == WP_NONE && !Q_stricmpn( self->NPC_type, "imp", 3 ) && !(self->NPC->scriptFlags & SCF_FORCED_MARCH) ) { - if ( self->client->ps.stats[STAT_WEAPONS] & ( 1 << WP_BLASTER ) ) + if ( self->client->ps.weapons[WP_BLASTER] ) { ChangeWeapon( self, WP_BLASTER ); self->client->ps.weapon = WP_BLASTER; self->client->ps.weaponstate = WEAPON_READY; - G_CreateG2AttachedWeaponModel( self, weaponData[WP_BLASTER].weaponMdl, self->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel( self, weaponData[WP_BLASTER].worldModel, self->handRBolt, 0 ); } - else if ( self->client->ps.stats[STAT_WEAPONS] & ( 1 << WP_BLASTER_PISTOL ) ) + else if ( self->client->ps.weapons[WP_BLASTER_PISTOL] ) { ChangeWeapon( self, WP_BLASTER_PISTOL ); self->client->ps.weapon = WP_BLASTER_PISTOL; self->client->ps.weaponstate = WEAPON_READY; - G_CreateG2AttachedWeaponModel( self, weaponData[WP_BLASTER_PISTOL].weaponMdl, self->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel( self, weaponData[WP_BLASTER_PISTOL].worldModel, self->handRBolt, 0 ); } } return; @@ -634,51 +661,93 @@ void G_SetEnemy( gentity_t *self, gentity_t *enemy ) self->enemy = enemy; } - -/* -int ChooseBestWeapon( void ) +qboolean HaveWeapon(gentity_t* ent, int weapon) { - int n; - int weapon; + return (ent->client->ps.stats[STAT_WEAPONS] & (1 << weapon)); +} - // check weapons in the NPC's weapon preference order - for ( n = 0; n < MAX_WEAPONS; n++ ) +/* + these funcs deals with NPC multi-weapon support. + currently supported: + - choose next "best" weapon + - choose a random weapon, optionally by "group" (heavy, blaster, melee, Boba-types do this) +*/ +qboolean WeaponInGroup(int weapon, int wpnGroup, int altFire = 0) +{//this and the funcs it calls do the heavy lifting for weapon grouping + if ((heavyWeap(weapon) && wpnGroup == WEAPS_HEAVY) + || (lightBlasterWeap(weapon) && wpnGroup == WEAPS_LIGHTBLASTER) + || (heavyBlasterWeap(weapon) && wpnGroup == WEAPS_HEAVYBLASTER) + || (blasterWeap(weapon) && wpnGroup == WEAPS_BLASTER) + || wpnGroup == WEAPS_ALL) { - weapon = NPCInfo->weaponOrder[n]; + return qtrue; + } - if ( weapon == WP_NONE ) - { - break; - } + return qfalse; +} - if ( !HaveWeapon( weapon ) ) - { - continue; - } +struct wpnList { //a helper struct + int list[MAX_WEAPONS]; + int listSize = 0; +}; + +extern int allWeaponOrder[]; +wpnList getWeaponList(gentity_t *ent, int wpnGroup) +{//give a list of what weapons this client has + int i = 0; + int weapon; + wpnList w; + + for (int n = 0; n < MAX_WEAPONS; n++) + { + weapon = allWeaponOrder[n]; - if ( client->ps.ammo[weaponData[weapon].ammoIndex] ) + if (HaveWeapon(ent, weapon) && WeaponInGroup(weapon, wpnGroup)) { - return weapon; + w.list[i] = weapon; + w.listSize++; + i++; } } - // check weapons serially (mainly in case a weapon is not on the NPC's list) - for ( weapon = 1; weapon < WP_NUM_WEAPONS; weapon++ ) + while (i < MAX_WEAPONS) { //just incase + w.list[i] = WP_NONE; + i++; + } + + return w; +} + +int ChooseWeaponRandom(gentity_t *ent, int wpnGroup) +{ + wpnList w = getWeaponList(ent, wpnGroup); + + if (w.listSize) + return w.list[Q_irand(0, w.listSize)]; + else + return WP_NONE; +} + +int ChooseBestWeapon(gentity_t* ent) +{ + int n; + int weapon; + + // check weapons in the NPC's weapon preference order + for (n = 0; n < MAX_WEAPONS; n++) { - if ( !HaveWeapon( weapon ) ) + weapon = allWeaponOrder[n]; + + if (!HaveWeapon(ent, weapon)) { continue; } - if ( client->ps.ammo[weaponData[weapon].ammoIndex] ) - { - return weapon; - } + return weapon; } - return client->ps.weapon; + return WP_NONE; } -*/ void ChangeWeapon( gentity_t *ent, int newWeapon ) { @@ -900,13 +969,6 @@ void ChangeWeapon( gentity_t *ent, int newWeapon ) ent->NPC->burstSpacing = 2000;//2 seconds ent->NPC->attackHold = 1000;//Hold attack button for a 1-second burst break; - - case WP_TRICORDER: - ent->NPC->aiFlags |= NPCAI_BURST_WEAPON; - ent->NPC->burstMin = 5; - ent->NPC->burstMax = 30; - ent->NPC->burstSpacing = 1000; - break; */ case WP_BLASTER: @@ -939,6 +1001,17 @@ void ChangeWeapon( gentity_t *ent, int newWeapon ) break; case WP_MELEE: + ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON; + ent->NPC->burstSpacing = 1000;//attackdebounce + if (ent->NPC->aiFlags & NPCAI_HEAVY_MELEE) + { //heavy melee guys punch a bit slower + ent->NPC->burstSpacing = 1000;//attackdebounce + } + else + { //regular melee guys punch faster but weaker + ent->NPC->burstSpacing = 500;//attackdebounce + } + break; case WP_TUSKEN_STAFF: ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON; ent->NPC->burstSpacing = 1000;//attackdebounce @@ -1022,11 +1095,67 @@ void ChangeWeapon( gentity_t *ent, int newWeapon ) else ent->NPC->burstSpacing = 750;//attack debounce break; + + case WP_E5_CARBINE: + ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON; + if ( g_spskill->integer == 0 ) + ent->NPC->burstSpacing = 1000;//attack debounce + else if ( g_spskill->integer == 1 ) + ent->NPC->burstSpacing = 750;//attack debounce + else + ent->NPC->burstSpacing = 500;//attack debounce + break; + + case WP_DC15S_CARBINE: + ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON; + if ( g_spskill->integer == 0 ) + ent->NPC->burstSpacing = 1000;//attack debounce + else if ( g_spskill->integer == 1 ) + ent->NPC->burstSpacing = 750;//attack debounce + else + ent->NPC->burstSpacing = 500;//attack debounce + break; + + case WP_Z6_ROTARY: + ent->NPC->aiFlags |= NPCAI_BURST_WEAPON; + ent->NPC->burstMin = 3; +#ifdef BASE_SAVE_COMPAT + ent->NPC->burstMean = 6; +#endif + ent->NPC->burstMax = 10; + if ( g_spskill->integer == 0 ) + ent->NPC->burstSpacing = 1500;//attack debounce + else if ( g_spskill->integer == 1 ) + ent->NPC->burstSpacing = 1000;//attack debounce + else + ent->NPC->burstSpacing = 500;//attack debounce + break; + + case WP_SONIC_BLASTER: + ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON; + ent->NPC->burstSpacing = 1000;//attackdebounce + break; + + case WP_DC15A_RIFLE: + ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON; + if ( g_spskill->integer == 0 ) + ent->NPC->burstSpacing = 1000;//attack debounce + else if ( g_spskill->integer == 1 ) + ent->NPC->burstSpacing = 750;//attack debounce + else + ent->NPC->burstSpacing = 500;//attack debounce + break; + default: ent->NPC->aiFlags &= ~NPCAI_BURST_WEAPON; break; } + + if (ent->NPC->scriptFlags&SCF_ALT_FIRE) + ent->NPC->burstSpacing *= weaponData[newWeapon].npcAltFireTimeMult; + else + ent->NPC->burstSpacing *= weaponData[newWeapon].npcFireTimeMult; } void NPC_ChangeWeapon( int newWeapon ) @@ -1046,10 +1175,37 @@ void NPC_ChangeWeapon( int newWeapon ) if ( NPC->client->ps.weapon == WP_SABER ) { WP_SaberAddG2SaberModels( NPC ); + G_RemoveHolsterModels( NPC ); + } + else + { + G_CreateG2AttachedWeaponModel( NPC, weaponData[NPC->client->ps.weapon].worldModel, NPC->handRBolt, 0 ); + WP_SaberAddHolsteredG2SaberModels( NPC ); + } + } +} + +void NPC_ChangeWeapon(gentity_t* NPC, int newWeapon) +{ + qboolean changing = qfalse; + if (newWeapon != NPC->client->ps.weapon) + { + changing = qtrue; + } + if (changing) + { + G_RemoveWeaponModels(NPC); + } + ChangeWeapon(NPC, newWeapon); + if (changing && NPC->client->ps.weapon != WP_NONE) + { + if (NPC->client->ps.weapon == WP_SABER) + { + WP_SaberAddG2SaberModels(NPC); } else { - G_CreateG2AttachedWeaponModel( NPC, weaponData[NPC->client->ps.weapon].weaponMdl, NPC->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel(NPC, weaponData[NPC->client->ps.weapon].weaponMdl, NPC->handRBolt, 0); } } } @@ -1202,10 +1358,11 @@ void WeaponThink( qboolean inCombat ) } // can't shoot while shield is up - if (NPC->flags&FL_SHIELDED && NPC->client->NPC_class==CLASS_ASSASSIN_DROID) + /*if (NPC->flags&FL_SHIELDED && NPC->client->NPC_class==CLASS_ASSASSIN_DROID) { return; - } + }*/ + //now we can? - Dusty // Can't Fire While Cloaked if (NPC->client && @@ -1251,7 +1408,7 @@ HaveWeapon qboolean HaveWeapon( int weapon ) { - return ( client->ps.stats[STAT_WEAPONS] & ( 1 << weapon ) ); + return ( (qboolean)(client->ps.weapons[weapon] != 0) ); } qboolean EntIsGlass (gentity_t *check) @@ -1334,7 +1491,7 @@ qboolean CanShoot ( gentity_t *ent, gentity_t *shooter ) //Actually, we should just check to fire in dir we're facing and if it's close enough, //and we didn't hit someone on our own team, shoot VectorSubtract(spot, tr.endpos, diff); - if(VectorLength(diff) < random() * 32) + if(VectorLength(diff) < Q_flrand(0.0f, 1.0f) * 32) { return qtrue; } @@ -1444,11 +1601,6 @@ int NPC_AttackDebounceForWeapon (void) case WP_SABER: return 100; break; - - - case WP_TRICORDER: - return 0;//tricorder - break; */ case WP_SABER: if ( NPC->client->NPC_class == CLASS_KYLE @@ -1516,11 +1668,6 @@ float NPC_MaxDistSquaredForWeapon (void) case WP_SABER: return 1024 * 1024; break; - - - case WP_TRICORDER: - return 0;//tricorder - break; */ case WP_SABER: if ( NPC->client && NPC->client->ps.SaberLength() ) @@ -1584,7 +1731,7 @@ You can mix and match any of those options (example: find closest visible player FIXME: this should go through the snapshot and find the closest enemy */ -gentity_t *NPC_PickEnemy( gentity_t *closestTo, int enemyTeam, qboolean checkVis, qboolean findPlayersFirst, qboolean findClosest ) +gentity_t *NPC_PickEnemy(gentity_t *closestTo, int enemyTeam, qboolean checkVis, qboolean findPlayersFirst, qboolean findClosest) { int num_choices = 0; int choice[128];//FIXME: need a different way to determine how many choices? @@ -1877,6 +2024,151 @@ gentity_t *NPC_PickEnemy( gentity_t *closestTo, int enemyTeam, qboolean checkVis return &g_entities[ choice[rand() % num_choices] ]; } +//returns the number of enemies around an NPC + +int NPC_CheckMultipleEnemies(gentity_t *closestTo, int enemyTeam, qboolean checkVis) +{ + int num_choices = 0; + int choice[128];//FIXME: need a different way to determine how many choices? + gentity_t *newenemy = NULL; + gentity_t *closestEnemy = NULL; + int entNum; + vec3_t diff; + float relDist; + float bestDist = Q3_INFINITE; + qboolean failed = qfalse; + int visChecks = (CHECK_360 | CHECK_FOV | CHECK_VISRANGE); + int minVis = VIS_FOV; + + if (enemyTeam == TEAM_NEUTRAL) + { + return NULL; + } + + if (NPCInfo->behaviorState == BS_STAND_AND_SHOOT || + NPCInfo->behaviorState == BS_HUNT_AND_KILL) + {//Formations guys don't require inFov to pick up a target + //These other behavior states are active battle states and should not + //use FOV. FOV checks are for enemies who are patrolling, guarding, etc. + visChecks &= ~CHECK_FOV; + minVis = VIS_360; + } + + /* + //FIXME: used to have an option to look *only* for the player... now...? Still need it? + if ( enemyTeam == TEAM_PLAYER ) + {//couldn't find the player + return NULL; + } + */ + + num_choices = 0; + bestDist = Q3_INFINITE; + closestEnemy = NULL; + + for (entNum = 0; entNum < globals.num_entities; entNum++) + { + newenemy = &g_entities[entNum]; + + if (newenemy != NPC && (newenemy->client || newenemy->svFlags & SVF_NONNPC_ENEMY) && !(newenemy->flags & FL_NOTARGET) && !(newenemy->s.eFlags & EF_NODRAW)) + { + if (newenemy->health > 0) + { + if ((newenemy->client && NPC_ValidEnemy(newenemy)) + || (!newenemy->client && newenemy->noDamageTeam == enemyTeam)) + {//FIXME: check for range and FOV or vis? + if (NPC->client->playerTeam == TEAM_PLAYER && enemyTeam == TEAM_PLAYER) + {//player allies turning on ourselves? How? + if (newenemy->s.number) + {//only turn on the player, not other player allies + continue; + } + } + + if (newenemy != NPC->lastEnemy) + {//Make sure we're not just going back and forth here + if (!gi.inPVS(newenemy->currentOrigin, NPC->currentOrigin)) + { + continue; + } + + if (NPCInfo->behaviorState == BS_INVESTIGATE || NPCInfo->behaviorState == BS_PATROL) + { + if (!NPC->enemy) + { + if (!InVisrange(newenemy)) + { + continue; + } + else if (NPC_CheckVisibility(newenemy, CHECK_360 | CHECK_FOV | CHECK_VISRANGE) != VIS_FOV) + { + continue; + } + } + } + + VectorSubtract(closestTo->currentOrigin, newenemy->currentOrigin, diff); + relDist = VectorLengthSquared(diff); + if (newenemy->client && newenemy->client->hiddenDist > 0) + { + if (relDist > newenemy->client->hiddenDist*newenemy->client->hiddenDist) + { + //out of hidden range + if (VectorLengthSquared(newenemy->client->hiddenDir)) + {//They're only hidden from a certain direction, check + float dot; + + VectorNormalize(diff); + dot = DotProduct(newenemy->client->hiddenDir, diff); + if (dot > 0.5) + {//I'm not looking in the right dir toward them to see them + continue; + } + else + { + Debug_Printf(debugNPCAI, DEBUG_LEVEL_INFO, "%s saw %s trying to hide - hiddenDir %s targetDir %s dot %f\n", NPC->targetname, newenemy->targetname, vtos(newenemy->client->hiddenDir), vtos(diff), dot); + } + } + else + { + continue; + } + } + else + { + Debug_Printf(debugNPCAI, DEBUG_LEVEL_INFO, "%s saw %s trying to hide - hiddenDist %f\n", NPC->targetname, newenemy->targetname, newenemy->client->hiddenDist); + } + } + + if (!NPC_EnemyTooFar(newenemy, 0, qfalse)) + { + if (checkVis) + { + //if( NPC_CheckVisibility ( newenemy, CHECK_360|CHECK_FOV|CHECK_VISRANGE ) == VIS_FOV ) + if (NPC_CheckVisibility(newenemy, CHECK_360 | CHECK_VISRANGE) >= VIS_360) + { + choice[num_choices++] = newenemy->s.number; + } + } + else + { + choice[num_choices++] = newenemy->s.number; + } + } + } + } + } + } + } + + if (!num_choices) + { + return 0; + } + + return num_choices; +} + /* gentity_t *NPC_PickAlly ( void ) @@ -1974,6 +2266,33 @@ gentity_t *NPC_PickAlly ( qboolean facingEachOther, float range, qboolean ignore return closestAlly; } +qboolean NPC_JediClassGoodGuy(class_t npc_class) { + switch (npc_class) { + case CLASS_JEDI: + case CLASS_LUKE: + case CLASS_KYLE: + case CLASS_MORGANKATARN: + return qtrue; + default: + return qfalse; + } +} + +qboolean NPC_CheckAttackSurrenderedEnemy(gentity_t *NPC) +{ + if (NPC->enemy + && NPC->enemy->NPC + && (NPC->enemy->NPC->surrenderTime > level.time || NPC->enemy->s.weapon == WP_NONE + || (NPC->enemy->s.weapon == WP_MELEE && !NPC->enemy)) + && NPC->client->playerTeam == TEAM_PLAYER + && NPC_JediClassGoodGuy(NPC->client->NPC_class)) + {//enemy is surrendering or not attacking and I'm a good guy Jedi + return qfalse; + } + + return qtrue; +} + gentity_t *NPC_CheckEnemy( qboolean findNew, qboolean tooFarOk, qboolean setEnemy ) { qboolean forcefindNew = qfalse; @@ -2193,6 +2512,13 @@ gentity_t *NPC_CheckEnemy( qboolean findNew, qboolean tooFarOk, qboolean setEnem } } } + + /*if (!NPC_CheckAttackSurrenderedEnemy(NPC)) + { + G_ClearEnemy(NPC); + return NULL; + }*/ + return newEnemy; } @@ -2347,7 +2673,7 @@ qboolean NPC_CheckDefend (float scale) if(!scale) scale = 1.0; - if((float)(NPCInfo->stats.evasion) > random() * 4 * scale) + if((float)(NPCInfo->stats.evasion) > Q_flrand(0.0f, 1.0f) * 4 * scale) return qtrue; return qfalse; @@ -2522,13 +2848,13 @@ qboolean NPC_CheckCanAttack (float attack_scale, qboolean stationary) VectorMA ( muzzle, distanceToEnemy, forward, hitspot); VectorSubtract(hitspot, enemy_org, diff); aim_off = VectorLength(diff); - if(aim_off > random() * max_aim_off)//FIXME: use aim value to allow poor aim? + if(aim_off > Q_flrand(0.0f, 1.0f) * max_aim_off)//FIXME: use aim value to allow poor aim? { attack_scale *= 0.75; //see if where we're going to shoot is too far from his head VectorSubtract(hitspot, enemy_org, diff); aim_off = VectorLength(diff); - if(aim_off > random() * max_aim_off) + if(aim_off > Q_flrand(0.0f, 1.0f) * max_aim_off) { attack_ok = qfalse; } @@ -2585,10 +2911,6 @@ float IdealDistance ( gentity_t *self ) ideal += 50; break; -/* case WP_TRICORDER: - ideal = 0; - break; -*/ case WP_SABER: case WP_BRYAR_PISTOL: case WP_BLASTER_PISTOL: @@ -2794,7 +3116,7 @@ int NPC_FindCombatPoint( const vec3_t position, const vec3_t avoidPosition, vec3 } NPC_CollectCombatPoints( destPosition, collRad, points, flags );//position - for ( cpi = points.begin(); cpi != points.end(); cpi++ ) + for ( cpi = points.begin(); cpi != points.end(); ++cpi ) { const int i = (*cpi).second; diff --git a/code/game/NPC_goal.cpp b/code/game/NPC_goal.cpp index f82fb87d6a..871eefdf6d 100644 --- a/code/game/NPC_goal.cpp +++ b/code/game/NPC_goal.cpp @@ -161,7 +161,7 @@ qboolean ReachedGoal( gentity_t *goal ) NPCInfo->aiFlags &= ~NPCAI_TOUCHED_GOAL; return qtrue; } - return STEER::Reached(NPC, goal, NPCInfo->goalRadius, !!FlyingCreature(NPC)); + return (qboolean)STEER::Reached(NPC, goal, NPCInfo->goalRadius, FlyingCreature(NPC) != qfalse); } /* diff --git a/code/game/NPC_move.cpp b/code/game/NPC_move.cpp index da51cef715..7e6212568f 100644 --- a/code/game/NPC_move.cpp +++ b/code/game/NPC_move.cpp @@ -345,6 +345,7 @@ void NPC_JumpAnimation() int jumpAnim = BOTH_JUMP1; if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA || (NPC->client->NPC_class == CLASS_REBORN && NPC->s.weapon != WP_SABER) || NPC->client->NPC_class == CLASS_ROCKETTROOPER ||( NPCInfo->rank != RANK_CREWMAN && NPCInfo->rank <= RANK_LT_JG ) ) @@ -374,6 +375,7 @@ void NPC_JumpSound() //FIXME: can I delay the actual jump so that it matches the anim...? } else if ( NPC->client->NPC_class == CLASS_BOBAFETT + || NPC->client->NPC_class == CLASS_MANDA || NPC->client->NPC_class == CLASS_ROCKETTROOPER ) { // does this really need to be here? @@ -612,7 +614,7 @@ qboolean NPC_JumpBackingUp() NPCInfo->jumpBackupTime = 0; return NPC_TryJump(); } - return false; + return qfalse; } @@ -821,7 +823,7 @@ qboolean NPC_MoveToGoal( qboolean tryStraight ) //FIXME: tryStraight not even us #if AI_TIMERS navTime += GetTime( startTime ); #endif// AI_TIMERS - return moveSuccess; + return (qboolean)moveSuccess; } /* diff --git a/code/game/NPC_reactions.cpp b/code/game/NPC_reactions.cpp index 22320d9b23..f420179335 100644 --- a/code/game/NPC_reactions.cpp +++ b/code/game/NPC_reactions.cpp @@ -144,7 +144,7 @@ static void NPC_CheckAttacker( gentity_t *other, int mod ) } //Randomly pick up the target - if ( random() > luckThreshold ) + if ( Q_flrand(0.0f, 1.0f) > luckThreshold ) { G_ClearEnemy( other ); other->enemy = NPC; @@ -158,14 +158,10 @@ void NPC_SetPainEvent( gentity_t *self ) { if ( !self->NPC || !(self->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) ) { - // no more borg - // if( self->client->playerTeam != TEAM_BORG ) - // { - if ( !Q3_TaskIDPending( self, TID_CHAN_VOICE ) ) - { - G_AddEvent( self, EV_PAIN, floor((float)self->health/self->max_health*100.0f) ); - } - // } + if ( !Q3_TaskIDPending( self, TID_CHAN_VOICE ) ) + { + G_AddEvent( self, EV_PAIN, floor((float)self->health/self->max_health*100.0f) ); + } } } @@ -227,8 +223,8 @@ void NPC_ChoosePainAnimation( gentity_t *self, gentity_t *other, const vec3_t po } int pain_anim = -1; - float pain_chance; - + float pain_chance; + int punchImmuneTime = 0; if ( self->s.weapon == WP_THERMAL && self->client->fireDelay > 0 ) {//don't interrupt thermal throwing anim return; @@ -282,7 +278,16 @@ void NPC_ChoosePainAnimation( gentity_t *self, gentity_t *other, const vec3_t po } else if ( mod == MOD_MELEE ) {//higher in rank (skill) we are, less likely we are to be fazed by a punch - pain_chance = 1.0f - ((RANK_CAPTAIN-self->NPC->rank)/(float)RANK_CAPTAIN); + /*if (NPC->painDebounceTime < level.time - 350 * self->NPC->rank) + {//strong enemies are immune for a short time after being staggered by a punch + pain_chance = 0.0f; + } + else + { + pain_chance = 1.0f - ((self->NPC->rank) / (float)(RANK_CAPTAIN + 5)); + }*/ + + pain_chance = 1.0f - ((self->NPC->rank) / (float)(RANK_CAPTAIN + 2)); } else if ( self->client->NPC_class == CLASS_PROTOCOL ) { @@ -299,7 +304,7 @@ void NPC_ChoosePainAnimation( gentity_t *self, gentity_t *other, const vec3_t po } //See if we're going to flinch - if ( random() < pain_chance ) + if ( Q_flrand(0.0f, 1.0f) < pain_chance ) { //Pick and play our animation if ( (self->client->ps.eFlags&EF_FORCE_GRIPPED) ) @@ -483,7 +488,7 @@ void NPC_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const ve } else if ( self->NPC && !other->s.number )//should be assumed, but... {//dammit, stop that! - if ( self->NPC->charmedTime > level.time ) + if ( self->NPC->charmedTime > level.time || self->NPC->darkCharmedTime > level.time ) {//mindtricked return; } @@ -537,9 +542,20 @@ void NPC_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const ve if ( NPCInfo->ignorePain == qfalse ) { NPCInfo->confusionTime = 0;//clear any charm or confusion, regardless + if (NPCInfo->insanityTime && NPCInfo->insanityTime > level.time) + { + NPCInfo->insanityTime = 0; + NPC->client->ps.torsoAnimTimer = 0; + NPC->client->ps.weaponTime -= level.time - NPCInfo->insanityTime; + if (NPC->client->ps.weaponTime < 0) + { + NPC->client->ps.weaponTime = 0; + } + } if ( NPC->ghoul2.size() && NPC->headBolt != -1 ) { G_StopEffect("force/confusion", NPC->playerModel, NPC->headBolt, NPC->s.number ); + G_StopEffect("force/drain_hand", NPC->playerModel, NPC->headBolt, NPC->s.number ); } if ( damage != -1 ) {//-1 == don't play pain anim @@ -573,7 +589,10 @@ void NPC_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const ve G_UseTargets2(self, other, self->paintarget); } - if (self->client && self->client->NPC_class==CLASS_BOBAFETT) + if (self->client && + (self->client->NPC_class==CLASS_BOBAFETT + || self->client->NPC_class == CLASS_MANDA + || self->client->NPC_class == CLASS_COMMANDO)) { Boba_Pain( self, inflictor, damage, mod); } @@ -989,7 +1008,7 @@ void NPC_Respond( gentity_t *self, int userNum ) if ( event != -1 ) { //hack here because we reuse some "combat" and "extra" sounds - qboolean addFlag = (self->NPC->scriptFlags&SCF_NO_COMBAT_TALK); + qboolean addFlag = (qboolean)((self->NPC->scriptFlags&SCF_NO_COMBAT_TALK) != 0); self->NPC->scriptFlags &= ~SCF_NO_COMBAT_TALK; G_AddVoiceEvent( self, event, 3000 ); diff --git a/code/game/NPC_senses.cpp b/code/game/NPC_senses.cpp index 2e3fc0baa5..8b25531d31 100644 --- a/code/game/NPC_senses.cpp +++ b/code/game/NPC_senses.cpp @@ -120,7 +120,7 @@ qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold dot = DotProduct( dir, forward ); - return (dot > threshHold); + return (qboolean)(dot > threshHold); } float DotToSpot( vec3_t spot, vec3_t from, vec3_t fromAngles ) @@ -599,7 +599,7 @@ qboolean G_RememberAlertEvent( gentity_t *self, int alertIndex ) if (at.owner==self) {//don't care about events that I made - return false; + return qfalse; } self->NPC->lastAlertID = at.ID; @@ -916,7 +916,7 @@ qboolean RemoveOldestAlert( void ) //make sure this never drops below zero... if it does, something very very bad happened assert( level.numAlertEvents >= 0 ); //return true is have room for one now - return (level.numAlertEventsNPC->scriptFlags |= SCF_DONT_FLEE; break; default: @@ -240,15 +243,18 @@ void G_ClassSetDontFlee( gentity_t *self ) extern void Vehicle_Register(gentity_t *ent); extern void RT_FlyStart( gentity_t *self ); extern void SandCreature_ClearTimers( gentity_t *ent ); +void NPC_SetMiscDefaultData(gentity_t *ent) +extern void NPC_GalakMech_Init( gentity_t *ent ); + void NPC_SetMiscDefaultData( gentity_t *ent ) { - if ( ent->spawnflags & SFB_CINEMATIC ) + if (ent->spawnflags & SFB_CINEMATIC) {//if a cinematic guy, default us to wait bState ent->NPC->behaviorState = BS_CINEMATIC; } - if ( ent->client->NPC_class == CLASS_RANCOR ) + if (ent->client->NPC_class == CLASS_RANCOR) { - if ( Q_stricmp( "mutant_rancor", ent->NPC_type ) == 0 ) + if (Q_stricmp("mutant_rancor", ent->NPC_type) == 0) { ent->spawnflags |= 1;//just so I know it's a mutant rancor as opposed to a normal one ent->NPC->aiFlags |= NPCAI_NAV_THROUGH_BREAKABLES; @@ -261,37 +267,38 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) } ent->flags |= FL_NO_KNOCKBACK; } - else if ( ent->client->NPC_class == CLASS_SAND_CREATURE ) + else if (ent->client->NPC_class == CLASS_SAND_CREATURE) {//??? - ent->clipmask = CONTENTS_SOLID|CONTENTS_MONSTERCLIP;//it can go through others + ent->clipmask = CONTENTS_SOLID | CONTENTS_MONSTERCLIP;//it can go through others ent->contents = 0;//can't be hit? ent->takedamage = qfalse;//can't be killed ent->flags |= FL_NO_KNOCKBACK; - SandCreature_ClearTimers( ent ); + SandCreature_ClearTimers(ent); } - else if ( ent->client->NPC_class == CLASS_BOBAFETT ) + else if (ent->client->NPC_class == CLASS_BOBAFETT) {//set some stuff, precache - ent->client->ps.forcePowersKnown |= ( 1 << FP_LEVITATION ); + ent->client->ps.forcePowersKnown |= (1 << FP_LEVITATION); ent->client->ps.forcePowerLevel[FP_LEVITATION] = FORCE_LEVEL_3; - ent->client->ps.forcePower = 100; - ent->NPC->scriptFlags |= (SCF_NAV_CAN_FLY|SCF_FLY_WITH_JET|SCF_NAV_CAN_JUMP); - NPC->flags |= FL_UNDYING; // Can't Kill Boba + ent->client->ps.forcePower = 100; + ent->NPC->scriptFlags |= (SCF_NAV_CAN_FLY | SCF_FLY_WITH_JET | SCF_NAV_CAN_JUMP); + if (!Q_stricmp("boba_fett", ent->NPC_type)) + NPC->flags |= FL_UNDYING; // Can't Kill Boba } - else if ( ent->client->NPC_class == CLASS_ROCKETTROOPER ) + else if (ent->client->NPC_class == CLASS_ROCKETTROOPER) {//set some stuff, precache - ent->client->ps.forcePowersKnown |= ( 1 << FP_LEVITATION ); + ent->client->ps.forcePowersKnown |= (1 << FP_LEVITATION); ent->client->ps.forcePowerLevel[FP_LEVITATION] = FORCE_LEVEL_3; ent->client->ps.forcePower = 100; - ent->NPC->scriptFlags |= (SCF_NAV_CAN_FLY|SCF_FLY_WITH_JET|SCF_NAV_CAN_JUMP);//no groups, no combat points! - if ( Q_stricmp( "rockettrooper2Officer", ent->NPC_type ) == 0 ) + ent->NPC->scriptFlags |= (SCF_NAV_CAN_FLY | SCF_FLY_WITH_JET | SCF_NAV_CAN_JUMP);//no groups, no combat points! + if (Q_stricmp("rockettrooper2Officer", ent->NPC_type) == 0) {//start in the air, use spotlight //ent->NPC->scriptFlags |= SCF_NO_GROUPS; ent->NPC->scriptFlags &= ~SCF_FLY_WITH_JET; - RT_FlyStart( ent ); - NPC_SetMoveGoal( ent, ent->currentOrigin, 16, qfalse, -1, NULL ); - VectorCopy( ent->currentOrigin, ent->pos1 ); + RT_FlyStart(ent); + NPC_SetMoveGoal(ent, ent->currentOrigin, 16, qfalse, -1, NULL); + VectorCopy(ent->currentOrigin, ent->pos1); } - if ( (ent->spawnflags&2) ) + if ((ent->spawnflags & 2)) {//spotlight ent->client->ps.eFlags |= EF_SPOTLIGHT; } @@ -300,126 +307,163 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) { ent->flags |= FL_NO_KNOCKBACK; } - else if ( ent->client->NPC_class == CLASS_SABOTEUR ) + else if (ent->client->NPC_class == CLASS_SABOTEUR) {//can cloak ent->NPC->aiFlags |= NPCAI_SHIELDS;//give them the ability to cloak - if ( (ent->spawnflags&16) ) + if ((ent->spawnflags & 16)) {//start cloaked - Saboteur_Cloak( ent ); + Saboteur_Cloak(ent); } } - else if ( ent->client->NPC_class == CLASS_ASSASSIN_DROID ) + else if (ent->client->NPC_class == CLASS_ASSASSIN_DROID) { ent->client->ps.stats[STAT_ARMOR] = 250; // start with full armor - if (ent->s.weapon==WP_BLASTER) + if (ent->s.weapon == WP_BLASTER) { ent->NPC->scriptFlags |= SCF_ALT_FIRE; } ent->flags |= (FL_NO_KNOCKBACK); } - if (ent->spawnflags&4096) + if (ent->spawnflags & 4096) { ent->NPC->scriptFlags |= SCF_NO_GROUPS;//don't use combat points or group AI } - if ( Q_stricmp( "DKothos", ent->NPC_type ) == 0 - || Q_stricmp( "VKothos", ent->NPC_type ) == 0 ) + if (Q_stricmp("DKothos", ent->NPC_type) == 0 + || Q_stricmp("VKothos", ent->NPC_type) == 0) { ent->NPC->scriptFlags |= SCF_DONT_FIRE; ent->NPC->aiFlags |= NPCAI_HEAL_ROSH; ent->count = 100; } - else if ( Q_stricmp( "rosh_dark", ent->NPC_type ) == 0 ) + else if (Q_stricmp("rosh_dark", ent->NPC_type) == 0) { ent->NPC->aiFlags |= NPCAI_ROSH; } - if ( Q_stricmpn( ent->NPC_type, "hazardtrooper", 13 ) == 0 ) + if (Q_stricmpn(ent->NPC_type, "hazardtrooper", 13) == 0) {//hazard trooper ent->NPC->scriptFlags |= SCF_NO_GROUPS;//don't use combat points or group AI - ent->flags |= (FL_SHIELDED|FL_NO_KNOCKBACK);//low-level shots bounce off, no knockback + ent->flags |= (FL_SHIELDED | FL_NO_KNOCKBACK);//low-level shots bounce off, no knockback } - if ( !Q_stricmp( "Yoda", ent->NPC_type ) ) + if (!Q_stricmp("Yoda", ent->NPC_type)) {//FIXME: extern this into NPC.cfg? ent->NPC->scriptFlags |= SCF_NO_FORCE;//force powers don't work on him ent->NPC->aiFlags |= NPCAI_BOSS_CHARACTER; } - if ( !Q_stricmp( "emperor", ent->NPC_type ) - || !Q_stricmp( "cultist_grip", ent->NPC_type ) - || !Q_stricmp( "cultist_drain", ent->NPC_type ) - || !Q_stricmp( "cultist_lightning", ent->NPC_type )) + if (!Q_stricmp("emperor", ent->NPC_type) + || !Q_stricmp("cultist_grip", ent->NPC_type) + || !Q_stricmp("cultist_drain", ent->NPC_type) + || !Q_stricmp("cultist_lightning", ent->NPC_type) + || ent->client->NPC_class == CLASS_CULTIST) {//FIXME: extern this into NPC.cfg? ent->NPC->scriptFlags |= SCF_DONT_FIRE;//so he uses only force powers } - if (!Q_stricmp( "Rax", ent->NPC_type ) ) + if (!Q_stricmp("Rax", ent->NPC_type)) { ent->NPC->scriptFlags |= SCF_DONT_FLEE; } - if ( !Q_stricmp( "cultist_destroyer", ent->NPC_type ) ) + if (!Q_stricmp("cultist_destroyer", ent->NPC_type)) { ent->splashDamage = 1000; ent->splashRadius = 384; //FIXME: precache these! - ent->fxID = G_EffectIndex( "force/destruction_exp" ); - ent->NPC->scriptFlags |= (SCF_DONT_FLEE|SCF_IGNORE_ALERTS); + ent->fxID = G_EffectIndex("force/destruction_exp"); + ent->NPC->scriptFlags |= (SCF_DONT_FLEE | SCF_IGNORE_ALERTS); ent->NPC->ignorePain = qtrue; } - if ( Q_stricmp( "chewie", ent->NPC_type ) ) + if (!Q_stricmp("chewie", ent->NPC_type) + || ent->client->NPC_class == CLASS_WOOKIEE + || ent->client->NPC_class == CLASS_GRAN + || ent->client->NPC_class == CLASS_TRANDOSHAN) { //in case chewie ever loses his gun... + //or if Trando or Gran want to get rough ent->NPC->aiFlags |= NPCAI_HEAVY_MELEE; } //================== - if ( ent->client->ps.saber[0].type != SABER_NONE - && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) ) + if (ent->client->ps.saber[0].type != SABER_NONE + && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON) || !ent->weaponModel[0])) {//if I'm equipped with a saber, initialize it (them) ent->client->ps.SaberDeactivate(); - ent->client->ps.SetSaberLength( 0 ); - WP_SaberInitBladeData( ent ); - if ( ent->client->ps.weapon == WP_SABER ) + ent->client->ps.SetSaberLength(0); + WP_SaberInitBladeData(ent); + if (ent->client->ps.weapon == WP_SABER) {//this is our current weapon, add the models now WP_SaberAddG2SaberModels( ent ); + G_RemoveHolsterModels( ent ); } - Jedi_ClearTimers( ent ); + Jedi_ClearTimers(ent); } - if ( ent->client->ps.forcePowersKnown != 0 ) + if (ent->client->ps.forcePowersKnown != 0) { - WP_InitForcePowers( ent ); + WP_InitForcePowers(ent); if (ent->client->ps.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) { ent->NPC->scriptFlags |= SCF_NAV_CAN_JUMP; // anyone who has any force jump can jump } } - if ( ent->client->NPC_class == CLASS_HOWLER ) + if (ent->client->NPC_class == CLASS_HOWLER) { - Howler_ClearTimers( ent ); + Howler_ClearTimers(ent); ent->NPC->scriptFlags |= SCF_NO_FALLTODEATH; ent->flags |= FL_NO_IMPACT_DMG; ent->NPC->scriptFlags |= SCF_NAV_CAN_JUMP; // These jokers can jump } - if ( ent->client->NPC_class == CLASS_DESANN + if (ent->client->NPC_class == CLASS_DESANN || ent->client->NPC_class == CLASS_TAVION || ent->client->NPC_class == CLASS_LUKE || ent->client->NPC_class == CLASS_KYLE - || Q_stricmp("tavion_scepter", ent->NPC_type ) == 0 - || Q_stricmp("alora_dual", ent->NPC_type ) == 0 ) + || Q_stricmp("tavion_scepter", ent->NPC_type) == 0 + || Q_stricmp("alora_dual", ent->NPC_type) == 0) { ent->NPC->aiFlags |= NPCAI_BOSS_CHARACTER; } - else if ( Q_stricmp( "alora", ent->NPC_type ) == 0 - || Q_stricmp( "rosh_dark", ent->NPC_type ) == 0 ) + else if (Q_stricmp("alora", ent->NPC_type) == 0 + || Q_stricmp("rosh_dark", ent->NPC_type) == 0) { ent->NPC->aiFlags |= NPCAI_SUBBOSS_CHARACTER; } - if ( ent->client->NPC_class == CLASS_TUSKEN ) + if (ent->client->NPC_class == CLASS_TUSKEN) { - if ( g_spskill->integer > 1 ) + if (g_spskill->integer > 1) {//on hard, tusken raiders are faster than you ent->NPC->stats.runSpeed = 280; ent->NPC->stats.walkSpeed = 65; } } + + //new checks - Dusty + /* + if (ent->NPC) + { + if (ent->NPC->stats.rareFire || ent->NPC->stats.dontFire) + { + ent->NPC->scriptFlags |= SCF_DONT_FIRE; + } + if (ent->NPC->stats.dontFlee) + { + ent->NPC->scriptFlags |= SCF_DONT_FLEE; + } + if (ent->NPC && ent->NPC->stats.altFire) + { + ent->NPC->scriptFlags |= SCF_ALT_FIRE; + } + if (ent->NPC->stats.heavyMelee > -1) + { + if (ent->NPC->stats.heavyMelee == 0) ent->NPC->aiFlags &= ~NPCAI_HEAVY_MELEE; + else ent->NPC->aiFlags |= NPCAI_HEAVY_MELEE; + } + if (ent->NPC->stats.undying) + { + ent->flags |= FL_UNDYING; + } + } + */ + + + //***I'm not sure whether I should leave this as a TEAM_ switch, I think NPC_class may be more appropriate - dmv switch(ent->client->playerTeam) { @@ -451,12 +495,19 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) && ent->client->ps.weapon != WP_SABER //sabers done above && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) )//they do this themselves { - G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].worldModel, ent->handRBolt, 0 ); + WP_SaberAddHolsteredG2SaberModels( ent ); } switch ( ent->client->ps.weapon ) { case WP_BRYAR_PISTOL://FIXME: new weapon: imp blaster pistol case WP_BLASTER_PISTOL: + if (ent->flags&FL_DUALPISTOLS + && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON) || !ent->weaponModel[0]))//they do this themselves + {//dual blaster pistols, so add the left-hand one, too + G_CreateG2AttachedWeaponModel(ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handLBolt, 1); + } + break; case WP_DISRUPTOR: case WP_BOWCASTER: case WP_REPEATER: @@ -479,6 +530,10 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) } break; } + if ( !Q_stricmp( "galak_mech", ent->NPC_type ) ) + {//starts with armor + NPC_GalakMech_Init( ent ); + } } if ( ent->client->NPC_class == CLASS_PLAYER || ent->client->NPC_class == CLASS_VEHICLE @@ -552,7 +607,8 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) && ent->client->ps.weapon != WP_SABER//sabers done above && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) )//they do this themselves { - G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].worldModel, ent->handRBolt, 0 ); + WP_SaberAddHolsteredG2SaberModels( ent ); } switch ( ent->client->ps.weapon ) { @@ -564,7 +620,12 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) && ent->NPC->rank >= RANK_LT_COMM && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) )//they do this themselves {//dual blaster pistols, so add the left-hand one, too - G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handLBolt, 1 ); + G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].worldModel, ent->handLBolt, 1 ); + } + else if (ent->flags&FL_DUALPISTOLS + && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON) || !ent->weaponModel[0]))//they do this themselves + {//dual blaster pistols, so add the left-hand one, too + G_CreateG2AttachedWeaponModel(ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handLBolt, 1); } break; case WP_DISRUPTOR: @@ -607,13 +668,14 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) NPCInfo->scriptFlags |= SCF_PILOT; ST_ClearTimers( ent ); - if ( ent->NPC->rank >= RANK_COMMANDER ) - {//commanders use alt-fire - //ent->NPC->scriptFlags |= SCF_ALT_FIRE; + if ( ent->NPC->rank >= RANK_COMMANDER + && ent->client->NPC_class == CLASS_IMPERIAL ) + {//imp commanders and rodians with E11s use alt-fire + ent->NPC->scriptFlags |= SCF_ALT_FIRE; } - if ( !Q_stricmp( "rodian2", ent->NPC_type ) ) + if ( ent->client->NPC_class == CLASS_RODIAN ) { - //ent->NPC->scriptFlags |= SCF_ALT_FIRE; + ent->NPC->scriptFlags |= SCF_ALT_FIRE; } break; } @@ -628,12 +690,12 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) && ent->client->ps.weapon != WP_SABER//sabers done above && (!(ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON)||!ent->weaponModel[0]) )//they do this themselves { - G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].worldModel, ent->handRBolt, 0 ); + WP_SaberAddHolsteredG2SaberModels( ent ); } break; } - - + if ( ent->client->NPC_class == CLASS_ATST || ent->client->NPC_class == CLASS_MARK1 ) // chris/steve/kevin requested that the mark1 be shielded also { ent->flags |= (FL_SHIELDED|FL_NO_KNOCKBACK); @@ -648,6 +710,7 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) ent->client->NPC_class==CLASS_GLIDER || ent->client->NPC_class==CLASS_IMPWORKER || ent->client->NPC_class==CLASS_BOBAFETT || + ent->client->NPC_class == CLASS_MANDA || ent->client->NPC_class==CLASS_ROCKETTROOPER ) { @@ -659,11 +722,11 @@ void NPC_SetMiscDefaultData( gentity_t *ent ) Vehicle_Register(ent); } - if ( ent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SCEPTER] ) { if ( !ent->weaponModel[1] ) {//we have the scepter, so put it in our left hand if we don't already have a second weapon - G_CreateG2AttachedWeaponModel( ent, weaponData[WP_SCEPTER].weaponMdl, ent->handLBolt, 1 ); + G_CreateG2AttachedWeaponModel( ent, weaponData[WP_SCEPTER].worldModel, ent->handLBolt, 1 ); } ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->weaponModel[1]], "*flash"); } @@ -699,17 +762,6 @@ int NPC_WeaponsForTeam( team_t team, int spawnflags, const char *NPC_type ) switch(team) { // no longer exists -// case TEAM_BORG: -// break; - -// case TEAM_HIROGEN: -// if( Q_stricmp( "hirogenalpha", NPC_type ) == 0 ) -// return ( 1 << WP_BLASTER); - //Falls through - -// case TEAM_KLINGON: - - //NOTENOTE: Falls through // case TEAM_IMPERIAL: case TEAM_ENEMY: @@ -721,8 +773,6 @@ int NPC_WeaponsForTeam( team_t team, int spawnflags, const char *NPC_type ) // return ( 1 << WP_IMPERIAL_BLADE); //NOTENOTE: Falls through if not a knife user -// case TEAM_SCAVENGERS: -// case TEAM_MALON: //FIXME: default weapon in npc config? if ( Q_stricmpn( "stofficer", NPC_type, 9 ) == 0 ) { @@ -848,9 +898,6 @@ int NPC_WeaponsForTeam( team_t team, int spawnflags, const char *NPC_type ) case TEAM_PLAYER: -// if(spawnflags & SFB_TRICORDER) -// return ( 1 << WP_TRICORDER); - if(spawnflags & SFB_RIFLEMAN) return ( 1 << WP_REPEATER); @@ -940,12 +987,15 @@ void NPC_SetWeapons( gentity_t *ent ) int bestWeap = WP_NONE; int weapons = NPC_WeaponsForTeam( ent->client->playerTeam, ent->spawnflags, ent->NPC_type ); - ent->client->ps.stats[STAT_WEAPONS] = 0; + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + ent->client->ps.weapons[i] = 0; + } for ( int curWeap = WP_SABER; curWeap < WP_NUM_WEAPONS; curWeap++ ) { if ( (weapons & ( 1 << curWeap )) ) { - ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << curWeap ); + ent->client->ps.weapons[curWeap] = 1; RegisterItem( FindItemForWeapon( (weapon_t)(curWeap) ) ); //precache the weapon ent->NPC->currentAmmo = ent->client->ps.ammo[weaponData[curWeap].ammoIndex] = 100;//FIXME: max ammo @@ -1074,7 +1124,14 @@ void NPC_Begin (gentity_t *ent) } else { - ent->NPC->stats.health += ent->NPC->stats.health/4 * g_spskill->integer; // 100% on easy, 125% on medium, 150% on hard + if (ent->client->playerTeam == TEAM_PLAYER) + { //good guys lose health for difficulty, don't gain + ent->NPC->stats.health = (ent->NPC->stats.health * 1.5) - (ent->NPC->stats.health / 4 * g_spskill->integer); // 150% on easy, 125% on medium, 100% on hard + } + else + { //bad guys get health bonuses + ent->NPC->stats.health += ent->NPC->stats.health / 4 * g_spskill->integer; // 100% on easy, 125% on medium, 150% on hard + } } } @@ -1316,7 +1373,7 @@ void NPC_Begin (gentity_t *ent) // run a client frame to drop exactly to the floor, // initialize animations and other things memset( &ucmd, 0, sizeof( ucmd ) ); - _VectorCopy( client->pers.cmd_angles, ucmd.angles ); + VectorCopyM( client->pers.cmd_angles, ucmd.angles ); ent->client->ps.groundEntityNum = ENTITYNUM_NONE; @@ -1396,7 +1453,8 @@ void NPC_DefaultScriptFlags( gentity_t *ent ) return; } //Set up default script flags - ent->NPC->scriptFlags = (SCF_CHASE_ENEMIES|SCF_LOOK_FOR_ENEMIES); + ent->NPC->scriptFlags |= SCF_CHASE_ENEMIES; + ent->NPC->scriptFlags |= SCF_LOOK_FOR_ENEMIES; } #define MAX_SAFESPAWN_ENTS 4 @@ -1519,7 +1577,7 @@ gentity_t *NPC_Spawn_Do( gentity_t *ent, qboolean fullSpawnNow ) return NULL; } - newent->client = (gclient_s *)gi.Malloc(sizeof(gclient_s), TAG_G_ALLOC, qtrue); + newent->client = (gclient_t *)gi.Malloc(sizeof(gclient_t), TAG_G_ALLOC, qtrue); newent->svFlags |= SVF_NPC; @@ -1561,6 +1619,13 @@ gentity_t *NPC_Spawn_Do( gentity_t *ent, qboolean fullSpawnNow ) newent->message = G_NewString(ent->message);//copy the key name newent->flags |= FL_NO_KNOCKBACK;//don't fall off ledges } + + if ( ent->radarIcon && ent->radarIcon[0] ) + { + newent->svFlags |= SVF_BROADCAST; + newent->s.eFlags2 |= EF2_RADAROBJECT; + newent->s.radarIcon = G_IconIndex(ent->radarIcon); + } // If this is a vehicle we need to see what kind it is so we properlly allocate it. if ( Q_stricmp( ent->classname, "NPC_Vehicle" ) == 0 ) @@ -2046,6 +2111,11 @@ void SP_NPC_spawner( gentity_t *self) { self->svFlags |= SVF_NPC_PRECACHE; } + + if ( self->radarIcon && self->radarIcon[0] ) + { + G_IconIndex(self->radarIcon); + } //We have to load the animation.cfg now because spawnscripts are going to want to set anims and we need to know their length and if they're valid NPC_PrecacheAnimationCFG( self->NPC_type ); diff --git a/code/game/NPC_stats.cpp b/code/game/NPC_stats.cpp index 19611d82ce..2a33b126fb 100644 --- a/code/game/NPC_stats.cpp +++ b/code/game/NPC_stats.cpp @@ -88,6 +88,14 @@ stringID_table_t FPTable[] = ENUM2STRING(FP_ABSORB), ENUM2STRING(FP_DRAIN), ENUM2STRING(FP_SEE), + //new bonus powers + ENUM2STRING(FP_DESTRUCTION), + ENUM2STRING(FP_INSANITY), + ENUM2STRING(FP_STASIS), + ENUM2STRING(FP_BLINDING), + ENUM2STRING(FP_DEADLYSIGHT), + ENUM2STRING(FP_REPULSE), + ENUM2STRING(FP_INVULNERABILITY), { "", -1 } }; @@ -175,6 +183,10 @@ stringID_table_t ClassTable[] = ENUM2STRING(CLASS_ASSASSIN_DROID), ENUM2STRING(CLASS_HAZARD_TROOPER), ENUM2STRING(CLASS_VEHICLE), + ENUM2STRING(CLASS_CULTIST), + ENUM2STRING(CLASS_WOOKIEE), + ENUM2STRING(CLASS_MANDA), + { "CLASS_GALAK_MECH", CLASS_GALAKMECH }, { "", -1 } }; @@ -297,7 +309,14 @@ saber_colors_t TranslateSaberColor( const char *name ) { return ((saber_colors_t)(Q_irand( SABER_ORANGE, SABER_PURPLE ))); } - return SABER_BLUE; + float colors[3]; + Q_parseSaberColor(name, colors); + int colourArray[3]; + for (int i = 0; i < 3; i++) + { + colourArray[i] = (int)(colors[i] * 255); + } + return (saber_colors_t)((colourArray[0]) + (colourArray[1] << 8) + (colourArray[2] << 16) + (1 << 24)); } /* static int MethodNameToNumber( const char *name ) { @@ -1227,7 +1246,7 @@ void G_LoadAnimFileSet( gentity_t *ent, const char *pModelName ) } else { - Q_strncpyz( animName, GLAName, sizeof( animName ), qtrue ); + Q_strncpyz( animName, GLAName, sizeof( animName ) ); slash = strrchr( animName, '/' ); if ( slash ) { @@ -1318,7 +1337,7 @@ void NPC_PrecacheAnimationCFG( const char *NPC_type ) continue; } //must copy data out of this pointer into a different part of memory because the funcs we're about to call will call COM_ParseExt - Q_strncpyz( filename, value, sizeof( filename ), qtrue ); + Q_strncpyz( filename, value, sizeof( filename ) ); G_ParseAnimFileSet( filename ); COM_EndParseSession( ); return; @@ -1342,7 +1361,7 @@ void NPC_PrecacheAnimationCFG( const char *NPC_type ) GLAName = gi.G2API_GetAnimFileNameIndex( handle ); if ( GLAName ) { - Q_strncpyz( animName, GLAName, sizeof( animName ), qtrue ); + Q_strncpyz( animName, GLAName, sizeof( animName ) ); slash = strrchr( animName, '/' ); if ( slash ) { @@ -1351,7 +1370,7 @@ void NPC_PrecacheAnimationCFG( const char *NPC_type ) strippedName = COM_SkipPath( animName ); //must copy data out of this pointer into a different part of memory because the funcs we're about to call will call COM_ParseExt - Q_strncpyz( filename, value, sizeof( filename ), qtrue ); + Q_strncpyz( filename, value, sizeof( filename ) ); G_ParseAnimFileSet(strippedName, filename); COM_EndParseSession( ); @@ -1376,19 +1395,7 @@ void NPC_PrecacheWeapons( team_t playerTeam, int spawnflags, char *NPCtype ) CG_RegisterItemSounds( (item-bg_itemlist) ); CG_RegisterItemVisuals( (item-bg_itemlist) ); //precache the in-hand/in-world ghoul2 weapon model - - char weaponModel[64]; - - strcpy (weaponModel, weaponData[curWeap].weaponMdl); - if (char *spot = strstr(weaponModel, ".md3") ) { - *spot = 0; - spot = strstr(weaponModel, "_w");//i'm using the in view weapon array instead of scanning the item list, so put the _w back on - if (!spot) { - strcat (weaponModel, "_w"); - } - strcat (weaponModel, ".glm"); //and change to ghoul2 - } - gi.G2API_PrecacheGhoul2Model( weaponModel ); // correct way is item->world_model + gi.G2API_PrecacheGhoul2Model( weaponData[curWeap].worldModel ); // correct way is item->world_model } } } @@ -1428,6 +1435,7 @@ extern void NPC_Rosh_Dark_Precache( void ); extern void NPC_Tusken_Precache( void ); extern void NPC_Saboteur_Precache( void ); extern void NPC_CultistDestroyer_Precache( void ); +extern void NPC_GalakMech_Precache( void ); void NPC_Jawa_Precache( void ) { for ( int i = 1; i < 7; i++ ) @@ -1525,6 +1533,10 @@ void NPC_PrecacheByClassName( const char* type ) { NPC_Protocol_Precache(); } + else if ( !Q_stricmp( "galak_mech", type )) + { + NPC_GalakMech_Precache(); + } else if ( !Q_stricmp( "boba_fett", type )) { Boba_Precache(); @@ -1660,7 +1672,7 @@ void CG_NPC_Precache ( gentity_t *spawner ) } else { - Q_strncpyz( ri.headModelName, value, sizeof(ri.headModelName), qtrue); + Q_strncpyz( ri.headModelName, value, sizeof(ri.headModelName)); } md3Model = qtrue; continue; @@ -1679,7 +1691,7 @@ void CG_NPC_Precache ( gentity_t *spawner ) } else { - Q_strncpyz( ri.torsoModelName, value, sizeof(ri.torsoModelName), qtrue); + Q_strncpyz( ri.torsoModelName, value, sizeof(ri.torsoModelName)); } md3Model = qtrue; continue; @@ -1692,7 +1704,7 @@ void CG_NPC_Precache ( gentity_t *spawner ) { continue; } - Q_strncpyz( ri.legsModelName, value, sizeof(ri.legsModelName), qtrue); + Q_strncpyz( ri.legsModelName, value, sizeof(ri.legsModelName)); md3Model = qtrue; continue; } @@ -1704,7 +1716,7 @@ void CG_NPC_Precache ( gentity_t *spawner ) { continue; } - Q_strncpyz( playerModel, value, sizeof(playerModel), qtrue); + Q_strncpyz( playerModel, value, sizeof(playerModel)); md3Model = qfalse; continue; } @@ -1716,7 +1728,7 @@ void CG_NPC_Precache ( gentity_t *spawner ) { continue; } - Q_strncpyz( customSkin, value, sizeof(customSkin), qtrue); + Q_strncpyz( customSkin, value, sizeof(customSkin)); continue; } @@ -1861,6 +1873,22 @@ void CG_NPC_Precache ( gentity_t *spawner ) { cgi_R_RegisterShader( saber.g2WeaponMarkShader2 ); } + if ( saber.ignitionFlare[0] ) + { + cgi_R_RegisterShader( saber.ignitionFlare ); + } + if ( saber.ignitionFlare2[0] ) + { + cgi_R_RegisterShader( saber.ignitionFlare2 ); + } + if ( saber.blackIgnitionFlare[0] ) + { + cgi_R_RegisterShader( saber.blackIgnitionFlare ); + } + if ( saber.blackIgnitionFlare2[0] ) + { + cgi_R_RegisterShader( saber.blackIgnitionFlare2 ); + } continue; } @@ -1930,6 +1958,14 @@ void NPC_BuildRandom( gentity_t *NPC ) extern void G_MatchPlayerWeapon( gentity_t *ent ); extern void G_InitPlayerFromCvars( gentity_t *ent ); extern void G_SetG2PlayerModel( gentity_t * const ent, const char *modelName, const char *customSkin, const char *surfOff, const char *surfOn ); +extern qboolean blasterWeap(int wp); +extern qboolean lightBlasterWeap(int wp); +extern qboolean heavyBlasterWeap(int wp); +extern qboolean heavyWeap(int wp); +extern cvar_t* g_handicap_matchNPChp; + +extern void G_ChangeHeadModel( gentity_t *ent, const char *newModel ); + qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) { const char *token; @@ -1941,6 +1977,8 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) char sound[MAX_QPATH]; char playerModel[MAX_QPATH]; char customSkin[MAX_QPATH]; + char playerHeadModel[MAX_QPATH] = {0}; + char customHeadSkin[MAX_QPATH] = {0}; clientInfo_t *ci = &NPC->client->clientInfo; renderInfo_t *ri = &NPC->client->renderInfo; gNPCstats_t *stats = NULL; @@ -1949,6 +1987,10 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) char surfOn[1024]={0}; qboolean parsingPlayer = qfalse; + + qboolean forcedRGBSaberColours[MAX_SABERS][MAX_BLADES] = {{qfalse, qfalse, qfalse, qfalse, qfalse, qfalse, qfalse, qfalse}, + {qfalse, qfalse, qfalse, qfalse, qfalse, qfalse, qfalse, qfalse}}; + strcpy(customSkin,"default"); if ( !NPCName || !NPCName[0]) { @@ -1963,16 +2005,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) if ( NPC->NPC ) { stats = &NPC->NPC->stats; -/* - NPC->NPC->allWeaponOrder[0] = WP_BRYAR_PISTOL; - NPC->NPC->allWeaponOrder[1] = WP_SABER; - NPC->NPC->allWeaponOrder[2] = WP_IMOD; - NPC->NPC->allWeaponOrder[3] = WP_SCAVENGER_RIFLE; - NPC->NPC->allWeaponOrder[4] = WP_TRICORDER; - NPC->NPC->allWeaponOrder[6] = WP_NONE; - NPC->NPC->allWeaponOrder[6] = WP_NONE; - NPC->NPC->allWeaponOrder[7] = WP_NONE; -*/ + // fill in defaults stats->sex = SEX_MALE; stats->aggression = 3; @@ -1996,6 +2029,14 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) stats->walkSpeed = 90; stats->runSpeed = 300; stats->acceleration = 15;//Increase/descrease speed this much per frame (20fps) + + //new stats + stats->altFire = qfalse; + stats->rareFire = qfalse; + stats->restrictJediPowers = qfalse; + stats->meleeKicks = 0; + stats->meleeKatas = 0; + stats->saberMeleeKatas = qfalse; } else { @@ -2486,7 +2527,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) } else { - Q_strncpyz( ri->headModelName, value, sizeof(ri->headModelName), qtrue); + Q_strncpyz( ri->headModelName, value, sizeof(ri->headModelName)); } continue; } @@ -2510,7 +2551,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) } else { - Q_strncpyz( ri->torsoModelName, value, sizeof(ri->torsoModelName), qtrue); + Q_strncpyz( ri->torsoModelName, value, sizeof(ri->torsoModelName)); } continue; } @@ -2522,7 +2563,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) { continue; } - Q_strncpyz( ri->legsModelName, value, sizeof(ri->legsModelName), qtrue); + Q_strncpyz( ri->legsModelName, value, sizeof(ri->legsModelName)); //Need to do this here to get the right index ci->animFileIndex = G_ParseAnimFileSet(ri->legsModelName); continue; @@ -2535,7 +2576,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) { continue; } - Q_strncpyz( playerModel, value, sizeof(playerModel), qtrue); + Q_strncpyz( playerModel, value, sizeof(playerModel)); md3Model = qfalse; continue; } @@ -2547,10 +2588,32 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) { continue; } - Q_strncpyz( customSkin, value, sizeof(customSkin), qtrue); + Q_strncpyz( customSkin, value, sizeof(customSkin)); continue; } - + + // playerModel + if ( !Q_stricmp( token, "playerHeadModel" ) ) + { + if ( COM_ParseString( &p, &value ) ) + { + continue; + } + Q_strncpyz( playerHeadModel, value, sizeof(playerHeadModel)); + continue; + } + + // customSkin + if ( !Q_stricmp( token, "customHeadSkin" ) ) + { + if ( COM_ParseString( &p, &value ) ) + { + continue; + } + Q_strncpyz( customHeadSkin, value, sizeof(customHeadSkin)); + continue; + } + // surfOff if ( !Q_stricmp( token, "surfOff" ) ) { @@ -2565,7 +2628,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) } else { - Q_strncpyz( surfOff, value, sizeof(surfOff), qtrue); + Q_strncpyz( surfOff, value, sizeof(surfOff)); } continue; } @@ -2584,7 +2647,7 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) } else { - Q_strncpyz( surfOn, value, sizeof(surfOn), qtrue); + Q_strncpyz( surfOn, value, sizeof(surfOn)); } continue; } @@ -3068,7 +3131,10 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) } else if ( parsingPlayer ) { - NPC->client->ps.stats[STAT_MAX_HEALTH] = NPC->client->pers.maxHealth = NPC->max_health = n; + if (g_handicap_matchNPChp->integer) + NPC->client->ps.stats[STAT_MAX_HEALTH] = NPC->client->pers.maxHealth = NPC->max_health = n; + else + NPC->client->ps.stats[STAT_MAX_HEALTH] = NPC->client->pers.maxHealth; } continue; } @@ -3523,12 +3589,12 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) if ( weap >= WP_NONE && weap < WP_NUM_WEAPONS ) { NPC->client->ps.weapon = weap; - NPC->client->ps.stats[STAT_WEAPONS] |= ( 1 << weap ); + NPC->client->ps.weapons[weap] = 1; if ( weap > WP_NONE ) { RegisterItem( FindItemForWeapon( (weapon_t)(weap) ) ); //precache the weapon NPC->client->ps.ammo[weaponData[weap].ammoIndex] = ammoData[weaponData[weap].ammoIndex].max; - } + } } continue; } @@ -3546,15 +3612,263 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) if ( NPC->NPC ) { if ( n != 0 ) + { + stats->altFire = qtrue; + } + } + if (NPC->NPC) + { + if (n != 0) { NPC->NPC->scriptFlags |= SCF_ALT_FIRE; } } continue; + continue; + } + + if (!Q_stricmp(token, "dontFire")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->client) + { + if (n != 0) + { + NPC->NPC->scriptFlags |= SCF_DONT_FIRE; + } + } + continue; + } + + if (!Q_stricmp(token, "dontFlee")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->client) + { + if (n != 0) + { + NPC->NPC->scriptFlags |= SCF_DONT_FLEE; + } + } + continue; + } + + if (!Q_stricmp(token, "rareFire")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->client) + { + if (n != 0) + { + NPC->NPC->scriptFlags |= SCF_DONT_FIRE; + stats->rareFire = qtrue; + } + } + continue; + } + + //special stats that force certain attributes regardless of class + if (!Q_stricmp(token, "dualPistols")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->client && NPC->client->ps.weapon == WP_BLASTER_PISTOL) + { + if (n != 0) + { + NPC->flags |= FL_DUALPISTOLS; + } + } + + continue; + } + + //heavyMelee + if (!Q_stricmp(token, "heavyMelee")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->client) + { + if (n != 0) + { + NPC->NPC->aiFlags |= NPCAI_HEAVY_MELEE; + } + } + + continue; + } + + //can they do melee katas with saber out like Boss_Kyle? + if (!Q_stricmp(token, "saberMeleeKatas")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + + if (NPC->client) + { + if (n != 0) + { + stats->saberMeleeKatas = qtrue; + } + } + + continue; + } + + if (!Q_stricmp(token, "undying")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + + if (NPC->client) + { + if (n != 0) + { + NPC->flags |= FL_UNDYING; + } + } + + continue; + } + } + + + //these stats can affect the player + if (!Q_stricmp(token, "cortosis")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->client) + { + if (n != 0) + { + NPC->flags |= FL_CORTOSIS; + } + } + continue; + } + + if (!Q_stricmp(token, "magPlating")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->client) + { + if (n != 0) + { + NPC->flags |= FL_MAGPLATING; + } + } + continue; + } + + if (!Q_stricmp(token, "blastArmor")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->client) + { + if (n != 0) + { + NPC->flags |= FL_BLASTARMOR; + } } - //Other unique behaviors/numbers that are currently hardcoded? + continue; } + //special melee characteristics + if (!Q_stricmp(token, "meleeKatas")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->NPC) + { + if (n != 0) + { + stats->meleeKatas = n; + NPC->flags |= FL_MELEEKATAS; + } + } + continue; + } + + //can they do kicks in Melee? + if (!Q_stricmp(token, "meleeKicks")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->NPC) + { + if (n != 0) + { + stats->meleeKicks = n; + NPC->flags |= FL_MELEEKICKS; + } + } + continue; + } + + if (!Q_stricmp(token, "meleeKatasNoForceFx")) + { + if (COM_ParseInt(&p, &n)) + { + SkipRestOfLine(&p); + continue; + } + if (NPC->client) + { + if (n != 0) + { + NPC->flags |= FL_MELEEKATA_NOFORCEFX; + } + } + continue; + } + + + + //Other unique behaviors/numbers that are currently hardcoded? + //force powers int fp = GetIDForString( FPTable, token ); if ( fp >= FP_FIRST && fp < NUM_FORCE_POWERS ) @@ -3693,7 +4007,10 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) saber_colors_t color = TranslateSaberColor( value ); for ( n = 0; n < MAX_BLADES; n++ ) { - NPC->client->ps.saber[0].blade[n].color = color; + if (!forcedRGBSaberColours[0][n]) + { + NPC->client->ps.saber[0].blade[n].color = color; + } } } else if (strlen(token)==11) @@ -3708,7 +4025,10 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) { continue; } - NPC->client->ps.saber[0].blade[index].color = TranslateSaberColor( value ); + if (!forcedRGBSaberColours[0][index]) + { + NPC->client->ps.saber[0].blade[index].color = TranslateSaberColor( value ); + } } else { @@ -3734,7 +4054,10 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) saber_colors_t color = TranslateSaberColor( value ); for ( n = 0; n < MAX_BLADES; n++ ) { - NPC->client->ps.saber[1].blade[n].color = color; + if (!forcedRGBSaberColours[1][n]) + { + NPC->client->ps.saber[1].blade[n].color = color; + } } } else if (strlen(token)==12) @@ -3749,7 +4072,10 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) { continue; } - NPC->client->ps.saber[1].blade[n].color = TranslateSaberColor( value ); + if (!forcedRGBSaberColours[1][n]) + { + NPC->client->ps.saber[1].blade[n].color = TranslateSaberColor( value ); + } } else { @@ -3757,7 +4083,92 @@ qboolean NPC_ParseParms( const char *NPCName, gentity_t *NPC ) } continue; } - + + // saberColor + if ( !Q_stricmpn( token, "saberColorRGB", 13) ) + { + if ( !NPC->client ) + { + continue; + } + + if (strlen(token)==13) + { + if ( COM_ParseString( &p, &value ) ) + { + continue; + } + saber_colors_t color = TranslateSaberColor( value ); + forcedRGBSaberColours[0][0] = qtrue; + for ( n = 0; n < MAX_BLADES; n++ ) + { + NPC->client->ps.saber[0].blade[n].color = color; + } + } + else if (strlen(token)==14) + { + int index = atoi(&token[13])-1; + if (index > 7 || index < 1 ) + { + gi.Printf( S_COLOR_YELLOW"WARNING: bad saberColorRGB '%s' in %s\n", token, NPCName ); + continue; + } + if ( COM_ParseString( &p, &value ) ) + { + continue; + } + forcedRGBSaberColours[0][index] = qtrue; + NPC->client->ps.saber[0].blade[index].color = TranslateSaberColor( value ); + } + else + { + gi.Printf( S_COLOR_YELLOW"WARNING: bad saberColorRGB '%s' in %s\n", token, NPCName ); + } + continue; + } + + + if ( !Q_stricmpn( token, "saber2ColorRGB", 14 ) ) + { + if ( !NPC->client ) + { + continue; + } + + if (strlen(token)==14) + { + if ( COM_ParseString( &p, &value ) ) + { + continue; + } + forcedRGBSaberColours[1][0] = qtrue; + saber_colors_t color = TranslateSaberColor( value ); + for ( n = 0; n < MAX_BLADES; n++ ) + { + NPC->client->ps.saber[1].blade[n].color = color; + } + } + else if (strlen(token)==15) + { + n = atoi(&token[14])-1; + if (n > 7 || n < 1 ) + { + gi.Printf( S_COLOR_YELLOW"WARNING: bad saber2ColorRGB '%s' in %s\n", token, NPCName ); + continue; + } + if ( COM_ParseString( &p, &value ) ) + { + continue; + } + forcedRGBSaberColours[1][n] = qtrue; + NPC->client->ps.saber[1].blade[n].color = TranslateSaberColor( value ); + } + else + { + gi.Printf( S_COLOR_YELLOW"WARNING: bad saber2ColorRGB '%s' in %s\n", token, NPCName ); + } + continue; + } //saber length if ( !Q_stricmpn( token, "saberLength", 11) ) @@ -4019,7 +4430,7 @@ Ghoul2 Insert Start { int iVehIndex = BG_VehicleGetIndex( NPC->NPC_type ); strcpy(customSkin, "default"); // Ignore any custom skin that may have come from the NPC File - Q_strncpyz( playerModel, g_vehicleInfo[iVehIndex].model, sizeof(playerModel), qtrue); + Q_strncpyz( playerModel, g_vehicleInfo[iVehIndex].model, sizeof(playerModel)); if ( g_vehicleInfo[iVehIndex].skin && g_vehicleInfo[iVehIndex].skin[0] ) { bool forceSkin = false; @@ -4041,7 +4452,7 @@ Ghoul2 Insert Start //--------------------------------------------------- if (forceSkin) { - Q_strncpyz( customSkin, NPC->soundSet, sizeof(customSkin), qtrue); + Q_strncpyz( customSkin, NPC->soundSet, sizeof(customSkin)); } // Otherwise Choose A Random Skin @@ -4052,7 +4463,7 @@ Ghoul2 Insert Start { gi.Printf(S_COLOR_RED"WARNING: Unable to use skin (%s)", NPC->soundSet); } - Q_strncpyz( customSkin, *skinarray[Q_irand(0, skinarray.size()-1)], sizeof(customSkin), qtrue); + Q_strncpyz( customSkin, *skinarray[Q_irand(0, skinarray.size()-1)], sizeof(customSkin)); } if (NPC->soundSet && gi.bIsFromZone(NPC->soundSet, TAG_G_ALLOC)) { gi.Free(NPC->soundSet); @@ -4062,6 +4473,18 @@ Ghoul2 Insert Start } G_SetG2PlayerModel( NPC, playerModel, customSkin, surfOff, surfOn ); + + if ( playerHeadModel && playerHeadModel[0] ) + { + if (customHeadSkin && customHeadSkin[0]) + { + G_ChangeHeadModel( NPC, va("%s|%s", playerHeadModel, customHeadSkin) ); + } + else + { + G_ChangeHeadModel( NPC, va("%s|default", playerHeadModel) ); + } + } } } /* diff --git a/code/game/NPC_utils.cpp b/code/game/NPC_utils.cpp index fc507f4275..e938041389 100644 --- a/code/game/NPC_utils.cpp +++ b/code/game/NPC_utils.cpp @@ -27,6 +27,7 @@ along with this program; if not, see . #include "g_navigator.h" #include "../cgame/cg_local.h" #include "g_nav.h" +#include "g_functions.h" extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt ); @@ -280,7 +281,12 @@ qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw ) NPC->s.weapon==WP_REPEATER || NPC->s.weapon==WP_FLECHETTE || NPC->s.weapon==WP_BRYAR_PISTOL || - NPC->s.weapon==WP_NOGHRI_STICK) + NPC->s.weapon==WP_NOGHRI_STICK || + NPC->s.weapon==WP_SONIC_BLASTER || + NPC->s.weapon==WP_E5_CARBINE || + NPC->s.weapon==WP_DC15S_CARBINE || + NPC->s.weapon==WP_DC15A_RIFLE || + NPC->s.weapon==WP_Z6_ROTARY) { yawSpeed *= 10.0f; } @@ -419,7 +425,7 @@ qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw ) //Snap to if(fabs(error) > 10) { - if(random() > 0.6) + if(Q_flrand(0.0f, 1.0f) > 0.6) { doSound = qtrue; } @@ -493,7 +499,7 @@ qboolean NPC_UpdateAngles ( qboolean doPitch, qboolean doYaw ) //Snap to if(fabs(error) > 10) { - if(random() > 0.6) + if(Q_flrand(0.0f, 1.0f) > 0.6) { doSound = qtrue; } @@ -1166,16 +1172,6 @@ NPC_PickEnemyExt gentity_t *NPC_PickEnemyExt( qboolean checkAlerts = qfalse ) { - //Check for Hazard Team status and remove this check - /* - if ( NPC->client->playerTeam != TEAM_STARFLEET ) - { - //If we've found the player, return it - if ( NPC_FindPlayer() ) - return &g_entities[0]; - } - */ - //If we've asked for the closest enemy int entID = NPC_FindNearestEnemy( NPC ); @@ -1279,6 +1275,12 @@ qboolean NPC_FindEnemy( qboolean checkAlerts = qfalse ) G_ClearEnemy( NPC ); return qfalse; } + + if( NPCInfo->insanityTime > level.time ) + { + G_ClearEnemy( NPC ); + return qfalse; + } //Don't want a new enemy if ( ( NPC_ValidEnemy( NPC->enemy ) ) && ( NPC->svFlags & SVF_LOCKEDENEMY ) ) @@ -1531,6 +1533,7 @@ NPC_CheckCharmed ------------------------- */ extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime ); +extern qboolean PM_HasAnimation( gentity_t *ent, int animation ); void G_CheckCharmed( gentity_t *self ) { if ( self @@ -1557,6 +1560,49 @@ void G_CheckCharmed( gentity_t *self ) G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 ); } } + else if ( self + && self->client + && self->client->playerTeam == TEAM_PLAYER + && self->NPC + && self->NPC->darkCharmedTime + && (self->NPC->darkCharmedTime < level.time ||self->health <= 0) ) + {//we were charmed, set us back! + //NOTE: presumptions here... + team_t savTeam = self->client->enemyTeam; + self->client->enemyTeam = self->client->playerTeam; + self->client->playerTeam = savTeam; + self->client->leader = NULL; + self->NPC->darkCharmedTime = 0; + if ( self->health > 0 ) + { + if ( self->NPC->tempBehavior == BS_FOLLOW_LEADER ) + { + self->NPC->tempBehavior = BS_DEFAULT; + } + G_ClearEnemy( self ); + } + if (self->health > 0) + { + self->health = 0; + GEntity_DieFunc( self, self, self, self->max_health, MOD_DESTRUCTION); + } + } +} + +void G_CheckInsanity( gentity_t *self ) +{ + if ( self + && self->client + && self->NPC + && self->NPC->insanityTime + && self->NPC->insanityTime > level.time + && (self->client->ps.torsoAnim != BOTH_SONICPAIN_HOLD) + && PM_HasAnimation( self, BOTH_SONICPAIN_HOLD ) ) + { + NPC_SetAnim( self, SETANIM_LEGS, BOTH_SONICPAIN_HOLD, SETANIM_FLAG_NORMAL|SETANIM_FLAG_RESTART ); + NPC_SetAnim( self, SETANIM_TORSO, BOTH_SONICPAIN_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + self->client->ps.torsoAnimTimer += self->NPC->insanityTime - level.time; + } } @@ -1634,13 +1680,13 @@ extern qboolean Boba_Flying( gentity_t *self ); extern void Boba_FlyStart( gentity_t *self ); extern void Boba_FlyStop( gentity_t *self ); -qboolean JET_Flying( gentity_t *self ) +qboolean JET_Flying(gentity_t *self) { - if ( !self || !self->client ) + if (!self || !self->client) { return qfalse; } - if ( self->client->NPC_class == CLASS_BOBAFETT ) + if (self->client->NPC_class == CLASS_BOBAFETT || self->client->NPC_class == CLASS_MANDA) { return (Boba_Flying(self)); } @@ -1661,7 +1707,7 @@ void JET_FlyStart( gentity_t *self ) return; } self->lastInAirTime = level.time; - if ( self->client->NPC_class == CLASS_BOBAFETT ) + if (self->client->NPC_class == CLASS_BOBAFETT) { Boba_FlyStart( self ); } @@ -1677,7 +1723,7 @@ void JET_FlyStop( gentity_t *self ) { return; } - if ( self->client->NPC_class == CLASS_BOBAFETT ) + if (self->client->NPC_class == CLASS_BOBAFETT || self->client->NPC_class == CLASS_MANDA) { Boba_FlyStop( self ); } diff --git a/code/game/Q3_Interface.cpp b/code/game/Q3_Interface.cpp index 5d92156e8c..081aef0ac8 100644 --- a/code/game/Q3_Interface.cpp +++ b/code/game/Q3_Interface.cpp @@ -40,6 +40,7 @@ along with this program; if not, see . #include "wp_saber.h" #include "g_vehicles.h" #include "g_navigator.h" +#include "qcommon/ojk_saved_game_helper.h" extern cvar_t *com_buildScript; @@ -98,10 +99,111 @@ stringID_table_t BSTable[] = ENUM2STRING(BS_CINEMATIC),//# Does nothing but face it's angles and move to a goal if it has one ENUM2STRING(BS_FLEE),//# Run toward the nav goal, avoiding danger //the rest are internal only + // ... but we should list them anyway, otherwise workshop behavior will screw up + ENUM2STRING(BS_WAIT), + ENUM2STRING(BS_STAND_GUARD), + ENUM2STRING(BS_PATROL), + ENUM2STRING(BS_INVESTIGATE), + ENUM2STRING(BS_HUNT_AND_KILL), + ENUM2STRING(BS_FLEE), { "", -1 } }; +stringID_table_t NPCClassTable[] = +{ + ENUM2STRING(CLASS_NONE), + ENUM2STRING(CLASS_ATST), + ENUM2STRING(CLASS_BARTENDER), + ENUM2STRING(CLASS_BESPIN_COP), + ENUM2STRING(CLASS_CLAW), + ENUM2STRING(CLASS_COMMANDO), + ENUM2STRING(CLASS_DESANN), + ENUM2STRING(CLASS_FISH), + ENUM2STRING(CLASS_FLIER2), + ENUM2STRING(CLASS_GALAK), + ENUM2STRING(CLASS_GLIDER), + ENUM2STRING(CLASS_GONK), + ENUM2STRING(CLASS_GRAN), + ENUM2STRING(CLASS_HOWLER), + ENUM2STRING(CLASS_RANCOR), + ENUM2STRING(CLASS_SAND_CREATURE), + ENUM2STRING(CLASS_WAMPA), + ENUM2STRING(CLASS_IMPERIAL), + ENUM2STRING(CLASS_IMPWORKER), + ENUM2STRING(CLASS_INTERROGATOR), + ENUM2STRING(CLASS_JAN), + ENUM2STRING(CLASS_JEDI), + ENUM2STRING(CLASS_KYLE), + ENUM2STRING(CLASS_LANDO), + ENUM2STRING(CLASS_LIZARD), + ENUM2STRING(CLASS_LUKE), + ENUM2STRING(CLASS_MARK1), + ENUM2STRING(CLASS_MARK2), + ENUM2STRING(CLASS_GALAKMECH), + ENUM2STRING(CLASS_MINEMONSTER), + ENUM2STRING(CLASS_MONMOTHA), + ENUM2STRING(CLASS_MORGANKATARN), + ENUM2STRING(CLASS_MOUSE), + ENUM2STRING(CLASS_MURJJ), + ENUM2STRING(CLASS_PRISONER), + ENUM2STRING(CLASS_PROBE), + ENUM2STRING(CLASS_PROTOCOL), + ENUM2STRING(CLASS_R2D2), + ENUM2STRING(CLASS_R5D2), + ENUM2STRING(CLASS_REBEL), + ENUM2STRING(CLASS_REBORN), + ENUM2STRING(CLASS_REELO), + ENUM2STRING(CLASS_REMOTE), + ENUM2STRING(CLASS_RODIAN), + ENUM2STRING(CLASS_SEEKER), + ENUM2STRING(CLASS_SENTRY), + ENUM2STRING(CLASS_SHADOWTROOPER), + ENUM2STRING(CLASS_SABOTEUR), + ENUM2STRING(CLASS_STORMTROOPER), + ENUM2STRING(CLASS_SWAMP), + ENUM2STRING(CLASS_SWAMPTROOPER), + ENUM2STRING(CLASS_NOGHRI), + ENUM2STRING(CLASS_TAVION), + ENUM2STRING(CLASS_ALORA), + ENUM2STRING(CLASS_TRANDOSHAN), + ENUM2STRING(CLASS_UGNAUGHT), + ENUM2STRING(CLASS_JAWA), + ENUM2STRING(CLASS_WEEQUAY), + ENUM2STRING(CLASS_TUSKEN), + ENUM2STRING(CLASS_BOBAFETT), + ENUM2STRING(CLASS_ROCKETTROOPER), + ENUM2STRING(CLASS_SABER_DROID), + ENUM2STRING(CLASS_ASSASSIN_DROID), + ENUM2STRING(CLASS_HAZARD_TROOPER), + ENUM2STRING(CLASS_PLAYER), + ENUM2STRING(CLASS_VEHICLE), + + + { "", -1 }, +}; + +stringID_table_t RankTable[] = +{ + ENUM2STRING(RANK_CIVILIAN), + ENUM2STRING(RANK_CREWMAN), + ENUM2STRING(RANK_ENSIGN), + ENUM2STRING(RANK_LT_JG), + ENUM2STRING(RANK_LT), + ENUM2STRING(RANK_LT_COMM), + ENUM2STRING(RANK_COMMANDER), + ENUM2STRING(RANK_CAPTAIN), + + { "", -1 }, +}; +stringID_table_t MoveTypeTable[] = +{ + ENUM2STRING(MT_STATIC), + ENUM2STRING(MT_WALK), + ENUM2STRING(MT_RUNJUMP), + ENUM2STRING(MT_FLYSWIM), + { "", -1 } +}; stringID_table_t BSETTable[] = { @@ -160,6 +262,11 @@ stringID_table_t WPTable[] = ENUM2STRING(WP_TUSKEN_STAFF), ENUM2STRING(WP_SCEPTER), ENUM2STRING(WP_NOGHRI_STICK), + ENUM2STRING(WP_SONIC_BLASTER), + ENUM2STRING(WP_E5_CARBINE), + ENUM2STRING(WP_DC15S_CARBINE), + ENUM2STRING(WP_DC15A_RIFLE), + ENUM2STRING(WP_Z6_ROTARY), { "", 0 } }; @@ -418,6 +525,13 @@ stringID_table_t setTable[] = ENUM2STRING(SET_FORCE_ABSORB_LEVEL), ENUM2STRING(SET_FORCE_DRAIN_LEVEL), ENUM2STRING(SET_FORCE_SIGHT_LEVEL), + ENUM2STRING(SET_FORCE_DESTRUCTION_LEVEL), + ENUM2STRING(SET_FORCE_INSANITY_LEVEL), + ENUM2STRING(SET_FORCE_STASIS_LEVEL), + ENUM2STRING(SET_FORCE_BLINDING_LEVEL), + ENUM2STRING(SET_FORCE_DEADLYSIGHT_LEVEL), + ENUM2STRING(SET_FORCE_REPULSE_LEVEL), + ENUM2STRING(SET_FORCE_INVULNERABILITY_LEVEL), ENUM2STRING(SET_SABER1_COLOR1), ENUM2STRING(SET_SABER1_COLOR2), ENUM2STRING(SET_SABER2_COLOR1), @@ -484,10 +598,19 @@ stringID_table_t setTable[] = ENUM2STRING(SET_FORCE_PROTECT), ENUM2STRING(SET_FORCE_ABSORB), ENUM2STRING(SET_FORCE_DRAIN), + ENUM2STRING(SET_FORCE_DESTRUCTION), + ENUM2STRING(SET_FORCE_INSANITY), + ENUM2STRING(SET_FORCE_STASIS), + ENUM2STRING(SET_FORCE_BLINDING), + ENUM2STRING(SET_FORCE_DEADLYSIGHT), + ENUM2STRING(SET_FORCE_REPULSE), + ENUM2STRING(SET_FORCE_INVULNERABILITY), ENUM2STRING(SET_WINTER_GEAR), ENUM2STRING(SET_NO_ANGLES), ENUM2STRING(SET_SABER_ORIGIN), ENUM2STRING(SET_SKIN), + ENUM2STRING(SET_RADAR_ICON), + ENUM2STRING(SET_RADAR_OBJECT), { "", SET_ } }; @@ -530,39 +653,39 @@ static void SetTextColor ( vec4_t textcolor,const char *color) if (Q_stricmp(color,"BLACK") == 0) { - Vector4Copy( colorTable[CT_BLACK], textcolor ); + VectorCopy4( colorTable[CT_BLACK], textcolor ); } else if (Q_stricmp(color,"RED") == 0) { - Vector4Copy( colorTable[CT_RED], textcolor ); + VectorCopy4( colorTable[CT_RED], textcolor ); } else if (Q_stricmp(color,"GREEN") == 0) { - Vector4Copy( colorTable[CT_GREEN], textcolor ); + VectorCopy4( colorTable[CT_GREEN], textcolor ); } else if (Q_stricmp(color,"YELLOW") == 0) { - Vector4Copy( colorTable[CT_YELLOW], textcolor ); + VectorCopy4( colorTable[CT_YELLOW], textcolor ); } else if (Q_stricmp(color,"BLUE") == 0) { - Vector4Copy( colorTable[CT_BLUE], textcolor ); + VectorCopy4( colorTable[CT_BLUE], textcolor ); } else if (Q_stricmp(color,"CYAN") == 0) { - Vector4Copy( colorTable[CT_CYAN], textcolor ); + VectorCopy4( colorTable[CT_CYAN], textcolor ); } else if (Q_stricmp(color,"MAGENTA") == 0) { - Vector4Copy( colorTable[CT_MAGENTA], textcolor ); + VectorCopy4( colorTable[CT_MAGENTA], textcolor ); } else if (Q_stricmp(color,"WHITE") == 0) { - Vector4Copy( colorTable[CT_WHITE], textcolor ); + VectorCopy4( colorTable[CT_WHITE], textcolor ); } else { - Vector4Copy( colorTable[CT_WHITE], textcolor ); + VectorCopy4( colorTable[CT_WHITE], textcolor ); } return; @@ -764,10 +887,10 @@ static void Q3_SetObjective(const char *ObjEnum, int status) switch (status) { case SET_OBJ_HIDE : - objective->display = OBJECTIVE_HIDE; + objective->display = (qboolean)(OBJECTIVE_HIDE != 0); break; case SET_OBJ_SHOW : - objective->display = OBJECTIVE_SHOW; + objective->display = (qboolean)(OBJECTIVE_SHOW != 0); objectivesShown++; missionInfo_Updated = qtrue; // Activate flashing text break; @@ -1669,7 +1792,7 @@ Q3_Lerp2Origin Lerps the origin to the destination value ============= */ -static void Q3_Lerp2Origin( int taskID, int entID, vec3_t origin, float duration ) +void Q3_Lerp2Origin( int taskID, int entID, vec3_t origin, float duration ) { gentity_t *ent = &g_entities[entID]; @@ -2191,19 +2314,6 @@ static void Q3_SetLeader( int entID, const char *name ) stringID_table_t teamTable [] = { ENUM2STRING(TEAM_FREE), -// ENUM2STRING(TEAM_STARFLEET), -// ENUM2STRING(TEAM_BORG), -// ENUM2STRING(TEAM_PARASITE), -// ENUM2STRING(TEAM_SCAVENGERS), -// ENUM2STRING(TEAM_KLINGON), -// ENUM2STRING(TEAM_MALON), -// ENUM2STRING(TEAM_HIROGEN), -// ENUM2STRING(TEAM_IMPERIAL), -// ENUM2STRING(TEAM_STASIS), -// ENUM2STRING(TEAM_8472), -// ENUM2STRING(TEAM_BOTS), -// ENUM2STRING(TEAM_FORGE), -// ENUM2STRING(TEAM_DISGUISE), ENUM2STRING(TEAM_PLAYER), ENUM2STRING(TEAM_ENEMY), ENUM2STRING(TEAM_NEUTRAL), @@ -3246,13 +3356,17 @@ void G_SetWeapon( gentity_t *self, int wp ) gitem_t *item = FindItemForWeapon( (weapon_t) wp); RegisterItem( item ); //make sure the weapon is cached in case this runs at startup - if ( self->client->ps.stats[STAT_WEAPONS]&( 1 << wp ) ) + if ( self->client->ps.weapons[wp] ) { hadWeapon = qtrue; } if ( self->NPC ) {//Should NPCs have only 1 weapon at a time? - self->client->ps.stats[STAT_WEAPONS] = ( 1 << wp ); + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + self->client->ps.weapons[i] = 0; + } + self->client->ps.weapons[wp] = 1; self->client->ps.ammo[weaponData[wp].ammoIndex] = 999; ChangeWeapon( self, wp ); @@ -3262,7 +3376,7 @@ void G_SetWeapon( gentity_t *self, int wp ) } else { - self->client->ps.stats[STAT_WEAPONS] |= ( 1 << wp ); + self->client->ps.weapons[wp] = 1; self->client->ps.ammo[weaponData[wp].ammoIndex] = ammoData[weaponData[wp].ammoIndex].max; G_AddEvent( self, EV_ITEM_PICKUP, (item - bg_itemlist) ); @@ -3279,10 +3393,12 @@ void G_SetWeapon( gentity_t *self, int wp ) WP_SaberInitBladeData( self ); } WP_SaberAddG2SaberModels( self ); + G_RemoveHolsterModels( self ); } else { - G_CreateG2AttachedWeaponModel( self, weaponData[wp].weaponMdl, self->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel( self, weaponData[wp].worldModel, self->handRBolt, 0 ); + WP_SaberAddHolsteredG2SaberModels( self ); } } @@ -4648,7 +4764,7 @@ static void Q3_SetBobaJetPack(int entID, qboolean add) } // make sure we this is Boba Fett - if ( ent->client && ent->client->NPC_class != CLASS_BOBAFETT ) + if (ent->client && ent->client->NPC_class != CLASS_BOBAFETT && ent->client->NPC_class != CLASS_MANDA) { Quake3Game()->DebugPrint( IGameInterface::WL_WARNING, "Q3_SetBobaJetPack: '%s' is not Boba Fett!\n", ent->targetname ); return; @@ -4827,9 +4943,11 @@ static void Q3_SetNoMindTrick( int entID, qboolean add) { ent->NPC->scriptFlags |= SCF_NO_MIND_TRICK; ent->NPC->confusionTime = 0; + ent->NPC->insanityTime = 0; if ( ent->ghoul2.size() && ent->headBolt != -1 ) { G_StopEffect("force/confusion", ent->playerModel, ent->headBolt, ent->s.number ); + G_StopEffect("force/drain_hand", ent->playerModel, ent->headBolt, ent->s.number ); } } else @@ -6420,7 +6538,7 @@ static void Q3_SetSaberActive( int entID, qboolean active ) if ( ent->client->ps.weapon != WP_SABER ) { - if ( (ent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SABER]) ) {//change to it right now if ( ent->NPC ) { @@ -6484,7 +6602,7 @@ static void Q3_SetSaberBladeActive( int entID, int iSaber, int iBlade, qboolean if ( ent->client->ps.weapon != WP_SABER ) { - if ( (ent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SABER]) ) {//change to it right now if ( ent->NPC ) { @@ -7219,7 +7337,13 @@ VariableSaveFloats void CQuake3GameInterface::VariableSaveFloats( varFloat_m &fmap ) { int numFloats = fmap.size(); - gi.AppendToSaveGame( INT_ID('F','V','A','R'), &numFloats, sizeof( numFloats ) ); + + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.write_chunk( + INT_ID('F', 'V', 'A', 'R'), + numFloats); varFloat_m::iterator vfi; STL_ITERATE( vfi, fmap ) @@ -7228,11 +7352,19 @@ void CQuake3GameInterface::VariableSaveFloats( varFloat_m &fmap ) int idSize = strlen( ((*vfi).first).c_str() ); //Save out the real data - gi.AppendToSaveGame( INT_ID('F','I','D','L'), &idSize, sizeof( idSize ) ); - gi.AppendToSaveGame( INT_ID('F','I','D','S'), (void *) ((*vfi).first).c_str(), idSize ); + saved_game.write_chunk( + INT_ID('F', 'I', 'D', 'L'), + idSize); + + saved_game.write_chunk( + INT_ID('F', 'I', 'D', 'S'), + ((*vfi).first).c_str(), + idSize); //Save out the float value - gi.AppendToSaveGame( INT_ID('F','V','A','L'), &((*vfi).second), sizeof( float ) ); + saved_game.write_chunk( + INT_ID('F', 'V', 'A', 'L'), + (*vfi).second); } } @@ -7245,7 +7377,13 @@ VariableSaveStrings void CQuake3GameInterface::VariableSaveStrings( varString_m &smap ) { int numStrings = smap.size(); - gi.AppendToSaveGame( INT_ID('S','V','A','R'), &numStrings, sizeof( numStrings ) ); + + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.write_chunk( + INT_ID('S', 'V', 'A', 'R'), + numStrings); varString_m::iterator vsi; STL_ITERATE( vsi, smap ) @@ -7254,14 +7392,26 @@ void CQuake3GameInterface::VariableSaveStrings( varString_m &smap ) int idSize = strlen( ((*vsi).first).c_str() ); //Save out the real data - gi.AppendToSaveGame( INT_ID('S','I','D','L'), &idSize, sizeof( idSize ) ); - gi.AppendToSaveGame( INT_ID('S','I','D','S'), (void *) ((*vsi).first).c_str(), idSize ); + saved_game.write_chunk( + INT_ID('S', 'I', 'D', 'L'), + idSize); + + saved_game.write_chunk( + INT_ID('S', 'I', 'D', 'S'), + ((*vsi).first).c_str(), + idSize); //Save out the string value idSize = strlen( ((*vsi).second).c_str() ); - gi.AppendToSaveGame( INT_ID('S','V','S','Z'), &idSize, sizeof( idSize ) ); - gi.AppendToSaveGame( INT_ID('S','V','A','L'), (void *) ((*vsi).second).c_str(), idSize ); + saved_game.write_chunk( + INT_ID('S', 'V', 'S', 'Z'), + idSize); + + saved_game.write_chunk( + INT_ID('S', 'V', 'A', 'L'), + ((*vsi).second).c_str(), + idSize); } } @@ -7288,22 +7438,41 @@ VariableLoadFloats void CQuake3GameInterface::VariableLoadFloats( varFloat_m &fmap ) { - int numFloats; + int numFloats = 0; char tempBuffer[1024]; - gi.ReadFromSaveGame( INT_ID('F','V','A','R'), &numFloats, sizeof( numFloats ), NULL ); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.read_chunk( + INT_ID('F', 'V', 'A', 'R'), + numFloats); for ( int i = 0; i < numFloats; i++ ) { - int idSize; + int idSize = 0; + + saved_game.read_chunk( + INT_ID('F', 'I', 'D', 'L'), + idSize); + + if (idSize < 0 || static_cast(idSize) >= sizeof(tempBuffer)) + { + idSize = 0; + } + + saved_game.read_chunk( + INT_ID('F', 'I', 'D', 'S'), + tempBuffer, + idSize); - gi.ReadFromSaveGame( INT_ID('F','I','D','L'), &idSize, sizeof( idSize ), NULL ); - gi.ReadFromSaveGame( INT_ID('F','I','D','S'), &tempBuffer, idSize, NULL ); tempBuffer[ idSize ] = 0; - float val; + float val = 0.0F; - gi.ReadFromSaveGame( INT_ID('F','V','A','L'), &val, sizeof( float ), NULL ); + saved_game.read_chunk( + INT_ID('F', 'V', 'A', 'L'), + val); DeclareVariable( TK_FLOAT, (const char *) &tempBuffer ); SetFloatVariable( (const char *) &tempBuffer, val ); @@ -7318,22 +7487,51 @@ VariableLoadStrings void CQuake3GameInterface::VariableLoadStrings( int type, varString_m &fmap ) { - int numFloats; + int numFloats = 0; char tempBuffer[1024]; char tempBuffer2[1024]; - gi.ReadFromSaveGame( INT_ID('S','V','A','R'), &numFloats, sizeof( numFloats ), NULL ); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.read_chunk( + INT_ID('S', 'V', 'A', 'R'), + numFloats); for ( int i = 0; i < numFloats; i++ ) { - int idSize; + int idSize = 0; + + saved_game.read_chunk( + INT_ID('S', 'I', 'D', 'L'), + idSize); + + if (idSize < 0 || static_cast(idSize) >= sizeof(tempBuffer)) + { + idSize = 0; + } + + saved_game.read_chunk( + INT_ID('S', 'I', 'D', 'S'), + tempBuffer, + idSize); - gi.ReadFromSaveGame( INT_ID('S','I','D','L'), &idSize, sizeof( idSize ), NULL ); - gi.ReadFromSaveGame( INT_ID('S','I','D','S'), &tempBuffer, idSize, NULL ); tempBuffer[ idSize ] = 0; - gi.ReadFromSaveGame( INT_ID('S','V','S','Z'), &idSize, sizeof( idSize ), NULL ); - gi.ReadFromSaveGame( INT_ID('S','V','A','L'), &tempBuffer2, idSize, NULL ); + saved_game.read_chunk( + INT_ID('S', 'V', 'S', 'Z'), + idSize); + + if (idSize < 0 || static_cast(idSize) >= sizeof(tempBuffer2)) + { + idSize = 0; + } + + saved_game.read_chunk( + INT_ID('S', 'V', 'A', 'L'), + tempBuffer2, + idSize); + tempBuffer2[ idSize ] = 0; switch ( type ) @@ -7433,7 +7631,7 @@ CQuake3GameInterface::~CQuake3GameInterface() } // Clear out all precached script's. - for ( iterScript = m_ScriptList.begin(); iterScript != m_ScriptList.end(); iterScript++ ) + for ( iterScript = m_ScriptList.begin(); iterScript != m_ScriptList.end(); ++iterScript ) { Free( (*iterScript).second->buffer ); delete (*iterScript).second; @@ -7858,7 +8056,7 @@ int CQuake3GameInterface::PlayIcarusSound( int taskID, int entID, const char *n soundChannel_t voice_chan = CHAN_VOICE; // set a default so the compiler doesn't bitch qboolean type_voice = qfalse; - Q_strncpyz( finalName, name, MAX_QPATH, 0 ); + Q_strncpyz( finalName, name, MAX_QPATH ); Q_strlwr(finalName); G_AddSexToPlayerString( finalName, qtrue ); @@ -8165,7 +8363,7 @@ void CQuake3GameInterface::Set( int taskID, int entID, const char *type_name, co if(!Q_stricmpn(type_name, "cvar_", 5) && strlen(type_name) > 5) { - cgi_Cvar_Set(type_name+5, data); + gi.cvar_set(type_name+5, data); return; } @@ -8402,7 +8600,7 @@ void CQuake3GameInterface::Set( int taskID, int entID, const char *type_name, co case SET_ICARUS_FREEZE: case SET_ICARUS_UNFREEZE: - Q3_SetICARUSFreeze( entID, (char *) data, (toSet==SET_ICARUS_FREEZE) ); + Q3_SetICARUSFreeze( entID, (char *) data, (qboolean)(toSet==SET_ICARUS_FREEZE) ); break; case SET_WEAPON: @@ -9372,6 +9570,13 @@ extern void LockDoors(gentity_t *const ent); case SET_FORCE_ABSORB_LEVEL: case SET_FORCE_DRAIN_LEVEL: case SET_FORCE_SIGHT_LEVEL: + case SET_FORCE_DESTRUCTION_LEVEL: + case SET_FORCE_INSANITY_LEVEL: + case SET_FORCE_STASIS_LEVEL: + case SET_FORCE_BLINDING_LEVEL: + case SET_FORCE_DEADLYSIGHT_LEVEL: + case SET_FORCE_REPULSE_LEVEL: + case SET_FORCE_INVULNERABILITY_LEVEL: int_data = atoi((char *) data); Q3_SetForcePowerLevel( entID, (toSet-SET_FORCE_HEAL_LEVEL), int_data ); break; @@ -9460,11 +9665,36 @@ extern void Saboteur_Cloak( gentity_t *self ); case SET_FORCE_DRAIN: Q3_SetForcePower( entID, FP_DRAIN, (qboolean)(Q_stricmp("true",(char*)data)==0) ); break; + case SET_FORCE_DESTRUCTION: + Q3_SetForcePower( entID, FP_DESTRUCTION, (qboolean)(Q_stricmp("true",(char*)data)==0) ); + break; + case SET_FORCE_INSANITY: + Q3_SetForcePower( entID, FP_INSANITY, (qboolean)(Q_stricmp("true",(char*)data)==0) ); + break; + case SET_FORCE_STASIS: + Q3_SetForcePower( entID, FP_STASIS, (qboolean)(Q_stricmp("true",(char*)data)==0) ); + break; + case SET_FORCE_BLINDING: + Q3_SetForcePower( entID, FP_BLINDING, (qboolean)(Q_stricmp("true",(char*)data)==0) ); + break; + case SET_FORCE_DEADLYSIGHT: + Q3_SetForcePower( entID, FP_DEADLYSIGHT, (qboolean)(Q_stricmp("true",(char*)data)==0) ); + break; + case SET_FORCE_REPULSE: + Q3_SetForcePower( entID, FP_REPULSE, (qboolean)(Q_stricmp("true",(char*)data)==0) ); + break; + case SET_FORCE_INVULNERABILITY: + Q3_SetForcePower( entID, FP_INVULNERABILITY, (qboolean)(Q_stricmp("true",(char*)data)==0) ); + break; + extern cvar_t *g_char_model; extern cvar_t *g_char_skin_head; extern cvar_t *g_char_skin_torso; extern cvar_t *g_char_skin_legs; +extern void G_SetHeadSurfaceOnOff( gentity_t *ent ); +extern void G_SetHeadSkin( gentity_t *ent ); + case SET_WINTER_GEAR: // Created: 03/26/03 by AReis. { // If this is a (fake) Player NPC or this IS the Player... @@ -9489,6 +9719,9 @@ extern cvar_t *g_char_skin_legs; { gi.G2API_SetSkin( &ent->ghoul2[ent->playerModel], G_SkinIndex( strSkin ), iSkinID ); } + + G_SetHeadSurfaceOnOff( ent ); + G_SetHeadSkin( ent ); } break; } @@ -9519,7 +9752,25 @@ extern cvar_t *g_char_skin_legs; } } break; - + case SET_RADAR_OBJECT: + if ( entID >= 0 && entID < ENTITYNUM_WORLD ) + { + if ( (Q_stricmp("true",(char*)data)==0) ) + { + g_entities[entID].s.eFlags2 |= EF2_RADAROBJECT; + } + else + { + g_entities[entID].s.eFlags2 &= ~EF2_RADAROBJECT; + } + } + break; + case SET_RADAR_ICON: + if ( entID >= 0 && entID < ENTITYNUM_WORLD ) + { + ent->s.radarIcon = G_IconIndex((char*)data); + } + break; default: //DebugPrint( WL_ERROR, "Set: '%s' is not a valid set field\n", type_name ); SetVar( taskID, entID, type_name, data ); @@ -10514,7 +10765,7 @@ int CQuake3GameInterface::GetString( int entID, const char *name, char **value case SET_ANIM_BOTH: *value = (char *) Q3_GetAnimBoth( ent ); - if ( VALIDSTRING( value ) == false ) + if ( VALIDSTRING( *value ) == false ) return false; break; @@ -11113,14 +11364,9 @@ void CQuake3GameInterface::FreeVariable( const char *name ) } //Save / Load functions -int CQuake3GameInterface::WriteSaveData( unsigned int chid, void *data, int length ) +ojk::ISavedGame* CQuake3GameInterface::get_saved_game_file() { - return gi.AppendToSaveGame( chid, data, length ); -} - -int CQuake3GameInterface::ReadSaveData( unsigned int chid, void *address, int length, void **addressptr ) -{ - return gi.ReadFromSaveGame( chid, address, length, addressptr ); + return ::gi.saved_game; } int CQuake3GameInterface::LinkGame( int entID, int icarusID ) @@ -11241,9 +11487,9 @@ void CQuake3GameInterface::PrecacheScript( const char *name ) void CQuake3GameInterface::PrecacheSound( const char *name ) { - char finalName[MAX_QPATH]; + char finalName[MAX_QPATH]; - Q_strncpyz( finalName, name, MAX_QPATH, 0 ); + Q_strncpyz( finalName, name, MAX_QPATH ); Q_strlwr(finalName); if (com_buildScript->integer) { //get the male sound first diff --git a/code/game/Q3_Interface.h b/code/game/Q3_Interface.h index df15fdad8e..81018237b2 100644 --- a/code/game/Q3_Interface.h +++ b/code/game/Q3_Interface.h @@ -262,6 +262,14 @@ typedef enum //# setType_e SET_FORCE_PROTECT,//## %t="BOOL_TYPES" # Causes this ent to start a force protect at whatever level of force protect they have SET_FORCE_ABSORB,//## %t="BOOL_TYPES" # Causes this ent to do start a force absorb at whatever level of force absorb they have SET_FORCE_DRAIN,//## %t="BOOL_TYPES" # Causes this ent to start force draining their enemy at whatever level of force drain they have (will drain until scripted to stop) + SET_FORCE_DESTRUCTION, + SET_FORCE_INSANITY, + SET_FORCE_STASIS, + SET_FORCE_BLINDING, + SET_FORCE_DEADLYSIGHT, + SET_FORCE_REPULSE, + SET_FORCE_INVULNERABILITY, + SET_WINTER_GEAR, //## %t="BOOL_TYPES" # Set the player to wear his/her winter gear (skins torso_g1 and lower_e1), or restore the default skins. SET_NO_ANGLES, //## %t="BOOL_TYPES" # This NPC/player will not have any bone angle overrides or pitch or roll (should only be used in cinematics) @@ -298,6 +306,13 @@ typedef enum //# setType_e SET_FORCE_ABSORB_LEVEL,//## %t="FORCE_LEVELS" # Change force power level SET_FORCE_DRAIN_LEVEL,//## %t="FORCE_LEVELS" # Change force power level SET_FORCE_SIGHT_LEVEL,//## %t="FORCE_LEVELS" # Change force power level + SET_FORCE_DESTRUCTION_LEVEL, + SET_FORCE_INSANITY_LEVEL, + SET_FORCE_STASIS_LEVEL, + SET_FORCE_BLINDING_LEVEL, + SET_FORCE_DEADLYSIGHT_LEVEL, + SET_FORCE_REPULSE_LEVEL, + SET_FORCE_INVULNERABILITY_LEVEL, SET_SABER1_COLOR1, //## %t="SABER_COLORS" # Set color of first blade of first saber SET_SABER1_COLOR2, //## %t="SABER_COLORS" # Set color of second blade of first saber SET_SABER2_COLOR1, //## %t="SABER_COLORS" # Set color of first blade of first saber @@ -327,6 +342,9 @@ typedef enum //# setType_e //in-bhc tables SET_LEAN,//## %t="LEAN_TYPES" # Lean left, right or stop leaning + + SET_RADAR_ICON, + SET_RADAR_OBJECT, //# #eol SET_ @@ -697,10 +715,10 @@ class CQuake3GameInterface : public IGameInterface void FreeVariable( const char *name ); //Save / Load functions - int WriteSaveData( unsigned int chid, void *data, int length ); - int ReadSaveData( unsigned int chid, void *address, int length, void **addressptr = NULL ); int LinkGame( int entID, int icarusID ); + ojk::ISavedGame* get_saved_game_file() override; + // Access functions int CreateIcarus( int entID); //Polls the engine for the sequencer of the entity matching the name passed diff --git a/code/game/SpeederNPC.cpp b/code/game/SpeederNPC.cpp index 80f8464dd5..fbce5bfe97 100644 --- a/code/game/SpeederNPC.cpp +++ b/code/game/SpeederNPC.cpp @@ -864,7 +864,7 @@ void AnimateRiders( Vehicle_t *pVeh ) { if (pVeh->m_pPilot->s.numberm_pPilot->client->ps.stats[ STAT_WEAPONS ] |= 1; // Riding means you get WP_NONE + pVeh->m_pPilot->client->ps.weapons[WP_NONE] = 1; // Riding means you get WP_NONE CG_ChangeWeapon(WP_NONE); } diff --git a/code/game/ai.h b/code/game/ai.h index da79271682..329dfe8c84 100644 --- a/code/game/ai.h +++ b/code/game/ai.h @@ -59,7 +59,8 @@ typedef enum //# rank_e RANK_LT, RANK_LT_COMM, RANK_COMMANDER, - RANK_CAPTAIN + RANK_CAPTAIN, + RANK_MAX } rank_t; qboolean NPC_CheckPlayerTeamStealth( void ); @@ -115,18 +116,39 @@ void NPC_BSAnimal_Default( void ); //Group AI #define MAX_FRAME_GROUPS 32 // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct AIGroupMember_s +class AIGroupMember_t { +public: int number; int waypoint; int pathCostToEnemy; int closestBuddy; -} AIGroupMember_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(number); + saved_game.write(waypoint); + saved_game.write(pathCostToEnemy); + saved_game.write(closestBuddy); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(number); + saved_game.read(waypoint); + saved_game.read(pathCostToEnemy); + saved_game.read(closestBuddy); + } +}; // AIGroupMember_t #define MAX_GROUP_MEMBERS 32 // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct AIGroupInfo_s +class AIGroupInfo_t { +public: int numGroup; qboolean processed; team_t team; @@ -144,7 +166,53 @@ typedef struct AIGroupInfo_s vec3_t enemyLastSeenPos; int numState[ NUM_SQUAD_STATES ]; AIGroupMember_t member[ MAX_GROUP_MEMBERS ]; -} AIGroupInfo_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(numGroup); + saved_game.write(processed); + saved_game.write(team); + saved_game.write(enemy); + saved_game.write(enemyWP); + saved_game.write(speechDebounceTime); + saved_game.write(lastClearShotTime); + saved_game.write(lastSeenEnemyTime); + saved_game.write(morale); + saved_game.write(moraleAdjust); + saved_game.write(moraleDebounce); + saved_game.write(memberValidateTime); + saved_game.write(activeMemberNum); + saved_game.write(commander); + saved_game.write(enemyLastSeenPos); + saved_game.write(numState); + saved_game.write<>(member); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(numGroup); + saved_game.read(processed); + saved_game.read(team); + saved_game.read(enemy); + saved_game.read(enemyWP); + saved_game.read(speechDebounceTime); + saved_game.read(lastClearShotTime); + saved_game.read(lastSeenEnemyTime); + saved_game.read(morale); + saved_game.read(moraleAdjust); + saved_game.read(moraleDebounce); + saved_game.read(memberValidateTime); + saved_game.read(activeMemberNum); + saved_game.read(commander); + saved_game.read(enemyLastSeenPos); + saved_game.read(numState); + saved_game.read<>(member); + } +}; // AIGroupInfo_t + int AI_GetGroupSize( vec3_t origin, int radius, team_t playerTeam, gentity_t *avoid = NULL ); int AI_GetGroupSize( gentity_t *ent, int radius ); diff --git a/code/game/anims.h b/code/game/anims.h index e234f5f73b..05ebe31892 100644 --- a/code/game/anims.h +++ b/code/game/anims.h @@ -1640,6 +1640,10 @@ typedef enum //# animNumber_e BOTH_SIT6, BOTH_SIT7, + BOTH_ALERT1, + BOTH_TRIUMPHANT1START, + BOTH_TRIUMPHANT1STARTGESTURE, + BOTH_TRIUMPHANT1STOP, //================================================= //ANIMS IN WHICH ONLY THE UPPER OBJECTS ARE IN MD3 @@ -1670,6 +1674,10 @@ typedef enum //# animNumber_e TORSO_HANDSIGNAL3, TORSO_HANDSIGNAL4, TORSO_HANDSIGNAL5, + + TORSO_RAISEWEAP2, + TORSO_DROPWEAP2, + TORSO_WEAPONIDLE1, //================================================= @@ -1817,6 +1825,65 @@ typedef enum //# animNumber_e #define SABER_ANIM_GROUP_SIZE (BOTH_A2_T__B_ - BOTH_A1_T__B_) +/* + ====================================== + Viewmodel Animations + ====================================== + */ + +enum { + // Basic + VM_READY, + VM_IDLE, + VM_RAISE, + VM_LOWER, + VM_FIRE, + VM_MELEE, + VM_RELOAD, + + // Force powers + VM_FPUSH, + VM_FPULL, + VM_FGRIP, + VM_FGRIP_HOLD, + VM_FGRIP_RELEASE, + VM_FGRIP_THROW, + VM_FHEAL_QUICK, + VM_FHEAL_START, + VM_FHEAL_STOP, + VM_FLIGHTNING, + VM_FLIGHTNING_START, + VM_FLIGHTNING_HOLD, + VM_FLIGHTNING_RELEASE, + VM_FRESISTPUSH, + VM_FMINDTRICK1, + VM_FMINDTRICK2, + + // JA stuff + VM_2H_FLIGHTNING, + VM_2H_FLIGHTNING_HOLD, + VM_2H_FLIGHTNING_RELEASE, + VM_FDRAIN, + VM_FDRAIN_START, + VM_FDRAIN_HOLD, + VM_FDRAIN_RELEASE, + VM_FPROTECT, + VM_FPROTECT_FAST, + VM_FRAGE, + VM_FABSORB, + VM_FABSORB_START, + VM_FABSORB_END, + + MAX_VIEWMODEL_ANIMATIONS +}; + +typedef struct +{ + char filename[MAX_QPATH]; + animation_t animations[MAX_VIEWMODEL_ANIMATIONS]; +} viewModelAnimSet_t; + +extern stringID_table_t vmAnimTable [MAX_VIEWMODEL_ANIMATIONS+1]; #endif// #ifndef __ANIMS_H__ diff --git a/code/game/b_local.h b/code/game/b_local.h index aee10a6478..5bbfa1f0cf 100644 --- a/code/game/b_local.h +++ b/code/game/b_local.h @@ -113,11 +113,10 @@ extern void NPC_StartFlee( gentity_t *enemy, vec3_t dangerPoint, int dangerLevel extern void G_StartFlee( gentity_t *self, gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int fleeTimeMin, int fleeTimeMax ); //NPC_combat -extern int ChooseBestWeapon( void ); extern void NPC_ChangeWeapon( int newWeapon ); extern void ShootThink( void ); extern void WeaponThink( qboolean inCombat ); -extern qboolean HaveWeapon( int weapon ); +extern qboolean HaveWeapon( gentity_t* ent, int weapon ); extern qboolean CanShoot ( gentity_t *ent, gentity_t *shooter ); extern void NPC_CheckPossibleEnemy( gentity_t *other, visibility_t vis ); extern gentity_t *NPC_PickEnemy (gentity_t *closestTo, int enemyTeam, qboolean checkVis, qboolean findPlayersFirst, qboolean findClosest); @@ -152,15 +151,8 @@ extern void NPC_DeleteFromFormation (gentity_t *self); #define NUM_POSITIONS 30 //NPC spawnflags -#define SFB_SMALLHULL 1 - #define SFB_RIFLEMAN 2 -#define SFB_OLDBORG 2//Borg #define SFB_PHASER 4 -#define SFB_GUN 4//Borg -#define SFB_TRICORDER 8 -#define SFB_TASER 8//Borg -#define SFB_DRILL 16//Borg #define SFB_CINEMATIC 32 #define SFB_NOTSOLID 64 @@ -317,8 +309,8 @@ extern qboolean NPC_SetCombatPoint( int combatPointID ); #define MAX_COMBAT_POINT_CHECK 32 -extern int NPC_ValidEnemy( gentity_t *ent ); -extern int NPC_CheckEnemyExt( qboolean checkAlerts = qfalse ); +extern qboolean NPC_ValidEnemy( gentity_t *ent ); +extern qboolean NPC_CheckEnemyExt( qboolean checkAlerts = qfalse ); extern qboolean NPC_FindPlayer( void ); extern qboolean NPC_CheckCanAttackExt( void ); diff --git a/code/game/b_public.h b/code/game/b_public.h index c2c9ddb622..231e7a71a7 100644 --- a/code/game/b_public.h +++ b/code/game/b_public.h @@ -132,8 +132,9 @@ typedef enum } sexType_t; // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct gNPCstats_e +class gNPCstats_t {//Stats, loaded in, and can be set by scripts +public: //AI int aggression; // " int aim; // " @@ -155,16 +156,82 @@ typedef struct gNPCstats_e int acceleration; //sex sexType_t sex; //male, female, etc. -} gNPCstats_t; + //new fields + qboolean altFire; //NPC ONLY: forces to use altFire always, allows NPC to remember its native firing mode + qboolean restrictJediPowers; //NPC ONLY: keeps from *ever* using dodge and push abilities without possessing those force powers + int meleeKicks; //use kicks with WP_MELEE? If 1-5 controls frequency + int meleeKatas; //use melee katas with WP_MELEE? If 1-5 controls frequency + int saberMeleeKatas; //NPC ONLY: use melee katas with WP_SABER? kyle_boss ignores this + qboolean rareFire; + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(aggression); + saved_game.write(aim); + saved_game.write(earshot); + saved_game.write(evasion); + saved_game.write(hfov); + saved_game.write(intelligence); + saved_game.write(move); + saved_game.write(reactions); + saved_game.write(shootDistance); + saved_game.write(vfov); + saved_game.write(vigilance); + saved_game.write(visrange); + saved_game.write(runSpeed); + saved_game.write(walkSpeed); + saved_game.write(yawSpeed); + saved_game.write(health); + saved_game.write(acceleration); + saved_game.write(sex); + saved_game.write(altFire); + saved_game.write(restrictJediPowers); + saved_game.write(meleeKicks); + saved_game.write(meleeKatas); + saved_game.write(saberMeleeKatas); + saved_game.write(rareFire); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(aggression); + saved_game.read(aim); + saved_game.read(earshot); + saved_game.read(evasion); + saved_game.read(hfov); + saved_game.read(intelligence); + saved_game.read(move); + saved_game.read(reactions); + saved_game.read(shootDistance); + saved_game.read(vfov); + saved_game.read(vigilance); + saved_game.read(visrange); + saved_game.read(runSpeed); + saved_game.read(walkSpeed); + saved_game.read(yawSpeed); + saved_game.read(health); + saved_game.read(acceleration); + saved_game.read(sex); + saved_game.read(altFire); + saved_game.read(restrictJediPowers); + saved_game.read(meleeKicks); + saved_game.read(meleeKatas); + saved_game.read(saberMeleeKatas); + saved_game.read(rareFire); + } +}; // gNPCstats_t #define MAX_ENEMY_POS_LAG 2400 #define ENEMY_POS_LAG_INTERVAL 100 #define ENEMY_POS_LAG_STEPS (MAX_ENEMY_POS_LAG/ENEMY_POS_LAG_INTERVAL) // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct +class gNPC_t { +public: //FIXME: Put in playerInfo or something int timeOfDeath; //FIXME do we really need both of these gentity_t *touchedByPlayer; @@ -323,6 +390,9 @@ typedef struct int controlledTime; //controlled by player int surrenderTime; //Hands up int kneelTime; //kneeling (for troopers) + + int insanityTime; //Insanity! + int darkCharmedTime; //Charmed to enemy team (dark version) //Lagging enemy position - FIXME: seems awful wasteful... vec3_t enemyLaggedPos[ENEMY_POS_LAG_STEPS]; @@ -332,7 +402,255 @@ typedef struct int ffireCount; //sigh... you'd think I'd be able to find a way to do this without having to use 3 int fields, but... int ffireDebounce; int ffireFadeDebounce; -} gNPC_t; + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(timeOfDeath); + saved_game.write(touchedByPlayer); + saved_game.write(enemyLastVisibility); + saved_game.write(aimTime); + saved_game.write(desiredYaw); + saved_game.write(desiredPitch); + saved_game.write(lockedDesiredYaw); + saved_game.write(lockedDesiredPitch); + saved_game.write(aimingBeam); + saved_game.write(enemyLastSeenLocation); + saved_game.write(enemyLastSeenTime); + saved_game.write(enemyLastHeardLocation); + saved_game.write(enemyLastHeardTime); + saved_game.write(lastAlertID); + saved_game.write(eFlags); + saved_game.write(aiFlags); + saved_game.write(currentAmmo); + saved_game.write(shotTime); + saved_game.write(burstCount); + saved_game.write(burstMin); + +#ifdef BASE_SAVE_COMPAT + saved_game.write(burstMean); +#endif // BASE_SAVE_COMPAT + + saved_game.write(burstMax); + saved_game.write(burstSpacing); + saved_game.write(attackHold); + saved_game.write(attackHoldTime); + saved_game.write(shootAngles); + saved_game.write(rank); + saved_game.write(behaviorState); + saved_game.write(defaultBehavior); + saved_game.write(tempBehavior); + saved_game.write(ignorePain); + saved_game.write(duckDebounceTime); + saved_game.write(walkDebounceTime); + saved_game.write(enemyCheckDebounceTime); + saved_game.write(investigateDebounceTime); + saved_game.write(investigateCount); + saved_game.write(investigateGoal); + saved_game.write(investigateSoundDebounceTime); + saved_game.write(greetingDebounceTime); + saved_game.write(eventOwner); + saved_game.write(coverTarg); + saved_game.write(jumpState); + saved_game.write(followDist); + saved_game.write(tempGoal); + saved_game.write(goalEntity); + saved_game.write(lastGoalEntity); + saved_game.write(eventualGoal); + saved_game.write(captureGoal); + saved_game.write(defendEnt); + saved_game.write(greetEnt); + saved_game.write(goalTime); + saved_game.write(straightToGoal); + saved_game.write(distToGoal); + saved_game.write(navTime); + saved_game.write(blockingEntNum); + saved_game.write(blockedSpeechDebounceTime); + saved_game.write(homeWp); + saved_game.write(avoidSide); + saved_game.write(leaderAvoidSide); + saved_game.write(lastAvoidSteerSide); + saved_game.write(lastAvoidSteerSideDebouncer); + saved_game.write(group); + saved_game.write(troop); + saved_game.write(lastPathAngles); + saved_game.write<>(stats); + saved_game.write(aimErrorDebounceTime); + saved_game.write(lastAimErrorYaw); + saved_game.write(lastAimErrorPitch); + saved_game.write(aimOfs); + saved_game.write(currentAim); + saved_game.write(currentAggression); + saved_game.write(scriptFlags); + saved_game.write(desiredSpeed); + saved_game.write(currentSpeed); + saved_game.write(last_forwardmove); + saved_game.write(last_rightmove); + saved_game.skip(2); + saved_game.write(lastClearOrigin); + saved_game.write(shoveCount); + saved_game.write(blockedDebounceTime); + saved_game.write(blockedEntity); + saved_game.write(blockedTargetPosition); + saved_game.write(blockedTargetEntity); + saved_game.write(jumpDest); + saved_game.write(jumpTarget); + saved_game.write(jumpMaxXYDist); + saved_game.write(jumpMazZDist); + saved_game.write(jumpSide); + saved_game.write(jumpTime); + saved_game.write(jumpBackupTime); + saved_game.write(jumpNextCheckTime); + saved_game.write(combatPoint); + saved_game.write(lastFailedCombatPoint); + saved_game.write(movementSpeech); + saved_game.write(movementSpeechChance); + saved_game.write(nextBStateThink); + saved_game.write<>(last_ucmd); + saved_game.write(combatMove); + saved_game.write(goalRadius); + saved_game.write(pauseTime); + saved_game.write(standTime); + saved_game.write(localState); + saved_game.write(squadState); + saved_game.write(confusionTime); + saved_game.write(charmedTime); + saved_game.write(controlledTime); + saved_game.write(surrenderTime); + saved_game.write(kneelTime); + saved_game.write(insanityTime); + saved_game.write(darkCharmedTime); + saved_game.write(enemyLaggedPos); + saved_game.write(watchTarget); + saved_game.write(ffireCount); + saved_game.write(ffireDebounce); + saved_game.write(ffireFadeDebounce); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(timeOfDeath); + saved_game.read(touchedByPlayer); + saved_game.read(enemyLastVisibility); + saved_game.read(aimTime); + saved_game.read(desiredYaw); + saved_game.read(desiredPitch); + saved_game.read(lockedDesiredYaw); + saved_game.read(lockedDesiredPitch); + saved_game.read(aimingBeam); + saved_game.read(enemyLastSeenLocation); + saved_game.read(enemyLastSeenTime); + saved_game.read(enemyLastHeardLocation); + saved_game.read(enemyLastHeardTime); + saved_game.read(lastAlertID); + saved_game.read(eFlags); + saved_game.read(aiFlags); + saved_game.read(currentAmmo); + saved_game.read(shotTime); + saved_game.read(burstCount); + saved_game.read(burstMin); + +#ifdef BASE_SAVE_COMPAT + saved_game.read(burstMean); +#endif // BASE_SAVE_COMPAT + + saved_game.read(burstMax); + saved_game.read(burstSpacing); + saved_game.read(attackHold); + saved_game.read(attackHoldTime); + saved_game.read(shootAngles); + saved_game.read(rank); + saved_game.read(behaviorState); + saved_game.read(defaultBehavior); + saved_game.read(tempBehavior); + saved_game.read(ignorePain); + saved_game.read(duckDebounceTime); + saved_game.read(walkDebounceTime); + saved_game.read(enemyCheckDebounceTime); + saved_game.read(investigateDebounceTime); + saved_game.read(investigateCount); + saved_game.read(investigateGoal); + saved_game.read(investigateSoundDebounceTime); + saved_game.read(greetingDebounceTime); + saved_game.read(eventOwner); + saved_game.read(coverTarg); + saved_game.read(jumpState); + saved_game.read(followDist); + saved_game.read(tempGoal); + saved_game.read(goalEntity); + saved_game.read(lastGoalEntity); + saved_game.read(eventualGoal); + saved_game.read(captureGoal); + saved_game.read(defendEnt); + saved_game.read(greetEnt); + saved_game.read(goalTime); + saved_game.read(straightToGoal); + saved_game.read(distToGoal); + saved_game.read(navTime); + saved_game.read(blockingEntNum); + saved_game.read(blockedSpeechDebounceTime); + saved_game.read(homeWp); + saved_game.read(avoidSide); + saved_game.read(leaderAvoidSide); + saved_game.read(lastAvoidSteerSide); + saved_game.read(lastAvoidSteerSideDebouncer); + saved_game.read(group); + saved_game.read(troop); + saved_game.read(lastPathAngles); + saved_game.read<>(stats); + saved_game.read(aimErrorDebounceTime); + saved_game.read(lastAimErrorYaw); + saved_game.read(lastAimErrorPitch); + saved_game.read(aimOfs); + saved_game.read(currentAim); + saved_game.read(currentAggression); + saved_game.read(scriptFlags); + saved_game.read(desiredSpeed); + saved_game.read(currentSpeed); + saved_game.read(last_forwardmove); + saved_game.read(last_rightmove); + saved_game.skip(2); + saved_game.read(lastClearOrigin); + saved_game.read(shoveCount); + saved_game.read(blockedDebounceTime); + saved_game.read(blockedEntity); + saved_game.read(blockedTargetPosition); + saved_game.read(blockedTargetEntity); + saved_game.read(jumpDest); + saved_game.read(jumpTarget); + saved_game.read(jumpMaxXYDist); + saved_game.read(jumpMazZDist); + saved_game.read(jumpSide); + saved_game.read(jumpTime); + saved_game.read(jumpBackupTime); + saved_game.read(jumpNextCheckTime); + saved_game.read(combatPoint); + saved_game.read(lastFailedCombatPoint); + saved_game.read(movementSpeech); + saved_game.read(movementSpeechChance); + saved_game.read(nextBStateThink); + saved_game.read<>(last_ucmd); + saved_game.read(combatMove); + saved_game.read(goalRadius); + saved_game.read(pauseTime); + saved_game.read(standTime); + saved_game.read(localState); + saved_game.read(squadState); + saved_game.read(confusionTime); + saved_game.read(charmedTime); + saved_game.read(controlledTime); + saved_game.read(surrenderTime); + saved_game.read(kneelTime); + saved_game.read(insanityTime); + saved_game.read(darkCharmedTime); + saved_game.read(enemyLaggedPos); + saved_game.read(watchTarget); + saved_game.read(ffireCount); + saved_game.read(ffireDebounce); + saved_game.read(ffireFadeDebounce); + } +}; // gNPC_t void G_SquadPathsInit(void); void NPC_InitGame( void ); diff --git a/code/game/bg_local.h b/code/game/bg_local.h index 88e1e4eb76..a54085e4ec 100644 --- a/code/game/bg_local.h +++ b/code/game/bg_local.h @@ -78,6 +78,8 @@ void PM_AddEvent( int newEvent ); qboolean PM_SlideMove( float gravity ); void PM_StepSlideMove( float gravity ); +extern qboolean BG_AllowThirdPersonSpecialMove( playerState_t *ps ); + #endif diff --git a/code/game/bg_misc.cpp b/code/game/bg_misc.cpp index 8bf42c7f28..583b0bbe07 100644 --- a/code/game/bg_misc.cpp +++ b/code/game/bg_misc.cpp @@ -348,7 +348,7 @@ qboolean BG_CanItemBeGrabbed( const entityState_t *ent, const playerState_t *ps case IT_WEAPON: // See if we already have this weapon. - if ( !(ps->stats[ STAT_WEAPONS ] & ( 1 << item->giTag ))) + if ( !(ps->weapons[item->giTag]) ) { // Don't have this weapon yet, so pick it up. return qtrue; @@ -375,19 +375,19 @@ qboolean BG_CanItemBeGrabbed( const entityState_t *ent, const playerState_t *ps switch( item->giTag ) { case AMMO_THERMAL: - if( !(ps->stats[STAT_WEAPONS] & ( 1 << WP_THERMAL ) ) ) + if( !(ps->weapons[WP_THERMAL] ) ) { return qtrue; } break; case AMMO_DETPACK: - if( !(ps->stats[STAT_WEAPONS] & ( 1 << WP_DET_PACK ) ) ) + if( !(ps->weapons[WP_DET_PACK] ) ) { return qtrue; } break; case AMMO_TRIPMINE: - if( !(ps->stats[STAT_WEAPONS] & ( 1 << WP_TRIP_MINE ) ) ) + if( !(ps->weapons[WP_TRIP_MINE] ) ) { return qtrue; } diff --git a/code/game/bg_pangles.cpp b/code/game/bg_pangles.cpp index 052dc31cf0..9104d536bf 100644 --- a/code/game/bg_pangles.cpp +++ b/code/game/bg_pangles.cpp @@ -49,6 +49,7 @@ extern qboolean PM_InGetUpNoRoll( playerState_t *ps ); extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent ); extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); extern qboolean G_ControlledByPlayer( gentity_t *self ); +extern qboolean PlayerAffectedByStasis( void ); extern qboolean cg_usingInFrontOf; extern qboolean player_locked; @@ -368,11 +369,18 @@ void PM_IKUpdate( gentity_t *ent ) void BG_G2SetBoneAngles( centity_t *cent, gentity_t *gent, int boneIndex, const vec3_t angles, const int flags, - const Eorientations up, const Eorientations right, const Eorientations forward, qhandle_t *modelList ) + const Eorientations up, const Eorientations right, const Eorientations forward, qhandle_t *modelList, qboolean isHead ) { if (boneIndex!=-1) { - gi.G2API_SetBoneAnglesIndex( ¢->gent->ghoul2[0], boneIndex, angles, flags, up, right, forward, modelList, 0, 0 ); + if (isHead && cent->gent->headModel > 0) + { + gi.G2API_SetBoneAnglesIndex( ¢->gent->ghoul2[cent->gent->headModel], boneIndex, angles, flags, up, right, forward, modelList, 0, 0 ); + } + else + { + gi.G2API_SetBoneAnglesIndex( ¢->gent->ghoul2[0], boneIndex, angles, flags, up, right, forward, modelList, 0, 0 ); + } } } @@ -510,7 +518,7 @@ qboolean PM_AdjustAngleForWallRun( gentity_t *ent, usercmd_t *ucmd, qboolean doM {//still a vertical wall there //FIXME: don't pull around 90 turns //FIXME: simulate stepping up steps here, somehow? - if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || !player_locked ) + if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || (!player_locked && !PlayerAffectedByStasis()) ) { if ( ent->client->ps.legsAnim == BOTH_WALL_RUN_RIGHT ) { @@ -536,7 +544,7 @@ qboolean PM_AdjustAngleForWallRun( gentity_t *ent, usercmd_t *ucmd, qboolean doM SetClientViewAngle( ent, ent->client->ps.viewangles ); } ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW]; - if ( (ent->s.number&&!G_ControlledByPlayer(ent)) || !player_locked ) + if ( (ent->s.number&&!G_ControlledByPlayer(ent)) || (!player_locked && !PlayerAffectedByStasis()) ) { if ( doMove ) { @@ -645,7 +653,7 @@ qboolean PM_AdjustAnglesForSpinningFlip( gentity_t *ent, usercmd_t *ucmd, qboole //push me if ( ent->client->ps.legsAnimTimer > 300 )//&& ent->client->ps.groundEntityNum == ENTITYNUM_NONE ) {//haven't landed or reached end of anim yet - if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || !player_locked ) + if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || (!player_locked && !PlayerAffectedByStasis()) ) { vec3_t pushDir, pushAngles = {0,ent->angle,0}; AngleVectors( pushAngles, pushDir, NULL, NULL ); @@ -729,6 +737,7 @@ qboolean PM_AdjustAnglesForSaberLock( gentity_t *ent, usercmd_t *ucmd ) return qfalse; } +extern qboolean NPC_JediClass(int className); int G_MinGetUpTime( gentity_t *ent ) { if ( ent @@ -763,7 +772,20 @@ int G_MinGetUpTime( gentity_t *ent ) return getUpTime; } } - return 200; + else if (ent->NPC && NPC_JediClass(ent->client->NPC_class)) + {//you must have Jedi reflexes... + int getUpTime = 250; + if (ent->NPC->rank > RANK_ENSIGN) + { + getUpTime *= 2; + } + if (g_spskill->integer >= 2) { + getUpTime *= 2; + } //max 1000 + + return getUpTime; + } + return 200; //200 } qboolean PM_AdjustAnglesForKnockdown( gentity_t *ent, usercmd_t *ucmd, qboolean angleClampOnly ) @@ -893,7 +915,7 @@ qboolean PM_AdjustAngleForWallRunUp( gentity_t *ent, usercmd_t *ucmd, qboolean d {//all clear, keep going //FIXME: don't pull around 90 turns //FIXME: simulate stepping up steps here, somehow? - if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || !player_locked ) + if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || (!player_locked && !PlayerAffectedByStasis()) ) { ucmd->forwardmove = 127; } @@ -912,7 +934,7 @@ qboolean PM_AdjustAngleForWallRunUp( gentity_t *ent, usercmd_t *ucmd, qboolean d SetClientViewAngle( ent, ent->client->ps.viewangles ); } ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW]; - if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || !player_locked ) + if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || (!player_locked && !PlayerAffectedByStasis()) ) { if ( doMove ) { @@ -1052,7 +1074,7 @@ qboolean PM_AdjustAngleForWallJump( gentity_t *ent, usercmd_t *ucmd, qboolean do SetClientViewAngle( ent, ent->client->ps.viewangles ); } ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW]; - if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || !player_locked ) + if ( (ent->s.number>=MAX_CLIENTS&&!G_ControlledByPlayer(ent)) || (!player_locked && !PlayerAffectedByStasis()) ) { if ( doMove ) { @@ -1349,7 +1371,7 @@ qboolean G_OkayToLean( playerState_t *ps, usercmd_t *cmd, qboolean interruptOkay && !ps->legsAnimTimer//not in any held legs anim && !ps->torsoAnimTimer) //not in any held torso anim ) - && !(cmd->buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_LIGHTNING|BUTTON_USE_FORCE|BUTTON_FORCE_DRAIN|BUTTON_FORCEGRIP))//not trying to attack + && !(cmd->buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_LIGHTNING|BUTTON_USE_FORCE|BUTTON_FORCE_DRAIN|BUTTON_FORCEGRIP|BUTTON_REPULSE))//not trying to attack //&& (ps->forcePowersActive&(1<velocity, vec3_origin )//not moving && !cg_usingInFrontOf )//use button wouldn't be used for anything else @@ -1395,6 +1417,11 @@ void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, gentity_t *gent ) return; // no view changes at all } + if ( ps->stasisTime > level.time ) + { + return; + } + // if ( player_locked ) // {//can't turn // return; @@ -1591,7 +1618,7 @@ void PM_UpdateViewAngles( playerState_t *ps, usercmd_t *cmd, gentity_t *gent ) { //only in the real pmove if ( (cmd->buttons & BUTTON_USE) ) {//check leaning - if ( cg.renderingThirdPerson ) + if ( BG_AllowThirdPersonSpecialMove( ps ) ) {//third person lean if ( G_OkayToLean( ps, cmd, qtrue ) && (cmd->rightmove || (cmd->forwardmove && g_debugMelee->integer ) ) )//pushing a direction diff --git a/code/game/bg_panimate.cpp b/code/game/bg_panimate.cpp index 48b23f04f6..8da729a43d 100644 --- a/code/game/bg_panimate.cpp +++ b/code/game/bg_panimate.cpp @@ -3,23 +3,20 @@ Copyright (C) 2000 - 2013, Raven Software, Inc. Copyright (C) 2001 - 2013, Activision, Inc. Copyright (C) 2013 - 2015, OpenJK contributors - This file is part of the OpenJK source code. - OpenJK is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. - This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along with this program; if not, see . =========================================================================== */ +// this include must remain at the top of every bg_xxxx CPP file #include "common_headers.h" @@ -51,6 +48,10 @@ extern cvar_t *g_saberAnimSpeed; extern cvar_t *g_saberAutoAim; extern cvar_t *g_speederControlScheme; extern cvar_t *g_saberNewControlScheme; +extern cvar_t *g_saberNewCombat; +extern cvar_t *g_saberForceDrains; +extern cvar_t *g_saberForceDrainAmount; +extern cvar_t *g_noIgniteTwirl; extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); @@ -90,23 +91,23 @@ extern qboolean G_ControlledByPlayer( gentity_t *self ); extern int g_crosshairEntNum; -int PM_AnimLength( int index, animNumber_t anim ); -qboolean PM_LockedAnim( int anim ); -qboolean PM_StandingAnim( int anim ); -qboolean PM_InOnGroundAnim ( playerState_t *ps ); -qboolean PM_SuperBreakWinAnim( int anim ); -qboolean PM_SuperBreakLoseAnim( int anim ); -qboolean PM_LockedAnim( int anim ); -saberMoveName_t PM_SaberFlipOverAttackMove( void ); -qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ); -saberMoveName_t PM_SaberJumpForwardAttackMove( void ); -qboolean PM_CheckJumpForwardAttackMove( void ); -saberMoveName_t PM_SaberBackflipAttackMove( void ); -qboolean PM_CheckBackflipAttackMove( void ); -saberMoveName_t PM_SaberDualJumpAttackMove( void ); -qboolean PM_CheckDualJumpAttackMove( void ); -saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ); -qboolean PM_CheckLungeAttackMove( void ); +int PM_AnimLength(int index, animNumber_t anim); +qboolean PM_LockedAnim(int anim); +qboolean PM_StandingAnim(int anim); +qboolean PM_InOnGroundAnim(playerState_t *ps); +qboolean PM_SuperBreakWinAnim(int anim); +qboolean PM_SuperBreakLoseAnim(int anim); +qboolean PM_LockedAnim(int anim); +saberMoveName_t PM_SaberFlipOverAttackMove(void); +qboolean PM_CheckFlipOverAttackMove(qboolean checkEnemy); +saberMoveName_t PM_SaberJumpForwardAttackMove(void); +qboolean PM_CheckJumpForwardAttackMove(void); +saberMoveName_t PM_SaberBackflipAttackMove(void); +qboolean PM_CheckBackflipAttackMove(void); +saberMoveName_t PM_SaberDualJumpAttackMove(void); +qboolean PM_CheckDualJumpAttackMove(void); +saberMoveName_t PM_SaberLungeAttackMove(qboolean fallbackToNormalLunge); +qboolean PM_CheckLungeAttackMove(void); // Okay, here lies the much-dreaded Pat-created FSM movement chart... Heretic II strikes again! // Why am I inflicting this on you? Well, it's better than hardcoded states. // Ideally this will be replaced with an external file or more sophisticated move-picker @@ -121,205 +122,205 @@ qboolean PM_CheckLungeAttackMove( void ); //FIXME: add the alternate anims for each style? saberMoveData_t saberMoveData[LS_MOVE_MAX] = {// NB:randomized // name anim(do all styles?)startQ endQ setanimflag blend, blocking chain_idle chain_attack trailLen - {"None", BOTH_STAND1, Q_R, Q_R, AFLAG_IDLE, 350, BLK_NO, LS_NONE, LS_NONE, 0 }, // LS_NONE = 0, + { "None", BOTH_STAND1, Q_R, Q_R, AFLAG_IDLE, 350, BLK_NO, LS_NONE, LS_NONE, 0 }, // LS_NONE = 0, // General movements with saber - {"Ready", BOTH_STAND2, Q_R, Q_R, AFLAG_IDLE, 350, BLK_WIDE, LS_READY, LS_S_R2L, 0 }, // LS_READY, - {"Draw", BOTH_STAND1TO2, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_DRAW, - {"Putaway", BOTH_STAND2TO1, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_PUTAWAY, + { "Ready", BOTH_STAND2, Q_R, Q_R, AFLAG_IDLE, 350, BLK_WIDE, LS_READY, LS_S_R2L, 0 }, // LS_READY, + { "Draw", BOTH_STAND1TO2, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_DRAW, + { "Putaway", BOTH_STAND2TO1, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_PUTAWAY, // Attacks //UL2LR - {"TL2BR Att", BOTH_A1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TL2BR, LS_R_TL2BR, 200 }, // LS_A_TL2BR + { "TL2BR Att", BOTH_A1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TL2BR, LS_R_TL2BR, 200 }, // LS_A_TL2BR //SLASH LEFT - {"L2R Att", BOTH_A1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_L2R, LS_R_L2R, 200 }, // LS_A_L2R + { "L2R Att", BOTH_A1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_L2R, LS_R_L2R, 200 }, // LS_A_L2R //LL2UR - {"BL2TR Att", BOTH_A1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_TIGHT, LS_R_BL2TR, LS_R_BL2TR, 200 }, // LS_A_BL2TR + { "BL2TR Att", BOTH_A1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_TIGHT, LS_R_BL2TR, LS_R_BL2TR, 200 }, // LS_A_BL2TR //LR2UL - {"BR2TL Att", BOTH_A1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_BR2TL, LS_R_BR2TL, 200 }, // LS_A_BR2TL + { "BR2TL Att", BOTH_A1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_BR2TL, LS_R_BR2TL, 200 }, // LS_A_BR2TL //SLASH RIGHT - {"R2L Att", BOTH_A1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_R2L, LS_R_R2L, 200 },// LS_A_R2L + { "R2L Att", BOTH_A1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_R2L, LS_R_R2L, 200 },// LS_A_R2L //UR2LL - {"TR2BL Att", BOTH_A1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TR2BL, LS_R_TR2BL, 200 }, // LS_A_TR2BL + { "TR2BL Att", BOTH_A1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TR2BL, LS_R_TR2BL, 200 }, // LS_A_TR2BL //SLASH DOWN - {"T2B Att", BOTH_A1_T__B_, Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_T2B, LS_R_T2B, 200 }, // LS_A_T2B + { "T2B Att", BOTH_A1_T__B_, Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_T2B, LS_R_T2B, 200 }, // LS_A_T2B //special attacks - {"Back Stab", BOTH_A2_STABBACK1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACKSTAB - {"Back Att", BOTH_ATTACK_BACK, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK - {"CR Back Att", BOTH_CROUCHATTACKBACK1,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK_CR - {"RollStab", BOTH_ROLL_STAB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_ROLL_STAB - {"Lunge Att", BOTH_LUNGE2_B__T_, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_LUNGE - {"Jump Att", BOTH_FORCELEAP2_T__B_,Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_JUMP_T__B_ - {"Flip Stab", BOTH_JUMPFLIPSTABDOWN,Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_FLIP_STAB - {"Flip Slash", BOTH_JUMPFLIPSLASHDOWN1,Q_L,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R_T_, 200 }, // LS_A_FLIP_SLASH - {"DualJump Atk",BOTH_JUMPATTACK6, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_BL_TR, 200 }, // LS_JUMPATTACK_DUAL - - {"DualJumpAtkL_A",BOTH_ARIAL_LEFT, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TL2BR, 200 }, // LS_JUMPATTACK_ARIAL_LEFT - {"DualJumpAtkR_A",BOTH_ARIAL_RIGHT, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TR2BL, 200 }, // LS_JUMPATTACK_ARIAL_RIGHT - - {"DualJumpAtkL_A",BOTH_CARTWHEEL_LEFT, Q_R,Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TL_BR, 200 }, // LS_JUMPATTACK_CART_LEFT - {"DualJumpAtkR_A",BOTH_CARTWHEEL_RIGHT, Q_R,Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TR_BL, 200 }, // LS_JUMPATTACK_CART_RIGHT - - {"DualJumpAtkLStaff", BOTH_BUTTERFLY_FL1,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_JUMPATTACK_STAFF_LEFT - {"DualJumpAtkRStaff", BOTH_BUTTERFLY_FR1,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_JUMPATTACK_STAFF_RIGHT - - {"ButterflyLeft", BOTH_BUTTERFLY_LEFT,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_BUTTERFLY_LEFT - {"ButterflyRight", BOTH_BUTTERFLY_RIGHT,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_BUTTERFLY_RIGHT - - {"BkFlip Atk", BOTH_JUMPATTACK7, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_BACKFLIP_ATK - {"DualSpinAtk", BOTH_SPINATTACK6, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_DUAL - {"StfSpinAtk", BOTH_SPINATTACK7, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK - {"LngLeapAtk", BOTH_FORCELONGLEAP_ATTACK,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_LEAP_ATTACK - {"SwoopAtkR", BOTH_VS_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_RIGHT - {"SwoopAtkL", BOTH_VS_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_LEFT - {"TauntaunAtkR",BOTH_VT_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_RIGHT - {"TauntaunAtkL",BOTH_VT_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_LEFT - {"StfKickFwd", BOTH_A7_KICK_F, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F - {"StfKickBack", BOTH_A7_KICK_B, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B - {"StfKickRight",BOTH_A7_KICK_R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R - {"StfKickLeft", BOTH_A7_KICK_L, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L - {"StfKickSpin", BOTH_A7_KICK_S, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_S - {"StfKickBkFwd",BOTH_A7_KICK_BF, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_BF - {"StfKickSplit",BOTH_A7_KICK_RL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_RL - {"StfKickFwdAir",BOTH_A7_KICK_F_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F_AIR - {"StfKickBackAir",BOTH_A7_KICK_B_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B_AIR - {"StfKickRightAir",BOTH_A7_KICK_R_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R_AIR - {"StfKickLeftAir",BOTH_A7_KICK_L_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L_AIR - {"StabDown", BOTH_STABDOWN, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN - {"StabDownStf", BOTH_STABDOWN_STAFF,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_STAFF - {"StabDownDual",BOTH_STABDOWN_DUAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_DUAL - {"dualspinprot",BOTH_A6_SABERPROTECT,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_DUAL_SPIN_PROTECT - {"StfSoulCal", BOTH_A7_SOULCAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_STAFF_SOULCAL - {"specialfast", BOTH_A1_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A1_SPECIAL - {"specialmed", BOTH_A2_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A2_SPECIAL - {"specialstr", BOTH_A3_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A3_SPECIAL - {"upsidedwnatk",BOTH_FLIP_ATTACK7, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_UPSIDE_DOWN_ATTACK - {"pullatkstab", BOTH_PULL_IMPALE_STAB,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_STAB - {"pullatkswing",BOTH_PULL_IMPALE_SWING,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_SWING - {"AloraSpinAtk",BOTH_ALORA_SPIN_SLASH,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_ALORA - {"Dual FB Atk", BOTH_A6_FB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_FB - {"Dual LR Atk", BOTH_A6_LR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_LR - {"StfHiltBash", BOTH_A7_HILT, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_HILT_BASH + { "Back Stab", BOTH_A2_STABBACK1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACKSTAB + { "Back Att", BOTH_ATTACK_BACK, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK + { "CR Back Att", BOTH_CROUCHATTACKBACK1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK_CR + { "RollStab", BOTH_ROLL_STAB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_ROLL_STAB + { "Lunge Att", BOTH_LUNGE2_B__T_, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_LUNGE + { "Jump Att", BOTH_FORCELEAP2_T__B_, Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_JUMP_T__B_ + { "Flip Stab", BOTH_JUMPFLIPSTABDOWN, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_FLIP_STAB + { "Flip Slash", BOTH_JUMPFLIPSLASHDOWN1, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R_T_, 200 }, // LS_A_FLIP_SLASH + { "DualJump Atk", BOTH_JUMPATTACK6, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_BL_TR, 200 }, // LS_JUMPATTACK_DUAL + + { "DualJumpAtkL_A", BOTH_ARIAL_LEFT, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TL2BR, 200 }, // LS_JUMPATTACK_ARIAL_LEFT + { "DualJumpAtkR_A", BOTH_ARIAL_RIGHT, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TR2BL, 200 }, // LS_JUMPATTACK_ARIAL_RIGHT + + { "DualJumpAtkL_A", BOTH_CARTWHEEL_LEFT, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TL_BR, 200 }, // LS_JUMPATTACK_CART_LEFT + { "DualJumpAtkR_A", BOTH_CARTWHEEL_RIGHT, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TR_BL, 200 }, // LS_JUMPATTACK_CART_RIGHT + + { "DualJumpAtkLStaff", BOTH_BUTTERFLY_FL1, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_JUMPATTACK_STAFF_LEFT + { "DualJumpAtkRStaff", BOTH_BUTTERFLY_FR1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_JUMPATTACK_STAFF_RIGHT + + { "ButterflyLeft", BOTH_BUTTERFLY_LEFT, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_BUTTERFLY_LEFT + { "ButterflyRight", BOTH_BUTTERFLY_RIGHT, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_BUTTERFLY_RIGHT + + { "BkFlip Atk", BOTH_JUMPATTACK7, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_BACKFLIP_ATK + { "DualSpinAtk", BOTH_SPINATTACK6, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_DUAL + { "StfSpinAtk", BOTH_SPINATTACK7, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK + { "LngLeapAtk", BOTH_FORCELONGLEAP_ATTACK, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_LEAP_ATTACK + { "SwoopAtkR", BOTH_VS_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_RIGHT + { "SwoopAtkL", BOTH_VS_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_LEFT + { "TauntaunAtkR", BOTH_VT_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_RIGHT + { "TauntaunAtkL", BOTH_VT_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_LEFT + { "StfKickFwd", BOTH_A7_KICK_F, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F + { "StfKickBack", BOTH_A7_KICK_B, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B + { "StfKickRight", BOTH_A7_KICK_R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R + { "StfKickLeft", BOTH_A7_KICK_L, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L + { "StfKickSpin", BOTH_A7_KICK_S, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_S + { "StfKickBkFwd", BOTH_A7_KICK_BF, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_BF + { "StfKickSplit", BOTH_A7_KICK_RL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_RL + { "StfKickFwdAir", BOTH_A7_KICK_F_AIR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F_AIR + { "StfKickBackAir", BOTH_A7_KICK_B_AIR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B_AIR + { "StfKickRightAir", BOTH_A7_KICK_R_AIR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R_AIR + { "StfKickLeftAir", BOTH_A7_KICK_L_AIR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L_AIR + { "StabDown", BOTH_STABDOWN, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN + { "StabDownStf", BOTH_STABDOWN_STAFF, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_STAFF + { "StabDownDual", BOTH_STABDOWN_DUAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_DUAL + { "dualspinprot", BOTH_A6_SABERPROTECT, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_DUAL_SPIN_PROTECT + { "StfSoulCal", BOTH_A7_SOULCAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_STAFF_SOULCAL + { "specialfast", BOTH_A1_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000 }, // LS_A1_SPECIAL + { "specialmed", BOTH_A2_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000 }, // LS_A2_SPECIAL + { "specialstr", BOTH_A3_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000 }, // LS_A3_SPECIAL + { "upsidedwnatk", BOTH_FLIP_ATTACK7, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_UPSIDE_DOWN_ATTACK + { "pullatkstab", BOTH_PULL_IMPALE_STAB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_PULL_ATTACK_STAB + { "pullatkswing", BOTH_PULL_IMPALE_SWING, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_PULL_ATTACK_SWING + { "AloraSpinAtk", BOTH_ALORA_SPIN_SLASH, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_ALORA + { "Dual FB Atk", BOTH_A6_FB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_FB + { "Dual LR Atk", BOTH_A6_LR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_LR + { "StfHiltBash", BOTH_A7_HILT, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_HILT_BASH //starts - {"TL2BR St", BOTH_S1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TL2BR, LS_A_TL2BR, 200 }, // LS_S_TL2BR - {"L2R St", BOTH_S1_S1__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_L2R, LS_A_L2R, 200 }, // LS_S_L2R - {"BL2TR St", BOTH_S1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BL2TR, LS_A_BL2TR, 200 }, // LS_S_BL2TR - {"BR2TL St", BOTH_S1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BR2TL, LS_A_BR2TL, 200 }, // LS_S_BR2TL - {"R2L St", BOTH_S1_S1__R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_R2L, LS_A_R2L, 200 }, // LS_S_R2L - {"TR2BL St", BOTH_S1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TR2BL, LS_A_TR2BL, 200 }, // LS_S_TR2BL - {"T2B St", BOTH_S1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_T2B, LS_A_T2B, 200 }, // LS_S_T2B + { "TL2BR St", BOTH_S1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TL2BR, LS_A_TL2BR, 200 }, // LS_S_TL2BR + { "L2R St", BOTH_S1_S1__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_L2R, LS_A_L2R, 200 }, // LS_S_L2R + { "BL2TR St", BOTH_S1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BL2TR, LS_A_BL2TR, 200 }, // LS_S_BL2TR + { "BR2TL St", BOTH_S1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BR2TL, LS_A_BR2TL, 200 }, // LS_S_BR2TL + { "R2L St", BOTH_S1_S1__R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_R2L, LS_A_R2L, 200 }, // LS_S_R2L + { "TR2BL St", BOTH_S1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TR2BL, LS_A_TR2BL, 200 }, // LS_S_TR2BL + { "T2B St", BOTH_S1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_T2B, LS_A_T2B, 200 }, // LS_S_T2B //returns - {"TL2BR Ret", BOTH_R1_BR_S1, Q_BR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TL2BR - {"L2R Ret", BOTH_R1__R_S1, Q_R, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_L2R - {"BL2TR Ret", BOTH_R1_TR_S1, Q_TR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BL2TR - {"BR2TL Ret", BOTH_R1_TL_S1, Q_TL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BR2TL - {"R2L Ret", BOTH_R1__L_S1, Q_L, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_R2L - {"TR2BL Ret", BOTH_R1_BL_S1, Q_BL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TR2BL - {"T2B Ret", BOTH_R1_B__S1, Q_B, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_T2B + { "TL2BR Ret", BOTH_R1_BR_S1, Q_BR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TL2BR + { "L2R Ret", BOTH_R1__R_S1, Q_R, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_L2R + { "BL2TR Ret", BOTH_R1_TR_S1, Q_TR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BL2TR + { "BR2TL Ret", BOTH_R1_TL_S1, Q_TL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BR2TL + { "R2L Ret", BOTH_R1__L_S1, Q_L, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_R2L + { "TR2BL Ret", BOTH_R1_BL_S1, Q_BL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TR2BL + { "T2B Ret", BOTH_R1_B__S1, Q_B, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_T2B //Transitions - {"BR2R Trans", BOTH_T1_BR__R, Q_BR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc bottom right to right - {"BR2TR Trans", BOTH_T1_BR_TR, Q_BR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc bottom right to top right (use: BOTH_T1_TR_BR) - {"BR2T Trans", BOTH_T1_BR_T_, Q_BR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom right to top (use: BOTH_T1_T__BR) - {"BR2TL Trans", BOTH_T1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast weak spin bottom right to top left - {"BR2L Trans", BOTH_T1_BR__L, Q_BR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin bottom right to left - {"BR2BL Trans", BOTH_T1_BR_BL, Q_BR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin bottom right to bottom left - {"R2BR Trans", BOTH_T1__R_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc right to bottom right (use: BOTH_T1_BR__R) - {"R2TR Trans", BOTH_T1__R_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc right to top right - {"R2T Trans", BOTH_T1__R_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast ar right to top (use: BOTH_T1_T___R) - {"R2TL Trans", BOTH_T1__R_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc right to top left - {"R2L Trans", BOTH_T1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin right to left - {"R2BL Trans", BOTH_T1__R_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin right to bottom left - {"TR2BR Trans", BOTH_T1_TR_BR, Q_TR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top right to bottom right - {"TR2R Trans", BOTH_T1_TR__R, Q_TR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top right to right (use: BOTH_T1__R_TR) - {"TR2T Trans", BOTH_T1_TR_T_, Q_TR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top right to top (use: BOTH_T1_T__TR) - {"TR2TL Trans", BOTH_T1_TR_TL, Q_TR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top right to top left - {"TR2L Trans", BOTH_T1_TR__L, Q_TR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top right to left - {"TR2BL Trans", BOTH_T1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin top right to bottom left - {"T2BR Trans", BOTH_T1_T__BR, Q_T, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top to bottom right - {"T2R Trans", BOTH_T1_T___R, Q_T, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top to right - {"T2TR Trans", BOTH_T1_T__TR, Q_T, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top to top right - {"T2TL Trans", BOTH_T1_T__TL, Q_T, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top to top left - {"T2L Trans", BOTH_T1_T___L, Q_T, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top to left - {"T2BL Trans", BOTH_T1_T__BL, Q_T, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top to bottom left - {"TL2BR Trans", BOTH_T1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin top left to bottom right - {"TL2R Trans", BOTH_T1_TL__R, Q_TL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top left to right (use: BOTH_T1__R_TL) - {"TL2TR Trans", BOTH_T1_TL_TR, Q_TL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top left to top right (use: BOTH_T1_TR_TL) - {"TL2T Trans", BOTH_T1_TL_T_, Q_TL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top left to top (use: BOTH_T1_T__TL) - {"TL2L Trans", BOTH_T1_TL__L, Q_TL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top left to left (use: BOTH_T1__L_TL) - {"TL2BL Trans", BOTH_T1_TL_BL, Q_TL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top left to bottom left - {"L2BR Trans", BOTH_T1__L_BR, Q_L, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin left to bottom right - {"L2R Trans", BOTH_T1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin left to right - {"L2TR Trans", BOTH_T1__L_TR, Q_L, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc left to top right (use: BOTH_T1_TR__L) - {"L2T Trans", BOTH_T1__L_T_, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc left to top (use: BOTH_T1_T___L) - {"L2TL Trans", BOTH_T1__L_TL, Q_L, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc left to top left - {"L2BL Trans", BOTH_T1__L_BL, Q_L, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc left to bottom left (use: BOTH_T1_BL__L) - {"BL2BR Trans", BOTH_T1_BL_BR, Q_BL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin bottom left to bottom right - {"BL2R Trans", BOTH_T1_BL__R, Q_BL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin bottom left to right - {"BL2TR Trans", BOTH_T1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast weak spin bottom left to top right - {"BL2T Trans", BOTH_T1_BL_T_, Q_BL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom left to top (use: BOTH_T1_T__BL) - {"BL2TL Trans", BOTH_T1_BL_TL, Q_BL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc bottom left to top left (use: BOTH_T1_TL_BL) - {"BL2L Trans", BOTH_T1_BL__L, Q_BL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc bottom left to left + { "BR2R Trans", BOTH_T1_BR__R, Q_BR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc bottom right to right + { "BR2TR Trans", BOTH_T1_BR_TR, Q_BR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc bottom right to top right (use: BOTH_T1_TR_BR) + { "BR2T Trans", BOTH_T1_BR_T_, Q_BR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom right to top (use: BOTH_T1_T__BR) + { "BR2TL Trans", BOTH_T1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast weak spin bottom right to top left + { "BR2L Trans", BOTH_T1_BR__L, Q_BR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin bottom right to left + { "BR2BL Trans", BOTH_T1_BR_BL, Q_BR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin bottom right to bottom left + { "R2BR Trans", BOTH_T1__R_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc right to bottom right (use: BOTH_T1_BR__R) + { "R2TR Trans", BOTH_T1__R_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc right to top right + { "R2T Trans", BOTH_T1__R_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast ar right to top (use: BOTH_T1_T___R) + { "R2TL Trans", BOTH_T1__R_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc right to top left + { "R2L Trans", BOTH_T1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin right to left + { "R2BL Trans", BOTH_T1__R_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin right to bottom left + { "TR2BR Trans", BOTH_T1_TR_BR, Q_TR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top right to bottom right + { "TR2R Trans", BOTH_T1_TR__R, Q_TR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top right to right (use: BOTH_T1__R_TR) + { "TR2T Trans", BOTH_T1_TR_T_, Q_TR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top right to top (use: BOTH_T1_T__TR) + { "TR2TL Trans", BOTH_T1_TR_TL, Q_TR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top right to top left + { "TR2L Trans", BOTH_T1_TR__L, Q_TR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top right to left + { "TR2BL Trans", BOTH_T1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin top right to bottom left + { "T2BR Trans", BOTH_T1_T__BR, Q_T, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top to bottom right + { "T2R Trans", BOTH_T1_T___R, Q_T, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top to right + { "T2TR Trans", BOTH_T1_T__TR, Q_T, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top to top right + { "T2TL Trans", BOTH_T1_T__TL, Q_T, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top to top left + { "T2L Trans", BOTH_T1_T___L, Q_T, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top to left + { "T2BL Trans", BOTH_T1_T__BL, Q_T, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top to bottom left + { "TL2BR Trans", BOTH_T1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin top left to bottom right + { "TL2R Trans", BOTH_T1_TL__R, Q_TL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top left to right (use: BOTH_T1__R_TL) + { "TL2TR Trans", BOTH_T1_TL_TR, Q_TL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top left to top right (use: BOTH_T1_TR_TL) + { "TL2T Trans", BOTH_T1_TL_T_, Q_TL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top left to top (use: BOTH_T1_T__TL) + { "TL2L Trans", BOTH_T1_TL__L, Q_TL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top left to left (use: BOTH_T1__L_TL) + { "TL2BL Trans", BOTH_T1_TL_BL, Q_TL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top left to bottom left + { "L2BR Trans", BOTH_T1__L_BR, Q_L, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin left to bottom right + { "L2R Trans", BOTH_T1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin left to right + { "L2TR Trans", BOTH_T1__L_TR, Q_L, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc left to top right (use: BOTH_T1_TR__L) + { "L2T Trans", BOTH_T1__L_T_, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc left to top (use: BOTH_T1_T___L) + { "L2TL Trans", BOTH_T1__L_TL, Q_L, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc left to top left + { "L2BL Trans", BOTH_T1__L_BL, Q_L, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc left to bottom left (use: BOTH_T1_BL__L) + { "BL2BR Trans", BOTH_T1_BL_BR, Q_BL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin bottom left to bottom right + { "BL2R Trans", BOTH_T1_BL__R, Q_BL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin bottom left to right + { "BL2TR Trans", BOTH_T1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast weak spin bottom left to top right + { "BL2T Trans", BOTH_T1_BL_T_, Q_BL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom left to top (use: BOTH_T1_T__BL) + { "BL2TL Trans", BOTH_T1_BL_TL, Q_BL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc bottom left to top left (use: BOTH_T1_TL_BL) + { "BL2L Trans", BOTH_T1_BL__L, Q_BL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc bottom left to left //Bounces - {"Bounce BR", BOTH_B1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, - {"Bounce R", BOTH_B1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, - {"Bounce TR", BOTH_B1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, - {"Bounce T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, - {"Bounce TL", BOTH_B1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, - {"Bounce L", BOTH_B1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, - {"Bounce BL", BOTH_B1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + { "Bounce BR", BOTH_B1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + { "Bounce R", BOTH_B1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + { "Bounce TR", BOTH_B1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + { "Bounce T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + { "Bounce TL", BOTH_B1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + { "Bounce L", BOTH_B1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + { "Bounce BL", BOTH_B1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, //Deflected attacks (like bounces, but slide off enemy saber, not straight back) - {"Deflect BR", BOTH_D1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, - {"Deflect R", BOTH_D1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, - {"Deflect TR", BOTH_D1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, - {"Deflect T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, - {"Deflect TL", BOTH_D1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, - {"Deflect L", BOTH_D1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, - {"Deflect BL", BOTH_D1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, - {"Deflect B", BOTH_D1_B____, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + { "Deflect BR", BOTH_D1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + { "Deflect R", BOTH_D1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + { "Deflect TR", BOTH_D1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + { "Deflect T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + { "Deflect TL", BOTH_D1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + { "Deflect L", BOTH_D1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + { "Deflect BL", BOTH_D1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + { "Deflect B", BOTH_D1_B____, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, //Reflected attacks - {"Reflected BR",BOTH_V1_BR_S1, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BR - {"Reflected R", BOTH_V1__R_S1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__R - {"Reflected TR",BOTH_V1_TR_S1, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TR - {"Reflected T", BOTH_V1_T__S1, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_T_ - {"Reflected TL",BOTH_V1_TL_S1, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TL - {"Reflected L", BOTH_V1__L_S1, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__L - {"Reflected BL",BOTH_V1_BL_S1, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BL - {"Reflected B", BOTH_V1_B__S1, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_B_ + { "Reflected BR", BOTH_V1_BR_S1, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BR + { "Reflected R", BOTH_V1__R_S1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__R + { "Reflected TR", BOTH_V1_TR_S1, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TR + { "Reflected T", BOTH_V1_T__S1, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_T_ + { "Reflected TL", BOTH_V1_TL_S1, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TL + { "Reflected L", BOTH_V1__L_S1, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__L + { "Reflected BL", BOTH_V1_BL_S1, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BL + { "Reflected B", BOTH_V1_B__S1, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_B_ // Broken parries - {"BParry Top", BOTH_H1_S1_T_, Q_T, Q_B, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UP, - {"BParry UR", BOTH_H1_S1_TR, Q_TR, Q_BL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UR, - {"BParry UL", BOTH_H1_S1_TL, Q_TL, Q_BR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UL, - {"BParry LR", BOTH_H1_S1_BL, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR, - {"BParry Bot", BOTH_H1_S1_B_, Q_B, Q_T, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL - {"BParry LL", BOTH_H1_S1_BR, Q_BR, Q_TL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + { "BParry Top", BOTH_H1_S1_T_, Q_T, Q_B, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UP, + { "BParry UR", BOTH_H1_S1_TR, Q_TR, Q_BL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UR, + { "BParry UL", BOTH_H1_S1_TL, Q_TL, Q_BR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UL, + { "BParry LR", BOTH_H1_S1_BL, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR, + { "BParry Bot", BOTH_H1_S1_B_, Q_B, Q_T, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + { "BParry LL", BOTH_H1_S1_BR, Q_BR, Q_TL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL // Knockaways - {"Knock Top", BOTH_K1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_T__BR, 150 }, // LS_PARRY_UP, - {"Knock UR", BOTH_K1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_TR__R, 150 }, // LS_PARRY_UR, - {"Knock UL", BOTH_K1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_T1_TL__L, 150 }, // LS_PARRY_UL, - {"Knock LR", BOTH_K1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_T1_BL_TL, 150 }, // LS_PARRY_LR, - {"Knock LL", BOTH_K1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_T1_BR_TR, 150 }, // LS_PARRY_LL + { "Knock Top", BOTH_K1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_T__BR, 150 }, // LS_PARRY_UP, + { "Knock UR", BOTH_K1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_TR__R, 150 }, // LS_PARRY_UR, + { "Knock UL", BOTH_K1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_T1_TL__L, 150 }, // LS_PARRY_UL, + { "Knock LR", BOTH_K1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_T1_BL_TL, 150 }, // LS_PARRY_LR, + { "Knock LL", BOTH_K1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_T1_BR_TR, 150 }, // LS_PARRY_LL // Parry - {"Parry Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 150 }, // LS_PARRY_UP, - {"Parry UR", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 150 }, // LS_PARRY_UR, - {"Parry UL", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 150 }, // LS_PARRY_UL, - {"Parry LR", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 150 }, // LS_PARRY_LR, - {"Parry LL", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 150 }, // LS_PARRY_LL + { "Parry Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 150 }, // LS_PARRY_UP, + { "Parry UR", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 150 }, // LS_PARRY_UR, + { "Parry UL", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 150 }, // LS_PARRY_UL, + { "Parry LR", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 150 }, // LS_PARRY_LR, + { "Parry LL", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 150 }, // LS_PARRY_LL // Reflecting a missile - {"Reflect Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 300 }, // LS_PARRY_UP, - {"Reflect UR", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 300 }, // LS_PARRY_UR, - {"Reflect UL", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 300 }, // LS_PARRY_UL, - {"Reflect LR", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 300 }, // LS_PARRY_LR - {"Reflect LL", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 300 }, // LS_PARRY_LL, + { "Reflect Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 300 }, // LS_PARRY_UP, + { "Reflect UR", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 300 }, // LS_PARRY_UR, + { "Reflect UL", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 300 }, // LS_PARRY_UL, + { "Reflect LR", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 300 }, // LS_PARRY_LR + { "Reflect LL", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 300 }, // LS_PARRY_LL, }; @@ -407,666 +408,1190 @@ saberMoveName_t transitionMove[Q_NUM_QUADS][Q_NUM_QUADS] = } }; -void PM_VelocityForSaberMove( playerState_t *ps, vec3_t throwDir ) +void PM_VelocityForSaberMove(playerState_t *ps, vec3_t throwDir) { vec3_t vForward = { 0.0f }, vRight = { 0.0f }, vUp = { 0.0f }, startQ = { 0.0f }, endQ = { 0.0f }; - AngleVectors( ps->viewangles, vForward, vRight, vUp ); + AngleVectors(ps->viewangles, vForward, vRight, vUp); - switch ( saberMoveData[ps->saberMove].startQuad ) + switch (saberMoveData[ps->saberMove].startQuad) { case Q_BR: - VectorScale( vRight, 1, startQ ); - VectorMA( startQ, -1, vUp, startQ ); + VectorScale(vRight, 1, startQ); + VectorMA(startQ, -1, vUp, startQ); break; case Q_R: - VectorScale( vRight, 2, startQ ); + VectorScale(vRight, 2, startQ); break; case Q_TR: - VectorScale( vRight, 1, startQ ); - VectorMA( startQ, 1, vUp, startQ ); + VectorScale(vRight, 1, startQ); + VectorMA(startQ, 1, vUp, startQ); break; case Q_T: - VectorScale( vUp, 2, startQ ); + VectorScale(vUp, 2, startQ); break; case Q_TL: - VectorScale( vRight, -1, startQ ); - VectorMA( startQ, 1, vUp, startQ ); + VectorScale(vRight, -1, startQ); + VectorMA(startQ, 1, vUp, startQ); break; case Q_L: - VectorScale( vRight, -2, startQ ); + VectorScale(vRight, -2, startQ); break; case Q_BL: - VectorScale( vRight, -1, startQ ); - VectorMA( startQ, -1, vUp, startQ ); + VectorScale(vRight, -1, startQ); + VectorMA(startQ, -1, vUp, startQ); break; case Q_B: - VectorScale( vUp, -2, startQ ); + VectorScale(vUp, -2, startQ); break; } - switch ( saberMoveData[ps->saberMove].endQuad ) + switch (saberMoveData[ps->saberMove].endQuad) { case Q_BR: - VectorScale( vRight, 1, endQ ); - VectorMA( endQ, -1, vUp, endQ ); + VectorScale(vRight, 1, endQ); + VectorMA(endQ, -1, vUp, endQ); break; case Q_R: - VectorScale( vRight, 2, endQ ); + VectorScale(vRight, 2, endQ); break; case Q_TR: - VectorScale( vRight, 1, endQ ); - VectorMA( endQ, 1, vUp, endQ ); + VectorScale(vRight, 1, endQ); + VectorMA(endQ, 1, vUp, endQ); break; case Q_T: - VectorScale( vUp, 2, endQ ); + VectorScale(vUp, 2, endQ); break; case Q_TL: - VectorScale( vRight, -1, endQ ); - VectorMA( endQ, 1, vUp, endQ ); + VectorScale(vRight, -1, endQ); + VectorMA(endQ, 1, vUp, endQ); break; case Q_L: - VectorScale( vRight, -2, endQ ); + VectorScale(vRight, -2, endQ); break; case Q_BL: - VectorScale( vRight, -1, endQ ); - VectorMA( endQ, -1, vUp, endQ ); + VectorScale(vRight, -1, endQ); + VectorMA(endQ, -1, vUp, endQ); break; case Q_B: - VectorScale( vUp, -2, endQ ); + VectorScale(vUp, -2, endQ); break; } - VectorMA( endQ, 2, vForward, endQ ); - VectorScale( throwDir, 125, throwDir );//FIXME: pass in the throw strength? - VectorSubtract( endQ, startQ, throwDir ); + VectorMA(endQ, 2, vForward, endQ); + VectorScale(throwDir, 125, throwDir);//FIXME: pass in the throw strength? + VectorSubtract(endQ, startQ, throwDir); } -qboolean PM_VelocityForBlockedMove( playerState_t *ps, vec3_t throwDir ) +qboolean PM_VelocityForBlockedMove(playerState_t *ps, vec3_t throwDir) { vec3_t vForward, vRight, vUp; - AngleVectors( ps->viewangles, vForward, vRight, vUp ); - switch ( ps->saberBlocked ) + AngleVectors(ps->viewangles, vForward, vRight, vUp); + switch (ps->saberBlocked) { case BLOCKED_UPPER_RIGHT: - VectorScale( vRight, 1, throwDir ); - VectorMA( throwDir, 1, vUp, throwDir ); + VectorScale(vRight, 1, throwDir); + VectorMA(throwDir, 1, vUp, throwDir); break; case BLOCKED_UPPER_LEFT: - VectorScale( vRight, -1, throwDir ); - VectorMA( throwDir, 1, vUp, throwDir ); + VectorScale(vRight, -1, throwDir); + VectorMA(throwDir, 1, vUp, throwDir); break; case BLOCKED_LOWER_RIGHT: - VectorScale( vRight, 1, throwDir ); - VectorMA( throwDir, -1, vUp, throwDir ); + VectorScale(vRight, 1, throwDir); + VectorMA(throwDir, -1, vUp, throwDir); break; case BLOCKED_LOWER_LEFT: - VectorScale( vRight, -1, throwDir ); - VectorMA( throwDir, -1, vUp, throwDir ); + VectorScale(vRight, -1, throwDir); + VectorMA(throwDir, -1, vUp, throwDir); break; case BLOCKED_TOP: - VectorScale( vUp, 2, throwDir ); + VectorScale(vUp, 2, throwDir); break; default: return qfalse; break; } - VectorMA( throwDir, 2, vForward, throwDir ); - VectorScale( throwDir, 250, throwDir );//FIXME: pass in the throw strength? + VectorMA(throwDir, 2, vForward, throwDir); + VectorScale(throwDir, 250, throwDir);//FIXME: pass in the throw strength? return qtrue; } -int PM_AnimLevelForSaberAnim( int anim ) +int PM_AnimLevelForSaberAnim(int anim) { - if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ ) + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____) { return FORCE_LEVEL_1; } - if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ ) + if (anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____) { return FORCE_LEVEL_2; } - if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ ) + if (anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____) { return FORCE_LEVEL_3; } - if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ ) + if (anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____) {//desann return FORCE_LEVEL_4; } - if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ ) + if (anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____) {//tavion return FORCE_LEVEL_5; } - if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ ) + if (anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____) {//dual return SS_DUAL; } - if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ ) + if (anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____) {//staff return SS_STAFF; } return FORCE_LEVEL_0; } -int PM_PowerLevelForSaberAnim( playerState_t *ps, int saberNum ) +int PM_PowerLevelForSaberAnim(playerState_t *ps, int saberNum) { - int anim = ps->torsoAnim; - int animTimeElapsed = PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim ) - ps->torsoAnimTimer; - if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ ) - { - //FIXME: these two need their own style - if ( ps->saber[0].type == SABER_LANCE ) - { - return FORCE_LEVEL_4; + if (g_saberNewCombat->integer) + { //new code + int anim = ps->torsoAnim; + int animTimeElapsed = PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim) - ps->torsoAnimTimer; + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____) + { + //FIXME: these two need their own style + if (ps->saber[0].type == SABER_LANCE) + { + return FORCE_LEVEL_4; + } + else if (ps->saber[0].type == SABER_TRIDENT) + { + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_1; } - else if ( ps->saber[0].type == SABER_TRIDENT ) + if (anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____) { return FORCE_LEVEL_3; } - return FORCE_LEVEL_1; - } - if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ ) - { - return FORCE_LEVEL_2; - } - if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ ) - { - return FORCE_LEVEL_3; - } - if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ ) - {//desann - return FORCE_LEVEL_4; - } - if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ ) - {//tavion - return FORCE_LEVEL_2; - } - if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ ) - {//dual - return FORCE_LEVEL_2; - } - if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ ) - {//staff - return FORCE_LEVEL_2; - } - if ( ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR ) - || ( anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR ) - || ( anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR ) ) - {//parries - switch ( ps->saberAnimLevel ) + if (anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____) { - case SS_STRONG: - case SS_DESANN: - return FORCE_LEVEL_3; - break; - case SS_TAVION: - case SS_STAFF: - case SS_DUAL: - case SS_MEDIUM: - return FORCE_LEVEL_2; - break; - case SS_FAST: - return FORCE_LEVEL_1; - break; - default: - return FORCE_LEVEL_0; - break; - } - } - if ( ( anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR ) - || ( anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR ) - || ( anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR ) ) - {//knockaways - return FORCE_LEVEL_3; - } - if ( ( anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1 ) - || ( anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6 ) - || ( anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7 ) ) - {//knocked-away attacks - return FORCE_LEVEL_1; - } - if ( ( anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR ) - || ( anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR ) - || ( anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR ) ) - {//broken parries - return FORCE_LEVEL_0; - } - switch ( anim ) - { - case BOTH_A2_STABBACK1: - if ( ps->torsoAnimTimer < 450 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 400 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_ATTACK_BACK: - if ( ps->torsoAnimTimer < 500 ) - {//end of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_CROUCHATTACKBACK1: - if ( ps->torsoAnimTimer < 800 ) - {//end of anim - return FORCE_LEVEL_0; + return FORCE_LEVEL_5; } - return FORCE_LEVEL_3; - break; - case BOTH_BUTTERFLY_LEFT: - case BOTH_BUTTERFLY_RIGHT: - case BOTH_BUTTERFLY_FL1: - case BOTH_BUTTERFLY_FR1: - //FIXME: break up? - return FORCE_LEVEL_3; - break; - case BOTH_FJSS_TR_BL: - case BOTH_FJSS_TL_BR: - //FIXME: break up? - return FORCE_LEVEL_3; - break; - case BOTH_K1_S1_T_: //# knockaway saber top - case BOTH_K1_S1_TR: //# knockaway saber top right - case BOTH_K1_S1_TL: //# knockaway saber top left - case BOTH_K1_S1_BL: //# knockaway saber bottom left - case BOTH_K1_S1_B_: //# knockaway saber bottom - case BOTH_K1_S1_BR: //# knockaway saber bottom right - //FIXME: break up? - return FORCE_LEVEL_3; - break; - case BOTH_LUNGE2_B__T_: - if ( ps->torsoAnimTimer < 400 ) - {//end of anim - return FORCE_LEVEL_0; + if (anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____) + {//desann + return FORCE_LEVEL_5; } - else if ( animTimeElapsed < 150 ) - {//beginning of anim - return FORCE_LEVEL_0; + if (anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____) + {//tavion + return FORCE_LEVEL_2; } - return FORCE_LEVEL_3; - break; - case BOTH_FORCELEAP2_T__B_: - if ( ps->torsoAnimTimer < 400 ) - {//end of anim - return FORCE_LEVEL_0; + if (anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____) + {//dual + return FORCE_LEVEL_2; } - else if ( animTimeElapsed < 550 ) - {//beginning of anim - return FORCE_LEVEL_0; + if (anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____) + {//staff + return FORCE_LEVEL_4; } - return FORCE_LEVEL_3; - break; - case BOTH_VS_ATR_S: - case BOTH_VS_ATL_S: - case BOTH_VT_ATR_S: - case BOTH_VT_ATL_S: - return FORCE_LEVEL_3;//??? - break; - case BOTH_JUMPFLIPSLASHDOWN1: - if ( ps->torsoAnimTimer <= 900 ) - {//end of anim - return FORCE_LEVEL_0; + if ((anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR) + || (anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR) + || (anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR)) + {//parries + switch (ps->saberAnimLevel) + { + case SS_DESANN: + case SS_STRONG: + return FORCE_LEVEL_5; + break; + case SS_STAFF: + return FORCE_LEVEL_4; + break; + case SS_MEDIUM: + case SS_KATARN: + return FORCE_LEVEL_3; + break; + case SS_DUAL: + case SS_TAVION: + return FORCE_LEVEL_2; + break; + case SS_FAST: + return FORCE_LEVEL_1; + break; + default: + return FORCE_LEVEL_0; + break; + } } - else if ( animTimeElapsed < 550 ) - {//beginning of anim - return FORCE_LEVEL_0; + if ((anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR) + || (anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR) + || (anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR)) + {//knockaways + return FORCE_LEVEL_3; } - return FORCE_LEVEL_3; - break; - case BOTH_JUMPFLIPSTABDOWN: - if ( ps->torsoAnimTimer <= 1200 ) - {//end of anim - return FORCE_LEVEL_0; + if ((anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1) + || (anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6) + || (anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7)) + {//knocked-away attacks + return FORCE_LEVEL_1; } - else if ( animTimeElapsed <= 250 ) - {//beginning of anim + if ((anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR) + || (anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR) + || (anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR)) + {//broken parries return FORCE_LEVEL_0; } - return FORCE_LEVEL_3; - break; - case BOTH_JUMPATTACK6: - /* - if (pm->ps) + switch (anim) { + case BOTH_A2_STABBACK1: + if (ps->torsoAnimTimer < 450) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_ATTACK_BACK: + if (ps->torsoAnimTimer < 500) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_CROUCHATTACKBACK1: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + //FIXME: break up? + return FORCE_LEVEL_5; + break; + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + //FIXME: break up? + return FORCE_LEVEL_5; + break; + case BOTH_K1_S1_T_: //# knockaway saber top + case BOTH_K1_S1_TR: //# knockaway saber top right + case BOTH_K1_S1_TL: //# knockaway saber top left + case BOTH_K1_S1_BL: //# knockaway saber bottom left + case BOTH_K1_S1_B_: //# knockaway saber bottom + case BOTH_K1_S1_BR: //# knockaway saber bottom right + //FIXME: break up? + return FORCE_LEVEL_5; + break; + case BOTH_LUNGE2_B__T_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_FORCELEAP2_T__B_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + return FORCE_LEVEL_5;//??? + break; + case BOTH_JUMPFLIPSLASHDOWN1: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_JUMPFLIPSTABDOWN: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed <= 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_JUMPATTACK6: + /* + if (pm->ps) + { if ( ( pm->ps->legsAnimTimer >= 1450 - && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) - ||(pm->ps->legsAnimTimer >= 400 - && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) + ||(pm->ps->legsAnimTimer >= 400 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) {//pretty much sideways - return FORCE_LEVEL_3; + return FORCE_LEVEL_5; + } + } + */ + if ((ps->torsoAnimTimer >= 1450 + && animTimeElapsed >= 400) + || (ps->torsoAnimTimer >= 400 + && animTimeElapsed >= 1100)) + {//pretty much sideways + return FORCE_LEVEL_5; } - } - */ - if ( ( ps->torsoAnimTimer >= 1450 - && animTimeElapsed >= 400 ) - ||(ps->torsoAnimTimer >= 400 - && animTimeElapsed >= 1100 ) ) - {//pretty much sideways - return FORCE_LEVEL_3; - } - return FORCE_LEVEL_0; - break; - case BOTH_JUMPATTACK7: - if ( ps->torsoAnimTimer <= 1200 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 200 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_SPINATTACK6: - if ( animTimeElapsed <= 200 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_SPINATTACK7: - if ( ps->torsoAnimTimer <= 500 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 500 ) - {//beginning of anim return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_FORCELONGLEAP_ATTACK: - if ( animTimeElapsed <= 200 ) - {//1st four frames of anim - return FORCE_LEVEL_3; - } - break; - /* - case BOTH_A7_KICK_F://these kicks attack, too - case BOTH_A7_KICK_B: - case BOTH_A7_KICK_R: - case BOTH_A7_KICK_L: - //FIXME: break up - return FORCE_LEVEL_3; - break; - */ - case BOTH_STABDOWN: - if ( ps->torsoAnimTimer <= 900 ) - {//end of anim - return FORCE_LEVEL_3; - } - break; - case BOTH_STABDOWN_STAFF: - if ( ps->torsoAnimTimer <= 850 ) - {//end of anim + break; + case BOTH_JUMPATTACK7: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_SPINATTACK6: + if (animTimeElapsed <= 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_SPINATTACK7: + if (ps->torsoAnimTimer <= 500) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 500) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_FORCELONGLEAP_ATTACK: + if (animTimeElapsed <= 200) + {//1st four frames of anim + return FORCE_LEVEL_5; + } + break; + /* + case BOTH_A7_KICK_F://these kicks attack, too + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + //FIXME: break up + return FORCE_LEVEL_5; + break; + */ + case BOTH_STABDOWN: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_5; + } + break; + case BOTH_STABDOWN_STAFF: + if (ps->torsoAnimTimer <= 850) + {//end of anim + return FORCE_LEVEL_5; + } + break; + case BOTH_STABDOWN_DUAL: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_5; + } + break; + case BOTH_A6_SABERPROTECT: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A7_SOULCAL: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 600) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A1_SPECIAL: + if (ps->torsoAnimTimer < 600) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A2_SPECIAL: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A3_SPECIAL: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_FLIP_ATTACK7: + return FORCE_LEVEL_5; + break; + case BOTH_PULL_IMPALE_STAB: + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } return FORCE_LEVEL_3; - } - break; - case BOTH_STABDOWN_DUAL: - if ( ps->torsoAnimTimer <= 900 ) - {//end of anim + break; + case BOTH_PULL_IMPALE_SWING: + if (ps->torsoAnimTimer < 500)//750 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650)//600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } return FORCE_LEVEL_3; - } - break; - case BOTH_A6_SABERPROTECT: - if ( ps->torsoAnimTimer < 650 ) - {//end of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_A7_SOULCAL: - if ( ps->torsoAnimTimer < 650 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 600 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_A1_SPECIAL: - if ( ps->torsoAnimTimer < 600 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 200 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_A2_SPECIAL: - if ( ps->torsoAnimTimer < 300 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 200 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_A3_SPECIAL: - if ( ps->torsoAnimTimer < 700 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 200 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_FLIP_ATTACK7: - return FORCE_LEVEL_3; - break; - case BOTH_PULL_IMPALE_STAB: - if ( ps->torsoAnimTimer < 1000 ) - {//end of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_PULL_IMPALE_SWING: - if ( ps->torsoAnimTimer < 500 )//750 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 650 )//600 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_ALORA_SPIN_SLASH: - if ( ps->torsoAnimTimer < 900 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 250 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_A6_FB: - if ( ps->torsoAnimTimer < 250 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 250 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_3; - break; - case BOTH_A6_LR: - if ( ps->torsoAnimTimer < 250 ) - {//end of anim + break; + case BOTH_ALORA_SPIN_SLASH: + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A6_FB: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A6_LR: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A7_HILT: return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 250 ) - {//beginning of anim + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_LK_S_DL_T_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_S_SB_1_W: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + if (ps->torsoAnimTimer < 150) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_T_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + if (animTimeElapsed < 1000) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_ST_T_SB_1_W: + if (ps->torsoAnimTimer < 950) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_S_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 450) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_T_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_S_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_T_SB_1_W: + //special suberbreak - doesn't kill, just kicks them backwards return FORCE_LEVEL_0; + break; + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 350) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + return FORCE_LEVEL_5; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_HANG_ATTACK: + //FIME: break up + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + else + {//sweet spot + return FORCE_LEVEL_5; + } + break; + case BOTH_ROLL_STAB: + if (animTimeElapsed > 400) + {//end of anim + return FORCE_LEVEL_0; + } + else + { + return FORCE_LEVEL_5; + } + break; } - return FORCE_LEVEL_3; - break; - case BOTH_A7_HILT: return FORCE_LEVEL_0; - break; -//===SABERLOCK SUPERBREAKS START=========================================================================== - case BOTH_LK_S_DL_T_SB_1_W: - if ( ps->torsoAnimTimer < 700 ) - {//end of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_5; - break; - case BOTH_LK_S_ST_S_SB_1_W: - if ( ps->torsoAnimTimer < 300 ) - {//end of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_5; - break; - case BOTH_LK_S_DL_S_SB_1_W: - case BOTH_LK_S_S_S_SB_1_W: - if ( ps->torsoAnimTimer < 700 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 400 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_5; - break; - case BOTH_LK_S_ST_T_SB_1_W: - case BOTH_LK_S_S_T_SB_1_W: - if ( ps->torsoAnimTimer < 150 ) - {//end of anim - return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 400 ) - {//beginning of anim - return FORCE_LEVEL_0; - } - return FORCE_LEVEL_5; - break; - case BOTH_LK_DL_DL_T_SB_1_W: - return FORCE_LEVEL_5; - break; - case BOTH_LK_DL_DL_S_SB_1_W: - case BOTH_LK_DL_ST_S_SB_1_W: - if ( animTimeElapsed < 1000 ) - {//beginning of anim - return FORCE_LEVEL_0; + } + else + { //old code + int anim = ps->torsoAnim; + int animTimeElapsed = PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim) - ps->torsoAnimTimer; + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____) + { + //FIXME: these two need their own style + if (ps->saber[0].type == SABER_LANCE) + { + return FORCE_LEVEL_4; + } + else if (ps->saber[0].type == SABER_TRIDENT) + { + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_1; } - return FORCE_LEVEL_5; - break; - case BOTH_LK_DL_ST_T_SB_1_W: - if ( ps->torsoAnimTimer < 950 ) - {//end of anim - return FORCE_LEVEL_0; + if (anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____) + { + return FORCE_LEVEL_3; } - else if ( animTimeElapsed < 650 ) - {//beginning of anim - return FORCE_LEVEL_0; + if (anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____) + { + return FORCE_LEVEL_5; } - return FORCE_LEVEL_5; - break; - case BOTH_LK_DL_S_S_SB_1_W: - if ( saberNum != 0 ) - {//only right hand saber does damage in this suberbreak - return FORCE_LEVEL_0; + if (anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____) + {//desann + return FORCE_LEVEL_5; } - if ( ps->torsoAnimTimer < 900 ) - {//end of anim - return FORCE_LEVEL_0; + if (anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____) + {//tavion + return FORCE_LEVEL_2; } - else if ( animTimeElapsed < 450 ) - {//beginning of anim - return FORCE_LEVEL_0; + if (anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____) + {//dual + return FORCE_LEVEL_2; } - return FORCE_LEVEL_5; - break; - case BOTH_LK_DL_S_T_SB_1_W: - if ( saberNum != 0 ) - {//only right hand saber does damage in this suberbreak - return FORCE_LEVEL_0; + if (anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____) + {//staff + return FORCE_LEVEL_4; } - if ( ps->torsoAnimTimer < 250 ) - {//end of anim - return FORCE_LEVEL_0; + if ((anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR) + || (anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR) + || (anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR)) + {//parries + switch (ps->saberAnimLevel) + { + case SS_STRONG: + case SS_DESANN: + return FORCE_LEVEL_3; + break; + case SS_TAVION: + case SS_STAFF: + case SS_DUAL: + case SS_MEDIUM: + return FORCE_LEVEL_2; + break; + case SS_FAST: + return FORCE_LEVEL_1; + break; + default: + return FORCE_LEVEL_0; + break; + } } - else if ( animTimeElapsed < 150 ) - {//beginning of anim - return FORCE_LEVEL_0; + if ((anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR) + || (anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR) + || (anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR)) + {//knockaways + return FORCE_LEVEL_3; } - return FORCE_LEVEL_5; - break; - case BOTH_LK_ST_DL_S_SB_1_W: - return FORCE_LEVEL_5; - break; - case BOTH_LK_ST_DL_T_SB_1_W: - //special suberbreak - doesn't kill, just kicks them backwards - return FORCE_LEVEL_0; - break; - case BOTH_LK_ST_ST_S_SB_1_W: - case BOTH_LK_ST_S_S_SB_1_W: - if ( ps->torsoAnimTimer < 800 ) - {//end of anim - return FORCE_LEVEL_0; + if ((anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1) + || (anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6) + || (anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7)) + {//knocked-away attacks + return FORCE_LEVEL_1; } - else if ( animTimeElapsed < 350 ) - {//beginning of anim + if ((anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR) + || (anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR) + || (anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR)) + {//broken parries return FORCE_LEVEL_0; } - return FORCE_LEVEL_5; - break; - case BOTH_LK_ST_ST_T_SB_1_W: - case BOTH_LK_ST_S_T_SB_1_W: - return FORCE_LEVEL_5; - break; -//===SABERLOCK SUPERBREAKS START=========================================================================== - case BOTH_HANG_ATTACK: - //FIME: break up - if ( ps->torsoAnimTimer < 1000 ) - {//end of anim + switch (anim) + { + case BOTH_A2_STABBACK1: + if (ps->torsoAnimTimer < 450) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ATTACK_BACK: + if (ps->torsoAnimTimer < 500) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_CROUCHATTACKBACK1: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_K1_S1_T_: //# knockaway saber top + case BOTH_K1_S1_TR: //# knockaway saber top right + case BOTH_K1_S1_TL: //# knockaway saber top left + case BOTH_K1_S1_BL: //# knockaway saber bottom left + case BOTH_K1_S1_B_: //# knockaway saber bottom + case BOTH_K1_S1_BR: //# knockaway saber bottom right + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_LUNGE2_B__T_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELEAP2_T__B_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + return FORCE_LEVEL_3;//??? + break; + case BOTH_JUMPFLIPSLASHDOWN1: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPFLIPSTABDOWN: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed <= 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPATTACK6: + /* + if (pm->ps) + { + if ( ( pm->ps->legsAnimTimer >= 1450 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) + ||(pm->ps->legsAnimTimer >= 400 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) + {//pretty much sideways + return FORCE_LEVEL_3; + } + } + */ + if ((ps->torsoAnimTimer >= 1450 + && animTimeElapsed >= 400) + || (ps->torsoAnimTimer >= 400 + && animTimeElapsed >= 1100)) + {//pretty much sideways + return FORCE_LEVEL_3; + } return FORCE_LEVEL_0; - } - else if ( animTimeElapsed < 250 ) - {//beginning of anim + break; + case BOTH_JUMPATTACK7: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK6: + if (animTimeElapsed <= 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK7: + if (ps->torsoAnimTimer <= 500) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 500) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELONGLEAP_ATTACK: + if (animTimeElapsed <= 200) + {//1st four frames of anim + return FORCE_LEVEL_3; + } + break; + /* + case BOTH_A7_KICK_F://these kicks attack, too + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + //FIXME: break up + return FORCE_LEVEL_3; + break; + */ + case BOTH_STABDOWN: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_STAFF: + if (ps->torsoAnimTimer <= 850) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_DUAL: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_A6_SABERPROTECT: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_SOULCAL: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 600) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A1_SPECIAL: + if (ps->torsoAnimTimer < 600) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A2_SPECIAL: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A3_SPECIAL: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FLIP_ATTACK7: + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_STAB: + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_SWING: + if (ps->torsoAnimTimer < 500)//750 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650)//600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ALORA_SPIN_SLASH: + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_FB: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_LR: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_HILT: return FORCE_LEVEL_0; - } - else - {//sweet spot + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_LK_S_DL_T_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } return FORCE_LEVEL_5; - } - break; - case BOTH_ROLL_STAB: - if ( animTimeElapsed > 400 ) - {//end of anim + break; + case BOTH_LK_S_ST_S_SB_1_W: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + if (ps->torsoAnimTimer < 150) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_T_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + if (animTimeElapsed < 1000) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_ST_T_SB_1_W: + if (ps->torsoAnimTimer < 950) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_S_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 450) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_T_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_S_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_T_SB_1_W: + //special suberbreak - doesn't kill, just kicks them backwards return FORCE_LEVEL_0; + break; + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 350) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + return FORCE_LEVEL_5; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_HANG_ATTACK: + //FIME: break up + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + else + {//sweet spot + return FORCE_LEVEL_5; + } + break; + case BOTH_ROLL_STAB: + if (animTimeElapsed > 400) + {//end of anim + return FORCE_LEVEL_0; + } + else + { + return FORCE_LEVEL_3; + } + break; } - else - { - return FORCE_LEVEL_3; - } - break; + return FORCE_LEVEL_0; } - return FORCE_LEVEL_0; } -qboolean PM_InAnimForSaberMove( int anim, int saberMove ) +qboolean PM_InAnimForSaberMove(int anim, int saberMove) { - switch ( anim ) + switch (anim) {//special case anims case BOTH_A2_STABBACK1: case BOTH_ATTACK_BACK: @@ -1138,68 +1663,68 @@ qboolean PM_InAnimForSaberMove( int anim, int saberMove ) case BOTH_HANG_ATTACK: return qtrue; } - if ( PM_SaberDrawPutawayAnim( anim ) ) + if (PM_SaberDrawPutawayAnim(anim)) { - if ( saberMove == LS_DRAW || saberMove == LS_PUTAWAY ) + if (saberMove == LS_DRAW || saberMove == LS_PUTAWAY) { return qtrue; } return qfalse; } - else if ( PM_SaberStanceAnim( anim ) ) + else if (PM_SaberStanceAnim(anim)) { - if ( saberMove == LS_READY ) + if (saberMove == LS_READY) { return qtrue; } return qfalse; } - int animLevel = PM_AnimLevelForSaberAnim( anim ); - if ( animLevel <= 0 ) + int animLevel = PM_AnimLevelForSaberAnim(anim); + if (animLevel <= 0) {//NOTE: this will always return false for the ready poses and putaway/draw... return qfalse; } //drop the anim to the first level and start the checks there - anim -= (animLevel-FORCE_LEVEL_1)*SABER_ANIM_GROUP_SIZE; + anim -= (animLevel - FORCE_LEVEL_1)*SABER_ANIM_GROUP_SIZE; //check level 1 - if ( anim == saberMoveData[saberMove].animToUse ) + if (anim == saberMoveData[saberMove].animToUse) { return qtrue; } //check level 2 anim += SABER_ANIM_GROUP_SIZE; - if ( anim == saberMoveData[saberMove].animToUse ) + if (anim == saberMoveData[saberMove].animToUse) { return qtrue; } //check level 3 anim += SABER_ANIM_GROUP_SIZE; - if ( anim == saberMoveData[saberMove].animToUse ) + if (anim == saberMoveData[saberMove].animToUse) { return qtrue; } //check level 4 anim += SABER_ANIM_GROUP_SIZE; - if ( anim == saberMoveData[saberMove].animToUse ) + if (anim == saberMoveData[saberMove].animToUse) { return qtrue; } //check level 5 anim += SABER_ANIM_GROUP_SIZE; - if ( anim == saberMoveData[saberMove].animToUse ) + if (anim == saberMoveData[saberMove].animToUse) { return qtrue; } - if ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_H1_S1_BR ) + if (anim >= BOTH_P1_S1_T_ && anim <= BOTH_H1_S1_BR) {//parries, knockaways and broken parries - return (anim==saberMoveData[saberMove].animToUse); + return (qboolean)(anim==saberMoveData[saberMove].animToUse) } return qfalse; } -qboolean PM_SaberInIdle( int move ) +qboolean PM_SaberInIdle(int move) { - switch ( move ) + switch (move) { case LS_NONE: case LS_READY: @@ -1210,9 +1735,9 @@ qboolean PM_SaberInIdle( int move ) } return qfalse; } -qboolean PM_SaberInSpecialAttack( int anim ) +qboolean PM_SaberInSpecialAttack(int anim) { - switch ( anim ) + switch (anim) { case BOTH_A2_STABBACK1: case BOTH_ATTACK_BACK: @@ -1287,21 +1812,21 @@ qboolean PM_SaberInSpecialAttack( int anim ) return qfalse; } -qboolean PM_SaberInAttackPure( int move ) +qboolean PM_SaberInAttackPure(int move) { - if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + if (move >= LS_A_TL2BR && move <= LS_A_T2B) { return qtrue; } return qfalse; } -qboolean PM_SaberInAttack( int move ) +qboolean PM_SaberInAttack(int move) { - if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + if (move >= LS_A_TL2BR && move <= LS_A_T2B) { return qtrue; } - switch ( move ) + switch (move) { case LS_A_BACK: case LS_A_BACK_CR: @@ -1359,106 +1884,106 @@ qboolean PM_SaberInAttack( int move ) } return qfalse; } -qboolean PM_SaberInTransition( int move ) +qboolean PM_SaberInTransition(int move) { - if ( move >= LS_T1_BR__R && move <= LS_T1_BL__L ) + if (move >= LS_T1_BR__R && move <= LS_T1_BL__L) { return qtrue; } return qfalse; } -qboolean PM_SaberInStart( int move ) +qboolean PM_SaberInStart(int move) { - if ( move >= LS_S_TL2BR && move <= LS_S_T2B ) + if (move >= LS_S_TL2BR && move <= LS_S_T2B) { return qtrue; } return qfalse; } -qboolean PM_SaberInReturn( int move ) +qboolean PM_SaberInReturn(int move) { - if ( move >= LS_R_TL2BR && move <= LS_R_T2B ) + if (move >= LS_R_TL2BR && move <= LS_R_T2B) { return qtrue; } return qfalse; } -qboolean PM_SaberInTransitionAny( int move ) +qboolean PM_SaberInTransitionAny(int move) { - if ( PM_SaberInStart( move ) ) + if (PM_SaberInStart(move)) { return qtrue; } - else if ( PM_SaberInTransition( move ) ) + else if (PM_SaberInTransition(move)) { return qtrue; } - else if ( PM_SaberInReturn( move ) ) + else if (PM_SaberInReturn(move)) { return qtrue; } return qfalse; } -qboolean PM_SaberInBounce( int move ) +qboolean PM_SaberInBounce(int move) { - if ( move >= LS_B1_BR && move <= LS_B1_BL ) + if (move >= LS_B1_BR && move <= LS_B1_BL) { return qtrue; } - if ( move >= LS_D1_BR && move <= LS_D1_BL ) + if (move >= LS_D1_BR && move <= LS_D1_BL) { return qtrue; } return qfalse; } -qboolean PM_SaberInBrokenParry( int move ) +qboolean PM_SaberInBrokenParry(int move) { - if ( move >= LS_V1_BR && move <= LS_V1_B_ ) + if (move >= LS_V1_BR && move <= LS_V1_B_) { return qtrue; } - if ( move >= LS_H1_T_ && move <= LS_H1_BL ) + if (move >= LS_H1_T_ && move <= LS_H1_BL) { return qtrue; } return qfalse; } -qboolean PM_SaberInDeflect( int move ) +qboolean PM_SaberInDeflect(int move) { - if ( move >= LS_D1_BR && move <= LS_D1_B_ ) + if (move >= LS_D1_BR && move <= LS_D1_B_) { return qtrue; } return qfalse; } -qboolean PM_SaberInParry( int move ) +qboolean PM_SaberInParry(int move) { - if ( move >= LS_PARRY_UP && move <= LS_PARRY_LL ) + if (move >= LS_PARRY_UP && move <= LS_PARRY_LL) { return qtrue; } return qfalse; } -qboolean PM_SaberInKnockaway( int move ) +qboolean PM_SaberInKnockaway(int move) { - if ( move >= LS_K1_T_ && move <= LS_K1_BL ) + if (move >= LS_K1_T_ && move <= LS_K1_BL) { return qtrue; } return qfalse; } -qboolean PM_SaberInReflect( int move ) +qboolean PM_SaberInReflect(int move) { - if ( move >= LS_REFLECT_UP && move <= LS_REFLECT_LL ) + if (move >= LS_REFLECT_UP && move <= LS_REFLECT_LL) { return qtrue; } return qfalse; } -qboolean PM_SaberInSpecial( int move ) +qboolean PM_SaberInSpecial(int move) { - switch( move ) + switch (move) { case LS_A_BACK: case LS_A_BACK_CR: @@ -1516,9 +2041,9 @@ qboolean PM_SaberInSpecial( int move ) return qfalse; } -qboolean PM_KickMove( int move ) +qboolean PM_KickMove(int move) { - switch( move ) + switch (move) { case LS_KICK_F: case LS_KICK_B: @@ -1537,11 +2062,11 @@ qboolean PM_KickMove( int move ) return qfalse; } -qboolean PM_SaberCanInterruptMove( int move, int anim ) +qboolean PM_SaberCanInterruptMove(int move, int anim) { - if ( PM_InAnimForSaberMove( anim, move ) ) + if (PM_InAnimForSaberMove(anim, move)) { - switch( move ) + switch (move) { case LS_A_BACK: case LS_A_BACK_CR: @@ -1587,44 +2112,44 @@ qboolean PM_SaberCanInterruptMove( int move, int anim ) return qfalse; } - if ( PM_SaberInAttackPure( move ) ) + if (PM_SaberInAttackPure(move)) { return qfalse; } - if ( PM_SaberInStart( move ) ) + if (PM_SaberInStart(move)) { return qfalse; } - if ( PM_SaberInTransition( move ) ) + if (PM_SaberInTransition(move)) { return qfalse; } - if ( PM_SaberInBounce( move ) ) + if (PM_SaberInBounce(move)) { return qfalse; } - if ( PM_SaberInBrokenParry( move ) ) + if (PM_SaberInBrokenParry(move)) { return qfalse; } - if ( PM_SaberInDeflect( move ) ) + if (PM_SaberInDeflect(move)) { return qfalse; } - if ( PM_SaberInParry( move ) ) + if (PM_SaberInParry(move)) { return qfalse; } - if ( PM_SaberInKnockaway( move ) ) + if (PM_SaberInKnockaway(move)) { return qfalse; } - if ( PM_SaberInReflect( move ) ) + if (PM_SaberInReflect(move)) { return qfalse; } } - switch ( anim ) + switch (anim) { case BOTH_A2_STABBACK1: case BOTH_ATTACK_BACK: @@ -1691,12 +2216,12 @@ qboolean PM_SaberCanInterruptMove( int move, int anim ) return qtrue; } -saberMoveName_t PM_BrokenParryForAttack( int move ) +saberMoveName_t PM_BrokenParryForAttack(int move) { //Our attack was knocked away by a knockaway parry //FIXME: need actual anims for this //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center - switch ( saberMoveData[move].startQuad ) + switch (saberMoveData[move].startQuad) { case Q_B: return LS_V1_B_; @@ -1726,15 +2251,15 @@ saberMoveName_t PM_BrokenParryForAttack( int move ) return LS_NONE; } -saberMoveName_t PM_BrokenParryForParry( int move ) +saberMoveName_t PM_BrokenParryForParry(int move) { //FIXME: need actual anims for this //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center - switch ( move ) + switch (move) { case LS_PARRY_UP: //Hmm... since we don't know what dir the hit came from, randomly pick knock down or knock back - if ( Q_irand( 0, 1 ) ) + if (Q_irand(0, 1)) { return LS_H1_B_; } @@ -1762,11 +2287,11 @@ saberMoveName_t PM_BrokenParryForParry( int move ) return LS_NONE; } -saberMoveName_t PM_KnockawayForParry( int move ) +saberMoveName_t PM_KnockawayForParry(int move) { //FIXME: need actual anims for this //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center - switch ( move ) + switch (move) { case BLOCKED_TOP://LS_PARRY_UP: return LS_K1_T_;//push up @@ -1788,9 +2313,9 @@ saberMoveName_t PM_KnockawayForParry( int move ) //return LS_NONE; } -saberMoveName_t PM_SaberBounceForAttack( int move ) +saberMoveName_t PM_SaberBounceForAttack(int move) { - switch ( saberMoveData[move].startQuad ) + switch (saberMoveData[move].startQuad) { case Q_B: case Q_BR: @@ -1818,9 +2343,9 @@ saberMoveName_t PM_SaberBounceForAttack( int move ) return LS_NONE; } -saberMoveName_t PM_AttackMoveForQuad( int quad ) +saberMoveName_t PM_AttackMoveForQuad(int quad) { - switch ( quad ) + switch (quad) { case Q_B: case Q_BR: @@ -1932,20 +2457,20 @@ int saberMoveTransitionAngle[Q_NUM_QUADS][Q_NUM_QUADS] = } }; -int PM_SaberAttackChainAngle( int move1, int move2 ) +int PM_SaberAttackChainAngle(int move1, int move2) { - if ( move1 == -1 || move2 == -1 ) + if (move1 == -1 || move2 == -1) { return -1; } return saberMoveTransitionAngle[saberMoveData[move1].endQuad][saberMoveData[move2].startQuad]; } -qboolean PM_SaberKataDone( int curmove = LS_NONE, int newmove = LS_NONE ) +qboolean PM_SaberKataDone(int curmove = LS_NONE, int newmove = LS_NONE) { - if ( pm->ps->forceRageRecoveryTime > level.time ) + if (pm->ps->forceRageRecoveryTime > level.time) {//rage recovery, only 1 swing at a time (tired) - if ( pm->ps->saberAttackChainCount > 0 ) + if (pm->ps->saberAttackChainCount > 0) {//swung once return qtrue; } @@ -1954,17 +2479,17 @@ qboolean PM_SaberKataDone( int curmove = LS_NONE, int newmove = LS_NONE ) return qfalse; } } - else if ( (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1 << FP_RAGE))) {//infinite chaining when raged return qfalse; } - else if ( pm->ps->saber[0].maxChain == -1 ) + else if (pm->ps->saber[0].maxChain == -1) { return qfalse; } - else if ( pm->ps->saber[0].maxChain != 0 ) + else if (pm->ps->saber[0].maxChain != 0) { - if ( pm->ps->saberAttackChainCount >= pm->ps->saber[0].maxChain ) + if (pm->ps->saberAttackChainCount >= pm->ps->saber[0].maxChain) { return qtrue; } @@ -1974,85 +2499,206 @@ qboolean PM_SaberKataDone( int curmove = LS_NONE, int newmove = LS_NONE ) } } - if ( pm->ps->saberAnimLevel == SS_DESANN || pm->ps->saberAnimLevel == SS_TAVION ) + if (pm->ps->saberAnimLevel == SS_DESANN || pm->ps->saberAnimLevel == SS_TAVION) {//desann and tavion can link up as many attacks as they want return qfalse; } + + if ( pm->ps->saberAnimLevel == SS_KATARN ) + { + if (pm->ps->saberAttackChainCount > 0) + { + return qtrue; + } + return qfalse; + } //FIXME: instead of random, apply some sort of logical conditions to whether or // not you can chain? Like if you were completely missed, you can't chain as much, or...? // And/Or based on FP_SABER_OFFENSE level? So number of attacks you can chain // increases with your FP_SABER_OFFENSE skill? - if ( pm->ps->saberAnimLevel == SS_STAFF ) + if (pm->ps->saberAnimLevel == SS_STAFF) { //TEMP: for now, let staff attacks infinitely chain return qfalse; /* if ( pm->ps->saberAttackChainCount > Q_irand( 3, 4 ) ) { - return qtrue; + return qtrue; } else if ( pm->ps->saberAttackChainCount > 0 ) { - int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); - if ( chainAngle < 135 || chainAngle > 215 ) + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 3 ) + { + return qtrue; + } + } + } + */ + } + else if (pm->ps->saberAnimLevel == SS_DUAL) + { + //TEMP: for now, let staff attacks infinitely chain + return qfalse; + } + else if (pm->ps->saberAnimLevel == FORCE_LEVEL_3) + { + if (curmove == LS_NONE || newmove == LS_NONE) + { + if (pm->ps->saberAnimLevel >= FORCE_LEVEL_3 && pm->ps->saberAttackChainCount > Q_irand(0, 1)) + { + return qtrue; + } + } + else if (pm->ps->saberAttackChainCount > Q_irand(2, 3)) + { + return qtrue; + } + else if (pm->ps->saberAttackChainCount > 0) + { + int chainAngle = PM_SaberAttackChainAngle(curmove, newmove); + if (chainAngle < 135 || chainAngle > 215) {//if trying to chain to a move that doesn't continue the momentum - if ( pm->ps->saberAttackChainCount > 1 ) - { - return qtrue; - } + return qtrue; } - else if ( chainAngle == 180 ) + else if (chainAngle == 180) {//continues the momentum perfectly, allow it to chain 66% of the time - if ( pm->ps->saberAttackChainCount > 2 ) + if (pm->ps->saberAttackChainCount > 1) { return qtrue; } } else {//would continue the movement somewhat, 50% chance of continuing - if ( pm->ps->saberAttackChainCount > 3 ) + if (pm->ps->saberAttackChainCount > 2) { return qtrue; } } } - */ } - else if ( pm->ps->saberAnimLevel == SS_DUAL ) + else + {//FIXME: have chainAngle influence fast and medium chains as well? + if ((pm->ps->saberAnimLevel == FORCE_LEVEL_2 || pm->ps->saberAnimLevel == SS_DUAL) + && pm->ps->saberAttackChainCount > Q_irand(2, 5)) + { + return qtrue; + } + } + return qfalse; +} + +qboolean PM_SaberKataDoneNew(int curmove = LS_NONE, int newmove = LS_NONE) +{ + if (pm->ps->forceRageRecoveryTime > level.time) + {//rage recovery, only 1 swing at a time (tired) + if (pm->ps->saberAttackChainCount > 0) + {//swung once + return qtrue; + } + else + {//allow one attack + return qfalse; + } + } + else if (PM_RunningAnim(pm->ps->legsAnim)) + {//can only chain two attacks if running + if (pm->ps->saberAttackChainCount > 1) + { + return qtrue; + } + else + { + return qfalse; + } + } + else if ((pm->ps->forcePowersActive&(1 << FP_RAGE))) + {//infinite chaining when raged + return qfalse; + } + else if (pm->ps->saber[0].maxChain == -1) { - //TEMP: for now, let staff attacks infinitely chain return qfalse; } - else if ( pm->ps->saberAnimLevel == FORCE_LEVEL_3 ) + else if (pm->ps->saber[0].maxChain != 0) + { + if (pm->ps->saberAttackChainCount >= pm->ps->saber[0].maxChain) + { + return qtrue; + } + else + { + return qfalse; + } + } + + if (pm->ps->saberAnimLevel == SS_DESANN && pm->ps->saberAttackChainCount > 3) + { + return qtrue; + } + + if (pm->ps->saberAnimLevel == SS_TAVION && pm->ps->saberAttackChainCount > 5) + {//desann and tavion can link up as many attacks as they want + return qtrue; + } + //FIXME: instead of random, apply some sort of logical conditions to whether or + // not you can chain? Like if you were completely missed, you can't chain as much, or...? + // And/Or based on FP_SABER_OFFENSE level? So number of attacks you can chain + // increases with your FP_SABER_OFFENSE skill? + if (pm->ps->saberAnimLevel == SS_STAFF && pm->ps->saberAttackChainCount > 5) + { + return qtrue; + } + else if (pm->ps->saberAnimLevel == SS_DUAL && pm->ps->saberAttackChainCount > 5) + { + return qtrue; + } + else if (pm->ps->saberAnimLevel == SS_STRONG) { - if ( curmove == LS_NONE || newmove == LS_NONE ) + if (curmove == LS_NONE || newmove == LS_NONE) { - if ( pm->ps->saberAnimLevel >= FORCE_LEVEL_3 && pm->ps->saberAttackChainCount > Q_irand( 0, 1 ) ) + if (pm->ps->saberAnimLevel >= SS_STRONG && pm->ps->saberAttackChainCount > 1) { return qtrue; } } - else if ( pm->ps->saberAttackChainCount > Q_irand( 2, 3 ) ) + else if (pm->ps->saberAttackChainCount > 3) { return qtrue; } - else if ( pm->ps->saberAttackChainCount > 0 ) + else if (pm->ps->saberAttackChainCount > 0) { - int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); - if ( chainAngle < 135 || chainAngle > 215 ) + int chainAngle = PM_SaberAttackChainAngle(curmove, newmove); + if (chainAngle < 135 || chainAngle > 215) {//if trying to chain to a move that doesn't continue the momentum return qtrue; } - else if ( chainAngle == 180 ) + else if (chainAngle == 180) {//continues the momentum perfectly, allow it to chain 66% of the time - if ( pm->ps->saberAttackChainCount > 1 ) + if (pm->ps->saberAttackChainCount > 1) { return qtrue; } } else {//would continue the movement somewhat, 50% chance of continuing - if ( pm->ps->saberAttackChainCount > 2 ) + if (pm->ps->saberAttackChainCount > 2) { return qtrue; } @@ -2061,49 +2707,49 @@ qboolean PM_SaberKataDone( int curmove = LS_NONE, int newmove = LS_NONE ) } else {//FIXME: have chainAngle influence fast and medium chains as well? - if ( (pm->ps->saberAnimLevel == FORCE_LEVEL_2 || pm->ps->saberAnimLevel == SS_DUAL) - && pm->ps->saberAttackChainCount > Q_irand( 2, 5 ) ) + if (pm->ps->saberAnimLevel == SS_MEDIUM && pm->ps->saberAttackChainCount > 5) { return qtrue; } } + return qfalse; } -qboolean PM_CheckEnemyInBack( float backCheckDist ) +qboolean PM_CheckEnemyInBack(float backCheckDist) { - if ( !pm->gent || !pm->gent->client ) + if (!pm->gent || !pm->gent->client) { return qfalse; } - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) - && !g_saberAutoAim->integer && pm->cmd.forwardmove >= 0 ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + && !g_saberAutoAim->integer && pm->cmd.forwardmove >= 0) {//don't auto-backstab return qfalse; } - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE) {//only when on ground return qfalse; } trace_t trace; - vec3_t end, fwd, fwdAngles = {0,pm->ps->viewangles[YAW],0}; + vec3_t end, fwd, fwdAngles = { 0, pm->ps->viewangles[YAW], 0 }; - AngleVectors( fwdAngles, fwd, NULL, NULL ); - VectorMA( pm->ps->origin, -backCheckDist, fwd, end ); + AngleVectors(fwdAngles, fwd, NULL, NULL); + VectorMA(pm->ps->origin, -backCheckDist, fwd, end); - pm->trace( &trace, pm->ps->origin, vec3_origin, vec3_origin, end, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 ); - if ( trace.fraction < 1.0f && trace.entityNum < ENTITYNUM_WORLD ) + pm->trace(&trace, pm->ps->origin, vec3_origin, vec3_origin, end, pm->ps->clientNum, CONTENTS_SOLID | CONTENTS_BODY, (EG2_Collision)0, 0); + if (trace.fraction < 1.0f && trace.entityNum < ENTITYNUM_WORLD) { gentity_t *traceEnt = &g_entities[trace.entityNum]; - if ( traceEnt + if (traceEnt && traceEnt->health > 0 && traceEnt->client && traceEnt->client->playerTeam == pm->gent->client->enemyTeam - && traceEnt->client->ps.groundEntityNum != ENTITYNUM_NONE ) + && traceEnt->client->ps.groundEntityNum != ENTITYNUM_NONE) { - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) {//player - if ( pm->gent ) + if (pm->gent) {//set player enemy to traceEnt so he auto-aims at him pm->gent->enemy = traceEnt; } @@ -2114,16 +2760,16 @@ qboolean PM_CheckEnemyInBack( float backCheckDist ) return qfalse; } -saberMoveName_t PM_PickBackStab( void ) +saberMoveName_t PM_PickBackStab(void) { - if ( !pm->gent || !pm->gent->client ) + if (!pm->gent || !pm->gent->client) { return LS_READY; } - if ( pm->ps->dualSabers - && pm->ps->saber[1].Active() ) + if (pm->ps->dualSabers + && pm->ps->saber[1].Active()) { - if ( pm->ps->pm_flags & PMF_DUCKED ) + if (pm->ps->pm_flags & PMF_DUCKED) { return LS_A_BACK_CR; } @@ -2132,17 +2778,29 @@ saberMoveName_t PM_PickBackStab( void ) return LS_A_BACK; } } - if ( pm->gent->client->ps.saberAnimLevel == SS_TAVION ) + if (pm->gent->client->ps.saberAnimLevel == SS_TAVION) { return LS_A_BACKSTAB; } - else if ( pm->gent->client->ps.saberAnimLevel == SS_DESANN ) + else if (pm->gent->client->ps.saberAnimLevel == SS_DESANN) { - if ( pm->ps->saberMove == LS_READY || !Q_irand( 0, 3 ) ) + if (pm->ps->saberMove == LS_READY || !Q_irand(0, 3)) + { + return LS_A_BACKSTAB; + } + else if (pm->ps->pm_flags & PMF_DUCKED) + { + return LS_A_BACK_CR; + } + else { - return LS_A_BACKSTAB; + return LS_A_BACK; } - else if ( pm->ps->pm_flags & PMF_DUCKED ) + } + else if ((pm->ps->saberAnimLevel == FORCE_LEVEL_3 + || pm->ps->saberAnimLevel == SS_DUAL) && g_saberNewCombat->integer) //new code + {//using medium attacks or dual sabers + if (pm->ps->pm_flags & PMF_DUCKED) { return LS_A_BACK_CR; } @@ -2151,10 +2809,10 @@ saberMoveName_t PM_PickBackStab( void ) return LS_A_BACK; } } - else if ( pm->ps->saberAnimLevel == FORCE_LEVEL_2 - || pm->ps->saberAnimLevel == SS_DUAL ) + else if (pm->ps->saberAnimLevel == FORCE_LEVEL_2 + || pm->ps->saberAnimLevel == SS_DUAL) //old code {//using medium attacks or dual sabers - if ( pm->ps->pm_flags & PMF_DUCKED ) + if (pm->ps->pm_flags & PMF_DUCKED) { return LS_A_BACK_CR; } @@ -2169,33 +2827,33 @@ saberMoveName_t PM_PickBackStab( void ) } } -saberMoveName_t PM_CheckStabDown( void ) +saberMoveName_t PM_CheckStabDown(void) { - if ( !pm->gent || !pm->gent->enemy || !pm->gent->enemy->client ) + if (!pm->gent || !pm->gent->enemy || !pm->gent->enemy->client) { return LS_NONE; } - if ( (pm->ps->saber[0].saberFlags&SFL_NO_STABDOWN) ) + if ((pm->ps->saber[0].saberFlags&SFL_NO_STABDOWN)) { return LS_NONE; } - if ( pm->ps->dualSabers - && (pm->ps->saber[1].saberFlags&SFL_NO_STABDOWN) ) + if (pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_STABDOWN)) { return LS_NONE; } - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY - {//player - if ( G_TryingKataAttack( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + {//player + if (G_TryingKataAttack(pm->gent, &pm->cmd))//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus {//want to try a special return LS_NONE; } } - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) {//player - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + if (pm->ps->groundEntityNum == ENTITYNUM_NONE)//in air {//sorry must be on ground (or have just jumped) - if ( level.time-pm->ps->lastOnGround <= 50 && (pm->ps->pm_flags&PMF_JUMPING) ) + if (level.time - pm->ps->lastOnGround <= 50 && (pm->ps->pm_flags&PMF_JUMPING)) {//just jumped, it's okay } else @@ -2208,23 +2866,23 @@ saberMoveName_t PM_CheckStabDown( void ) {//trying to jump } else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE //in air - && level.time-pm->ps->lastOnGround <= 250 //just left ground - && (pm->ps->pm_flags&PMF_JUMPING) )//jumped + && level.time-pm->ps->lastOnGround <= 250 //just left ground + && (pm->ps->pm_flags&PMF_JUMPING) )//jumped {//just jumped } else { - return LS_NONE; + return LS_NONE; } */ pm->ps->velocity[2] = 0; pm->cmd.upmove = 0; } - else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + else if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())) {//NPC - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + if (pm->ps->groundEntityNum == ENTITYNUM_NONE)//in air {//sorry must be on ground (or have just jumped) - if ( level.time-pm->ps->lastOnGround <= 250 && (pm->ps->pm_flags&PMF_JUMPING) ) + if (level.time - pm->ps->lastOnGround <= 250 && (pm->ps->pm_flags&PMF_JUMPING)) {//just jumped, it's okay } else @@ -2232,44 +2890,44 @@ saberMoveName_t PM_CheckStabDown( void ) return LS_NONE; } } - if ( !pm->gent->NPC ) + if (!pm->gent->NPC) {//wtf??? return LS_NONE; } - if ( Q_irand( 0, RANK_CAPTAIN ) > pm->gent->NPC->rank ) + if (Q_irand(0, RANK_CAPTAIN) > pm->gent->NPC->rank) {//lower ranks do this less often return LS_NONE; } } - vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; - AngleVectors( facingAngles, faceFwd, NULL, NULL ); - VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + vec3_t enemyDir, faceFwd, facingAngles = { 0, pm->ps->viewangles[YAW], 0 }; + AngleVectors(facingAngles, faceFwd, NULL, NULL); + VectorSubtract(pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir); float enemyZDiff = enemyDir[2]; enemyDir[2] = 0; - float enemyHDist = VectorNormalize( enemyDir )-(pm->gent->maxs[0]+pm->gent->enemy->maxs[0]); - float dot = DotProduct( enemyDir, faceFwd ); + float enemyHDist = VectorNormalize(enemyDir) - (pm->gent->maxs[0] + pm->gent->enemy->maxs[0]); + float dot = DotProduct(enemyDir, faceFwd); if ( //(pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) dot > 0.65f //&& enemyHDist >= 32 //was 48 && enemyHDist <= 164//was 112 - && PM_InKnockDownOnGround( &pm->gent->enemy->client->ps )//still on ground - && !PM_InGetUpNoRoll( &pm->gent->enemy->client->ps )//not getting up yet - && enemyZDiff <= 20 ) + && PM_InKnockDownOnGround(&pm->gent->enemy->client->ps)//still on ground + && !PM_InGetUpNoRoll(&pm->gent->enemy->client->ps)//not getting up yet + && enemyZDiff <= 20) {//guy is on the ground below me, do a top-down attack - if ( pm->gent->enemy->s.number >= MAX_CLIENTS - || !G_ControlledByPlayer( pm->gent->enemy ) ) + if (pm->gent->enemy->s.number >= MAX_CLIENTS + || !G_ControlledByPlayer(pm->gent->enemy)) {//don't get up while I'm doing this //stop them from trying to get up for at least another 3 seconds - TIMER_Set( pm->gent->enemy, "noGetUpStraight", 3000 ); + TIMER_Set(pm->gent->enemy, "noGetUpStraight", 3000); } //pick the right anim - if ( pm->ps->saberAnimLevel == SS_DUAL - || (pm->ps->dualSabers&&pm->ps->saber[1].Active()) ) + if (pm->ps->saberAnimLevel == SS_DUAL + || (pm->ps->dualSabers&&pm->ps->saber[1].Active())) { return LS_STABDOWN_DUAL; } - else if ( pm->ps->saberAnimLevel == SS_STAFF ) + else if (pm->ps->saberAnimLevel == SS_STAFF) { return LS_STABDOWN_STAFF; } @@ -2281,78 +2939,78 @@ saberMoveName_t PM_CheckStabDown( void ) return LS_NONE; } -extern saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ); -saberMoveName_t PM_SaberFlipOverAttackMove( void ); -saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ) +extern saberMoveName_t PM_NPCSaberAttackFromQuad(int quad); +saberMoveName_t PM_SaberFlipOverAttackMove(void); +saberMoveName_t PM_AttackForEnemyPos(qboolean allowFB, qboolean allowStabDown) { saberMoveName_t autoMove = LS_INVALID; - if( !pm->gent->enemy ) + if (!pm->gent->enemy) { return LS_NONE; } - vec3_t enemy_org, enemyDir, faceFwd, faceRight, faceUp, facingAngles = {0, pm->ps->viewangles[YAW], 0}; - AngleVectors( facingAngles, faceFwd, faceRight, faceUp ); + vec3_t enemy_org, enemyDir, faceFwd, faceRight, faceUp, facingAngles = { 0, pm->ps->viewangles[YAW], 0 }; + AngleVectors(facingAngles, faceFwd, faceRight, faceUp); //FIXME: predict enemy position? - if ( pm->gent->enemy->client ) + if (pm->gent->enemy->client) { //VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); //HMM... using this will adjust for bbox size, so let's do that... vec3_t size; - VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); - VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + VectorSubtract(pm->gent->enemy->absmax, pm->gent->enemy->absmin, size); + VectorMA(pm->gent->enemy->absmin, 0.5, size, enemy_org); - VectorSubtract( pm->gent->enemy->client->renderInfo.eyePoint, pm->ps->origin, enemyDir ); + VectorSubtract(pm->gent->enemy->client->renderInfo.eyePoint, pm->ps->origin, enemyDir); } else { - if ( pm->gent->enemy->bmodel && VectorCompare( vec3_origin, pm->gent->enemy->currentOrigin ) ) + if (pm->gent->enemy->bmodel && VectorCompare(vec3_origin, pm->gent->enemy->currentOrigin)) {//a brush model without an origin brush vec3_t size; - VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); - VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + VectorSubtract(pm->gent->enemy->absmax, pm->gent->enemy->absmin, size); + VectorMA(pm->gent->enemy->absmin, 0.5, size, enemy_org); } else { - VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); + VectorCopy(pm->gent->enemy->currentOrigin, enemy_org); } - VectorSubtract( enemy_org, pm->ps->origin, enemyDir ); + VectorSubtract(enemy_org, pm->ps->origin, enemyDir); } float enemyZDiff = enemyDir[2]; - float enemyDist = VectorNormalize( enemyDir ); - float dot = DotProduct( enemyDir, faceFwd ); - if ( dot > 0 ) + float enemyDist = VectorNormalize(enemyDir); + float dot = DotProduct(enemyDir, faceFwd); + if (dot > 0) {//enemy is in front - if ( allowStabDown ) + if (allowStabDown) {//okay to try this saberMoveName_t stabDownMove = PM_CheckStabDown(); - if ( stabDownMove != LS_NONE ) + if (stabDownMove != LS_NONE) { return stabDownMove; } } - if ( (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && dot > 0.65f && enemyDist <= 64 && pm->gent->enemy->client - && (enemyZDiff <= 20 || PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) || PM_CrouchAnim( pm->gent->enemy->client->ps.legsAnim ) ) ) + && (enemyZDiff <= 20 || PM_InKnockDownOnGround(&pm->gent->enemy->client->ps) || PM_CrouchAnim(pm->gent->enemy->client->ps.legsAnim))) {//swing down at them return LS_A_T2B; } - if ( allowFB ) + if (allowFB) {//directly in front anim allowed - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK))) {//okay to do backstabs with this saber - if ( enemyDist > 200 || pm->gent->enemy->health <= 0 ) + if (enemyDist > 200 || pm->gent->enemy->health <= 0) {//hmm, look in back for an enemy - if ( pm->ps->clientNum && !PM_ControlledByPlayer() ) + if (pm->ps->clientNum && !PM_ControlledByPlayer()) {//player should never do this automatically - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum != ENTITYNUM_NONE) {//only when on ground - if ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) + if (pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand(0, pm->gent->NPC->rank) > RANK_ENSIGN) {//only fencers and higher can do this, higher rank does it more - if ( PM_CheckEnemyInBack( 100 ) ) + if (PM_CheckEnemyInBack(100)) { return PM_PickBackStab(); } @@ -2363,16 +3021,16 @@ saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ) } //this is the default only if they're *right* in front... if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) - || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && BG_AllowThirdPersonSpecialMove( pm->ps ) && !cg.zoomMode) ) {//NPC or player not in 1st person - if ( PM_CheckFlipOverAttackMove( qtrue ) ) + if (PM_CheckFlipOverAttackMove(qtrue)) {//enemy must be close and in front return PM_SaberFlipOverAttackMove(); } } - if ( PM_CheckLungeAttackMove() ) + if (PM_CheckLungeAttackMove()) {//NPC - autoMove = PM_SaberLungeAttackMove( qtrue ); + autoMove = PM_SaberLungeAttackMove(qtrue); } else { @@ -2381,7 +3039,7 @@ saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ) } else {//pick a random one - if ( Q_irand( 0, 1 ) ) + if (Q_irand(0, 1)) { autoMove = LS_A_TR2BL; } @@ -2390,63 +3048,63 @@ saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ) autoMove = LS_A_TL2BR; } } - float dotR = DotProduct( enemyDir, faceRight ); - if ( dotR > 0.35 ) + float dotR = DotProduct(enemyDir, faceRight); + if (dotR > 0.35) {//enemy is to far right autoMove = LS_A_L2R; } - else if ( dotR < -0.35 ) + else if (dotR < -0.35) {//far left autoMove = LS_A_R2L; } - else if ( dotR > 0.15 ) + else if (dotR > 0.15) {//enemy is to near right autoMove = LS_A_TR2BL; } - else if ( dotR < -0.15 ) + else if (dotR < -0.15) {//near left autoMove = LS_A_TL2BR; } - if ( DotProduct( enemyDir, faceUp ) > 0.5 ) + if (DotProduct(enemyDir, faceUp) > 0.5) {//enemy is above me - if ( autoMove == LS_A_TR2BL ) + if (autoMove == LS_A_TR2BL) { autoMove = LS_A_BL2TR; } - else if ( autoMove == LS_A_TL2BR ) + else if (autoMove == LS_A_TL2BR) { autoMove = LS_A_BR2TL; } } } - else if ( allowFB ) + else if (allowFB) {//back attack allowed //if ( !PM_InKnockDown( pm->ps ) ) - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK))) {//okay to do backstabs with this saber - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum != ENTITYNUM_NONE) {//only when on ground - if ( !pm->gent->enemy->client || pm->gent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE ) + if (!pm->gent->enemy->client || pm->gent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE) {//enemy not a client or is a client and on ground - if ( dot < -0.75f + if (dot < -0.75f && enemyDist < 128 - && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,2))) ) + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client && (pm->gent->client->NPC_class == CLASS_TAVION || pm->gent->client->NPC_class == CLASS_ALORA) && Q_irand(0, 2)))) {//fast back-stab - if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + if (!(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0) {//can't do it while ducked? - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG)) {//only fencers and above can do this autoMove = LS_A_BACKSTAB; } } } - else if ( pm->ps->saberAnimLevel != SS_FAST - && pm->ps->saberAnimLevel != SS_STAFF ) + else if (pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF) {//higher level back spin-attacks - if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && BG_AllowThirdPersonSpecialMove( pm->ps ) && !cg.zoomMode) ) { - if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + if ((pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0) { autoMove = LS_A_BACK_CR; } @@ -2463,89 +3121,93 @@ saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ) return autoMove; } -qboolean PM_InSecondaryStyle( void ) +qboolean PM_InSecondaryStyle(void) { - if ( pm->ps->saber[0].numBlades > 1 + if (pm->ps->saber[0].numBlades > 1 && pm->ps->saber[0].singleBladeStyle - && (pm->ps->saber[0].stylesForbidden&(1<ps->saber[0].singleBladeStyle)) - && pm->ps->saberAnimLevel == pm->ps->saber[0].singleBladeStyle ) + && (pm->ps->saber[0].stylesForbidden&(1 << pm->ps->saber[0].singleBladeStyle)) + && pm->ps->saberAnimLevel == pm->ps->saber[0].singleBladeStyle) { return qtrue; } - if ( pm->ps->dualSabers - && !pm->ps->saber[1].Active() )//pm->ps->saberAnimLevel != SS_DUAL ) + if (pm->ps->dualSabers + && !pm->ps->saber[1].Active())//pm->ps->saberAnimLevel != SS_DUAL ) { return qtrue; } return qfalse; } -saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ) +saberMoveName_t PM_SaberLungeAttackMove(qboolean fallbackToNormalLunge) { - G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER_FB ); + G_DrainPowerForSpecialMove(pm->gent, FP_SABER_OFFENSE, g_saberForceDrainAmount->integer); //SABER_ALT_ATTACK_POWER_FB); //see if we have an overridden (or cancelled) lunge move - if ( pm->ps->saber[0].lungeAtkMove != LS_INVALID ) + if (pm->ps->saber[0].lungeAtkMove != LS_INVALID) { - if ( pm->ps->saber[0].lungeAtkMove != LS_NONE ) + if (pm->ps->saber[0].lungeAtkMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[0].lungeAtkMove; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].lungeAtkMove != LS_INVALID ) + if (pm->ps->saber[1].lungeAtkMove != LS_INVALID) { - if ( pm->ps->saber[1].lungeAtkMove != LS_NONE ) + if (pm->ps->saber[1].lungeAtkMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[1].lungeAtkMove; } } } //no overrides, cancelled? - if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + if (pm->ps->saber[0].lungeAtkMove == LS_NONE) { return LS_NONE; } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + if (pm->ps->saber[1].lungeAtkMove == LS_NONE) { return LS_NONE; } } //do normal checks - if ( pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand( 0, 3 ) ) + if (pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand(0, 3)) {//alora NPC return LS_SPINATTACK_ALORA; } else { - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { return LS_SPINATTACK_DUAL; } - switch ( pm->ps->saberAnimLevel ) + switch (pm->ps->saberAnimLevel) { case SS_DUAL: return LS_SPINATTACK_DUAL; break; case SS_STAFF: + if ((pm->gent->s.number && !Q_irand(0, 3)) || pm->cmd.buttons&BUTTON_USE) + { + return LS_HILT_BASH; + } return LS_SPINATTACK; break; default://normal lunge - if ( fallbackToNormalLunge ) + if (fallbackToNormalLunge) { vec3_t fwdAngles, jumpFwd; - VectorCopy( pm->ps->viewangles, fwdAngles ); + VectorCopy(pm->ps->viewangles, fwdAngles); fwdAngles[PITCH] = fwdAngles[ROLL] = 0; //do the lunge - AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); - VectorScale( jumpFwd, 150, pm->ps->velocity ); + AngleVectors(fwdAngles, jumpFwd, NULL, NULL); + VectorScale(jumpFwd, 150, pm->ps->velocity); pm->ps->velocity[2] = 50; - PM_AddEvent( EV_JUMP ); + PM_AddEvent(EV_JUMP); return LS_A_LUNGE; } @@ -2555,15 +3217,15 @@ saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ) return LS_NONE; } -qboolean PM_CheckLungeAttackMove( void ) +qboolean PM_CheckLungeAttackMove(void) { //check to see if it's cancelled? - if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + if (pm->ps->saber[0].lungeAtkMove == LS_NONE) { - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].lungeAtkMove == LS_NONE - || pm->ps->saber[1].lungeAtkMove == LS_INVALID ) + if (pm->ps->saber[1].lungeAtkMove == LS_NONE + || pm->ps->saber[1].lungeAtkMove == LS_INVALID) { return qfalse; } @@ -2573,44 +3235,44 @@ qboolean PM_CheckLungeAttackMove( void ) return qfalse; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + if (pm->ps->saber[1].lungeAtkMove == LS_NONE) { - if ( pm->ps->saber[0].lungeAtkMove == LS_NONE - || pm->ps->saber[0].lungeAtkMove == LS_INVALID ) + if (pm->ps->saber[0].lungeAtkMove == LS_NONE + || pm->ps->saber[0].lungeAtkMove == LS_INVALID) { return qfalse; } } } //do normal checks - if ( pm->ps->saberAnimLevel == SS_FAST//fast + if (pm->ps->saberAnimLevel == SS_FAST//fast || pm->ps->saberAnimLevel == SS_DUAL//dual || pm->ps->saberAnimLevel == SS_STAFF //staff || pm->ps->saberAnimLevel == SS_DESANN - || pm->ps->dualSabers ) + || pm->ps->dualSabers) {//alt+back+attack using fast, dual or staff attacks - if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()) {//NPC - if ( pm->cmd.upmove < 0 || (pm->ps->pm_flags&PMF_DUCKED) ) + if (pm->cmd.upmove < 0 || (pm->ps->pm_flags&PMF_DUCKED)) {//ducking - if ( pm->ps->legsAnim == BOTH_STAND2 + if (pm->ps->legsAnim == BOTH_STAND2 || pm->ps->legsAnim == BOTH_SABERFAST_STANCE || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE || pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE || pm->ps->legsAnim == BOTH_SABERDUAL_STANCE - || (level.time-pm->ps->lastStationary) <= 500 ) + || (level.time - pm->ps->lastStationary) <= 500) {//standing or just stopped standing - if ( pm->gent + if (pm->gent && pm->gent->NPC //NPC && pm->gent->NPC->rank >= RANK_LT_JG //high rank - && ( pm->gent->NPC->rank == RANK_LT_JG || Q_irand( -3, pm->gent->NPC->rank ) >= RANK_LT_JG )//Q_irand( 0, pm->gent->NPC->rank ) >= RANK_LT_JG ) - && !Q_irand( 0, 3-g_spskill->integer ) ) + && (pm->gent->NPC->rank == RANK_LT_JG || Q_irand(-3, pm->gent->NPC->rank) >= RANK_LT_JG)//Q_irand( 0, pm->gent->NPC->rank ) >= RANK_LT_JG ) + && !Q_irand(0, 3 - g_spskill->integer)) {//only fencer and higher can do this - if ( pm->ps->saberAnimLevel == SS_DESANN ) + if (pm->ps->saberAnimLevel == SS_DESANN) { - if ( !Q_irand( 0, 4 ) ) + if (!Q_irand(0, 4)) { return qtrue; } @@ -2625,8 +3287,8 @@ qboolean PM_CheckLungeAttackMove( void ) } else {//player - if ( G_TryingLungeAttack( pm->gent, &pm->cmd ) - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + if (G_TryingLungeAttack(pm->gent, &pm->cmd) + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB)/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/)//have enough force power to pull it off { return qtrue; } @@ -2636,48 +3298,48 @@ qboolean PM_CheckLungeAttackMove( void ) return qfalse; } -saberMoveName_t PM_SaberJumpForwardAttackMove( void ) +saberMoveName_t PM_SaberJumpForwardAttackMove(void) { - G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + G_DrainPowerForSpecialMove(pm->gent, FP_LEVITATION, g_saberForceDrainAmount->integer);//SABER_ALT_ATTACK_POWER_FB); //see if we have an overridden (or cancelled) kata move - if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + if (pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID) { - if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + if (pm->ps->saber[0].jumpAtkFwdMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + if (pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID) { - if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + if (pm->ps->saber[1].jumpAtkFwdMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; } } } //no overrides, cancelled? - if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + if (pm->ps->saber[0].jumpAtkFwdMove == LS_NONE) { return LS_NONE; } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + if (pm->ps->saber[1].jumpAtkFwdMove == LS_NONE) { return LS_NONE; } } - if ( pm->ps->saberAnimLevel == SS_DUAL - || pm->ps->saberAnimLevel == SS_STAFF ) + if (pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF) { pm->cmd.upmove = 0;//no jump just yet - if ( pm->ps->saberAnimLevel == SS_STAFF ) + if (pm->ps->saberAnimLevel == SS_STAFF) { - if ( Q_irand(0, 1) ) + if (Q_irand(0, 1)) { return LS_JUMPATTACK_STAFF_LEFT; } @@ -2693,38 +3355,38 @@ saberMoveName_t PM_SaberJumpForwardAttackMove( void ) { vec3_t fwdAngles, jumpFwd; - VectorCopy( pm->ps->viewangles, fwdAngles ); + VectorCopy(pm->ps->viewangles, fwdAngles); fwdAngles[PITCH] = fwdAngles[ROLL] = 0; - AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); - VectorScale( jumpFwd, 200, pm->ps->velocity ); + AngleVectors(fwdAngles, jumpFwd, NULL, NULL); + VectorScale(jumpFwd, 200, pm->ps->velocity); pm->ps->velocity[2] = 180; pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height - pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + pm->ps->pm_flags |= PMF_JUMPING | PMF_SLOW_MO_FALL; //FIXME: NPCs yell? - PM_AddEvent( EV_JUMP ); - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + PM_AddEvent(EV_JUMP); + G_SoundOnEnt(pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav"); pm->cmd.upmove = 0; return LS_A_JUMP_T__B_; } } -qboolean PM_CheckJumpForwardAttackMove( void ) +qboolean PM_CheckJumpForwardAttackMove(void) { - if ( pm->ps->clientNum < MAX_CLIENTS - && PM_InSecondaryStyle() ) + if (pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle()) { return qfalse; } //check to see if it's cancelled? - if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + if (pm->ps->saber[0].jumpAtkFwdMove == LS_NONE) { - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE - || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + if (pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID) { return qfalse; } @@ -2734,12 +3396,12 @@ qboolean PM_CheckJumpForwardAttackMove( void ) return qfalse; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + if (pm->ps->saber[1].jumpAtkFwdMove == LS_NONE) { - if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE - || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + if (pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID) { return qfalse; } @@ -2747,28 +3409,28 @@ qboolean PM_CheckJumpForwardAttackMove( void ) } //do normal checks - if ( pm->cmd.forwardmove > 0 //going forward + if (pm->cmd.forwardmove > 0 //going forward && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one - && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped (if not player) + && (pm->ps->groundEntityNum != ENTITYNUM_NONE || level.time - pm->ps->lastOnGround <= 250) //on ground or just jumped (if not player) ) { - if ( pm->ps->saberAnimLevel == SS_DUAL - || pm->ps->saberAnimLevel == SS_STAFF ) + if (pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF) {//dual and staff - if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim - && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + if (!PM_SaberInTransitionAny(pm->ps->saberMove) //not going to/from/between an attack anim + && !PM_SaberInAttack(pm->ps->saberMove) //not in attack anim && pm->ps->weaponTime <= 0//not busy - && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + && (pm->cmd.buttons&BUTTON_ATTACK))//want to attack { - if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()) {//NPC - if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//jumping NPC + if (pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING))//jumping NPC { - if ( pm->gent + if (pm->gent && pm->gent->NPC - && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + && (pm->gent->NPC->rank == RANK_CREWMAN || pm->gent->NPC->rank >= RANK_LT)) { return qtrue; } @@ -2776,8 +3438,8 @@ qboolean PM_CheckJumpForwardAttackMove( void ) } else {//PLAYER - if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power to attack + if (G_TryingJumpForwardAttack(pm->gent, &pm->cmd) + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB))//have enough power to attack { return qtrue; } @@ -2785,8 +3447,8 @@ qboolean PM_CheckJumpForwardAttackMove( void ) } } //check strong - else if ( pm->ps->saberAnimLevel == SS_STRONG //strong style - || pm->ps->saberAnimLevel == SS_DESANN )//desann + else if (pm->ps->saberAnimLevel == SS_STRONG //strong style + || pm->ps->saberAnimLevel == SS_DESANN)//desann { if ( //&& !PM_InKnockDown( pm->ps ) !pm->ps->dualSabers @@ -2795,27 +3457,27 @@ qboolean PM_CheckJumpForwardAttackMove( void ) {//strong attack: jump-hack /* if ( pm->ps->legsAnim == BOTH_STAND2 - || pm->ps->legsAnim == BOTH_SABERFAST_STANCE - || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE - || level.time-pm->ps->lastStationary <= 250 )//standing or just started moving + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || level.time-pm->ps->lastStationary <= 250 )//standing or just started moving */ - if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()) {//NPC - if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//NPC jumping + if (pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING))//NPC jumping { - if ( pm->gent + if (pm->gent && pm->gent->NPC - && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + && (pm->gent->NPC->rank == RANK_CREWMAN || pm->gent->NPC->rank >= RANK_LT)) {//only acrobat or boss and higher can do this - if ( pm->ps->legsAnim == BOTH_STAND2 + if (pm->ps->legsAnim == BOTH_STAND2 || pm->ps->legsAnim == BOTH_SABERFAST_STANCE || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE - || level.time-pm->ps->lastStationary <= 250 ) + || level.time - pm->ps->lastStationary <= 250) {//standing or just started moving - if ( pm->gent->client - && pm->gent->client->NPC_class == CLASS_DESANN ) + if (pm->gent->client + && pm->gent->client->NPC_class == CLASS_DESANN) { - if ( !Q_irand( 0, 1 ) ) + if (!Q_irand(0, 1)) { return qtrue; } @@ -2830,8 +3492,8 @@ qboolean PM_CheckJumpForwardAttackMove( void ) } else {//player - if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) ) + if (G_TryingJumpForwardAttack(pm->gent, &pm->cmd) + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB)) { return qtrue; } @@ -2842,83 +3504,85 @@ qboolean PM_CheckJumpForwardAttackMove( void ) return qfalse; } -saberMoveName_t PM_SaberFlipOverAttackMove( void ) +saberMoveName_t PM_SaberFlipOverAttackMove(void) { + G_DrainPowerForSpecialMove(pm->gent, FP_SABER_OFFENSE, g_saberForceDrainAmount->integer); + //see if we have an overridden (or cancelled) kata move - if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + if (pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID) { - if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + if (pm->ps->saber[0].jumpAtkFwdMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + if (pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID) { - if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + if (pm->ps->saber[1].jumpAtkFwdMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; } } } //no overrides, cancelled? - if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + if (pm->ps->saber[0].jumpAtkFwdMove == LS_NONE) { return LS_NONE; } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + if (pm->ps->saber[1].jumpAtkFwdMove == LS_NONE) { return LS_NONE; } } //FIXME: check above for room enough to jump! - //FIXME: while in this jump, keep velocity[2] at a minimum until the end of the anim + //FIXME: while in this jump, keep velocity[2] at a minimum until the end of the anim vec3_t fwdAngles, jumpFwd; - VectorCopy( pm->ps->viewangles, fwdAngles ); + VectorCopy(pm->ps->viewangles, fwdAngles); fwdAngles[PITCH] = fwdAngles[ROLL] = 0; - AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); - VectorScale( jumpFwd, 150, pm->ps->velocity ); + AngleVectors(fwdAngles, jumpFwd, NULL, NULL); + VectorScale(jumpFwd, 150, pm->ps->velocity); pm->ps->velocity[2] = 250; //250 is normalized for a standing enemy at your z level, about 64 tall... adjust for actual maxs[2]-mins[2] of enemy and for zdiff in origins - if ( pm->gent && pm->gent->enemy ) + if (pm->gent && pm->gent->enemy) { //go higher for taller enemies - pm->ps->velocity[2] *= (pm->gent->enemy->maxs[2]-pm->gent->enemy->mins[2])/64.0f; + pm->ps->velocity[2] *= (pm->gent->enemy->maxs[2] - pm->gent->enemy->mins[2]) / 64.0f; //go higher for enemies higher than you, lower for those lower than you float zDiff = pm->gent->enemy->currentOrigin[2] - pm->ps->origin[2]; pm->ps->velocity[2] += (zDiff)*1.5f; //clamp to decent-looking values //FIXME: still jump too low sometimes - if ( zDiff <= 0 && pm->ps->velocity[2] < 200 ) + if (zDiff <= 0 && pm->ps->velocity[2] < 200) {//if we're on same level, don't let me jump so low, I clip into the ground pm->ps->velocity[2] = 200; } - else if ( pm->ps->velocity[2] < 50 ) + else if (pm->ps->velocity[2] < 50) { pm->ps->velocity[2] = 50; } - else if ( pm->ps->velocity[2] > 400 ) + else if (pm->ps->velocity[2] > 400) { pm->ps->velocity[2] = 400; } } pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height - pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + pm->ps->pm_flags |= PMF_JUMPING | PMF_SLOW_MO_FALL; //FIXME: NPCs yell? - PM_AddEvent( EV_JUMP ); - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + PM_AddEvent(EV_JUMP); + G_SoundOnEnt(pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav"); pm->cmd.upmove = 0; //FIXME: don't allow this to land on other people pm->gent->angle = pm->ps->viewangles[YAW];//so we know what yaw we started this at - G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + G_DrainPowerForSpecialMove(pm->gent, FP_LEVITATION, g_saberForceDrainAmount->integer);//SABER_ALT_ATTACK_POWER_FB); - if ( Q_irand( 0, 1 ) ) + if (Q_irand(0, 1)) { return LS_A_FLIP_STAB; } @@ -2928,20 +3592,20 @@ saberMoveName_t PM_SaberFlipOverAttackMove( void ) } } -qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) +qboolean PM_CheckFlipOverAttackMove(qboolean checkEnemy) { - if ( pm->ps->clientNum < MAX_CLIENTS - && PM_InSecondaryStyle() ) + if (pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle()) { return qfalse; } //check to see if it's cancelled? - if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + if (pm->ps->saber[0].jumpAtkFwdMove == LS_NONE) { - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE - || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + if (pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID) { return qfalse; } @@ -2951,12 +3615,12 @@ qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) return qfalse; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + if (pm->ps->saber[1].jumpAtkFwdMove == LS_NONE) { - if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE - || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + if (pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID) { return qfalse; } @@ -2964,22 +3628,22 @@ qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) } //do normal checks - if ( (pm->ps->saberAnimLevel == SS_MEDIUM //medium - || pm->ps->saberAnimLevel == SS_TAVION )//tavion + if ((pm->ps->saberAnimLevel == SS_MEDIUM //medium + || pm->ps->saberAnimLevel == SS_TAVION)//tavion && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one - && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->ps->groundEntityNum != ENTITYNUM_NONE || level.time - pm->ps->lastOnGround <= 250) //on ground or just jumped ) { qboolean tryMove = qfalse; - if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()) {//NPC - if ( pm->cmd.upmove > 0//want to jump - || (pm->ps->pm_flags&PMF_JUMPING) )//jumping + if (pm->cmd.upmove > 0//want to jump + || (pm->ps->pm_flags&PMF_JUMPING))//jumping {//flip over-forward down-attack - if ( (pm->gent->NPC - && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) - && !Q_irand(0, 2) ) )//NPC who can do this, 33% chance + if ((pm->gent->NPC + && (pm->gent->NPC->rank == RANK_CREWMAN || pm->gent->NPC->rank >= RANK_LT) + && !Q_irand(0, 2)))//NPC who can do this, 33% chance {//only player or acrobat or boss and higher can do this tryMove = qtrue; } @@ -2987,15 +3651,15 @@ qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) } else {//player - if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power + if (G_TryingJumpForwardAttack(pm->gent, &pm->cmd) + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB))//have enough power { - if ( !pm->cmd.rightmove ) + if (!pm->cmd.rightmove) { - if ( pm->ps->legsAnim == BOTH_JUMP1 + if (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_FORCEJUMP1 || pm->ps->legsAnim == BOTH_INAIR1 - || pm->ps->legsAnim == BOTH_FORCEINAIR1 ) + || pm->ps->legsAnim == BOTH_FORCEINAIR1) {//in a non-flip forward jump tryMove = qtrue; } @@ -3003,23 +3667,23 @@ qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) } } - if ( tryMove ) + if (tryMove) { - if ( !checkEnemy ) + if (!checkEnemy) {//based just on command input return qtrue; } else {//based on presence of enemy - if ( pm->gent->enemy )//have an enemy + if (pm->gent->enemy)//have an enemy { - vec3_t fwdAngles = {0,pm->ps->viewangles[YAW],0}; - if ( pm->gent->enemy->health > 0 + vec3_t fwdAngles = { 0, pm->ps->viewangles[YAW], 0 }; + if (pm->gent->enemy->health > 0 && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period && pm->gent->enemy->maxs[2] > 12 - && (!pm->gent->enemy->client || !PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) ) - && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 10000 - && InFront( pm->gent->enemy->currentOrigin, pm->gent->currentOrigin, fwdAngles, 0.3f ) ) + && (!pm->gent->enemy->client || !PM_InKnockDownOnGround(&pm->gent->enemy->client->ps)) + && DistanceSquared(pm->gent->currentOrigin, pm->gent->enemy->currentOrigin) < 10000 + && InFront(pm->gent->enemy->currentOrigin, pm->gent->currentOrigin, fwdAngles, 0.3f)) {//enemy must be alive, not low to ground, close and in front return qtrue; } @@ -3031,34 +3695,34 @@ qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) return qfalse; } -saberMoveName_t PM_SaberBackflipAttackMove( void ) +saberMoveName_t PM_SaberBackflipAttackMove(void) { //see if we have an overridden (or cancelled) kata move - if ( pm->ps->saber[0].jumpAtkBackMove != LS_INVALID ) + if (pm->ps->saber[0].jumpAtkBackMove != LS_INVALID) { - if ( pm->ps->saber[0].jumpAtkBackMove != LS_NONE ) + if (pm->ps->saber[0].jumpAtkBackMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[0].jumpAtkBackMove; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkBackMove != LS_INVALID ) + if (pm->ps->saber[1].jumpAtkBackMove != LS_INVALID) { - if ( pm->ps->saber[1].jumpAtkBackMove != LS_NONE ) + if (pm->ps->saber[1].jumpAtkBackMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[1].jumpAtkBackMove; } } } //no overrides, cancelled? - if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + if (pm->ps->saber[0].jumpAtkBackMove == LS_NONE) { return LS_NONE; } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + if (pm->ps->saber[1].jumpAtkBackMove == LS_NONE) { return LS_NONE; } @@ -3067,21 +3731,21 @@ saberMoveName_t PM_SaberBackflipAttackMove( void ) return LS_A_BACKFLIP_ATK; } -qboolean PM_CheckBackflipAttackMove( void ) +qboolean PM_CheckBackflipAttackMove(void) { - if ( pm->ps->clientNum < MAX_CLIENTS - && PM_InSecondaryStyle() ) + if (pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle()) { return qfalse; } //check to see if it's cancelled? - if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + if (pm->ps->saber[0].jumpAtkBackMove == LS_NONE) { - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE - || pm->ps->saber[1].jumpAtkBackMove == LS_INVALID ) + if (pm->ps->saber[1].jumpAtkBackMove == LS_NONE + || pm->ps->saber[1].jumpAtkBackMove == LS_INVALID) { return qfalse; } @@ -3091,12 +3755,12 @@ qboolean PM_CheckBackflipAttackMove( void ) return qfalse; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + if (pm->ps->saber[1].jumpAtkBackMove == LS_NONE) { - if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE - || pm->ps->saber[0].jumpAtkBackMove == LS_INVALID ) + if (pm->ps->saber[0].jumpAtkBackMove == LS_NONE + || pm->ps->saber[0].jumpAtkBackMove == LS_INVALID) { return qfalse; } @@ -3104,26 +3768,26 @@ qboolean PM_CheckBackflipAttackMove( void ) } //do normal checks - if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + if (pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one //&& (pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE || level.time-pm->ps->lastStationary<=250)//standing or just started moving - && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) )//on ground or just jumped (if not player) + && (pm->ps->groundEntityNum != ENTITYNUM_NONE || level.time - pm->ps->lastOnGround <= 250))//on ground or just jumped (if not player) { - if ( pm->cmd.forwardmove < 0 //moving backwards + if (pm->cmd.forwardmove < 0 //moving backwards && pm->ps->saberAnimLevel == SS_STAFF //using staff - && (pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING)) )//jumping + && (pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING)))//jumping {//jumping backwards and using staff - if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim - && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + if (!PM_SaberInTransitionAny(pm->ps->saberMove) //not going to/from/between an attack anim + && !PM_SaberInAttack(pm->ps->saberMove) //not in attack anim && pm->ps->weaponTime <= 0//not busy - && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + && (pm->cmd.buttons&BUTTON_ATTACK))//want to attack {//not already attacking - if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()) {//NPC - if ( pm->gent + if (pm->gent && pm->gent->NPC - && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + && (pm->gent->NPC->rank == RANK_CREWMAN || pm->gent->NPC->rank >= RANK_LT)) {//acrobat or boss and higher can do this return qtrue; } @@ -3138,65 +3802,65 @@ qboolean PM_CheckBackflipAttackMove( void ) return qfalse; } -saberMoveName_t PM_CheckDualSpinProtect( void ) +saberMoveName_t PM_CheckDualSpinProtect(void) { - if ( pm->ps->clientNum < MAX_CLIENTS - && PM_InSecondaryStyle() ) + if (pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle()) { return LS_NONE; } //see if we have an overridden (or cancelled) kata move - if ( pm->ps->saber[0].kataMove != LS_INVALID ) + if (pm->ps->saber[0].kataMove != LS_INVALID) { - if ( pm->ps->saber[0].kataMove != LS_NONE ) + if (pm->ps->saber[0].kataMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[0].kataMove; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].kataMove != LS_INVALID ) + if (pm->ps->saber[1].kataMove != LS_INVALID) { - if ( pm->ps->saber[1].kataMove != LS_NONE ) + if (pm->ps->saber[1].kataMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[1].kataMove; } } } //no overrides, cancelled? - if ( pm->ps->saber[0].kataMove == LS_NONE ) + if (pm->ps->saber[0].kataMove == LS_NONE) { return LS_NONE; } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].kataMove == LS_NONE ) + if (pm->ps->saber[1].kataMove == LS_NONE) { return LS_NONE; } } //do normal checks - if ( pm->ps->saberMove == LS_READY//ready + if (pm->ps->saberMove == LS_READY//ready //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? //&& pm->ps->viewangles[0] > 30 //looking down && pm->ps->saberAnimLevel == SS_DUAL//using dual saber style && pm->ps->saber[0].Active() && pm->ps->saber[1].Active()//both sabers on //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing - && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && G_TryingKataAttack(pm->gent, &pm->cmd)//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue)//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack ) {//FIXME: some NPC logic to do this? /* if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching - && g_crosshairEntNum >= ENTITYNUM_WORLD ) + && g_crosshairEntNum >= ENTITYNUM_WORLD ) */ { - if ( pm->gent ) + if (pm->gent) { - G_DrainPowerForSpecialMove( pm->gent, FP_PUSH, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + G_DrainPowerForSpecialMove(pm->gent, FP_PUSH, SABER_ALT_ATTACK_POWER, qtrue);//drain the required force power } return LS_DUAL_SPIN_PROTECT; } @@ -3204,65 +3868,65 @@ saberMoveName_t PM_CheckDualSpinProtect( void ) return LS_NONE; } -saberMoveName_t PM_CheckStaffKata( void ) +saberMoveName_t PM_CheckStaffKata(void) { - if ( pm->ps->clientNum < MAX_CLIENTS - && PM_InSecondaryStyle() ) + if (pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle()) { return LS_NONE; } //see if we have an overridden (or cancelled) kata move - if ( pm->ps->saber[0].kataMove != LS_INVALID ) + if (pm->ps->saber[0].kataMove != LS_INVALID) { - if ( pm->ps->saber[0].kataMove != LS_NONE ) + if (pm->ps->saber[0].kataMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[0].kataMove; } } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].kataMove != LS_INVALID ) + if (pm->ps->saber[1].kataMove != LS_INVALID) { - if ( pm->ps->saber[1].kataMove != LS_NONE ) + if (pm->ps->saber[1].kataMove != LS_NONE) { return (saberMoveName_t)pm->ps->saber[1].kataMove; } } } //no overrides, cancelled? - if ( pm->ps->saber[0].kataMove == LS_NONE ) + if (pm->ps->saber[0].kataMove == LS_NONE) { return LS_NONE; } - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].kataMove == LS_NONE ) + if (pm->ps->saber[1].kataMove == LS_NONE) { return LS_NONE; } } //do normal checks - if ( pm->ps->saberMove == LS_READY//ready + if (pm->ps->saberMove == LS_READY//ready //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? //&& pm->ps->viewangles[0] > 30 //looking down && pm->ps->saberAnimLevel == SS_STAFF//using dual saber style && pm->ps->saber[0].Active()//saber on //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing - && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && G_TryingKataAttack(pm->gent, &pm->cmd)//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue)//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack ) {//FIXME: some NPC logic to do this? /* if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching - && g_crosshairEntNum >= ENTITYNUM_WORLD ) + && g_crosshairEntNum >= ENTITYNUM_WORLD ) */ { - if ( pm->gent ) + if (pm->gent) { - G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + G_DrainPowerForSpecialMove(pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER, qtrue);//drain the required force power } return LS_STAFF_SOULCAL; } @@ -3270,41 +3934,41 @@ saberMoveName_t PM_CheckStaffKata( void ) return LS_NONE; } -extern qboolean WP_ForceThrowable( gentity_t *ent, gentity_t *forwardEnt, gentity_t *self, qboolean pull, float cone, float radius, vec3_t forward ); -saberMoveName_t PM_CheckPullAttack( void ) +extern qboolean WP_ForceThrowable(gentity_t *ent, gentity_t *forwardEnt, gentity_t *self, qboolean pull, float cone, float radius, vec3_t forward); +saberMoveName_t PM_CheckPullAttack(void) { - if ( pm->ps->clientNum < MAX_CLIENTS - && PM_InSecondaryStyle() ) + if (pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle()) { return LS_NONE; } - if ( (pm->ps->saber[0].saberFlags&SFL_NO_PULL_ATTACK) ) + if ((pm->ps->saber[0].saberFlags&SFL_NO_PULL_ATTACK)) { return LS_NONE; } - if ( pm->ps->dualSabers - && (pm->ps->saber[1].saberFlags&SFL_NO_PULL_ATTACK) ) + if (pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_PULL_ATTACK)) { return LS_NONE; } - if ( (pm->ps->saberMove == LS_READY||PM_SaberInReturn(pm->ps->saberMove)||PM_SaberInReflect(pm->ps->saberMove))//ready + if ((pm->ps->saberMove == LS_READY || PM_SaberInReturn(pm->ps->saberMove) || PM_SaberInReflect(pm->ps->saberMove))//ready //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY && pm->ps->saberAnimLevel >= SS_FAST//single saber styles - FIXME: Tavion? && pm->ps->saberAnimLevel <= SS_STRONG//single saber styles - FIXME: Tavion? - && G_TryingPullAttack( pm->gent, &pm->cmd, qfalse )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_TryingPullAttack(pm->gent, &pm->cmd, qfalse)//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus //&& pm->cmd.forwardmove<0//pulling back && (pm->cmd.buttons&BUTTON_ATTACK)//attacking - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB//have enough power + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB)//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB//have enough power ) {//FIXME: some NPC logic to do this? - qboolean doMove = g_saberNewControlScheme->integer?qtrue:qfalse;//in new control scheme, can always do this, even if there's no-one to do it to - if ( g_saberNewControlScheme->integer - || g_crosshairEntNum < ENTITYNUM_WORLD )//in old control scheme, there has to be someone there + qboolean doMove = g_saberNewControlScheme->integer ? qtrue : qfalse;//in new control scheme, can always do this, even if there's no-one to do it to + if (g_saberNewControlScheme->integer + || g_crosshairEntNum < ENTITYNUM_WORLD)//in old control scheme, there has to be someone there { saberMoveName_t pullAttackMove = LS_NONE; - if ( pm->ps->saberAnimLevel == SS_FAST ) + if (pm->ps->saberAnimLevel == SS_FAST) { pullAttackMove = LS_PULL_ATTACK_STAB; } @@ -3313,73 +3977,73 @@ saberMoveName_t PM_CheckPullAttack( void ) pullAttackMove = LS_PULL_ATTACK_SWING; } - if ( g_crosshairEntNum < ENTITYNUM_WORLD - && pm->gent && pm->gent->client ) + if (g_crosshairEntNum < ENTITYNUM_WORLD + && pm->gent && pm->gent->client) { gentity_t *targEnt = &g_entities[g_crosshairEntNum]; - if ( targEnt->client + if (targEnt->client && targEnt->health > 0 //FIXME: check other things like in knockdown, saberlock, uninterruptable anims, etc. - && !PM_InOnGroundAnim( &targEnt->client->ps ) - && !PM_LockedAnim( targEnt->client->ps.legsAnim ) - && !PM_SuperBreakLoseAnim( targEnt->client->ps.legsAnim ) - && !PM_SuperBreakWinAnim( targEnt->client->ps.legsAnim ) + && !PM_InOnGroundAnim(&targEnt->client->ps) + && !PM_LockedAnim(targEnt->client->ps.legsAnim) + && !PM_SuperBreakLoseAnim(targEnt->client->ps.legsAnim) + && !PM_SuperBreakWinAnim(targEnt->client->ps.legsAnim) && targEnt->client->ps.saberLockTime <= 0 - && WP_ForceThrowable( targEnt, targEnt, pm->gent, qtrue, 1.0f, 0.0f, NULL ) ) + && WP_ForceThrowable(targEnt, targEnt, pm->gent, qtrue, 1.0f, 0.0f, NULL)) { - if ( !g_saberNewControlScheme->integer ) + if (!g_saberNewControlScheme->integer) {//in old control scheme, make sure they're close or far enough away for the move we'll be doing - float targDist = Distance( targEnt->currentOrigin, pm->ps->origin ); - if ( pullAttackMove == LS_PULL_ATTACK_STAB ) + float targDist = Distance(targEnt->currentOrigin, pm->ps->origin); + if (pullAttackMove == LS_PULL_ATTACK_STAB) {//must be closer than 512 - if ( targDist > 384.0f ) + if (targDist > 384.0f) { return LS_NONE; } } else//if ( pullAttackMove == LS_PULL_ATTACK_SWING ) {//must be farther than 256 - if ( targDist > 512.0f ) + if (targDist > 512.0f) { return LS_NONE; } - if ( targDist < 192.0f ) + if (targDist < 192.0f) { return LS_NONE; } } } - vec3_t targAngles = {0,targEnt->client->ps.viewangles[YAW],0}; - if ( InFront( pm->ps->origin, targEnt->currentOrigin, targAngles ) ) + vec3_t targAngles = { 0, targEnt->client->ps.viewangles[YAW], 0 }; + if (InFront(pm->ps->origin, targEnt->currentOrigin, targAngles)) { - NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_F, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + NPC_SetAnim(targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_F, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD); } else { - NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_B, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + NPC_SetAnim(targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_B, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD); } //hold the anim until I'm with done pull anim - targEnt->client->ps.legsAnimTimer = targEnt->client->ps.torsoAnimTimer = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, (animNumber_t)saberMoveData[pullAttackMove].animToUse ); + targEnt->client->ps.legsAnimTimer = targEnt->client->ps.torsoAnimTimer = PM_AnimLength(pm->gent->client->clientInfo.animFileIndex, (animNumber_t)saberMoveData[pullAttackMove].animToUse); //set pullAttackTime - pm->gent->client->ps.pullAttackTime = targEnt->client->ps.pullAttackTime = level.time+targEnt->client->ps.legsAnimTimer; + pm->gent->client->ps.pullAttackTime = targEnt->client->ps.pullAttackTime = level.time + targEnt->client->ps.legsAnimTimer; //make us know about each other pm->gent->client->ps.pullAttackEntNum = g_crosshairEntNum; targEnt->client->ps.pullAttackEntNum = pm->ps->clientNum; //do effect and sound on me pm->ps->powerups[PW_FORCE_PUSH] = level.time + 1000; - if ( pm->gent ) + if (pm->gent) { - G_Sound( pm->gent, G_SoundIndex( "sound/weapons/force/pull.wav" ) ); + G_Sound(pm->gent, G_SoundIndex("sound/weapons/force/pull.wav")); } doMove = qtrue; } } - if ( doMove ) + if (doMove) { - if ( pm->gent ) + if (pm->gent) { - G_DrainPowerForSpecialMove( pm->gent, FP_PULL, SABER_ALT_ATTACK_POWER_FB ); + G_DrainPowerForSpecialMove(pm->gent, FP_PULL, SABER_ALT_ATTACK_POWER_FB); } return pullAttackMove; } @@ -3388,14 +4052,14 @@ saberMoveName_t PM_CheckPullAttack( void ) return LS_NONE; } -saberMoveName_t PM_CheckPlayerAttackFromParry( int curmove ) +saberMoveName_t PM_CheckPlayerAttackFromParry(int curmove) { - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) { - if ( curmove >= LS_PARRY_UP - && curmove <= LS_REFLECT_LL ) + if (curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL) {//in a parry - switch ( saberMoveData[curmove].endQuad ) + switch (saberMoveData[curmove].endQuad) { case Q_T: return LS_A_T2B; @@ -3412,7 +4076,7 @@ saberMoveName_t PM_CheckPlayerAttackFromParry( int curmove ) case Q_BL: return LS_A_BL2TR; break; - //shouldn't be a parry that ends at L, R or B + //shouldn't be a parry that ends at L, R or B } } } @@ -3420,25 +4084,25 @@ saberMoveName_t PM_CheckPlayerAttackFromParry( int curmove ) } -saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int curmove ) +saberMoveName_t PM_SaberAttackForMovement(int forwardmove, int rightmove, int curmove) { qboolean noSpecials = qfalse; - if ( pm->ps->clientNum < MAX_CLIENTS - && PM_InSecondaryStyle() ) + if (pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle()) { noSpecials = qtrue; } saberMoveName_t overrideJumpRightAttackMove = LS_INVALID; - if ( pm->ps->saber[0].jumpAtkRightMove != LS_INVALID ) + if (pm->ps->saber[0].jumpAtkRightMove != LS_INVALID) { - if ( pm->ps->saber[0].jumpAtkRightMove != LS_NONE ) + if (pm->ps->saber[0].jumpAtkRightMove != LS_NONE) {//actually overriding overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkRightMove; } - else if ( pm->ps->dualSabers - && pm->ps->saber[1].jumpAtkRightMove > LS_NONE ) + else if (pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove > LS_NONE) {//would be cancelling it, but check the second saber, too overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; } @@ -3447,21 +4111,21 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c overrideJumpRightAttackMove = LS_NONE; } } - else if ( pm->ps->dualSabers - && pm->ps->saber[1].jumpAtkRightMove != LS_INVALID ) + else if (pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove != LS_INVALID) {//first saber not overridden, check second overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; } saberMoveName_t overrideJumpLeftAttackMove = LS_INVALID; - if ( pm->ps->saber[0].jumpAtkLeftMove != LS_INVALID ) + if (pm->ps->saber[0].jumpAtkLeftMove != LS_INVALID) { - if ( pm->ps->saber[0].jumpAtkLeftMove != LS_NONE ) + if (pm->ps->saber[0].jumpAtkLeftMove != LS_NONE) {//actually overriding overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkLeftMove; } - else if ( pm->ps->dualSabers - && pm->ps->saber[1].jumpAtkLeftMove > LS_NONE ) + else if (pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove > LS_NONE) {//would be cancelling it, but check the second saber, too overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; } @@ -3470,84 +4134,84 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c overrideJumpLeftAttackMove = LS_NONE; } } - else if ( pm->ps->dualSabers - && pm->ps->saber[1].jumpAtkLeftMove != LS_INVALID ) + else if (pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove != LS_INVALID) {//first saber not overridden, check second overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; } - if ( rightmove > 0 ) + if (rightmove > 0) {//moving right - if ( !noSpecials + if (!noSpecials && overrideJumpRightAttackMove != LS_NONE - && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->ps->groundEntityNum != ENTITYNUM_NONE || level.time - pm->ps->lastOnGround <= 250) //on ground or just jumped && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power - && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC - ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR)//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) && pm->cmd.upmove > 0)//jumping NPC + || ((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)))//focus-holding player {//cartwheel right - vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; - if ( pm->gent ) - { - G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + vec3_t right, fwdAngles = { 0, pm->ps->viewangles[YAW], 0 }; + if (pm->gent) + {//seems to drain twice for some reason so 1/2 the drain amount + G_DrainPowerForSpecialMove(pm->gent, FP_LEVITATION, 0.5 * g_saberForceDrainAmount->integer);//SABER_ALT_ATTACK_POWER_LR); } pm->cmd.upmove = 0; - if ( overrideJumpRightAttackMove != LS_INVALID ) + if (overrideJumpRightAttackMove != LS_INVALID) {//overridden with another move return overrideJumpRightAttackMove; } - else if ( pm->ps->saberAnimLevel == SS_STAFF ) + else if (pm->ps->saberAnimLevel == SS_STAFF) { - AngleVectors( fwdAngles, NULL, right, NULL ); + AngleVectors(fwdAngles, NULL, right, NULL); pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, 190, right, pm->ps->velocity); return LS_BUTTERFLY_RIGHT; } else { - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS))) {//okay to do cartwheels with this saber /* if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {//still on ground - VectorClear( pm->ps->velocity ); - return LS_JUMPATTACK_CART_RIGHT; + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_CART_RIGHT; } else */ {//in air - AngleVectors( fwdAngles, NULL, right, NULL ); + AngleVectors(fwdAngles, NULL, right, NULL); pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); - PM_SetJumped( JUMP_VELOCITY, qtrue ); + VectorMA(pm->ps->velocity, 190, right, pm->ps->velocity); + PM_SetJumped(JUMP_VELOCITY, qtrue); return LS_JUMPATTACK_ARIAL_RIGHT; } } } } - else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_RIGHT - && pm->ps->legsAnim != BOTH_ARIAL_RIGHT ) + else if (pm->ps->legsAnim != BOTH_CARTWHEEL_RIGHT + && pm->ps->legsAnim != BOTH_ARIAL_RIGHT) {//not in a cartwheel/arial - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY - {//player - if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + {//player + if (G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)//Holding focus {//if no special worked, do nothing return LS_NONE; } } //checked all special attacks, if we're in a parry, attack from that move - saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); - if ( parryAttackMove != LS_NONE ) + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry(curmove); + if (parryAttackMove != LS_NONE) { return parryAttackMove; } //check regular attacks - if ( forwardmove > 0 ) + if (forwardmove > 0) {//forward right = TL2BR slash return LS_A_TL2BR; } - else if ( forwardmove < 0 ) + else if (forwardmove < 0) {//backward right = BL2TR uppercut return LS_A_BL2TR; } @@ -3557,79 +4221,79 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c } } } - else if ( rightmove < 0 ) + else if (rightmove < 0) {//moving left - if ( !noSpecials + if (!noSpecials && overrideJumpLeftAttackMove != LS_NONE - && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->ps->groundEntityNum != ENTITYNUM_NONE || level.time - pm->ps->lastOnGround <= 250) //on ground or just jumped && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power - && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC - ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR)//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) && pm->cmd.upmove > 0)//jumping NPC + || ((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)))//focus-holding player {//cartwheel left - vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; - if ( pm->gent ) - { - G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + vec3_t right, fwdAngles = { 0, pm->ps->viewangles[YAW], 0 }; + if (pm->gent) + {//seems to drain twice for some reason so 1/2 the drain amount + G_DrainPowerForSpecialMove(pm->gent, FP_LEVITATION, 0.5 * g_saberForceDrainAmount->integer);//SABER_ALT_ATTACK_POWER_LR); } pm->cmd.upmove = 0; - if ( overrideJumpRightAttackMove != LS_INVALID ) + if (overrideJumpRightAttackMove != LS_INVALID) {//overridden with another move return overrideJumpRightAttackMove; } - else if ( pm->ps->saberAnimLevel == SS_STAFF ) + else if (pm->ps->saberAnimLevel == SS_STAFF) { - AngleVectors( fwdAngles, NULL, right, NULL ); + AngleVectors(fwdAngles, NULL, right, NULL); pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, -190, right, pm->ps->velocity); return LS_BUTTERFLY_LEFT; } else { - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS))) {//okay to do cartwheels with this saber /* if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) {//still on ground - VectorClear( pm->ps->velocity ); - return LS_JUMPATTACK_ARIAL_LEFT; + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_ARIAL_LEFT; } else */ { - AngleVectors( fwdAngles, NULL, right, NULL ); + AngleVectors(fwdAngles, NULL, right, NULL); pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); - PM_SetJumped( JUMP_VELOCITY, qtrue ); + VectorMA(pm->ps->velocity, -190, right, pm->ps->velocity); + PM_SetJumped(JUMP_VELOCITY, qtrue); return LS_JUMPATTACK_CART_LEFT; } } } } - else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_LEFT - && pm->ps->legsAnim != BOTH_ARIAL_LEFT ) + else if (pm->ps->legsAnim != BOTH_CARTWHEEL_LEFT + && pm->ps->legsAnim != BOTH_ARIAL_LEFT) {//not in a left cartwheel/arial - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY - {//player - if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + {//player + if (G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)//Holding focus {//if no special worked, do nothing return LS_NONE; } } //checked all special attacks, if we're in a parry, attack from that move - saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); - if ( parryAttackMove != LS_NONE ) + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry(curmove); + if (parryAttackMove != LS_NONE) { return parryAttackMove; } //check regular attacks - if ( forwardmove > 0 ) + if (forwardmove > 0) {//forward left = TR2BL slash return LS_A_TR2BL; } - else if ( forwardmove < 0 ) + else if (forwardmove < 0) {//backward left = BR2TL uppercut return LS_A_BR2TL; } @@ -3641,58 +4305,58 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c } else {//not moving left or right - if ( forwardmove > 0 ) + if (forwardmove > 0) {//forward= T2B slash - saberMoveName_t stabDownMove = noSpecials?LS_NONE:PM_CheckStabDown(); - if ( stabDownMove != LS_NONE ) + saberMoveName_t stabDownMove = noSpecials ? LS_NONE : PM_CheckStabDown(); + if (stabDownMove != LS_NONE) { return stabDownMove; } - if ( ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zoomed in + if ( ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && BG_AllowThirdPersonSpecialMove( pm-> ps ) && !cg.zoomMode) )//player in third person, not zoomed in {//player in thirdperson, not zoomed in //flip-over attack logic - if ( !noSpecials && PM_CheckFlipOverAttackMove( qfalse ) ) + if (!noSpecials && PM_CheckFlipOverAttackMove(qfalse)) {//flip over-forward down-attack return PM_SaberFlipOverAttackMove(); } //lunge attack logic - else if ( PM_CheckLungeAttackMove() ) + else if (PM_CheckLungeAttackMove()) { - return PM_SaberLungeAttackMove( qtrue ); + return PM_SaberLungeAttackMove(qtrue); } //jump forward attack logic - else if ( !noSpecials && PM_CheckJumpForwardAttackMove() ) + else if (!noSpecials && PM_CheckJumpForwardAttackMove()) { return PM_SaberJumpForwardAttackMove(); } } //player NPC with enemy: autoMove logic - if ( pm->gent + if (pm->gent && pm->gent->enemy - && pm->gent->enemy->client ) + && pm->gent->enemy->client) {//I have an active enemy - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) {//a player who is running at an enemy //if the enemy is not a jedi, don't use top-down, pick a diagonal or side attack - if ( pm->gent->enemy->s.weapon != WP_SABER + if (pm->gent->enemy->s.weapon != WP_SABER && pm->gent->enemy->client->NPC_class != CLASS_REMOTE//too small to do auto-aiming accurately && pm->gent->enemy->client->NPC_class != CLASS_SEEKER//too small to do auto-aiming accurately && pm->gent->enemy->client->NPC_class != CLASS_GONK//too short to do auto-aiming accurately && pm->gent->enemy->client->NPC_class != CLASS_HOWLER//too short to do auto-aiming accurately - && g_saberAutoAim->integer ) + && g_saberAutoAim->integer) { - saberMoveName_t autoMove = PM_AttackForEnemyPos( qfalse, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer()) ); - if ( autoMove != LS_INVALID ) + saberMoveName_t autoMove = PM_AttackForEnemyPos(qfalse, (qboolean)(pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())); + if (autoMove != LS_INVALID) { return autoMove; } } } - if ( pm->ps->clientNum>=MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()) //NPC ONLY {//NPC - if ( PM_CheckFlipOverAttackMove( qtrue ) ) + if (PM_CheckFlipOverAttackMove(qtrue)) { return PM_SaberFlipOverAttackMove(); } @@ -3700,104 +4364,104 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c } //Regular NPCs - if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()) //NPC ONLY {//NPC or player in third person, not zoomed in //fwd jump attack logic - if ( PM_CheckJumpForwardAttackMove() ) + if (PM_CheckJumpForwardAttackMove()) { return PM_SaberJumpForwardAttackMove(); } //lunge attack logic - else if ( PM_CheckLungeAttackMove() ) + else if (PM_CheckLungeAttackMove()) { - return PM_SaberLungeAttackMove( qtrue ); + return PM_SaberLungeAttackMove(qtrue); } } - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY - {//player - if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + {//player + if (G_TryingSpecial(pm->gent, &pm->cmd))//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus {//if no special worked, do nothing return LS_NONE; } } //checked all special attacks, if we're in a parry, attack from that move - saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); - if ( parryAttackMove != LS_NONE ) + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry(curmove); + if (parryAttackMove != LS_NONE) { return parryAttackMove; } //check regular attacks return LS_A_T2B; } - else if ( forwardmove < 0 ) + else if (forwardmove < 0) {//backward= T2B slash//B2T uppercut? - if ( g_saberNewControlScheme->integer ) + if (g_saberNewControlScheme->integer) { saberMoveName_t pullAtk = PM_CheckPullAttack(); - if ( pullAtk != LS_NONE ) + if (pullAtk != LS_NONE) { return pullAtk; } } - if ( g_saberNewControlScheme->integer + if (g_saberNewControlScheme->integer && (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY - && (pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus, trying special backwards attacks + && (pm->cmd.buttons&BUTTON_FORCE_FOCUS))//Holding focus, trying special backwards attacks {//player lunge attack logic - if ( ( pm->ps->dualSabers //or dual - || pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->SaberStaff() )//or staff - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + if ((pm->ps->dualSabers //or dual + || pm->ps->saberAnimLevel == SS_STAFF)//pm->ps->SaberStaff() )//or staff + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB)/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/)//have enough force power to pull it off {//alt+back+attack using fast, dual or staff attacks - PM_SaberLungeAttackMove( qfalse ); + PM_SaberLungeAttackMove(qfalse); } } else if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) //NPC - || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zooomed + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && BG_AllowThirdPersonSpecialMove( pm->ps ) && !cg.zoomMode) )//player in third person, not zooomed {//NPC or player in third person, not zoomed - if ( PM_CheckBackflipAttackMove() ) + if (PM_CheckBackflipAttackMove()) { return PM_SaberBackflipAttackMove();//backflip attack } - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY - {//player - if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + {//player + if (G_TryingSpecial(pm->gent, &pm->cmd))//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus {//if no special worked, do nothing return LS_NONE; } } //if ( !PM_InKnockDown( pm->ps ) ) //check backstabs - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK))) {//okay to do backstabs with this saber - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum != ENTITYNUM_NONE) {//only when on ground - if ( pm->gent && pm->gent->enemy ) + if (pm->gent && pm->gent->enemy) {//FIXME: or just trace for a valid enemy standing behind me? And no enemy in front? - vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; - AngleVectors( facingAngles, faceFwd, NULL, NULL ); - VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); - float dot = DotProduct( enemyDir, faceFwd ); - if ( dot < 0 ) + vec3_t enemyDir, faceFwd, facingAngles = { 0, pm->ps->viewangles[YAW], 0 }; + AngleVectors(facingAngles, faceFwd, NULL, NULL); + VectorSubtract(pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir); + float dot = DotProduct(enemyDir, faceFwd); + if (dot < 0) {//enemy is behind me - if ( dot < -0.75f - && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 16384//128 squared - && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,1))) ) + if (dot < -0.75f + && DistanceSquared(pm->gent->currentOrigin, pm->gent->enemy->currentOrigin) < 16384//128 squared + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client && (pm->gent->client->NPC_class == CLASS_TAVION || pm->gent->client->NPC_class == CLASS_ALORA) && Q_irand(0, 1)))) {//fast attacks and Tavion - if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + if (!(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0) {//can't do it while ducked? - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG)) {//only fencers and above can do this return LS_A_BACKSTAB; } } } - else if ( pm->ps->saberAnimLevel != SS_FAST - && pm->ps->saberAnimLevel != SS_STAFF ) + else if (pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF) {//medium and higher attacks - if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + if ((pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0) { return LS_A_BACK_CR; } @@ -3809,30 +4473,30 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c } else {//enemy in front - float enemyDistSq = DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ); - if ( ((pm->ps->saberAnimLevel == FORCE_LEVEL_1 || - pm->ps->saberAnimLevel == SS_STAFF || - pm->gent->client->NPC_class == CLASS_TAVION || - pm->gent->client->NPC_class == CLASS_ALORA || - (pm->gent->client->NPC_class == CLASS_DESANN && !Q_irand(0,3))) && + float enemyDistSq = DistanceSquared(pm->gent->currentOrigin, pm->gent->enemy->currentOrigin); + if (((pm->ps->saberAnimLevel == FORCE_LEVEL_1 || + pm->ps->saberAnimLevel == SS_STAFF || + pm->gent->client->NPC_class == CLASS_TAVION || + pm->gent->client->NPC_class == CLASS_ALORA || + (pm->gent->client->NPC_class == CLASS_DESANN && !Q_irand(0, 3))) && enemyDistSq > 16384) || - pm->gent->enemy->health <= 0 )//128 squared + pm->gent->enemy->health <= 0)//128 squared {//my enemy is pretty far in front of me and I'm using fast attacks - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || - ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) || + (pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand(0, pm->gent->NPC->rank) > RANK_ENSIGN)) {//only fencers and higher can do this, higher rank does it more - if ( PM_CheckEnemyInBack( 128 ) ) + if (PM_CheckEnemyInBack(128)) { return PM_PickBackStab(); } } } - else if ( ((pm->ps->saberAnimLevel >= FORCE_LEVEL_2 || pm->gent->client->NPC_class == CLASS_DESANN) && enemyDistSq > 40000) || pm->gent->enemy->health <= 0 )//200 squared + else if (((pm->ps->saberAnimLevel >= FORCE_LEVEL_2 || pm->gent->client->NPC_class == CLASS_DESANN) && enemyDistSq > 40000) || pm->gent->enemy->health <= 0)//200 squared {//enemy is very faw away and I'm using medium/strong attacks - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || - ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) || + (pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand(0, pm->gent->NPC->rank) > RANK_ENSIGN)) {//only fencers and higher can do this, higher rank does it more - if ( PM_CheckEnemyInBack( 164 ) ) + if (PM_CheckEnemyInBack(164)) { return PM_PickBackStab(); } @@ -3842,9 +4506,9 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c } else {//no current enemy - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->gent && pm->gent->client ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->gent && pm->gent->client) {//only player - if ( PM_CheckEnemyInBack( 128 ) ) + if (PM_CheckEnemyInBack(128)) { return PM_PickBackStab(); } @@ -3854,17 +4518,17 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c } } - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY - {//player - if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + {//player + if (G_TryingSpecial(pm->gent, &pm->cmd))//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus {//if no special worked, do nothing return LS_NONE; } } //checked all special attacks, if we're in a parry, attack from that move - saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); - if ( parryAttackMove != LS_NONE ) + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry(curmove); + if (parryAttackMove != LS_NONE) { return parryAttackMove; } @@ -3874,25 +4538,26 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c } else {//not moving in any direction - if ( PM_SaberInBounce( curmove ) ) + if (PM_SaberInBounce(curmove)) {//bounces should go to their default attack if you don't specify a direction but are attacking - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY - {//player - if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + {//player + if (G_TryingSpecial(pm->gent, &pm->cmd))//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus {//if no special worked, do nothing return LS_NONE; } } saberMoveName_t newmove; - if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + if (pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand(0, 3)) {//use NPC random - newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + newmove = PM_NPCSaberAttackFromQuad(saberMoveData[curmove].endQuad); } else {//player uses chain-attack newmove = saberMoveData[curmove].chain_attack; } - if ( PM_SaberKataDone( curmove, newmove ) ) + if ((!g_saberNewCombat->integer && PM_SaberKataDone(curmove, newmove)) + || (g_saberNewCombat->integer && PM_SaberKataDoneNew(curmove, newmove))) { return saberMoveData[curmove].chain_idle; } @@ -3901,33 +4566,35 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c return newmove; } } - else if ( PM_SaberInKnockaway( curmove ) ) + else if (PM_SaberInKnockaway(curmove)) {//bounces should go to their default attack if you don't specify a direction but are attacking - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY - {//player - if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + {//player + if (G_TryingSpecial(pm->gent, &pm->cmd))//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus {//if no special worked, do nothing return LS_NONE; } } saberMoveName_t newmove; - if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + if (pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand(0, 3)) {//use NPC random - newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + newmove = PM_NPCSaberAttackFromQuad(saberMoveData[curmove].endQuad); } else { if ( pm->ps->saberAnimLevel == SS_FAST || - pm->ps->saberAnimLevel == SS_TAVION ) + pm->ps->saberAnimLevel == SS_TAVION || + pm->ps->saberAnimLevel == SS_KATARN ) {//player is in fast attacks, so come right back down from the same spot - newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad ); + newmove = PM_AttackMoveForQuad(saberMoveData[curmove].endQuad); } else {//use a transition to wrap to another attack from a different dir newmove = saberMoveData[curmove].chain_attack; } } - if ( PM_SaberKataDone( curmove, newmove ) ) + if ((!g_saberNewCombat->integer && PM_SaberKataDone(curmove, newmove)) + || (g_saberNewCombat->integer && PM_SaberKataDoneNew(curmove, newmove))) { return saberMoveData[curmove].chain_idle; } @@ -3936,56 +4603,56 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c return newmove; } } - else if ( curmove == LS_READY + else if (curmove == LS_READY || curmove == LS_A_FLIP_STAB || curmove == LS_A_FLIP_SLASH - || ( curmove >= LS_PARRY_UP - && curmove <= LS_REFLECT_LL ) ) + || (curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL)) {//Not moving at all, not too busy to attack //push + lookdown + attack + dual sabers = LS_DUAL_SPIN_PROTECT - if ( g_saberNewControlScheme->integer ) + if (g_saberNewControlScheme->integer) { - if ( PM_CheckDualSpinProtect() ) + if (PM_CheckDualSpinProtect()) { return LS_DUAL_SPIN_PROTECT; } - if ( PM_CheckStaffKata() ) + if (PM_CheckStaffKata()) { return LS_STAFF_SOULCAL; } } - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY - {//player - if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + {//player + if (G_TryingSpecial(pm->gent, &pm->cmd))//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus {//if no special worked, do nothing return LS_NONE; } } //checked all special attacks, if we're in a parry, attack from that move - saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); - if ( parryAttackMove != LS_NONE ) + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry(curmove); + if (parryAttackMove != LS_NONE) { return parryAttackMove; } //check regular attacks - if ( pm->ps->clientNum || g_saberAutoAim->integer ) + if (pm->ps->clientNum || g_saberAutoAim->integer) {//auto-aim - if ( pm->gent && pm->gent->enemy ) + if (pm->gent && pm->gent->enemy) {//based on enemy position, pick a proper attack - saberMoveName_t autoMove = PM_AttackForEnemyPos( qtrue, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS) ); - if ( autoMove != LS_INVALID ) + saberMoveName_t autoMove = PM_AttackForEnemyPos(qtrue, (qboolean)(pm->ps->clientNum >= MAX_CLIENTS)); + if (autoMove != LS_INVALID) { return autoMove; } } - else if ( fabs(pm->ps->viewangles[0]) > 30 ) + else if (fabs(pm->ps->viewangles[0]) > 30) {//looking far up or far down uses the top to bottom attack, presuming you want a vertical attack return LS_A_T2B; } } else {//for now, just pick a random attack - return ((saberMoveName_t)Q_irand( LS_A_TL2BR, LS_A_T2B )); + return ((saberMoveName_t)Q_irand(LS_A_TL2BR, LS_A_T2B)); } } } @@ -3994,13 +4661,13 @@ saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int c return LS_NONE; } -saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveName_t newmove ) +saberMoveName_t PM_SaberAnimTransitionMove(saberMoveName_t curmove, saberMoveName_t newmove) { //FIXME: take FP_SABER_OFFENSE into account here somehow? int retmove = newmove; - if ( curmove == LS_READY ) + if (curmove == LS_READY) {//just standing there - switch ( newmove ) + switch (newmove) { case LS_A_TL2BR: case LS_A_L2R: @@ -4010,7 +4677,7 @@ saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveNa case LS_A_TR2BL: case LS_A_T2B: //transition is the start - retmove = LS_S_TL2BR + (newmove-LS_A_TL2BR); + retmove = LS_S_TL2BR + (newmove - LS_A_TL2BR); break; default: break; @@ -4018,13 +4685,13 @@ saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveNa } else { - switch ( newmove ) + switch (newmove) { - //transitioning to ready pose + //transitioning to ready pose case LS_READY: - switch ( curmove ) + switch (curmove) { - //transitioning from an attack + //transitioning from an attack case LS_A_TL2BR: case LS_A_L2R: case LS_A_BL2TR: @@ -4033,13 +4700,13 @@ saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveNa case LS_A_TR2BL: case LS_A_T2B: //transition is the return - retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + retmove = LS_R_TL2BR + (newmove - LS_A_TL2BR); break; default: break; } break; - //transitioning to an attack + //transitioning to an attack case LS_A_TL2BR: case LS_A_L2R: case LS_A_BL2TR: @@ -4047,29 +4714,30 @@ saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveNa case LS_A_R2L: case LS_A_TR2BL: case LS_A_T2B: - if ( newmove == curmove ) + if (newmove == curmove) {//FIXME: need a spin or something or go to next level, but for now, just play the return //going into another attack... //allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3 //FIXME: don't let strong attacks chain to an attack in the opposite direction ( > 45 degrees?) - if ( PM_SaberKataDone( curmove, newmove ) ) + if ((!g_saberNewCombat->integer && PM_SaberKataDone(curmove, newmove)) + || (g_saberNewCombat->integer && PM_SaberKataDoneNew(curmove, curmove))) {//done with this kata, must return to ready before attack again - retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + retmove = LS_R_TL2BR + (newmove - LS_A_TL2BR); } else {//okay to chain to another attack retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; } } - else if ( saberMoveData[curmove].endQuad == saberMoveData[newmove].startQuad ) + else if (saberMoveData[curmove].endQuad == saberMoveData[newmove].startQuad) {//new move starts from same quadrant retmove = newmove; } else { - switch ( curmove ) + switch (curmove) { - //transitioning from an attack + //transitioning from an attack case LS_A_TL2BR: case LS_A_L2R: case LS_A_BL2TR: @@ -4087,7 +4755,7 @@ saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveNa case LS_D1_B_: retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; break; - //transitioning from a return + //transitioning from a return case LS_R_TL2BR: case LS_R_L2R: case LS_R_BL2TR: @@ -4095,25 +4763,25 @@ saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveNa case LS_R_R2L: case LS_R_TR2BL: case LS_R_T2B: - //transitioning from a bounce - /* - case LS_BOUNCE_UL2LL: - case LS_BOUNCE_LL2UL: - case LS_BOUNCE_L2LL: - case LS_BOUNCE_L2UL: - case LS_BOUNCE_UR2LR: - case LS_BOUNCE_LR2UR: - case LS_BOUNCE_R2LR: - case LS_BOUNCE_R2UR: - case LS_BOUNCE_TOP: - case LS_OVER_UR2UL: - case LS_OVER_UL2UR: - case LS_BOUNCE_UR: - case LS_BOUNCE_UL: - case LS_BOUNCE_LR: - case LS_BOUNCE_LL: - */ - //transitioning from a parry/reflection/knockaway/broken parry + //transitioning from a bounce + /* + case LS_BOUNCE_UL2LL: + case LS_BOUNCE_LL2UL: + case LS_BOUNCE_L2LL: + case LS_BOUNCE_L2UL: + case LS_BOUNCE_UR2LR: + case LS_BOUNCE_LR2UR: + case LS_BOUNCE_R2LR: + case LS_BOUNCE_R2UR: + case LS_BOUNCE_TOP: + case LS_OVER_UR2UL: + case LS_OVER_UL2UR: + case LS_BOUNCE_UR: + case LS_BOUNCE_UL: + case LS_BOUNCE_LR: + case LS_BOUNCE_LL: + */ + //transitioning from a parry/reflection/knockaway/broken parry case LS_PARRY_UP: case LS_PARRY_UR: case LS_PARRY_UL: @@ -4144,19 +4812,19 @@ saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveNa case LS_H1_BL: retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; break; - //NB: transitioning from transitions is fine + //NB: transitioning from transitions is fine default: break; } } break; - //transitioning to any other anim is not supported + //transitioning to any other anim is not supported default: break; } } - if ( retmove == LS_NONE ) + if (retmove == LS_NONE) { return newmove; } @@ -4170,37 +4838,37 @@ PM_LegsAnimForFrame Returns animNumber for current frame ------------------------- */ -int PM_LegsAnimForFrame( gentity_t *ent, int legsFrame ) +int PM_LegsAnimForFrame(gentity_t *ent, int legsFrame) { //Must be a valid client - if ( ent->client == NULL ) + if (ent->client == NULL) return -1; //Must have a file index entry - if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + if (ValidAnimFileIndex(ent->client->clientInfo.animFileIndex) == qfalse) return -1; animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); - for ( int animation = 0; animation < BOTH_CIN_1; animation++ ) //first anim after last legs + for (int animation = 0; animation < BOTH_CIN_1; animation++) //first anim after last legs { - if ( animation >= TORSO_DROPWEAP1 && animation < LEGS_TURN1 ) //first legs only anim + if (animation >= TORSO_DROPWEAP1 && animation < LEGS_TURN1) //first legs only anim {//not a possible legs anim continue; } - if ( animations[animation].glaIndex != glaIndex ) + if (animations[animation].glaIndex != glaIndex) { continue; } - if ( animations[animation].firstFrame > legsFrame ) + if (animations[animation].firstFrame > legsFrame) {//This anim starts after this frame continue; } - if ( animations[animation].firstFrame + animations[animation].numFrames < legsFrame ) + if (animations[animation].firstFrame + animations[animation].numFrames < legsFrame) {//This anim ends before this frame continue; } @@ -4209,32 +4877,32 @@ int PM_LegsAnimForFrame( gentity_t *ent, int legsFrame ) } //Not in ANY torsoAnim? SHOULD NEVER HAPPEN -// assert(0); + // assert(0); return -1; } -int PM_ValidateAnimRange( const int startFrame, const int endFrame, const float animSpeed ) +int PM_ValidateAnimRange(const int startFrame, const int endFrame, const float animSpeed) {//given a startframe and endframe, see if that lines up with any known animation animation_t *animations = level.knownAnimFileSets[0].animations; - for ( int anim = 0; anim < MAX_ANIMATIONS; anim++ ) + for (int anim = 0; anim < MAX_ANIMATIONS; anim++) { - if ( animSpeed < 0 ) + if (animSpeed < 0) {//playing backwards - if ( animations[anim].firstFrame == endFrame ) - { - if ( animations[anim].numFrames + animations[anim].firstFrame == startFrame ) + if (animations[anim].firstFrame == endFrame) + { + if (animations[anim].numFrames + animations[anim].firstFrame == startFrame) { //Com_Printf( "valid reverse anim: %s\n", animTable[anim].name ); return anim; } - } + } } else {//playing forwards - if ( animations[anim].firstFrame == startFrame ) + if (animations[anim].firstFrame == startFrame) {//This anim starts on this frame - if ( animations[anim].firstFrame + animations[anim].numFrames == endFrame ) + if (animations[anim].firstFrame + animations[anim].numFrames == endFrame) {//This anim ends on this frame //Com_Printf( "valid forward anim: %s\n", animTable[anim].name ); return anim; @@ -4245,7 +4913,7 @@ int PM_ValidateAnimRange( const int startFrame, const int endFrame, const float } //Not in ANY anim? SHOULD NEVER HAPPEN - Com_Printf( "invalid anim range %d to %d, speed %4.2f\n", startFrame, endFrame, animSpeed ); + Com_Printf("invalid anim range %d to %d, speed %4.2f\n", startFrame, endFrame, animSpeed); return -1; } /* @@ -4254,32 +4922,32 @@ PM_TorsoAnimForFrame Returns animNumber for current frame ------------------------- */ -int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame ) +int PM_TorsoAnimForFrame(gentity_t *ent, int torsoFrame) { //Must be a valid client - if ( ent->client == NULL ) + if (ent->client == NULL) return -1; //Must have a file index entry - if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + if (ValidAnimFileIndex(ent->client->clientInfo.animFileIndex) == qfalse) return -1; animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); - for ( int animation = 0; animation < LEGS_TURN1; animation++ ) //first legs only anim + for (int animation = 0; animation < LEGS_TURN1; animation++) //first legs only anim { - if ( animations[animation].glaIndex != glaIndex ) + if (animations[animation].glaIndex != glaIndex) { continue; } - if ( animations[animation].firstFrame > torsoFrame ) + if (animations[animation].firstFrame > torsoFrame) {//This anim starts after this frame continue; } - if ( animations[animation].firstFrame + animations[animation].numFrames < torsoFrame ) + if (animations[animation].firstFrame + animations[animation].numFrames < torsoFrame) {//This anim ends before this frame continue; } @@ -4288,27 +4956,27 @@ int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame ) } //Not in ANY torsoAnim? SHOULD NEVER HAPPEN -// assert(0); + // assert(0); return -1; } -qboolean PM_FinishedCurrentLegsAnim( gentity_t *self ) +qboolean PM_FinishedCurrentLegsAnim(gentity_t *self) { int junk, curFrame; float currentFrame, animSpeed; - if ( !self->client ) + if (!self->client) { return qtrue; } - gi.G2API_GetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &animSpeed, NULL ); - curFrame = floor( currentFrame ); + gi.G2API_GetBoneAnimIndex(&self->ghoul2[self->playerModel], self->rootBone, (cg.time ? cg.time : level.time), ¤tFrame, &junk, &junk, &junk, &animSpeed, NULL); + curFrame = floor(currentFrame); - int legsAnim = self->client->ps.legsAnim; - animation_t *animations = level.knownAnimFileSets[self->client->clientInfo.animFileIndex].animations; + int legsAnim = self->client->ps.legsAnim; + animation_t *animations = level.knownAnimFileSets[self->client->clientInfo.animFileIndex].animations; - if ( curFrame >= animations[legsAnim].firstFrame + (animations[legsAnim].numFrames - 2) ) + if (curFrame >= animations[legsAnim].firstFrame + (animations[legsAnim].numFrames - 2)) { return qtrue; } @@ -4322,37 +4990,37 @@ PM_HasAnimation ------------------------- */ -qboolean PM_HasAnimation( gentity_t *ent, int animation ) +qboolean PM_HasAnimation(gentity_t *ent, int animation) { //Must be a valid client - if ( !ent || ent->client == NULL ) + if (!ent || ent->client == NULL) return qfalse; //must be a valid anim number - if ( animation < 0 || animation >= MAX_ANIMATIONS ) + if (animation < 0 || animation >= MAX_ANIMATIONS) { return qfalse; } //Must have a file index entry - if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + if (ValidAnimFileIndex(ent->client->clientInfo.animFileIndex) == qfalse) return qfalse; animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; //No frames, no anim - if ( animations[animation].numFrames == 0 ) + if (animations[animation].numFrames == 0) return qfalse; //Has the sequence return qtrue; } -int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim ) +int PM_PickAnim(gentity_t *self, int minAnim, int maxAnim) { int anim; int count = 0; - if ( !self ) + if (!self) { return Q_irand(minAnim, maxAnim); } @@ -4361,8 +5029,7 @@ int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim ) { anim = Q_irand(minAnim, maxAnim); count++; - } - while ( !PM_HasAnimation( self, anim ) && count < 1000 ); + } while (!PM_HasAnimation(self, anim) && count < 1000); return anim; } @@ -4373,9 +5040,9 @@ PM_AnimLength ------------------------- */ -int PM_AnimLength( int index, animNumber_t anim ) +int PM_AnimLength(int index, animNumber_t anim) { - if ( ValidAnimFileIndex( index ) == false ) + if (ValidAnimFileIndex(index) == false) return 0; return level.knownAnimFileSets[index].animations[anim].numFrames * abs(level.knownAnimFileSets[index].animations[anim].frameLerp); @@ -4387,27 +5054,27 @@ PM_SetLegsAnimTimer ------------------------- */ -void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time ) +void PM_SetLegsAnimTimer(gentity_t *ent, int *legsAnimTimer, int time) { *legsAnimTimer = time; - if ( *legsAnimTimer < 0 && time != -1 ) + if (*legsAnimTimer < 0 && time != -1) {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional *legsAnimTimer = 0; } - if ( !*legsAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_LOWER ) ) + if (!*legsAnimTimer && ent && Q3_TaskIDPending(ent, TID_ANIM_LOWER)) {//Waiting for legsAnimTimer to complete, and it just got set to zero - if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + if (!Q3_TaskIDPending(ent, TID_ANIM_BOTH)) {//Not waiting for top - Q3_TaskIDComplete( ent, TID_ANIM_LOWER ); + Q3_TaskIDComplete(ent, TID_ANIM_LOWER); } else - {//Waiting for both to finish before complete - Q3_TaskIDClear( &ent->taskID[TID_ANIM_LOWER] );//Bottom is done, regardless - if ( !Q3_TaskIDPending( ent, TID_ANIM_UPPER) ) + {//Waiting for both to finish before complete + Q3_TaskIDClear(&ent->taskID[TID_ANIM_LOWER]);//Bottom is done, regardless + if (!Q3_TaskIDPending(ent, TID_ANIM_UPPER)) {//top is done and we're done - Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + Q3_TaskIDComplete(ent, TID_ANIM_BOTH); } } } @@ -4419,92 +5086,121 @@ PM_SetTorsoAnimTimer ------------------------- */ -void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time ) +void PM_SetTorsoAnimTimer(gentity_t *ent, int *torsoAnimTimer, int time) { *torsoAnimTimer = time; - if ( *torsoAnimTimer < 0 && time != -1 ) + if (*torsoAnimTimer < 0 && time != -1) {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional *torsoAnimTimer = 0; } - if ( !*torsoAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_UPPER ) ) + if (!*torsoAnimTimer && ent && Q3_TaskIDPending(ent, TID_ANIM_UPPER)) {//Waiting for torsoAnimTimer to complete, and it just got set to zero - if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + if (!Q3_TaskIDPending(ent, TID_ANIM_BOTH)) {//Not waiting for bottom - Q3_TaskIDComplete( ent, TID_ANIM_UPPER ); + Q3_TaskIDComplete(ent, TID_ANIM_UPPER); } else - {//Waiting for both to finish before complete - Q3_TaskIDClear( &ent->taskID[TID_ANIM_UPPER] );//Top is done, regardless - if ( !Q3_TaskIDPending( ent, TID_ANIM_LOWER) ) + {//Waiting for both to finish before complete + Q3_TaskIDClear(&ent->taskID[TID_ANIM_UPPER]);//Top is done, regardless + if (!Q3_TaskIDPending(ent, TID_ANIM_LOWER)) {//lower is done and we're done - Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + Q3_TaskIDComplete(ent, TID_ANIM_BOTH); } } } } -extern qboolean PM_SpinningSaberAnim( int anim ); +extern qboolean PM_SpinningSaberAnim(int anim); extern float saberAnimSpeedMod[NUM_FORCE_POWER_LEVELS]; -void PM_SaberStartTransAnim( int saberAnimLevel, int anim, float *animSpeed, gentity_t *gent ) +void PM_SaberStartTransAnim(int saberAnimLevel, int anim, float *animSpeed, gentity_t *gent) { - if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_ROLL_STAB ) + if (g_saberNewCombat->integer) //new code + { + if (anim == BOTH_V1_BL_S1 + || anim == BOTH_V1_BR_S1 + || anim == BOTH_V1_TL_S1 + || anim == BOTH_V1_TR_S1 + || anim == BOTH_V1_T__S1 + || (anim >= BOTH_V6_BL_S6 && anim <= BOTH_V7__R_S7)) + { //we're in a broken attack + //speed up recovery from broken attacks based on SO level + *animSpeed = saberAnimSpeedMod[gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]]; + } + if (g_saberAnimSpeed->value != 1.0f) + { + *animSpeed *= g_saberAnimSpeed->value; + } + else if (gent && gent->client && gent->client->ps.weapon == WP_SABER) + { + if (gent->client->ps.saber[0].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[0].animSpeedScale; + } + if (gent->client->ps.dualSabers + && gent->client->ps.saber[1].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[1].animSpeedScale; + } + } + } + else //old code { - if ( g_saberAnimSpeed->value != 1.0f ) + if (g_saberAnimSpeed->value != 1.0f) { *animSpeed *= g_saberAnimSpeed->value; } - else if ( gent && gent->client && gent->client->ps.weapon == WP_SABER ) + else if (gent && gent->client && gent->client->ps.weapon == WP_SABER) { - if ( gent->client->ps.saber[0].animSpeedScale != 1.0f ) + if (gent->client->ps.saber[0].animSpeedScale != 1.0f) { *animSpeed *= gent->client->ps.saber[0].animSpeedScale; } - if ( gent->client->ps.dualSabers - && gent->client->ps.saber[1].animSpeedScale != 1.0f ) + if (gent->client->ps.dualSabers + && gent->client->ps.saber[1].animSpeedScale != 1.0f) { *animSpeed *= gent->client->ps.saber[1].animSpeedScale; } } } - if ( gent + if (gent && gent->client - && gent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SCEPTER] && gent->client->ps.dualSabers && saberAnimLevel == SS_DUAL - && gent->weaponModel[1] ) + && gent->weaponModel[1]) {//using a scepter and dual style, slow down anims - if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H7_S7_BR ) + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_H7_S7_BR) { *animSpeed *= 0.75; } } - if ( gent && gent->client && gent->client->ps.forceRageRecoveryTime > level.time ) + if (gent && gent->client && gent->client->ps.forceRageRecoveryTime > level.time) {//rage recovery - if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR ) + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR) {//animate slower *animSpeed *= 0.75; } } - else if ( gent && gent->NPC && gent->NPC->rank == RANK_CIVILIAN ) + else if (gent && gent->NPC && gent->NPC->rank == RANK_CIVILIAN) {//grunt reborn - if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1) {//his fast attacks are slower - if ( !PM_SpinningSaberAnim( anim ) ) + if (!PM_SpinningSaberAnim(anim)) { *animSpeed *= 0.75; } return; } } - else if ( gent && gent->client ) + else if (gent && gent->client) { - if ( gent->client->ps.saber[0].type == SABER_LANCE || gent->client->ps.saber[0].type == SABER_TRIDENT ) + if (gent->client->ps.saber[0].type == SABER_LANCE || gent->client->ps.saber[0].type == SABER_TRIDENT) {//FIXME: hack for now - these use the fast anims, but slowed down. Should have own style - if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1) {//his fast attacks are slower - if ( !PM_SpinningSaberAnim( anim ) ) + if (!PM_SpinningSaberAnim(anim)) { *animSpeed *= 0.75; } @@ -4513,84 +5209,99 @@ void PM_SaberStartTransAnim( int saberAnimLevel, int anim, float *animSpeed, gen } } - if ( ( anim >= BOTH_T1_BR__R && - anim <= BOTH_T1_BL_TL ) || - ( anim >= BOTH_T3_BR__R && - anim <= BOTH_T3_BL_TL ) || - ( anim >= BOTH_T5_BR__R && - anim <= BOTH_T5_BL_TL ) ) - { - if ( saberAnimLevel == FORCE_LEVEL_1 || saberAnimLevel == FORCE_LEVEL_5 ) - {//FIXME: should not be necc for FORCE_LEVEL_1's - *animSpeed *= 1.5; + if ((anim >= BOTH_T1_BR__R && + anim <= BOTH_T1_BL_TL) || + (anim >= BOTH_T3_BR__R && + anim <= BOTH_T3_BL_TL) || + (anim >= BOTH_T5_BR__R && + anim <= BOTH_T5_BL_TL)) + { //what is this doing here exactly? + if (g_saberNewCombat->integer) //new code + { + if (saberAnimLevel == FORCE_LEVEL_1 /*|| saberAnimLevel == FORCE_LEVEL_5*/) + {//FIXME: should not be necc for FORCE_LEVEL_1's + *animSpeed *= 1.5; + } + else if (saberAnimLevel == FORCE_LEVEL_5) + { //both Desann and Strong styles suffer some transition speed penalty or something? + *animSpeed *= 0.75; + } } - else if ( saberAnimLevel == FORCE_LEVEL_3 ) + else //old code { - *animSpeed *= 0.75; + if (saberAnimLevel == FORCE_LEVEL_1 || saberAnimLevel == FORCE_LEVEL_5) + {//FIXME: should not be necc for FORCE_LEVEL_1's + *animSpeed *= 1.5; + } + else if (saberAnimLevel == FORCE_LEVEL_3) + { + *animSpeed *= 0.75; + } } } } /* void PM_SaberStartTransAnim( int anim, int entNum, int saberOffenseLevel, float *animSpeed ) { - //check starts - if ( ( anim >= BOTH_S1_S1_T_ && - anim <= BOTH_S1_S1_TR ) || - ( anim >= BOTH_S1_S1_T_ && - anim <= BOTH_S1_S1_TR ) || - ( anim >= BOTH_S3_S1_T_ && - anim <= BOTH_S3_S1_TR ) ) - { - if ( entNum == 0 ) - { - *animSpeed *= saberAnimSpeedMod[FORCE_LEVEL_3]; - } - else - { - *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; - } - } - //Check transitions - else if ( PM_SpinningSaberAnim( anim ) ) - {//spins stay normal speed - return; - } - else if ( ( anim >= BOTH_T1_BR__R && - anim <= BOTH_T1_BL_TL ) || - ( anim >= BOTH_T2_BR__R && - anim <= BOTH_T2_BL_TL ) || - ( anim >= BOTH_T3_BR__R && - anim <= BOTH_T3_BL_TL ) ) - {//slow down the transitions - if ( entNum == 0 && saberOffenseLevel <= FORCE_LEVEL_2 ) - { - *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; - } - else - { - *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]/2.0f; - } - } +//check starts +if ( ( anim >= BOTH_S1_S1_T_ && +anim <= BOTH_S1_S1_TR ) || +( anim >= BOTH_S1_S1_T_ && +anim <= BOTH_S1_S1_TR ) || +( anim >= BOTH_S3_S1_T_ && +anim <= BOTH_S3_S1_TR ) ) +{ +if ( entNum == 0 ) +{ +*animSpeed *= saberAnimSpeedMod[FORCE_LEVEL_3]; +} +else +{ +*animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; +} +} +//Check transitions +else if ( PM_SpinningSaberAnim( anim ) ) +{//spins stay normal speed +return; +} +else if ( ( anim >= BOTH_T1_BR__R && +anim <= BOTH_T1_BL_TL ) || +( anim >= BOTH_T2_BR__R && +anim <= BOTH_T2_BL_TL ) || +( anim >= BOTH_T3_BR__R && +anim <= BOTH_T3_BL_TL ) ) +{//slow down the transitions +if ( entNum == 0 && saberOffenseLevel <= FORCE_LEVEL_2 ) +{ +*animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; +} +else +{ +*animSpeed *= saberAnimSpeedMod[saberOffenseLevel]/2.0f; +} +} - return; +return; } */ extern qboolean player_locked; +extern qboolean PlayerAffectedByStasis( void ); extern qboolean MatrixMode; -float PM_GetTimeScaleMod( gentity_t *gent ) +float PM_GetTimeScaleMod(gentity_t *gent) { - if ( g_timescale->value ) + if (g_timescale->value) { - if ( !MatrixMode + if (!MatrixMode && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_START && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_ATTACK - && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_LAND ) + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_LAND) { - if ( gent && gent->s.clientNum == 0 && !player_locked && gent->client->ps.forcePowersActive&(1<s.clientNum == 0 && !player_locked && !PlayerAffectedByStasis() && gent->client->ps.forcePowersActive&(1<value); } - else if ( gent && gent->client && gent->client->ps.forcePowersActive&(1<client && gent->client->ps.forcePowersActive&(1 << FP_SPEED)) { return (1.0 / g_timescale->value); } @@ -4599,13 +5310,13 @@ float PM_GetTimeScaleMod( gentity_t *gent ) return 1.0f; } -static inline qboolean PM_IsHumanoid( CGhoul2Info *ghlInfo ) +static inline qboolean PM_IsHumanoid(CGhoul2Info *ghlInfo) { char *GLAName; - GLAName = gi.G2API_GetGLAName( ghlInfo ); + GLAName = gi.G2API_GetGLAName(ghlInfo); assert(GLAName); - if ( !Q_stricmp( "models/players/_humanoid/_humanoid", GLAName ) ) + if (!Q_stricmp("models/players/_humanoid/_humanoid", GLAName)) { return qtrue; } @@ -4619,14 +5330,14 @@ PM_SetAnimFinal ------------------------- */ #define G2_DEBUG_TIMING (0) -void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, - int setAnimParts,int anim,int setAnimFlags, - int *torsoAnimTimer,int *legsAnimTimer, - gentity_t *gent,int blendTime) // default blendTime=350 +void PM_SetAnimFinal(int *torsoAnim, int *legsAnim, + int setAnimParts, int anim, int setAnimFlags, + int *torsoAnimTimer, int *legsAnimTimer, + gentity_t *gent, int blendTime) // default blendTime=350 { -// BASIC SETUP AND SAFETY CHECKING -//================================= + // BASIC SETUP AND SAFETY CHECKING + //================================= // If It Is A Busted Entity, Don't Do Anything Here. //--------------------------------------------------- @@ -4637,12 +5348,12 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, // Make Sure This Character Has Such An Anim And A Model //------------------------------------------------------- - if (anim<0 || anim>=MAX_ANIMATIONS || !ValidAnimFileIndex(gent->client->clientInfo.animFileIndex)) + if (anim<0 || anim >= MAX_ANIMATIONS || !ValidAnimFileIndex(gent->client->clientInfo.animFileIndex)) { - #ifndef FINAL_BUILD - if (g_AnimWarning->integer) +#ifndef FINAL_BUILD + if (g_AnimWarning->integer) { - if (anim<0 || anim>=MAX_ANIMATIONS) + if (anim<0 || anim >= MAX_ANIMATIONS) { gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim Index (%d)!\n", anim); } @@ -4651,37 +5362,37 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim File Index (%d)!\n", gent->client->clientInfo.animFileIndex); } } - #endif +#endif return; } // Get Global Time Properties //---------------------------- - float timeScaleMod = PM_GetTimeScaleMod( gent ); - const int actualTime = (cg.time?cg.time:level.time); - const animation_t* animations = level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations; - const animation_t& curAnim = animations[anim]; + float timeScaleMod = PM_GetTimeScaleMod(gent); + const int actualTime = (cg.time ? cg.time : level.time); + const animation_t* animations = level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations; + const animation_t& curAnim = animations[anim]; // Make Sure This Character Has Such An Anim And A Model //------------------------------------------------------- - if (animations[anim].numFrames==0) + if (animations[anim].numFrames == 0) { - #ifndef FINAL_BUILD - static int LastAnimWarningNum=0; - if (LastAnimWarningNum!=anim) +#ifndef FINAL_BUILD + static int LastAnimWarningNum = 0; + if (LastAnimWarningNum != anim) { - if ((cg_debugAnim.integer==3) || // 3 = do everyone - (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player - (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else - (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + if ((cg_debugAnim.integer == 3) || // 3 = do everyone + (cg_debugAnim.integer == 1 && gent->s.number == 0) || // 1 = only the player + (cg_debugAnim.integer == 2 && gent->s.number != 0) || // 2 = only everyone else + (cg_debugAnim.integer == 4 && gent->s.number != cg_debugAnimTarget.integer) // 4 = specific entnum ) { - gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Anim %s does not exist in this model (%s)!\n", animTable[anim].name, gent->NPC_type ); + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Anim %s does not exist in this model (%s)!\n", animTable[anim].name, gent->NPC_type); } } LastAnimWarningNum = anim; - #endif +#endif return; } @@ -4703,26 +5414,33 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, // Lower Offensive Skill Slows Down The Saber Start Attack Animations //-------------------------------------------------------------------- - PM_SaberStartTransAnim( gent->client->ps.saberAnimLevel, anim, &timeScaleMod, gent ); - - - -// SETUP VALUES FOR INCOMMING ANIMATION -//====================================== - const bool animFootMove = (PM_WalkingAnim(anim) || PM_RunningAnim(anim) || anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK); - const bool animHoldless = (setAnimFlags&SETANIM_FLAG_HOLDLESS)!=0; - const bool animHold = (setAnimFlags&SETANIM_FLAG_HOLD)!=0; - const bool animRestart = (setAnimFlags&SETANIM_FLAG_RESTART)!=0; - const bool animOverride = (setAnimFlags&SETANIM_FLAG_OVERRIDE)!=0; - const bool animSync = (g_synchSplitAnims->integer!=0 && !animRestart); - float animCurrent = (-1.0f); - float animSpeed = (50.0f / curAnim.frameLerp * timeScaleMod); // animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps). - const float animFPS = (fabsf(curAnim.frameLerp)); - const int animDurMSec = (int)(((curAnim.numFrames - 1) * animFPS) / timeScaleMod); - const int animHoldMSec = ((animHoldless && timeScaleMod==1.0f)?((animDurMSec>1)?(animDurMSec-1):(animFPS)):(animDurMSec)); - int animFlags = (curAnim.loopFrames!=-1)?(BONE_ANIM_OVERRIDE_LOOP):(BONE_ANIM_OVERRIDE_FREEZE); - int animStart = (curAnim.firstFrame); - int animEnd = (curAnim.firstFrame)+(animations[anim].numFrames); + PM_SaberStartTransAnim(gent->client->ps.saberAnimLevel, anim, &timeScaleMod, gent); + + if (*torsoAnim == BOTH_MELEE1 || *torsoAnim == BOTH_MELEE2) + {//non-heavy punches are faster FIXME: Give player heavy punch cheat? + if (gent->s.number == 0 || !(gent->NPC->aiFlags&NPCAI_HEAVY_MELEE)) { + timeScaleMod *= 1.5; + } + } + + + + // SETUP VALUES FOR INCOMMING ANIMATION + //====================================== + const bool animFootMove = (PM_WalkingAnim(anim) || PM_RunningAnim(anim) || anim == BOTH_CROUCH1WALK || anim == BOTH_CROUCH1WALKBACK); + const bool animHoldless = (setAnimFlags&SETANIM_FLAG_HOLDLESS) != 0; + const bool animHold = (setAnimFlags&SETANIM_FLAG_HOLD) != 0; + const bool animRestart = (setAnimFlags&SETANIM_FLAG_RESTART) != 0; + const bool animOverride = (setAnimFlags&SETANIM_FLAG_OVERRIDE) != 0; + const bool animSync = (g_synchSplitAnims->integer != 0 && !animRestart); + float animCurrent = (-1.0f); + float animSpeed = (50.0f / curAnim.frameLerp * timeScaleMod); // animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps). + const float animFPS = (fabsf(curAnim.frameLerp)); + const int animDurMSec = (int)(((curAnim.numFrames - 1) * animFPS) / timeScaleMod); + const int animHoldMSec = ((animHoldless && timeScaleMod == 1.0f) ? ((animDurMSec>1) ? (animDurMSec - 1) : (animFPS)) : (animDurMSec)); + int animFlags = (curAnim.loopFrames != -1) ? (BONE_ANIM_OVERRIDE_LOOP) : (BONE_ANIM_OVERRIDE_FREEZE); + int animStart = (curAnim.firstFrame); + int animEnd = (curAnim.firstFrame) + (animations[anim].numFrames); // If We Have A Blend Timer, Add The Blend Flag //---------------------------------------------- @@ -4735,21 +5453,21 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, //------------------------------------------------------------- if (animSpeed<0.0f) { -// #ifndef FINAL_BUILD - #if 0 - if (g_AnimWarning->integer==1) + // #ifndef FINAL_BUILD +#if 0 + if (g_AnimWarning->integer == 1) { if (animFlags&BONE_ANIM_OVERRIDE_LOOP) { gi.Printf(S_COLOR_YELLOW"PM_SetAnimFinal: WARNING: Anim (%s) looping backwards!\n", animTable[anim].name); } } - #endif +#endif - int temp = animEnd; - animEnd = animStart; - animStart = temp; - blendTime = 0; + int temp = animEnd; + animEnd = animStart; + animStart = temp; + blendTime = 0; } // If The Animation Is Walking Or Running, Attempt To Scale The Playback Speed To Match @@ -4771,11 +5489,11 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, && gent->client->NPC_class != CLASS_SEEKER) { bool Walking = !!PM_WalkingAnim(anim); - bool HasDual = (gent->client->ps.saberAnimLevel==SS_DUAL); - bool HasStaff = (gent->client->ps.saberAnimLevel==SS_STAFF); - float moveSpeedOfAnim = 150.0f;//g_noFootSlideRunScale->value; + bool HasDual = (gent->client->ps.saberAnimLevel == SS_DUAL); + bool HasStaff = (gent->client->ps.saberAnimLevel == SS_STAFF); + float moveSpeedOfAnim = 150.0f;//g_noFootSlideRunScale->value; - if (anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK) + if (anim == BOTH_CROUCH1WALK || anim == BOTH_CROUCH1WALKBACK) { moveSpeedOfAnim = 75.0f; } @@ -4814,14 +5532,9 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, } } } - } - - - - + } - - animSpeed *= (gent->resultspeed/moveSpeedOfAnim); + animSpeed *= (gent->resultspeed / moveSpeedOfAnim); if (animSpeed<0.01f) { animSpeed = 0.01f; @@ -4837,64 +5550,64 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, } -// GET VALUES FOR EXISTING BODY ANIMATION -//========================================== - float bodySpeed = 0.0f; - float bodyCurrent = 0.0f; - int bodyStart = 0; - int bodyEnd = 0; - int bodyFlags = 0; - int bodyAnim = (*legsAnim); - int bodyBone = (gent->rootBone); - bool bodyTimerOn = ((*legsAnimTimer>0) || (*legsAnimTimer)==-1); - bool bodyPlay = ((setAnimParts&SETANIM_LEGS) && (bodyBone!=-1) && (animOverride || !bodyTimerOn)); + // GET VALUES FOR EXISTING BODY ANIMATION + //========================================== + float bodySpeed = 0.0f; + float bodyCurrent = 0.0f; + int bodyStart = 0; + int bodyEnd = 0; + int bodyFlags = 0; + int bodyAnim = (*legsAnim); + int bodyBone = (gent->rootBone); + bool bodyTimerOn = ((*legsAnimTimer>0) || (*legsAnimTimer) == -1); + bool bodyPlay = ((setAnimParts&SETANIM_LEGS) && (bodyBone != -1) && (animOverride || !bodyTimerOn)); bool bodyAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, actualTime, &bodyCurrent, &bodyStart, &bodyEnd, &bodyFlags, &bodySpeed, NULL); - bool bodyOnAnimNow = (bodyAnimating && bodyAnim==anim && bodyStart==animStart && bodyEnd==animEnd); + bool bodyOnAnimNow = (bodyAnimating && bodyAnim == anim && bodyStart == animStart && bodyEnd == animEnd); bool bodyMatchTorsFrame = false; -// GET VALUES FOR EXISTING TORSO ANIMATION -//=========================================== - float torsSpeed = 0.0f; - float torsCurrent = 0.0f; - int torsStart = 0; - int torsEnd = 0; - int torsFlags = 0; - int torsAnim = (*torsoAnim); - int torsBone = (gent->lowerLumbarBone); - bool torsTimerOn = ((*torsoAnimTimer)>0 || (*torsoAnimTimer)==-1); - bool torsPlay = (gent->client->NPC_class!=CLASS_RANCOR && (setAnimParts&SETANIM_TORSO) && (torsBone!=-1) && (animOverride || !torsTimerOn)); + // GET VALUES FOR EXISTING TORSO ANIMATION + //=========================================== + float torsSpeed = 0.0f; + float torsCurrent = 0.0f; + int torsStart = 0; + int torsEnd = 0; + int torsFlags = 0; + int torsAnim = (*torsoAnim); + int torsBone = (gent->lowerLumbarBone); + bool torsTimerOn = ((*torsoAnimTimer)>0 || (*torsoAnimTimer) == -1); + bool torsPlay = (gent->client->NPC_class != CLASS_RANCOR && (setAnimParts&SETANIM_TORSO) && (torsBone != -1) && (animOverride || !torsTimerOn)); bool torsAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, actualTime, &torsCurrent, &torsStart, &torsEnd, &torsFlags, &torsSpeed, NULL); - bool torsOnAnimNow = (torsAnimating && torsAnim==anim && torsStart==animStart && torsEnd==animEnd); + bool torsOnAnimNow = (torsAnimating && torsAnim == anim && torsStart == animStart && torsEnd == animEnd); bool torsMatchBodyFrame = false; -// APPLY SYNC TO TORSO -//===================== - if (animSync && torsPlay && !bodyPlay && bodyOnAnimNow && (!torsOnAnimNow || torsCurrent!=bodyCurrent)) + // APPLY SYNC TO TORSO + //===================== + if (animSync && torsPlay && !bodyPlay && bodyOnAnimNow && (!torsOnAnimNow || torsCurrent != bodyCurrent)) { torsMatchBodyFrame = true; - animCurrent = bodyCurrent; + animCurrent = bodyCurrent; } - if (animSync && bodyPlay && !torsPlay && torsOnAnimNow && (!bodyOnAnimNow || bodyCurrent!=torsCurrent)) + if (animSync && bodyPlay && !torsPlay && torsOnAnimNow && (!bodyOnAnimNow || bodyCurrent != torsCurrent)) { bodyMatchTorsFrame = true; - animCurrent = torsCurrent; + animCurrent = torsCurrent; } // If Already Doing These Exact Parameters, Then Don't Play //---------------------------------------------------------- if (!animRestart) { - torsPlay &= !(torsOnAnimNow && torsSpeed==animSpeed && !torsMatchBodyFrame); - bodyPlay &= !(bodyOnAnimNow && bodySpeed==animSpeed && !bodyMatchTorsFrame); + torsPlay &= !(torsOnAnimNow && torsSpeed == animSpeed && !torsMatchBodyFrame); + bodyPlay &= !(bodyOnAnimNow && bodySpeed == animSpeed && !bodyMatchTorsFrame); } #ifndef FINAL_BUILD - if ((cg_debugAnim.integer==3) || // 3 = do everyone - (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player - (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else - (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + if ((cg_debugAnim.integer == 3) || // 3 = do everyone + (cg_debugAnim.integer == 1 && gent->s.number == 0) || // 1 = only the player + (cg_debugAnim.integer == 2 && gent->s.number != 0) || // 2 = only everyone else + (cg_debugAnim.integer == 4 && gent->s.number != cg_debugAnimTarget.integer) // 4 = specific entnum ) { if (bodyPlay || torsPlay) @@ -4938,50 +5651,77 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, // Print It! //----------- - Com_Printf("[%10d] ent[%3d-%18s] %s anim[%3d] - %s\n", - actualTime, + Com_Printf("[%10d] ent[%3d-%18s] %s anim[%3d] - %s\n", + actualTime, gent->s.number, entName, location, anim, - animTable[anim].name ); + animTable[anim].name); } } #endif -// PLAY ON THE TORSO -//======================== + // PLAY ON THE TORSO + //======================== if (torsPlay) { *torsoAnim = anim; float oldAnimCurrent = animCurrent; - if (animCurrent!=bodyCurrent && torsOnAnimNow && !animRestart && !torsMatchBodyFrame) + if (animCurrent != bodyCurrent && torsOnAnimNow && !animRestart && !torsMatchBodyFrame) { animCurrent = torsCurrent; } - + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, animStart, animEnd, - (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + (torsOnAnimNow && !animRestart) ? (animFlags&~BONE_ANIM_BLEND) : (animFlags), animSpeed, actualTime, animCurrent, blendTime); - if (gent->motionBone!=-1) + if (gent->motionBone != -1) { gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->motionBone, animStart, animEnd, - (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + (torsOnAnimNow && !animRestart) ? (animFlags&~BONE_ANIM_BLEND) : (animFlags), animSpeed, actualTime, animCurrent, blendTime); } + + if (gent->headModel > 0) + { + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->headModel], curAnim.glaIndex); + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->headModel], gent->headLowerLumbarBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + if (gent->headMotionBone!=-1) + { + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->headModel], gent->headMotionBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + } + + } + animCurrent = oldAnimCurrent; @@ -4993,8 +5733,8 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, } } -// PLAY ON THE WHOLE BODY -//======================== + // PLAY ON THE WHOLE BODY + //======================== if (bodyPlay) { *legsAnim = anim; @@ -5003,16 +5743,31 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, { animCurrent = bodyCurrent; } - + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, animStart, animEnd, - (bodyOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + (bodyOnAnimNow && !animRestart) ? (animFlags&~BONE_ANIM_BLEND) : (animFlags), animSpeed, actualTime, animCurrent, blendTime); + + if (gent->headModel > 0) + { + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->headModel], curAnim.glaIndex); + + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->headModel], gent->headRootBone, + animStart, + animEnd, + (bodyOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + } // If This Animation Is To Be Locked And Held, Calculate The Duration And Set The Timer //-------------------------------------------------------------------------------------- @@ -5026,14 +5781,14 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, -// PRINT SOME DEBUG TEXT OF EXISTING VALUES -//========================================== + // PRINT SOME DEBUG TEXT OF EXISTING VALUES + //========================================== if (false) { gi.Printf("PLAYANIM: (%3d) Speed(%4.2f) ", anim, animSpeed); if (bodyAnimating) { - gi.Printf("BODY: (%4.2f) (%4.2f) ", bodyCurrent, bodySpeed); + gi.Printf("BODY: (%4.2f) (%4.2f) ", bodyCurrent, bodySpeed); } else { @@ -5041,7 +5796,7 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, } if (torsAnimating) { - gi.Printf("TORS: (%4.2f) (%4.2f)\n", torsCurrent, torsSpeed); + gi.Printf("TORS: (%4.2f) (%4.2f)\n", torsCurrent, torsSpeed); } else { @@ -5052,75 +5807,80 @@ void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, -void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime) +void PM_SetAnim(pmove_t *pm, int setAnimParts, int anim, int setAnimFlags, int blendTime) { // FIXME : once torsoAnim and legsAnim are in the same structure for NPC and Players // rename PM_SetAnimFinal to PM_SetAnim and have both NPC and Players call PM_SetAnim - if ( pm->ps->pm_type >= PM_DEAD ) + if (pm->ps->pm_type >= PM_DEAD) {//FIXME: sometimes we'll want to set anims when your dead... twitches, impacts, etc. return; } - + if ( pm->gent == NULL ) { return; } + if ( pm->ps->stasisTime > level.time ) + { + return; + } + if ( !pm->gent || pm->gent->health > 0 ) {//don't lock anims if the guy is dead - if ( pm->ps->torsoAnimTimer - && PM_LockedAnim( pm->ps->torsoAnim ) - && !PM_LockedAnim( anim ) ) + if (pm->ps->torsoAnimTimer + && PM_LockedAnim(pm->ps->torsoAnim) + && !PM_LockedAnim(anim)) {//nothing can override these special anims setAnimParts &= ~SETANIM_TORSO; } - if ( pm->ps->legsAnimTimer - && PM_LockedAnim( pm->ps->legsAnim ) - && !PM_LockedAnim( anim ) ) + if (pm->ps->legsAnimTimer + && PM_LockedAnim(pm->ps->legsAnim) + && !PM_LockedAnim(anim)) {//nothing can override these special anims setAnimParts &= ~SETANIM_LEGS; } } - if ( !setAnimParts ) + if (!setAnimParts) { return; } if (setAnimFlags&SETANIM_FLAG_OVERRIDE) { -// pm->ps->animationTimer = 0; + // pm->ps->animationTimer = 0; if (setAnimParts & SETANIM_TORSO) { - if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->torsoAnim != anim ) + if ((setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->torsoAnim != anim) { - PM_SetTorsoAnimTimer( pm->gent, &pm->ps->torsoAnimTimer, 0 ); + PM_SetTorsoAnimTimer(pm->gent, &pm->ps->torsoAnimTimer, 0); } } if (setAnimParts & SETANIM_LEGS) { - if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->legsAnim != anim ) + if ((setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->legsAnim != anim) { - PM_SetLegsAnimTimer( pm->gent, &pm->ps->legsAnimTimer, 0 ); + PM_SetLegsAnimTimer(pm->gent, &pm->ps->legsAnimTimer, 0); } } } - PM_SetAnimFinal(&pm->ps->torsoAnim,&pm->ps->legsAnim,setAnimParts,anim,setAnimFlags,&pm->ps->torsoAnimTimer,&pm->ps->legsAnimTimer,&g_entities[pm->ps->clientNum],blendTime);//was pm->gent + PM_SetAnimFinal(&pm->ps->torsoAnim, &pm->ps->legsAnim, setAnimParts, anim, setAnimFlags, &pm->ps->torsoAnimTimer, &pm->ps->legsAnimTimer, &g_entities[pm->ps->clientNum], blendTime);//was pm->gent } -bool TorsoAgainstWindTest( gentity_t* ent ) +bool TorsoAgainstWindTest(gentity_t* ent) { if (ent&&//valid ent - ent->client&&//a client - (ent->client->ps.weapon!=WP_SABER||ent->client->ps.saberMove==LS_READY)&&//either not holding a saber or the saber is in the ready pose - (ent->s.numberclient &&//a client + (ent->client->ps.weapon != WP_SABER || ent->client->ps.saberMove == LS_READY) &&//either not holding a saber or the saber is in the ready pose + (ent->s.numbercurrentOrigin) && - gi.WE_IsOutside(ent->currentOrigin) ) + gi.WE_IsOutside(ent->currentOrigin)) { - if (Q_stricmp(level.mapname, "t2_wedge")!=0) + if (Q_stricmp(level.mapname, "t2_wedge") != 0) { vec3_t fwd; vec3_t windDir; @@ -5130,7 +5890,7 @@ bool TorsoAgainstWindTest( gentity_t* ent ) AngleVectors(pm->gent->currentAngles, fwd, 0, 0); if (DotProduct(fwd, windDir)>0.65f) { - if (ent->client && ent->client->ps.torsoAnim!=BOTH_WIND) + if (ent->client && ent->client->ps.torsoAnim != BOTH_WIND) { NPC_SetAnim(ent, SETANIM_TORSO, BOTH_WIND, SETANIM_FLAG_NORMAL, 400); } @@ -5149,72 +5909,103 @@ PM_TorsoAnimLightsaber */ -// Note that this function is intended to set the animation for the player, but +// Note that this function is intended to set the animation for the player, but // only does idle-ish anims. Anything that has a timer associated, such as attacks and blocks, // are set by PM_WeaponLightsaber() -extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt ); -extern qboolean PM_LandingAnim( int anim ); -extern qboolean PM_JumpingAnim( int anim ); -qboolean PM_InCartwheel( int anim ); +extern Vehicle_t *G_IsRidingVehicle(gentity_t *pEnt); +extern qboolean PM_LandingAnim(int anim); +extern qboolean PM_JumpingAnim(int anim); +qboolean PM_InCartwheel(int anim); void PM_TorsoAnimLightsaber() { // ********************************************************* // WEAPON_READY // ********************************************************* - if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + if (pm->ps->forcePowersActive&(1 << FP_GRIP) && pm->ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1) {//holding an enemy aloft with force-grip return; } - if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + if (pm->ps->forcePowersActive&(1 << FP_LIGHTNING) && pm->ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1) {//lightning return; } - if ( pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1 << FP_DRAIN)) {//drain return; } - if ( pm->ps->saber[0].blade[0].active + if (pm->ps->saber[0].blade[0].active && pm->ps->saber[0].blade[0].length < 3 && !(pm->ps->saberEventFlags&SEF_HITWALL) - && pm->ps->weaponstate == WEAPON_RAISING ) + && pm->ps->weaponstate == WEAPON_RAISING) { if (!G_IsRidingVehicle(pm->gent)) { - PM_SetSaberMove(LS_DRAW); + if (!g_noIgniteTwirl->integer) + { + PM_SetSaberMove(LS_DRAW); + } + else + { + if ( (PM_RunningAnim( pm->ps->legsAnim ) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) + && pm->ps->saberBlockingTime < cg.time ) + {//running w/1-handed weapon uses full-body anim + int setFlags = SETANIM_FLAG_NORMAL; + if ( PM_LandingAnim( pm->ps->torsoAnim ) ) + { + setFlags = SETANIM_FLAG_OVERRIDE; + } + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,setFlags); + } + else + { + PM_SetSaberMove(LS_READY); + } + } } return; } - else if ( !pm->ps->SaberActive() && pm->ps->SaberLength() ) + else if (!pm->ps->SaberActive() && pm->ps->SaberLength()) { if (!G_IsRidingVehicle(pm->gent)) { - PM_SetSaberMove(LS_PUTAWAY); + if (!g_noIgniteTwirl->integer) + { + PM_SetSaberMove(LS_PUTAWAY); + } + else + { + //should never get here... + } } return; } if (pm->ps->weaponTime > 0) { // weapon is already busy. - if ( pm->ps->torsoAnim == BOTH_TOSS1 - || pm->ps->torsoAnim == BOTH_TOSS2 ) + if (pm->ps->torsoAnim == BOTH_TOSS1 + || pm->ps->torsoAnim == BOTH_TOSS2) {//in toss - if ( !pm->ps->torsoAnimTimer ) + if (!pm->ps->torsoAnimTimer) {//weird, get out of it, I guess - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } } return; } - if ( pm->ps->weaponstate == WEAPON_READY || - pm->ps->weaponstate == WEAPON_CHARGING || - pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + if (pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT) {//ready - if ( pm->ps->weapon == WP_SABER && (pm->ps->SaberLength()) ) + if ( pm->ps->weapon == WP_SABER && (pm->ps->SaberLength()) && (pm->ps->SaberActive() || !g_noIgniteTwirl->integer)) {//saber is on // Select the proper idle Lightsaber attack move from the chart. if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) @@ -5223,32 +6014,32 @@ void PM_TorsoAnimLightsaber() } else { - if ( PM_JumpingAnim( pm->ps->legsAnim ) - || PM_LandingAnim( pm->ps->legsAnim ) - || PM_InCartwheel( pm->ps->legsAnim ) - || PM_FlippingAnim( pm->ps->legsAnim )) + if (PM_JumpingAnim(pm->ps->legsAnim) + || PM_LandingAnim(pm->ps->legsAnim) + || PM_InCartwheel(pm->ps->legsAnim) + || PM_FlippingAnim(pm->ps->legsAnim)) { - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD) {//using something - if ( !pm->ps->useTime ) + if (!pm->ps->useTime) {//stopped holding it, release - PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); }//else still holding, leave it as it is } else { - if ( (PM_RunningAnim( pm->ps->legsAnim ) - || pm->ps->legsAnim == BOTH_WALK_STAFF - || pm->ps->legsAnim == BOTH_WALK_DUAL - || pm->ps->legsAnim == BOTH_WALKBACK_STAFF - || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) - && pm->ps->saberBlockingTime < cg.time ) + if ((PM_RunningAnim(pm->ps->legsAnim) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL) + && pm->ps->saberBlockingTime < cg.time) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL, 1000); } else { @@ -5259,75 +6050,75 @@ void PM_TorsoAnimLightsaber() } /* if ( PM_JumpingAnim( pm->ps->legsAnim ) - || PM_LandingAnim( pm->ps->legsAnim ) - || PM_InCartwheel( pm->ps->legsAnim ) - || PM_FlippingAnim( pm->ps->legsAnim )) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) {//jumping, landing cartwheel, flipping - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); } else { - PM_SetSaberMove( LS_READY ); + PM_SetSaberMove( LS_READY ); } */ } else if (TorsoAgainstWindTest(pm->gent)) { } - else if( pm->ps->legsAnim == BOTH_RUN1 ) + else if (pm->ps->legsAnim == BOTH_RUN1) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_RUN1, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_RUN2 )//&& pm->ps->saberAnimLevel != SS_STAFF ) + else if (pm->ps->legsAnim == BOTH_RUN2)//&& pm->ps->saberAnimLevel != SS_STAFF ) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_RUN2, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_RUN_STAFF ) + else if (pm->ps->legsAnim == BOTH_RUN_STAFF) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_RUN_STAFF, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_RUN_DUAL ) + else if (pm->ps->legsAnim == BOTH_RUN_DUAL) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_RUN_DUAL, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_WALK1 ) + else if (pm->ps->legsAnim == BOTH_WALK1) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_WALK1, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_WALK2 ) + else if (pm->ps->legsAnim == BOTH_WALK2) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_WALK2, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_WALK_STAFF ) + else if (pm->ps->legsAnim == BOTH_WALK_STAFF) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_WALK_STAFF, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_WALK_DUAL ) + else if (pm->ps->legsAnim == BOTH_WALK_DUAL) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_WALK_DUAL, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + else if (pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0)//player falls through { //??? Why nothing? What if you were running??? //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_JUMP1 ) + else if (pm->ps->legsAnim == BOTH_JUMP1) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_JUMP1, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } else {//Used to default to both_stand1 which is an arms-down anim -// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 // Select the next proper pose for the lightsaber assuming that there are no attacks. if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) { @@ -5335,20 +6126,20 @@ void PM_TorsoAnimLightsaber() } else { - if ( PM_JumpingAnim( pm->ps->legsAnim ) - || PM_LandingAnim( pm->ps->legsAnim ) - || PM_InCartwheel( pm->ps->legsAnim ) - || PM_FlippingAnim( pm->ps->legsAnim )) + if (PM_JumpingAnim(pm->ps->legsAnim) + || PM_LandingAnim(pm->ps->legsAnim) + || PM_InCartwheel(pm->ps->legsAnim) + || PM_FlippingAnim(pm->ps->legsAnim)) { - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD) {//using something - if ( !pm->ps->useTime ) + if (!pm->ps->useTime) {//stopped holding it, release - PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); }//else still holding, leave it as it is } else @@ -5364,95 +6155,95 @@ void PM_TorsoAnimLightsaber() // WEAPON_IDLE // ********************************************************* - else if ( pm->ps->weaponstate == WEAPON_IDLE ) + else if (pm->ps->weaponstate == WEAPON_IDLE) { if (TorsoAgainstWindTest(pm->gent)) { } - else if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + else if (pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + else if (pm->ps->legsAnim == BOTH_GUARD_IDLE1) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + else if (pm->ps->legsAnim == BOTH_STAND1IDLE1 || pm->ps->legsAnim == BOTH_STAND2IDLE1 || pm->ps->legsAnim == BOTH_STAND2IDLE2 || pm->ps->legsAnim == BOTH_STAND3IDLE1 - || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + || pm->ps->legsAnim == BOTH_STAND5IDLE1) { - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + else if (pm->ps->legsAnim == BOTH_STAND2TO4) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND2TO4, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + else if (pm->ps->legsAnim == BOTH_STAND4TO2) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND4TO2, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_STAND4 ) + else if (pm->ps->legsAnim == BOTH_STAND4) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND4, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } else { -// This is now set in SetSaberMove. - // Idle for Lightsaber - if ( pm->gent && pm->gent->client ) + // This is now set in SetSaberMove. + // Idle for Lightsaber + if (pm->gent && pm->gent->client) { -// pm->gent->client->saberTrail.inAction = qfalse; + // pm->gent->client->saberTrail.inAction = qfalse; } qboolean saberInAir = qtrue; - if ( pm->ps->saberInFlight ) + if (pm->ps->saberInFlight) {//guiding saber - if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + if (PM_SaberInBrokenParry(pm->ps->saberMove) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim(pm->ps->torsoAnim)) {//we're stuck in a broken parry saberInAir = qfalse; } - if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + if (pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0)//player is 0 {// - if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + if (&g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY) {//fell to the ground and we're not trying to pull it back saberInAir = qfalse; } } } - if ( pm->ps->saberInFlight + if (pm->ps->saberInFlight && saberInAir && (!pm->ps->dualSabers || !pm->ps->saber[1].Active())) { - if ( !PM_ForceAnim( pm->ps->torsoAnim ) - || pm->ps->torsoAnimTimer < 300 ) + if (!PM_ForceAnim(pm->ps->torsoAnim) + || pm->ps->torsoAnimTimer < 300) {//don't interrupt a force power anim - if ( pm->ps->torsoAnim != BOTH_LOSE_SABER - || !pm->ps->torsoAnimTimer ) + if (pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer) { - PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } } } else {//saber is on - // Idle for Lightsaber - if ( pm->gent && pm->gent->client ) + // Idle for Lightsaber + if (pm->gent && pm->gent->client) { - if ( !G_InCinematicSaberAnim( pm->gent ) ) + if (!G_InCinematicSaberAnim(pm->gent)) { - pm->gent->client->ps.SaberDeactivateTrail( 0 ); + pm->gent->client->ps.SaberDeactivateTrail(0); } } // Idle for idle/ready Lightsaber -// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONIDLE1 + // PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONIDLE1 // Select the proper idle Lightsaber attack move from the chart. if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) { @@ -5460,37 +6251,37 @@ void PM_TorsoAnimLightsaber() } else { - if ( PM_JumpingAnim( pm->ps->legsAnim ) - || PM_LandingAnim( pm->ps->legsAnim ) - || PM_InCartwheel( pm->ps->legsAnim ) - || PM_FlippingAnim( pm->ps->legsAnim )) + if (PM_JumpingAnim(pm->ps->legsAnim) + || PM_LandingAnim(pm->ps->legsAnim) + || PM_InCartwheel(pm->ps->legsAnim) + || PM_FlippingAnim(pm->ps->legsAnim)) { - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD) {//using something - if ( !pm->ps->useTime ) + if (!pm->ps->useTime) {//stopped holding it, release - PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); }//else still holding, leave it as it is } else { - if ( (PM_RunningAnim( pm->ps->legsAnim ) + if ((PM_RunningAnim(pm->ps->legsAnim) || pm->ps->legsAnim == BOTH_WALK_STAFF || pm->ps->legsAnim == BOTH_WALK_DUAL || pm->ps->legsAnim == BOTH_WALKBACK_STAFF - || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) - && pm->ps->saberBlockingTime < cg.time ) + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL) + && pm->ps->saberBlockingTime < cg.time) {//running w/1-handed weapon uses full-body anim int setFlags = SETANIM_FLAG_NORMAL; - if ( PM_LandingAnim( pm->ps->torsoAnim ) ) + if (PM_LandingAnim(pm->ps->torsoAnim)) { setFlags = SETANIM_FLAG_OVERRIDE; } - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,setFlags); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, setFlags); } else { @@ -5513,106 +6304,106 @@ PM_TorsoAnimation ------------------------- */ -void PM_TorsoAnimation( void ) +void PM_TorsoAnimation(void) {//FIXME: Write a much smarter and more appropriate anim picking routine logic... -// int oldAnim; - if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps )) + // int oldAnim; + if (PM_InKnockDown(pm->ps) || PM_InRoll(pm->ps)) {//in knockdown return; } - if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + if ((pm->ps->eFlags&EF_HELD_BY_WAMPA)) { return; } - if ( (pm->ps->eFlags&EF_FORCE_DRAINED) ) + if ((pm->ps->eFlags&EF_FORCE_DRAINED)) {//being drained //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); return; } - if ( (pm->ps->forcePowersActive&(1<ps->forceDrainEntityNum < ENTITYNUM_WORLD ) + if ((pm->ps->forcePowersActive&(1 << FP_DRAIN)) + && pm->ps->forceDrainEntityNum < ENTITYNUM_WORLD) {//draining //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGER1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); return; } - if( pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags & SCF_FORCED_MARCH) ) + if (pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags & SCF_FORCED_MARCH)) { return; } - if(pm->gent != NULL && pm->gent->client) + if (pm->gent != NULL && pm->gent->client) { pm->gent->client->renderInfo.torsoFpsMod = 1.0f; } - if ( pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON ) + if (pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON) { - if ( pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use )//ugly way to tell, but... + if (pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use)//ugly way to tell, but... {//full body - PM_SetAnim(pm,SETANIM_BOTH,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + PM_SetAnim(pm, SETANIM_BOTH, BOTH_GUNSIT1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL } else {//torso - PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + PM_SetAnim(pm, SETANIM_TORSO, BOTH_GUNSIT1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL } return; } -/* else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->ps->clientNum < MAX_CLIENTS && (m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].numHands == 2 || g_speederControlScheme->value == 2) ) + /* else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->ps->clientNum < MAX_CLIENTS && (m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].numHands == 2 || g_speederControlScheme->value == 2) ) {//can't look around - PM_SetAnim(pm,SETANIM_TORSO,m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].riderAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); - return; + PM_SetAnim(pm,SETANIM_TORSO,m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].riderAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + return; }*/ - if ( pm->ps->taunting > level.time ) + if (pm->ps->taunting > level.time) { - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA) { - PM_SetAnim(pm,SETANIM_BOTH,BOTH_ALORA_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + PM_SetAnim(pm, SETANIM_BOTH, BOTH_ALORA_TAUNT, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL } - else if ( pm->ps->weapon == WP_SABER && pm->ps->saberAnimLevel == SS_DUAL && PM_HasAnimation( pm->gent, BOTH_DUAL_TAUNT ) ) + else if (pm->ps->weapon == WP_SABER && pm->ps->saberAnimLevel == SS_DUAL && PM_HasAnimation(pm->gent, BOTH_DUAL_TAUNT)) { - PM_SetAnim(pm,SETANIM_BOTH,BOTH_DUAL_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + PM_SetAnim(pm, SETANIM_BOTH, BOTH_DUAL_TAUNT, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL } - else if ( pm->ps->weapon == WP_SABER - && pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->saber[0].type == SABER_STAFF ) + else if (pm->ps->weapon == WP_SABER + && pm->ps->saberAnimLevel == SS_STAFF)//pm->ps->saber[0].type == SABER_STAFF ) {//turn on the blades - if ( PM_HasAnimation( pm->gent, BOTH_STAFF_TAUNT ) ) + if (PM_HasAnimation(pm->gent, BOTH_STAFF_TAUNT)) { - PM_SetAnim(pm,SETANIM_BOTH,BOTH_STAFF_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + PM_SetAnim(pm, SETANIM_BOTH, BOTH_STAFF_TAUNT, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL } /* else { - if ( !pm->ps->saber[0].blade[0].active ) - {//first blade is off - //turn it on - pm->ps->SaberBladeActivate( 0, 0, qtrue ); - if ( !pm->ps->saber[0].blade[1].active ) - {//second blade is also off, extend time of this taunt so we have enough time to turn them both on - pm->ps->taunting = level.time + 3000; - } - } - else if ( (pm->ps->taunting - level.time) < 1500 ) - {//only 1500ms left in taunt - if ( !pm->ps->saber[0].blade[1].active ) - {//second blade is off - //turn it on - pm->ps->SaberBladeActivate( 0, 1, qtrue ); - } - } - //pose - PM_SetAnim(pm,SETANIM_BOTH,BOTH_SABERSTAFF_STANCE,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); - pm->ps->torsoAnimTimer = pm->ps->legsAnimTimer = (pm->ps->taunting - level.time); + if ( !pm->ps->saber[0].blade[0].active ) + {//first blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 0, qtrue ); + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is also off, extend time of this taunt so we have enough time to turn them both on + pm->ps->taunting = level.time + 3000; + } + } + else if ( (pm->ps->taunting - level.time) < 1500 ) + {//only 1500ms left in taunt + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 1, qtrue ); + } + } + //pose + PM_SetAnim(pm,SETANIM_BOTH,BOTH_SABERSTAFF_STANCE,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + pm->ps->torsoAnimTimer = pm->ps->legsAnimTimer = (pm->ps->taunting - level.time); } */ } - else if ( PM_HasAnimation( pm->gent, BOTH_GESTURE1 ) ) + else if (PM_HasAnimation(pm->gent, BOTH_GESTURE1)) { - PM_SetAnim(pm,SETANIM_BOTH,BOTH_GESTURE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL - pm->gent->client->ps.SaberActivateTrail( 100 ); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_GESTURE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + pm->gent->client->ps.SaberActivateTrail(100); //FIXME: will this reset? //FIXME: force-control (yellow glow) effect on hand and saber? } @@ -5623,106 +6414,107 @@ void PM_TorsoAnimation( void ) return; } - if (pm->ps->weapon == WP_SABER ) // WP_LIGHTSABER + if (pm->ps->weapon == WP_SABER) // WP_LIGHTSABER { qboolean saberInAir = qfalse; - if ( pm->ps->SaberLength() && !pm->ps->saberInFlight ) + + if ( pm->ps->SaberLength() && !pm->ps->saberInFlight && (pm->ps->SaberActive() || !g_noIgniteTwirl->integer) ) { PM_TorsoAnimLightsaber(); } else { - if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + if (pm->ps->forcePowersActive&(1 << FP_GRIP) && pm->ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1) {//holding an enemy aloft with force-grip return; } - if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + if (pm->ps->forcePowersActive&(1 << FP_LIGHTNING) && pm->ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1) {//lightning return; } - if ( pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1 << FP_DRAIN)) {//drain return; } saberInAir = qtrue; - if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + if (PM_SaberInBrokenParry(pm->ps->saberMove) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim(pm->ps->torsoAnim)) {//we're stuck in a broken parry PM_TorsoAnimLightsaber(); } else { - if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 - {// - if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + if (pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0)//player is 0 + {// + if (&g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY) {//fell to the ground and we're not trying to pull it back saberInAir = qfalse; } } - if ( pm->ps->saberInFlight + if (pm->ps->saberInFlight && saberInAir && (!pm->ps->dualSabers //not using 2 sabers - || !pm->ps->saber[1].Active() //left one off - || pm->ps->torsoAnim == BOTH_SABERDUAL_STANCE//not attacking - || pm->ps->torsoAnim == BOTH_SABERPULL//not attacking - || pm->ps->torsoAnim == BOTH_STAND1//not attacking - || PM_RunningAnim( pm->ps->torsoAnim ) //not attacking - || PM_WalkingAnim( pm->ps->torsoAnim ) //not attacking - || PM_JumpingAnim( pm->ps->torsoAnim )//not attacking - || PM_SwimmingAnim( pm->ps->torsoAnim ) )//not attacking + || !pm->ps->saber[1].Active() //left one off + || pm->ps->torsoAnim == BOTH_SABERDUAL_STANCE//not attacking + || pm->ps->torsoAnim == BOTH_SABERPULL//not attacking + || pm->ps->torsoAnim == BOTH_STAND1//not attacking + || PM_RunningAnim(pm->ps->torsoAnim) //not attacking + || PM_WalkingAnim(pm->ps->torsoAnim) //not attacking + || PM_JumpingAnim(pm->ps->torsoAnim)//not attacking + || PM_SwimmingAnim(pm->ps->torsoAnim))//not attacking ) { - if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 ) + if (!PM_ForceAnim(pm->ps->torsoAnim) || pm->ps->torsoAnimTimer < 300) {//don't interrupt a force power anim - if ( pm->ps->torsoAnim != BOTH_LOSE_SABER - || !pm->ps->torsoAnimTimer ) + if (pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer) { - PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } } } else { - if ( PM_InSlopeAnim( pm->ps->legsAnim ) ) + if (PM_InSlopeAnim(pm->ps->legsAnim)) {//HMM... this probably breaks the saber putaway and select anims - if ( pm->ps->SaberLength() > 0 ) + if ( pm->ps->SaberLength() > 0 && (pm->ps->SaberActive() || !g_noIgniteTwirl->integer) ) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND2, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } else { - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD) {//using something - if ( !pm->ps->useTime ) + if (!pm->ps->useTime) {//stopped holding it, release - PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); }//else still holding, leave it as it is } else { - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } } } } } - if (pm->ps->weaponTime<= 0 && (pm->ps->saberMove==LS_READY || pm->ps->SaberLength()==0) && !saberInAir) + if (pm->ps->weaponTime <= 0 && (pm->ps->saberMove == LS_READY || pm->ps->SaberLength() == 0) && !saberInAir) { TorsoAgainstWindTest(pm->gent); } return; } - if ( PM_ForceAnim( pm->ps->torsoAnim ) - && pm->ps->torsoAnimTimer > 0 ) + if (PM_ForceAnim(pm->ps->torsoAnim) + && pm->ps->torsoAnimTimer > 0) {//in a force anim, don't do a stand anim return; } @@ -5730,217 +6522,217 @@ void PM_TorsoAnimation( void ) qboolean weaponBusy = qfalse; - if ( pm->ps->weapon == WP_NONE ) + if (pm->ps->weapon == WP_NONE) { weaponBusy = qfalse; } - else if ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + else if (pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT) { weaponBusy = qtrue; } - else if ( pm->ps->lastShotTime > level.time - 3000 ) + else if (pm->ps->lastShotTime > level.time - 3000) { weaponBusy = qtrue; } - else if ( pm->ps->weaponTime > 0 ) + else if (pm->ps->weaponTime > 0) { weaponBusy = qtrue; } - else if ( pm->gent && pm->gent->client->fireDelay > 0 ) + else if (pm->gent && pm->gent->client->fireDelay > 0) { weaponBusy = qtrue; } - else if ( TorsoAgainstWindTest(pm->gent) ) + else if (TorsoAgainstWindTest(pm->gent)) { return; } - else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.zoomTime > cg.time - 5000 ) + else if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && cg.zoomTime > cg.time - 5000) {//if we used binoculars recently, aim weapon weaponBusy = qtrue; pm->ps->weaponstate = WEAPON_IDLE; } - else if ( pm->ps->pm_flags & PMF_DUCKED ) + else if (pm->ps->pm_flags & PMF_DUCKED) {//ducking is considered on alert... plus looks stupid to have arms hanging down when crouched weaponBusy = qtrue; } - if ( pm->ps->weapon == WP_NONE || - pm->ps->weaponstate == WEAPON_READY || - pm->ps->weaponstate == WEAPON_CHARGING || - pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + if (pm->ps->weapon == WP_NONE || + pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT) { - if ( pm->ps->weapon == WP_SABER && pm->ps->SaberLength() ) + if ( pm->ps->weapon == WP_SABER && pm->ps->SaberLength() && (pm->ps->SaberActive() || !g_noIgniteTwirl->integer)) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK1, SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 } - else if( pm->ps->legsAnim == BOTH_RUN1 && !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_RUN1 && !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_RUN1, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_RUN2 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + else if (pm->ps->legsAnim == BOTH_RUN2 && !weaponBusy)//&& pm->ps->saberAnimLevel != SS_STAFF ) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_RUN2, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_RUN4 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + else if (pm->ps->legsAnim == BOTH_RUN4 && !weaponBusy)//&& pm->ps->saberAnimLevel != SS_STAFF ) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN4,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_RUN4, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_RUN_STAFF && !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_RUN_STAFF && !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_RUN_STAFF, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_RUN_DUAL && !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_RUN_DUAL && !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_RUN_DUAL, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_WALK1 && !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_WALK1 && !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_WALK1, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_WALK2 && !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_WALK2 && !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_WALK2, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_WALK_STAFF && !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_WALK_STAFF && !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_WALK_STAFF, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_WALK_DUAL&& !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_WALK_DUAL&& !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_WALK_DUAL, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + else if (pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0)//player falls through { //??? Why nothing? What if you were running??? //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_JUMP1 && !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_JUMP1 && !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL, 100); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_TORSO, BOTH_JUMP1, SETANIM_FLAG_NORMAL, 100); // Only blend over 100ms } - else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 && !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_SWIM_IDLE1 && !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_SWIM_IDLE1, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_SWIMFORWARD && !weaponBusy ) + else if (pm->ps->legsAnim == BOTH_SWIMFORWARD && !weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_SWIMFORWARD, SETANIM_FLAG_NORMAL); } - else if ( pm->ps->weapon == WP_NONE ) + else if (pm->ps->weapon == WP_NONE) { int legsAnim = pm->ps->legsAnim; /* if ( PM_RollingAnim( legsAnim ) || - PM_FlippingAnim( legsAnim ) || - PM_JumpingAnim( legsAnim ) || - PM_PainAnim( legsAnim ) || - PM_SwimmingAnim( legsAnim ) ) + PM_FlippingAnim( legsAnim ) || + PM_JumpingAnim( legsAnim ) || + PM_PainAnim( legsAnim ) || + PM_SwimmingAnim( legsAnim ) ) */ { - PM_SetAnim(pm, SETANIM_TORSO, legsAnim, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, legsAnim, SETANIM_FLAG_NORMAL); } } else {//Used to default to both_stand1 which is an arms-down anim - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD) {//using something - if ( !pm->ps->useTime ) + if (!pm->ps->useTime) {//stopped holding it, release - PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); }//else still holding, leave it as it is } - else if ( pm->gent != NULL - && (pm->gent->s.numbergent)) + else if (pm->gent != NULL + && (pm->gent->s.numbergent)) && pm->ps->weaponstate != WEAPON_CHARGING - && pm->ps->weaponstate != WEAPON_CHARGING_ALT ) + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) {//PLayer- temp hack for weapon frame - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR) {//ignore } - else if ( pm->ps->weapon == WP_MELEE ) + else if (pm->ps->weapon == WP_MELEE) {//hehe - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND6, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } - else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + else if (PM_InSpecialJump(pm->ps->legsAnim)) {//use legs anim //FIXME: or just use whatever's currently playing? //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); } else { - switch(pm->ps->weapon) + switch (pm->ps->weapon) { - // ******************************************************** + // ******************************************************** case WP_SABER: // WP_LIGHTSABER // Ready pose for Lightsaber -// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 // Select the next proper pose for the lightsaber assuming that there are no attacks. if (pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX) { PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); } break; - // ******************************************************** + // ******************************************************** case WP_BRYAR_PISTOL: //FIXME: if recently fired, hold the ready! - if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + if (pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY2, SETANIM_FLAG_NORMAL); } - else if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + else if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY2, SETANIM_FLAG_NORMAL); } break; case WP_BLASTER_PISTOL: - if ( pm->gent - && pm->gent->weaponModel[1] > 0 ) + if (pm->gent + && pm->gent->weaponModel[1] > 0) {//dual pistols - if ( weaponBusy ) + if (weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_GUNSIT1, SETANIM_FLAG_NORMAL); } - else if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + else if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND6, SETANIM_FLAG_NORMAL); } } else {//single pistols - if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + if (pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY2, SETANIM_FLAG_NORMAL); } - else if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + else if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY2, SETANIM_FLAG_NORMAL); } } break; @@ -5948,35 +6740,35 @@ void PM_TorsoAnimation( void ) //NOTE: should never get here break; case WP_MELEE: - if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR) {//ignore } - else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + else if (pm->gent && pm->gent->client && !PM_DroidMelee(pm->gent->client->NPC_class)) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND6, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } break; case WP_TUSKEN_STAFF: - if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { @@ -5985,263 +6777,283 @@ void PM_TorsoAnimation( void ) break; case WP_NOGHRI_STICK: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); break; - case WP_BLASTER: + case WP_E5_CARBINE: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + break; + + case WP_DC15S_CARBINE: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + break; + + case WP_Z6_ROTARY: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + break; + + case WP_DC15A_RIFLE: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + break; + + case WP_SONIC_BLASTER: PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + break; + + case WP_BLASTER: + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); break; case WP_DISRUPTOR: case WP_TUSKEN_RIFLE: - if ( (pm->ps->weaponstate != WEAPON_FIRING - && pm->ps->weaponstate != WEAPON_CHARGING - && pm->ps->weaponstate != WEAPON_CHARGING_ALT) - || PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + if ((pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running sniper weapon uses normal ready - if ( pm->ps->clientNum ) + if (pm->ps->clientNum) { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); } } else { - if ( pm->ps->clientNum ) + if (pm->ps->clientNum) { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );//TORSO_WEAPONREADY4//SETANIM_FLAG_RESTART| + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//TORSO_WEAPONREADY4//SETANIM_FLAG_RESTART| } else { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL); } } break; case WP_BOT_LASER: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); break; case WP_THERMAL: - if ( pm->ps->weaponstate != WEAPON_FIRING + if (pm->ps->weaponstate != WEAPON_FIRING && pm->ps->weaponstate != WEAPON_CHARGING && pm->ps->weaponstate != WEAPON_CHARGING_ALT - && (PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim )) ) + && (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim))) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && (pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && (pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT)) {//player pulling back to throw - if ( PM_StandingAnim( pm->ps->legsAnim ) ) + if (PM_StandingAnim(pm->ps->legsAnim)) { - PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } - else if ( pm->ps->legsAnim == BOTH_THERMAL_READY ) + else if (pm->ps->legsAnim == BOTH_THERMAL_READY) {//sigh... hold it so pm_footsteps doesn't override - if ( pm->ps->legsAnimTimer < 100 ) + if (pm->ps->legsAnimTimer < 100) { pm->ps->legsAnimTimer = 100; } } - PM_SetAnim( pm, SETANIM_TORSO, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } else { - if ( weaponBusy ) + if (weaponBusy) { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY10, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY10, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } } break; case WP_REPEATER: - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH) {// - if ( pm->gent->alt_fire ) + if (pm->gent->alt_fire) { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY1, SETANIM_FLAG_NORMAL); } } else { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); } break; case WP_TRIP_MINE: case WP_DET_PACK: - if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - if ( weaponBusy ) + if (weaponBusy) { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } break; default: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); break; } } } } - else if ( pm->ps->weaponstate == WEAPON_IDLE ) + else if (pm->ps->weaponstate == WEAPON_IDLE) { - if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + if (pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_GUARD_LOOKAROUND1, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + else if (pm->ps->legsAnim == BOTH_GUARD_IDLE1) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_GUARD_IDLE1, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + else if (pm->ps->legsAnim == BOTH_STAND1IDLE1 || pm->ps->legsAnim == BOTH_STAND2IDLE1 || pm->ps->legsAnim == BOTH_STAND2IDLE2 || pm->ps->legsAnim == BOTH_STAND3IDLE1 - || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + || pm->ps->legsAnim == BOTH_STAND5IDLE1) { - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); pm->ps->saberMove = LS_READY; } - else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + else if (pm->ps->legsAnim == BOTH_STAND2TO4) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND2TO4, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + else if (pm->ps->legsAnim == BOTH_STAND4TO2) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND4TO2, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_STAND4 ) + else if (pm->ps->legsAnim == BOTH_STAND4) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND4, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 ) + else if (pm->ps->legsAnim == BOTH_SWIM_IDLE1) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_SWIM_IDLE1, SETANIM_FLAG_NORMAL); } - else if( pm->ps->legsAnim == BOTH_SWIMFORWARD ) + else if (pm->ps->legsAnim == BOTH_SWIMFORWARD) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_SWIMFORWARD, SETANIM_FLAG_NORMAL); } - else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + else if (PM_InSpecialJump(pm->ps->legsAnim)) {//use legs anim //FIXME: or just use whatever's currently playing? //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); } - else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + else if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD) {//using something - if ( !pm->ps->useTime ) + if (!pm->ps->useTime) {//stopped holding it, release - PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); }//else still holding, leave it as it is } else { - if ( !weaponBusy + if (!weaponBusy && pm->ps->weapon != WP_BOWCASTER && pm->ps->weapon != WP_REPEATER && pm->ps->weapon != WP_FLECHETTE && pm->ps->weapon != WP_ROCKET_LAUNCHER && pm->ps->weapon != WP_CONCUSSION - && ( PM_RunningAnim( pm->ps->legsAnim ) - || (PM_WalkingAnim( pm->ps->legsAnim ) && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) ) + && (PM_RunningAnim(pm->ps->legsAnim) + || (PM_WalkingAnim(pm->ps->legsAnim) && (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim))) {//running w/1-handed or light 2-handed weapon uses full-body anim if you're not using the weapon right now - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - switch ( pm->ps->weapon ) + switch (pm->ps->weapon) { - // ******************************************************** + // ******************************************************** case WP_SABER: // WP_LIGHTSABER // Shouldn't get here, should go to TorsoAnimLightsaber break; - // ******************************************************** + // ******************************************************** case WP_BRYAR_PISTOL: - if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + if (pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY2, SETANIM_FLAG_NORMAL); } - else if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + else if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE2, SETANIM_FLAG_NORMAL); } break; case WP_BLASTER_PISTOL: - if ( pm->gent - && pm->gent->weaponModel[1] > 0 ) + if (pm->gent + && pm->gent->weaponModel[1] > 0) {//dual pistols - if ( weaponBusy ) + if (weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_GUNSIT1, SETANIM_FLAG_NORMAL); } - else if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + else if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } else {//single pistols - if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + if (pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY2, SETANIM_FLAG_NORMAL); } - else if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + else if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE2, SETANIM_FLAG_NORMAL); } } break; @@ -6251,44 +7063,77 @@ void PM_TorsoAnimation( void ) break; case WP_MELEE: - if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR) {//ignore } - else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + else if (pm->gent && pm->gent->client && !PM_DroidMelee(pm->gent->client->NPC_class)) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND6, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } break; case WP_TUSKEN_STAFF: - if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND3, SETANIM_FLAG_NORMAL); } break; + + case WP_E5_CARBINE: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_DC15S_CARBINE: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_Z6_ROTARY: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; - case WP_NOGHRI_STICK: + case WP_DC15A_RIFLE: if ( weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); @@ -6299,7 +7144,7 @@ void PM_TorsoAnimation( void ) } break; - case WP_BLASTER: + case WP_SONIC_BLASTER: if ( weaponBusy ) { PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); @@ -6310,103 +7155,141 @@ void PM_TorsoAnimation( void ) } break; + + case WP_NOGHRI_STICK: + if (weaponBusy) + { + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL); + } + break; + + case WP_BLASTER: + if (weaponBusy) + { + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL); + } + break; + case WP_DISRUPTOR: case WP_TUSKEN_RIFLE: - if ( (pm->ps->weaponstate != WEAPON_FIRING - && pm->ps->weaponstate != WEAPON_CHARGING - && pm->ps->weaponstate != WEAPON_CHARGING_ALT) - || PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + if ((pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running sniper weapon uses normal ready - if ( pm->ps->clientNum ) + if (pm->ps->clientNum) { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); } } else { - if ( pm->ps->clientNum ) + if (pm->ps->clientNum) { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL); } } break; case WP_BOT_LASER: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); break; case WP_THERMAL: - if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - if ( weaponBusy ) + if (weaponBusy) { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE10, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE10, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } break; case WP_REPEATER: - if ( weaponBusy ) - { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); - } - else - { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); - } + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + {// + if ( pm->gent->alt_fire ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL); + } + } + else + { + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + } break; + case WP_TRIP_MINE: case WP_DET_PACK: - if ( PM_RunningAnim( pm->ps->legsAnim ) - || PM_WalkingAnim( pm->ps->legsAnim ) - || PM_JumpingAnim( pm->ps->legsAnim ) - || PM_SwimmingAnim( pm->ps->legsAnim ) ) + if (PM_RunningAnim(pm->ps->legsAnim) + || PM_WalkingAnim(pm->ps->legsAnim) + || PM_JumpingAnim(pm->ps->legsAnim) + || PM_SwimmingAnim(pm->ps->legsAnim)) {//running w/1-handed weapon uses full-body anim - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); } else { - if ( weaponBusy ) + if (weaponBusy) { - PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } break; default: - if ( weaponBusy ) + if (weaponBusy) { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL); } break; } @@ -6419,14 +7302,14 @@ void PM_TorsoAnimation( void ) // Anim checking utils //========================================================================= -int PM_GetTurnAnim( gentity_t *gent, int anim ) +int PM_GetTurnAnim(gentity_t *gent, int anim) { - if ( !gent ) + if (!gent) { return -1; } - switch( anim ) + switch (anim) { case BOTH_STAND1: //# Standing idle: no weapon: hands down case BOTH_STAND1IDLE1: //# Random standing idle @@ -6447,7 +7330,7 @@ int PM_GetTurnAnim( gentity_t *gent, int anim ) case BOTH_GESTURE2: //# Generic gesture: non-specific case BOTH_TALK1: //# Generic talk anim case BOTH_TALK2: //# Generic talk anim - if ( PM_HasAnimation( gent, LEGS_TURN1 ) ) + if (PM_HasAnimation(gent, LEGS_TURN1)) { return LEGS_TURN1; } @@ -6464,7 +7347,7 @@ int PM_GetTurnAnim( gentity_t *gent, int anim ) case BOTH_MELEE2: //# Second melee attack case BOTH_GUARD_LOOKAROUND1: //# Cradling weapon and looking around case BOTH_GUARD_IDLE1: //# Cradling weapon and standing - if ( PM_HasAnimation( gent, LEGS_TURN2 ) ) + if (PM_HasAnimation(gent, LEGS_TURN2)) { return LEGS_TURN2; } @@ -6479,18 +7362,18 @@ int PM_GetTurnAnim( gentity_t *gent, int anim ) } } -int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) +int PM_TurnAnimForLegsAnim(gentity_t *gent, int anim) { - if ( !gent ) + if (!gent) { return -1; } - switch( anim ) + switch (anim) { case BOTH_STAND1: //# Standing idle: no weapon: hands down case BOTH_STAND1IDLE1: //# Random standing idle - if ( PM_HasAnimation( gent, BOTH_TURNSTAND1 ) ) + if (PM_HasAnimation(gent, BOTH_TURNSTAND1)) { return BOTH_TURNSTAND1; } @@ -6504,7 +7387,7 @@ int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) case BOTH_SABERSLOW_STANCE: case BOTH_STAND2IDLE1: //# Random standing idle case BOTH_STAND2IDLE2: //# Random standing idle - if ( PM_HasAnimation( gent, BOTH_TURNSTAND2 ) ) + if (PM_HasAnimation(gent, BOTH_TURNSTAND2)) { return BOTH_TURNSTAND2; } @@ -6515,7 +7398,7 @@ int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) break; case BOTH_STAND3: //# Standing hands behind back: at ease: etc. case BOTH_STAND3IDLE1: //# Random standing idle - if ( PM_HasAnimation( gent, BOTH_TURNSTAND3 ) ) + if (PM_HasAnimation(gent, BOTH_TURNSTAND3)) { return BOTH_TURNSTAND3; } @@ -6525,7 +7408,7 @@ int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) } break; case BOTH_STAND4: //# two handed: gun down: relaxed stand - if ( PM_HasAnimation( gent, BOTH_TURNSTAND4 ) ) + if (PM_HasAnimation(gent, BOTH_TURNSTAND4)) { return BOTH_TURNSTAND4; } @@ -6536,7 +7419,7 @@ int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) break; case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight case BOTH_STAND5IDLE1: //# Random standing idle - if ( PM_HasAnimation( gent, BOTH_TURNSTAND5 ) ) + if (PM_HasAnimation(gent, BOTH_TURNSTAND5)) { return BOTH_TURNSTAND5; } @@ -6547,15 +7430,15 @@ int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) break; case BOTH_CROUCH1: //# Transition from standing to crouch case BOTH_CROUCH1IDLE: //# Crouching idle - /* - case BOTH_UNCROUCH1: //# Transition from crouch to standing - case BOTH_CROUCH2TOSTAND1: //# going from crouch2 to stand1 - case BOTH_CROUCH3: //# Desann crouching down to Kyle (cin 9) - case BOTH_UNCROUCH3: //# Desann uncrouching down to Kyle (cin 9) - case BOTH_CROUCH4: //# Slower version of crouch1 for cinematics - case BOTH_UNCROUCH4: //# Slower version of uncrouch1 for cinematics - */ - if ( PM_HasAnimation( gent, BOTH_TURNCROUCH1 ) ) + /* + case BOTH_UNCROUCH1: //# Transition from crouch to standing + case BOTH_CROUCH2TOSTAND1: //# going from crouch2 to stand1 + case BOTH_CROUCH3: //# Desann crouching down to Kyle (cin 9) + case BOTH_UNCROUCH3: //# Desann uncrouching down to Kyle (cin 9) + case BOTH_CROUCH4: //# Slower version of crouch1 for cinematics + case BOTH_UNCROUCH4: //# Slower version of uncrouch1 for cinematics + */ + if (PM_HasAnimation(gent, BOTH_TURNCROUCH1)) { return BOTH_TURNCROUCH1; } @@ -6570,9 +7453,9 @@ int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) } } -qboolean PM_InOnGroundAnim ( playerState_t *ps ) +qboolean PM_InOnGroundAnim(playerState_t *ps) { - switch( ps->legsAnim ) + switch (ps->legsAnim) { case BOTH_DEAD1: case BOTH_DEAD2: @@ -6588,28 +7471,28 @@ qboolean PM_InOnGroundAnim ( playerState_t *ps ) case BOTH_SLEEP1: //# laying on back-rknee up-rhand on torso return qtrue; break; - case BOTH_KNOCKDOWN1: //# - case BOTH_KNOCKDOWN2: //# - case BOTH_KNOCKDOWN3: //# - case BOTH_KNOCKDOWN4: //# - case BOTH_KNOCKDOWN5: //# + case BOTH_KNOCKDOWN1: //# + case BOTH_KNOCKDOWN2: //# + case BOTH_KNOCKDOWN3: //# + case BOTH_KNOCKDOWN4: //# + case BOTH_KNOCKDOWN5: //# case BOTH_LK_DL_ST_T_SB_1_L: case BOTH_RELEASED: - if ( ps->legsAnimTimer < 500 ) + if (ps->legsAnimTimer < 500) {//pretty much horizontal by this point return qtrue; } break; case BOTH_PLAYER_PA_3_FLY: - if ( ps->legsAnimTimer < 300 ) + if (ps->legsAnimTimer < 300) {//pretty much horizontal by this point return qtrue; } /* else if ( ps->clientNum < MAX_CLIENTS - && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) + && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) { - return qtrue; + return qtrue; } */ break; @@ -6628,7 +7511,7 @@ qboolean PM_InOnGroundAnim ( playerState_t *ps ) case BOTH_FORCE_GETUP_B4: case BOTH_FORCE_GETUP_B5: case BOTH_FORCE_GETUP_B6: - if ( ps->legsAnimTimer > PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim )-400 ) + if (ps->legsAnimTimer > PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim) - 400) {//still pretty much horizontal at this point return qtrue; } @@ -6638,9 +7521,9 @@ qboolean PM_InOnGroundAnim ( playerState_t *ps ) return qfalse; } -qboolean PM_InSpecialDeathAnim( int anim ) +qboolean PM_InSpecialDeathAnim(int anim) { - switch( pm->ps->legsAnim ) + switch (pm->ps->legsAnim) { case BOTH_DEATH_ROLL: //# Death anim from a roll case BOTH_DEATH_FLIP: //# Death anim from a flip @@ -6660,9 +7543,9 @@ qboolean PM_InSpecialDeathAnim( int anim ) } } -qboolean PM_InDeathAnim ( void ) +qboolean PM_InDeathAnim(void) {//Purposely does not cover stumbledeath and falldeath... - switch( pm->ps->legsAnim ) + switch (pm->ps->legsAnim) { case BOTH_DEATH1: //# First Death anim case BOTH_DEATH2: //# Second Death anim @@ -6671,26 +7554,26 @@ qboolean PM_InDeathAnim ( void ) case BOTH_DEATH5: //# Fifth Death anim case BOTH_DEATH6: //# Sixth Death anim case BOTH_DEATH7: //# Seventh Death anim - case BOTH_DEATH8: //# - case BOTH_DEATH9: //# - case BOTH_DEATH10: //# + case BOTH_DEATH8: //# + case BOTH_DEATH9: //# + case BOTH_DEATH10: //# case BOTH_DEATH11: //# - case BOTH_DEATH12: //# - case BOTH_DEATH13: //# - case BOTH_DEATH14: //# + case BOTH_DEATH12: //# + case BOTH_DEATH13: //# + case BOTH_DEATH14: //# case BOTH_DEATH14_UNGRIP: //# Desann's end death (cin #35) case BOTH_DEATH14_SITUP: //# Tavion sitting up after having been thrown (cin #23) - case BOTH_DEATH15: //# - case BOTH_DEATH16: //# - case BOTH_DEATH17: //# - case BOTH_DEATH18: //# - case BOTH_DEATH19: //# - case BOTH_DEATH20: //# - case BOTH_DEATH21: //# - case BOTH_DEATH22: //# - case BOTH_DEATH23: //# - case BOTH_DEATH24: //# - case BOTH_DEATH25: //# + case BOTH_DEATH15: //# + case BOTH_DEATH16: //# + case BOTH_DEATH17: //# + case BOTH_DEATH18: //# + case BOTH_DEATH19: //# + case BOTH_DEATH20: //# + case BOTH_DEATH21: //# + case BOTH_DEATH22: //# + case BOTH_DEATH23: //# + case BOTH_DEATH24: //# + case BOTH_DEATH25: //# case BOTH_DEATHFORWARD1: //# First Death in which they get thrown forward case BOTH_DEATHFORWARD2: //# Second Death in which they get thrown forward @@ -6704,7 +7587,7 @@ qboolean PM_InDeathAnim ( void ) case BOTH_FALLDEATH1: //# Fall forward off a high cliff and splat death - start case BOTH_FALLDEATH1INAIR: //# Fall forward off a high cliff and splat death - loop case BOTH_FALLDEATH1LAND: //# Fall forward off a high cliff and splat death - hit bottom - //# #sep case BOTH_ DEAD POSES # Should be last frame of corresponding previous anims + //# #sep case BOTH_ DEAD POSES # Should be last frame of corresponding previous anims case BOTH_DEAD1: //# First Death finished pose case BOTH_DEAD2: //# Second Death finished pose case BOTH_DEAD3: //# Third Death finished pose @@ -6712,24 +7595,24 @@ qboolean PM_InDeathAnim ( void ) case BOTH_DEAD5: //# Fifth Death finished pose case BOTH_DEAD6: //# Sixth Death finished pose case BOTH_DEAD7: //# Seventh Death finished pose - case BOTH_DEAD8: //# - case BOTH_DEAD9: //# - case BOTH_DEAD10: //# + case BOTH_DEAD8: //# + case BOTH_DEAD9: //# + case BOTH_DEAD10: //# case BOTH_DEAD11: //# - case BOTH_DEAD12: //# - case BOTH_DEAD13: //# - case BOTH_DEAD14: //# - case BOTH_DEAD15: //# - case BOTH_DEAD16: //# - case BOTH_DEAD17: //# - case BOTH_DEAD18: //# - case BOTH_DEAD19: //# - case BOTH_DEAD20: //# - case BOTH_DEAD21: //# - case BOTH_DEAD22: //# - case BOTH_DEAD23: //# - case BOTH_DEAD24: //# - case BOTH_DEAD25: //# + case BOTH_DEAD12: //# + case BOTH_DEAD13: //# + case BOTH_DEAD14: //# + case BOTH_DEAD15: //# + case BOTH_DEAD16: //# + case BOTH_DEAD17: //# + case BOTH_DEAD18: //# + case BOTH_DEAD19: //# + case BOTH_DEAD20: //# + case BOTH_DEAD21: //# + case BOTH_DEAD22: //# + case BOTH_DEAD23: //# + case BOTH_DEAD24: //# + case BOTH_DEAD25: //# case BOTH_DEADFORWARD1: //# First thrown forward death finished pose case BOTH_DEADFORWARD2: //# Second thrown forward death finished pose case BOTH_DEADBACKWARD1: //# First thrown backward death finished pose @@ -6737,7 +7620,7 @@ qboolean PM_InDeathAnim ( void ) case BOTH_LYINGDEAD1: //# Killed lying down death finished pose case BOTH_STUMBLEDEAD1: //# Stumble forward death finished pose case BOTH_FALLDEAD1LAND: //# Fall forward and splat death finished pose - //# #sep case BOTH_ DEAD TWITCH/FLOP # React to being shot from death poses + //# #sep case BOTH_ DEAD TWITCH/FLOP # React to being shot from death poses case BOTH_DEADFLOP1: //# React to being shot from First Death finished pose case BOTH_DEADFLOP2: //# React to being shot from Second Death finished pose case BOTH_DISMEMBER_HEAD1: //# @@ -6749,14 +7632,14 @@ qboolean PM_InDeathAnim ( void ) return qtrue; break; default: - return PM_InSpecialDeathAnim( pm->ps->legsAnim ); + return PM_InSpecialDeathAnim(pm->ps->legsAnim); break; } } -qboolean PM_InCartwheel( int anim ) +qboolean PM_InCartwheel(int anim) { - switch ( anim ) + switch (anim) { case BOTH_ARIAL_LEFT: case BOTH_ARIAL_RIGHT: @@ -6769,9 +7652,9 @@ qboolean PM_InCartwheel( int anim ) return qfalse; } -qboolean PM_InButterfly( int anim ) +qboolean PM_InButterfly(int anim) { - switch ( anim ) + switch (anim) { case BOTH_BUTTERFLY_LEFT: case BOTH_BUTTERFLY_RIGHT: @@ -6783,9 +7666,9 @@ qboolean PM_InButterfly( int anim ) return qfalse; } -qboolean PM_StandingAnim( int anim ) +qboolean PM_StandingAnim(int anim) {//NOTE: does not check idles or special (cinematic) stands - switch ( anim ) + switch (anim) { case BOTH_STAND1: case BOTH_STAND2: @@ -6798,9 +7681,9 @@ qboolean PM_StandingAnim( int anim ) return qfalse; } -qboolean PM_InAirKickingAnim( int anim ) +qboolean PM_InAirKickingAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_A7_KICK_F_AIR: case BOTH_A7_KICK_B_AIR: @@ -6811,9 +7694,9 @@ qboolean PM_InAirKickingAnim( int anim ) return qfalse; } -qboolean PM_KickingAnim( int anim ) +qboolean PM_KickingAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_A7_KICK_F: case BOTH_A7_KICK_B: @@ -6822,9 +7705,9 @@ qboolean PM_KickingAnim( int anim ) case BOTH_A7_KICK_S: case BOTH_A7_KICK_BF: case BOTH_A7_KICK_RL: - //NOT a kick, but acts like one: + //NOT a kick, but acts like one: case BOTH_A7_HILT: - //NOT kicks, but do kick traces anyway + //NOT kicks, but do kick traces anyway case BOTH_GETUP_BROLL_B: case BOTH_GETUP_BROLL_F: case BOTH_GETUP_FROLL_B: @@ -6832,15 +7715,15 @@ qboolean PM_KickingAnim( int anim ) return qtrue; break; default: - return PM_InAirKickingAnim( anim ); + return PM_InAirKickingAnim(anim); break; } //return qfalse; } -qboolean PM_StabDownAnim( int anim ) +qboolean PM_StabDownAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_STABDOWN: case BOTH_STABDOWN_STAFF: @@ -6850,23 +7733,23 @@ qboolean PM_StabDownAnim( int anim ) return qfalse; } -qboolean PM_GoingToAttackDown( playerState_t *ps ) +qboolean PM_GoingToAttackDown(playerState_t *ps) { - if ( PM_StabDownAnim( ps->torsoAnim )//stabbing downward + if (PM_StabDownAnim(ps->torsoAnim)//stabbing downward || ps->saberMove == LS_A_LUNGE//lunge || ps->saberMove == LS_A_JUMP_T__B_//death from above || ps->saberMove == LS_A_T2B//attacking top to bottom || ps->saberMove == LS_S_T2B//starting at attack downward - || (PM_SaberInTransition( ps->saberMove ) && saberMoveData[ps->saberMove].endQuad == Q_T) )//transitioning to a top to bottom attack + || (PM_SaberInTransition(ps->saberMove) && saberMoveData[ps->saberMove].endQuad == Q_T))//transitioning to a top to bottom attack { return qtrue; } return qfalse; } -qboolean PM_ForceUsingSaberAnim( int anim ) +qboolean PM_ForceUsingSaberAnim(int anim) {//saber/acrobatic anims that should prevent you from recharging force power while you're in them... - switch ( anim ) + switch (anim) { case BOTH_JUMPFLIPSLASHDOWN1: case BOTH_JUMPFLIPSTABDOWN: @@ -6972,22 +7855,22 @@ qboolean PM_ForceUsingSaberAnim( int anim ) return qfalse; } -qboolean G_HasKnockdownAnims( gentity_t *ent ) +qboolean G_HasKnockdownAnims(gentity_t *ent) { - if ( PM_HasAnimation( ent, BOTH_KNOCKDOWN1 ) - && PM_HasAnimation( ent, BOTH_KNOCKDOWN2 ) - && PM_HasAnimation( ent, BOTH_KNOCKDOWN3 ) - && PM_HasAnimation( ent, BOTH_KNOCKDOWN4 ) - && PM_HasAnimation( ent, BOTH_KNOCKDOWN5 ) ) + if (PM_HasAnimation(ent, BOTH_KNOCKDOWN1) + && PM_HasAnimation(ent, BOTH_KNOCKDOWN2) + && PM_HasAnimation(ent, BOTH_KNOCKDOWN3) + && PM_HasAnimation(ent, BOTH_KNOCKDOWN4) + && PM_HasAnimation(ent, BOTH_KNOCKDOWN5)) { return qtrue; } return qfalse; } -qboolean PM_InAttackRoll( int anim ) +qboolean PM_InAttackRoll(int anim) { - switch ( anim ) + switch (anim) { case BOTH_GETUP_BROLL_B: case BOTH_GETUP_BROLL_F: @@ -6998,9 +7881,9 @@ qboolean PM_InAttackRoll( int anim ) return qfalse; } -qboolean PM_LockedAnim( int anim ) +qboolean PM_LockedAnim(int anim) {//anims that can *NEVER* be overridden, regardless - switch ( anim ) + switch (anim) { case BOTH_KYLE_PA_1: case BOTH_KYLE_PA_2: @@ -7014,7 +7897,7 @@ qboolean PM_LockedAnim( int anim ) case BOTH_SCEPTER_START: case BOTH_SCEPTER_HOLD: case BOTH_SCEPTER_STOP: - //grabbed by wampa + //grabbed by wampa case BOTH_GRABBED: //# case BOTH_RELEASED: //# when Wampa drops player, transitions into fall on back case BOTH_HANG_IDLE: //# @@ -7025,9 +7908,9 @@ qboolean PM_LockedAnim( int anim ) return qfalse; } -qboolean PM_SuperBreakLoseAnim( int anim ) +qboolean PM_SuperBreakLoseAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_LK_S_DL_S_SB_1_L: //super break I lost case BOTH_LK_S_DL_T_SB_1_L: //super break I lost @@ -7053,9 +7936,9 @@ qboolean PM_SuperBreakLoseAnim( int anim ) return qfalse; } -qboolean PM_SuperBreakWinAnim( int anim ) +qboolean PM_SuperBreakWinAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_LK_S_DL_S_SB_1_W: //super break I won case BOTH_LK_S_DL_T_SB_1_W: //super break I won @@ -7081,9 +7964,9 @@ qboolean PM_SuperBreakWinAnim( int anim ) return qfalse; } -qboolean PM_SaberLockBreakAnim( int anim ) +qboolean PM_SaberLockBreakAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_BF1BREAK: case BOTH_BF2BREAK: @@ -7125,15 +8008,15 @@ qboolean PM_SaberLockBreakAnim( int anim ) case BOTH_LK_ST_S_S_B_1_W: //normal break I won case BOTH_LK_ST_S_T_B_1_L: //normal break I lost case BOTH_LK_ST_S_T_B_1_W: //normal break I won - return (PM_SuperBreakLoseAnim(anim)||PM_SuperBreakWinAnim(anim)); + return (qboolean)(PM_SuperBreakLoseAnim(anim)||PM_SuperBreakWinAnim(anim)); break; } return qfalse; } -qboolean PM_GetupAnimNoMove( int legsAnim ) +qboolean PM_GetupAnimNoMove(int legsAnim) { - switch( legsAnim ) + switch (legsAnim) { case BOTH_GETUP1: case BOTH_GETUP2: @@ -7155,59 +8038,59 @@ qboolean PM_GetupAnimNoMove( int legsAnim ) return qfalse; } -qboolean PM_KnockDownAnim( int anim ) +qboolean PM_KnockDownAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_KNOCKDOWN1: case BOTH_KNOCKDOWN2: case BOTH_KNOCKDOWN3: case BOTH_KNOCKDOWN4: case BOTH_KNOCKDOWN5: - /* - //special anims: - case BOTH_RELEASED: - case BOTH_LK_DL_ST_T_SB_1_L: - case BOTH_PLAYER_PA_3_FLY: - */ + /* + //special anims: + case BOTH_RELEASED: + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_PLAYER_PA_3_FLY: + */ return qtrue; break; - /* - default: + /* + default: return PM_InGetUp( ps ); break; - */ + */ } return qfalse; } -qboolean PM_KnockDownAnimExtended( int anim ) +qboolean PM_KnockDownAnimExtended(int anim) { - switch ( anim ) + switch (anim) { case BOTH_KNOCKDOWN1: case BOTH_KNOCKDOWN2: case BOTH_KNOCKDOWN3: case BOTH_KNOCKDOWN4: case BOTH_KNOCKDOWN5: - //special anims: + //special anims: case BOTH_RELEASED: case BOTH_LK_DL_ST_T_SB_1_L: case BOTH_PLAYER_PA_3_FLY: return qtrue; break; - /* - default: + /* + default: return PM_InGetUp( ps ); break; - */ + */ } return qfalse; } -qboolean PM_SaberInKata( saberMoveName_t saberMove ) +qboolean PM_SaberInKata(saberMoveName_t saberMove) { - switch ( saberMove ) + switch (saberMove) { case LS_A1_SPECIAL: case LS_A2_SPECIAL: @@ -7221,20 +8104,20 @@ qboolean PM_SaberInKata( saberMoveName_t saberMove ) return qfalse; } -qboolean PM_CanRollFromSoulCal( playerState_t *ps ) +qboolean PM_CanRollFromSoulCal(playerState_t *ps) { - if ( ps->legsAnim == BOTH_A7_SOULCAL + if (ps->legsAnim == BOTH_A7_SOULCAL && ps->legsAnimTimer < 700 - && ps->legsAnimTimer > 250 ) + && ps->legsAnimTimer > 250) { return qtrue; } return qfalse; } -qboolean BG_FullBodyTauntAnim( int anim ) +qboolean BG_FullBodyTauntAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_GESTURE1: case BOTH_DUAL_TAUNT: diff --git a/code/game/bg_panimate.cpp.BACKUP.8780.cpp b/code/game/bg_panimate.cpp.BACKUP.8780.cpp new file mode 100644 index 0000000000..ae4457a533 --- /dev/null +++ b/code/game/bg_panimate.cpp.BACKUP.8780.cpp @@ -0,0 +1,7964 @@ +/* +=========================================================================== +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +#include "common_headers.h" + + +// define GAME_INCLUDE so that g_public.h does not define the +// short, server-visible gclient_t and gentity_t structures, +// because we define the full size ones in this file +#define GAME_INCLUDE + +#include "../qcommon/q_shared.h" +#include "g_shared.h" +#include "bg_local.h" +#include "../cgame/cg_local.h" +#include "anims.h" +#include "Q3_Interface.h" +#include "g_local.h" +#include "wp_saber.h" +#include "g_vehicles.h" + +extern pmove_t *pm; +extern pml_t pml; +extern cvar_t *g_ICARUSDebug; +extern cvar_t *g_timescale; +extern cvar_t *g_synchSplitAnims; +extern cvar_t *g_AnimWarning; +extern cvar_t *g_noFootSlide; +extern cvar_t *g_noFootSlideRunScale; +extern cvar_t *g_noFootSlideWalkScale; +extern cvar_t *g_saberAnimSpeed; +extern cvar_t *g_saberAutoAim; +extern cvar_t *g_speederControlScheme; +extern cvar_t *g_saberNewControlScheme; +extern cvar_t *g_saberNewCombat; + +extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); +extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); +extern qboolean ValidAnimFileIndex ( int index ); +extern qboolean PM_ControlledByPlayer( void ); +extern qboolean PM_DroidMelee( int npc_class ); +extern qboolean PM_PainAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +extern qboolean PM_FlippingAnim( int anim ); +extern qboolean PM_RollingAnim( int anim ); +extern qboolean PM_SwimmingAnim( int anim ); +extern qboolean PM_InKnockDown( playerState_t *ps ); +extern qboolean PM_InRoll( playerState_t *ps ); +extern qboolean PM_DodgeAnim( int anim ); +extern qboolean PM_InSlopeAnim( int anim ); +extern qboolean PM_ForceAnim( int anim ); +extern qboolean PM_InKnockDownOnGround( playerState_t *ps ); +extern qboolean PM_InSpecialJump( int anim ); +extern qboolean PM_RunningAnim( int anim ); +extern qboolean PM_WalkingAnim( int anim ); +extern qboolean PM_SwimmingAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +extern qboolean PM_SaberStanceAnim( int anim ); +extern qboolean PM_SaberDrawPutawayAnim( int anim ); +extern void PM_SetJumped( float height, qboolean force ); +extern qboolean PM_InGetUpNoRoll( playerState_t *ps ); +extern qboolean PM_CrouchAnim( int anim ); +extern qboolean G_TryingKataAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingCartwheel( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingSpecial( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingJumpAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingJumpForwardAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingLungeAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingPullAttack( gentity_t *self, usercmd_t *cmd, qboolean amPulling ); +extern qboolean G_InCinematicSaberAnim( gentity_t *self ); +extern qboolean G_ControlledByPlayer( gentity_t *self ); + +extern int g_crosshairEntNum; + +int PM_AnimLength( int index, animNumber_t anim ); +qboolean PM_LockedAnim( int anim ); +qboolean PM_StandingAnim( int anim ); +qboolean PM_InOnGroundAnim ( playerState_t *ps ); +qboolean PM_SuperBreakWinAnim( int anim ); +qboolean PM_SuperBreakLoseAnim( int anim ); +qboolean PM_LockedAnim( int anim ); +saberMoveName_t PM_SaberFlipOverAttackMove( void ); +qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ); +saberMoveName_t PM_SaberJumpForwardAttackMove( void ); +qboolean PM_CheckJumpForwardAttackMove( void ); +saberMoveName_t PM_SaberBackflipAttackMove( void ); +qboolean PM_CheckBackflipAttackMove( void ); +saberMoveName_t PM_SaberDualJumpAttackMove( void ); +qboolean PM_CheckDualJumpAttackMove( void ); +saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ); +qboolean PM_CheckLungeAttackMove( void ); +// Okay, here lies the much-dreaded Pat-created FSM movement chart... Heretic II strikes again! +// Why am I inflicting this on you? Well, it's better than hardcoded states. +// Ideally this will be replaced with an external file or more sophisticated move-picker +// once the game gets out of prototype stage. + +// Silly, but I'm replacing these macros so they are shorter! +#define AFLAG_IDLE (SETANIM_FLAG_NORMAL) +#define AFLAG_ACTIVE (SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS) +#define AFLAG_WAIT (SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS) +#define AFLAG_FINISH (SETANIM_FLAG_HOLD) + +//FIXME: add the alternate anims for each style? +saberMoveData_t saberMoveData[LS_MOVE_MAX] = {// NB:randomized + // name anim(do all styles?)startQ endQ setanimflag blend, blocking chain_idle chain_attack trailLen + {"None", BOTH_STAND1, Q_R, Q_R, AFLAG_IDLE, 350, BLK_NO, LS_NONE, LS_NONE, 0 }, // LS_NONE = 0, + + // General movements with saber + {"Ready", BOTH_STAND2, Q_R, Q_R, AFLAG_IDLE, 350, BLK_WIDE, LS_READY, LS_S_R2L, 0 }, // LS_READY, + {"Draw", BOTH_STAND1TO2, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_DRAW, + {"Putaway", BOTH_STAND2TO1, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_PUTAWAY, + + // Attacks + //UL2LR + {"TL2BR Att", BOTH_A1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TL2BR, LS_R_TL2BR, 200 }, // LS_A_TL2BR + //SLASH LEFT + {"L2R Att", BOTH_A1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_L2R, LS_R_L2R, 200 }, // LS_A_L2R + //LL2UR + {"BL2TR Att", BOTH_A1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_TIGHT, LS_R_BL2TR, LS_R_BL2TR, 200 }, // LS_A_BL2TR + //LR2UL + {"BR2TL Att", BOTH_A1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_BR2TL, LS_R_BR2TL, 200 }, // LS_A_BR2TL + //SLASH RIGHT + {"R2L Att", BOTH_A1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_R2L, LS_R_R2L, 200 },// LS_A_R2L + //UR2LL + {"TR2BL Att", BOTH_A1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TR2BL, LS_R_TR2BL, 200 }, // LS_A_TR2BL + //SLASH DOWN + {"T2B Att", BOTH_A1_T__B_, Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_T2B, LS_R_T2B, 200 }, // LS_A_T2B + //special attacks + {"Back Stab", BOTH_A2_STABBACK1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACKSTAB + {"Back Att", BOTH_ATTACK_BACK, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK + {"CR Back Att", BOTH_CROUCHATTACKBACK1,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK_CR + {"RollStab", BOTH_ROLL_STAB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_ROLL_STAB + {"Lunge Att", BOTH_LUNGE2_B__T_, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_LUNGE + {"Jump Att", BOTH_FORCELEAP2_T__B_,Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_JUMP_T__B_ + {"Flip Stab", BOTH_JUMPFLIPSTABDOWN,Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_FLIP_STAB + {"Flip Slash", BOTH_JUMPFLIPSLASHDOWN1,Q_L,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R_T_, 200 }, // LS_A_FLIP_SLASH + {"DualJump Atk",BOTH_JUMPATTACK6, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_BL_TR, 200 }, // LS_JUMPATTACK_DUAL + + {"DualJumpAtkL_A",BOTH_ARIAL_LEFT, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TL2BR, 200 }, // LS_JUMPATTACK_ARIAL_LEFT + {"DualJumpAtkR_A",BOTH_ARIAL_RIGHT, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TR2BL, 200 }, // LS_JUMPATTACK_ARIAL_RIGHT + + {"DualJumpAtkL_A",BOTH_CARTWHEEL_LEFT, Q_R,Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TL_BR, 200 }, // LS_JUMPATTACK_CART_LEFT + {"DualJumpAtkR_A",BOTH_CARTWHEEL_RIGHT, Q_R,Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TR_BL, 200 }, // LS_JUMPATTACK_CART_RIGHT + + {"DualJumpAtkLStaff", BOTH_BUTTERFLY_FL1,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_JUMPATTACK_STAFF_LEFT + {"DualJumpAtkRStaff", BOTH_BUTTERFLY_FR1,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_JUMPATTACK_STAFF_RIGHT + + {"ButterflyLeft", BOTH_BUTTERFLY_LEFT,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_BUTTERFLY_LEFT + {"ButterflyRight", BOTH_BUTTERFLY_RIGHT,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_BUTTERFLY_RIGHT + + {"BkFlip Atk", BOTH_JUMPATTACK7, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_BACKFLIP_ATK + {"DualSpinAtk", BOTH_SPINATTACK6, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_DUAL + {"StfSpinAtk", BOTH_SPINATTACK7, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK + {"LngLeapAtk", BOTH_FORCELONGLEAP_ATTACK,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_LEAP_ATTACK + {"SwoopAtkR", BOTH_VS_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_RIGHT + {"SwoopAtkL", BOTH_VS_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_LEFT + {"TauntaunAtkR",BOTH_VT_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_RIGHT + {"TauntaunAtkL",BOTH_VT_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_LEFT + {"StfKickFwd", BOTH_A7_KICK_F, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F + {"StfKickBack", BOTH_A7_KICK_B, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B + {"StfKickRight",BOTH_A7_KICK_R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R + {"StfKickLeft", BOTH_A7_KICK_L, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L + {"StfKickSpin", BOTH_A7_KICK_S, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_S + {"StfKickBkFwd",BOTH_A7_KICK_BF, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_BF + {"StfKickSplit",BOTH_A7_KICK_RL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_RL + {"StfKickFwdAir",BOTH_A7_KICK_F_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F_AIR + {"StfKickBackAir",BOTH_A7_KICK_B_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B_AIR + {"StfKickRightAir",BOTH_A7_KICK_R_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R_AIR + {"StfKickLeftAir",BOTH_A7_KICK_L_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L_AIR + {"StabDown", BOTH_STABDOWN, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN + {"StabDownStf", BOTH_STABDOWN_STAFF,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_STAFF + {"StabDownDual",BOTH_STABDOWN_DUAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_DUAL + {"dualspinprot",BOTH_A6_SABERPROTECT,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_DUAL_SPIN_PROTECT + {"StfSoulCal", BOTH_A7_SOULCAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_STAFF_SOULCAL + {"specialfast", BOTH_A1_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A1_SPECIAL + {"specialmed", BOTH_A2_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A2_SPECIAL + {"specialstr", BOTH_A3_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A3_SPECIAL + {"upsidedwnatk",BOTH_FLIP_ATTACK7, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_UPSIDE_DOWN_ATTACK + {"pullatkstab", BOTH_PULL_IMPALE_STAB,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_STAB + {"pullatkswing",BOTH_PULL_IMPALE_SWING,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_SWING + {"AloraSpinAtk",BOTH_ALORA_SPIN_SLASH,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_ALORA + {"Dual FB Atk", BOTH_A6_FB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_FB + {"Dual LR Atk", BOTH_A6_LR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_LR + {"StfHiltBash", BOTH_A7_HILT, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_HILT_BASH + + //starts + {"TL2BR St", BOTH_S1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TL2BR, LS_A_TL2BR, 200 }, // LS_S_TL2BR + {"L2R St", BOTH_S1_S1__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_L2R, LS_A_L2R, 200 }, // LS_S_L2R + {"BL2TR St", BOTH_S1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BL2TR, LS_A_BL2TR, 200 }, // LS_S_BL2TR + {"BR2TL St", BOTH_S1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BR2TL, LS_A_BR2TL, 200 }, // LS_S_BR2TL + {"R2L St", BOTH_S1_S1__R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_R2L, LS_A_R2L, 200 }, // LS_S_R2L + {"TR2BL St", BOTH_S1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TR2BL, LS_A_TR2BL, 200 }, // LS_S_TR2BL + {"T2B St", BOTH_S1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_T2B, LS_A_T2B, 200 }, // LS_S_T2B + + //returns + {"TL2BR Ret", BOTH_R1_BR_S1, Q_BR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TL2BR + {"L2R Ret", BOTH_R1__R_S1, Q_R, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_L2R + {"BL2TR Ret", BOTH_R1_TR_S1, Q_TR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BL2TR + {"BR2TL Ret", BOTH_R1_TL_S1, Q_TL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BR2TL + {"R2L Ret", BOTH_R1__L_S1, Q_L, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_R2L + {"TR2BL Ret", BOTH_R1_BL_S1, Q_BL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TR2BL + {"T2B Ret", BOTH_R1_B__S1, Q_B, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_T2B + + //Transitions + {"BR2R Trans", BOTH_T1_BR__R, Q_BR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc bottom right to right + {"BR2TR Trans", BOTH_T1_BR_TR, Q_BR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc bottom right to top right (use: BOTH_T1_TR_BR) + {"BR2T Trans", BOTH_T1_BR_T_, Q_BR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom right to top (use: BOTH_T1_T__BR) + {"BR2TL Trans", BOTH_T1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast weak spin bottom right to top left + {"BR2L Trans", BOTH_T1_BR__L, Q_BR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin bottom right to left + {"BR2BL Trans", BOTH_T1_BR_BL, Q_BR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin bottom right to bottom left + {"R2BR Trans", BOTH_T1__R_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc right to bottom right (use: BOTH_T1_BR__R) + {"R2TR Trans", BOTH_T1__R_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc right to top right + {"R2T Trans", BOTH_T1__R_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast ar right to top (use: BOTH_T1_T___R) + {"R2TL Trans", BOTH_T1__R_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc right to top left + {"R2L Trans", BOTH_T1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin right to left + {"R2BL Trans", BOTH_T1__R_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin right to bottom left + {"TR2BR Trans", BOTH_T1_TR_BR, Q_TR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top right to bottom right + {"TR2R Trans", BOTH_T1_TR__R, Q_TR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top right to right (use: BOTH_T1__R_TR) + {"TR2T Trans", BOTH_T1_TR_T_, Q_TR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top right to top (use: BOTH_T1_T__TR) + {"TR2TL Trans", BOTH_T1_TR_TL, Q_TR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top right to top left + {"TR2L Trans", BOTH_T1_TR__L, Q_TR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top right to left + {"TR2BL Trans", BOTH_T1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin top right to bottom left + {"T2BR Trans", BOTH_T1_T__BR, Q_T, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top to bottom right + {"T2R Trans", BOTH_T1_T___R, Q_T, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top to right + {"T2TR Trans", BOTH_T1_T__TR, Q_T, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top to top right + {"T2TL Trans", BOTH_T1_T__TL, Q_T, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top to top left + {"T2L Trans", BOTH_T1_T___L, Q_T, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top to left + {"T2BL Trans", BOTH_T1_T__BL, Q_T, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top to bottom left + {"TL2BR Trans", BOTH_T1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin top left to bottom right + {"TL2R Trans", BOTH_T1_TL__R, Q_TL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top left to right (use: BOTH_T1__R_TL) + {"TL2TR Trans", BOTH_T1_TL_TR, Q_TL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top left to top right (use: BOTH_T1_TR_TL) + {"TL2T Trans", BOTH_T1_TL_T_, Q_TL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top left to top (use: BOTH_T1_T__TL) + {"TL2L Trans", BOTH_T1_TL__L, Q_TL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top left to left (use: BOTH_T1__L_TL) + {"TL2BL Trans", BOTH_T1_TL_BL, Q_TL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top left to bottom left + {"L2BR Trans", BOTH_T1__L_BR, Q_L, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin left to bottom right + {"L2R Trans", BOTH_T1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin left to right + {"L2TR Trans", BOTH_T1__L_TR, Q_L, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc left to top right (use: BOTH_T1_TR__L) + {"L2T Trans", BOTH_T1__L_T_, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc left to top (use: BOTH_T1_T___L) + {"L2TL Trans", BOTH_T1__L_TL, Q_L, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc left to top left + {"L2BL Trans", BOTH_T1__L_BL, Q_L, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc left to bottom left (use: BOTH_T1_BL__L) + {"BL2BR Trans", BOTH_T1_BL_BR, Q_BL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin bottom left to bottom right + {"BL2R Trans", BOTH_T1_BL__R, Q_BL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin bottom left to right + {"BL2TR Trans", BOTH_T1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast weak spin bottom left to top right + {"BL2T Trans", BOTH_T1_BL_T_, Q_BL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom left to top (use: BOTH_T1_T__BL) + {"BL2TL Trans", BOTH_T1_BL_TL, Q_BL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc bottom left to top left (use: BOTH_T1_TL_BL) + {"BL2L Trans", BOTH_T1_BL__L, Q_BL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc bottom left to left + + //Bounces + {"Bounce BR", BOTH_B1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + {"Bounce R", BOTH_B1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + {"Bounce TR", BOTH_B1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + {"Bounce T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + {"Bounce TL", BOTH_B1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + {"Bounce L", BOTH_B1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + {"Bounce BL", BOTH_B1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + + //Deflected attacks (like bounces, but slide off enemy saber, not straight back) + {"Deflect BR", BOTH_D1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + {"Deflect R", BOTH_D1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + {"Deflect TR", BOTH_D1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + {"Deflect T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + {"Deflect TL", BOTH_D1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + {"Deflect L", BOTH_D1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + {"Deflect BL", BOTH_D1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + {"Deflect B", BOTH_D1_B____, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + + //Reflected attacks + {"Reflected BR",BOTH_V1_BR_S1, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BR + {"Reflected R", BOTH_V1__R_S1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__R + {"Reflected TR",BOTH_V1_TR_S1, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TR + {"Reflected T", BOTH_V1_T__S1, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_T_ + {"Reflected TL",BOTH_V1_TL_S1, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TL + {"Reflected L", BOTH_V1__L_S1, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__L + {"Reflected BL",BOTH_V1_BL_S1, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BL + {"Reflected B", BOTH_V1_B__S1, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_B_ + + // Broken parries + {"BParry Top", BOTH_H1_S1_T_, Q_T, Q_B, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UP, + {"BParry UR", BOTH_H1_S1_TR, Q_TR, Q_BL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UR, + {"BParry UL", BOTH_H1_S1_TL, Q_TL, Q_BR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UL, + {"BParry LR", BOTH_H1_S1_BL, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR, + {"BParry Bot", BOTH_H1_S1_B_, Q_B, Q_T, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + {"BParry LL", BOTH_H1_S1_BR, Q_BR, Q_TL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + + // Knockaways + {"Knock Top", BOTH_K1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_T__BR, 150 }, // LS_PARRY_UP, + {"Knock UR", BOTH_K1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_TR__R, 150 }, // LS_PARRY_UR, + {"Knock UL", BOTH_K1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_T1_TL__L, 150 }, // LS_PARRY_UL, + {"Knock LR", BOTH_K1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_T1_BL_TL, 150 }, // LS_PARRY_LR, + {"Knock LL", BOTH_K1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_T1_BR_TR, 150 }, // LS_PARRY_LL + + // Parry + {"Parry Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 150 }, // LS_PARRY_UP, + {"Parry UR", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 150 }, // LS_PARRY_UR, + {"Parry UL", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 150 }, // LS_PARRY_UL, + {"Parry LR", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 150 }, // LS_PARRY_LR, + {"Parry LL", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 150 }, // LS_PARRY_LL + + // Reflecting a missile + {"Reflect Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 300 }, // LS_PARRY_UP, + {"Reflect UR", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 300 }, // LS_PARRY_UR, + {"Reflect UL", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 300 }, // LS_PARRY_UL, + {"Reflect LR", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 300 }, // LS_PARRY_LR + {"Reflect LL", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 300 }, // LS_PARRY_LL, +}; + + +saberMoveName_t transitionMove[Q_NUM_QUADS][Q_NUM_QUADS] = +{ + { + LS_NONE, //Can't transition to same pos! + LS_T1_BR__R,//40 + LS_T1_BR_TR, + LS_T1_BR_T_, + LS_T1_BR_TL, + LS_T1_BR__L, + LS_T1_BR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1__R_BR,//46 + LS_NONE, //Can't transition to same pos! + LS_T1__R_TR, + LS_T1__R_T_, + LS_T1__R_TL, + LS_T1__R__L, + LS_T1__R_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_TR_BR,//52 + LS_T1_TR__R, + LS_NONE, //Can't transition to same pos! + LS_T1_TR_T_, + LS_T1_TR_TL, + LS_T1_TR__L, + LS_T1_TR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_T__BR,//58 + LS_T1_T___R, + LS_T1_T__TR, + LS_NONE, //Can't transition to same pos! + LS_T1_T__TL, + LS_T1_T___L, + LS_T1_T__BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_TL_BR,//64 + LS_T1_TL__R, + LS_T1_TL_TR, + LS_T1_TL_T_, + LS_NONE, //Can't transition to same pos! + LS_T1_TL__L, + LS_T1_TL_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1__L_BR,//70 + LS_T1__L__R, + LS_T1__L_TR, + LS_T1__L_T_, + LS_T1__L_TL, + LS_NONE, //Can't transition to same pos! + LS_T1__L_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_BL_BR,//76 + LS_T1_BL__R, + LS_T1_BL_TR, + LS_T1_BL_T_, + LS_T1_BL_TL, + LS_T1_BL__L, + LS_NONE, //Can't transition to same pos! + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_BL_BR,//NOTE: there are no transitions from bottom, so re-use the bottom right transitions + LS_T1_BR__R, + LS_T1_BR_TR, + LS_T1_BR_T_, + LS_T1_BR_TL, + LS_T1_BR__L, + LS_T1_BR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + } +}; + +void PM_VelocityForSaberMove( playerState_t *ps, vec3_t throwDir ) +{ + vec3_t vForward = { 0.0f }, vRight = { 0.0f }, vUp = { 0.0f }, startQ = { 0.0f }, endQ = { 0.0f }; + + AngleVectors( ps->viewangles, vForward, vRight, vUp ); + + switch ( saberMoveData[ps->saberMove].startQuad ) + { + case Q_BR: + VectorScale( vRight, 1, startQ ); + VectorMA( startQ, -1, vUp, startQ ); + break; + case Q_R: + VectorScale( vRight, 2, startQ ); + break; + case Q_TR: + VectorScale( vRight, 1, startQ ); + VectorMA( startQ, 1, vUp, startQ ); + break; + case Q_T: + VectorScale( vUp, 2, startQ ); + break; + case Q_TL: + VectorScale( vRight, -1, startQ ); + VectorMA( startQ, 1, vUp, startQ ); + break; + case Q_L: + VectorScale( vRight, -2, startQ ); + break; + case Q_BL: + VectorScale( vRight, -1, startQ ); + VectorMA( startQ, -1, vUp, startQ ); + break; + case Q_B: + VectorScale( vUp, -2, startQ ); + break; + } + switch ( saberMoveData[ps->saberMove].endQuad ) + { + case Q_BR: + VectorScale( vRight, 1, endQ ); + VectorMA( endQ, -1, vUp, endQ ); + break; + case Q_R: + VectorScale( vRight, 2, endQ ); + break; + case Q_TR: + VectorScale( vRight, 1, endQ ); + VectorMA( endQ, 1, vUp, endQ ); + break; + case Q_T: + VectorScale( vUp, 2, endQ ); + break; + case Q_TL: + VectorScale( vRight, -1, endQ ); + VectorMA( endQ, 1, vUp, endQ ); + break; + case Q_L: + VectorScale( vRight, -2, endQ ); + break; + case Q_BL: + VectorScale( vRight, -1, endQ ); + VectorMA( endQ, -1, vUp, endQ ); + break; + case Q_B: + VectorScale( vUp, -2, endQ ); + break; + } + VectorMA( endQ, 2, vForward, endQ ); + VectorScale( throwDir, 125, throwDir );//FIXME: pass in the throw strength? + VectorSubtract( endQ, startQ, throwDir ); +} + +qboolean PM_VelocityForBlockedMove( playerState_t *ps, vec3_t throwDir ) +{ + vec3_t vForward, vRight, vUp; + AngleVectors( ps->viewangles, vForward, vRight, vUp ); + switch ( ps->saberBlocked ) + { + case BLOCKED_UPPER_RIGHT: + VectorScale( vRight, 1, throwDir ); + VectorMA( throwDir, 1, vUp, throwDir ); + break; + case BLOCKED_UPPER_LEFT: + VectorScale( vRight, -1, throwDir ); + VectorMA( throwDir, 1, vUp, throwDir ); + break; + case BLOCKED_LOWER_RIGHT: + VectorScale( vRight, 1, throwDir ); + VectorMA( throwDir, -1, vUp, throwDir ); + break; + case BLOCKED_LOWER_LEFT: + VectorScale( vRight, -1, throwDir ); + VectorMA( throwDir, -1, vUp, throwDir ); + break; + case BLOCKED_TOP: + VectorScale( vUp, 2, throwDir ); + break; + default: + return qfalse; + break; + } + VectorMA( throwDir, 2, vForward, throwDir ); + VectorScale( throwDir, 250, throwDir );//FIXME: pass in the throw strength? + return qtrue; +} + +int PM_AnimLevelForSaberAnim( int anim ) +{ + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ ) + { + return FORCE_LEVEL_1; + } + if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ ) + { + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ ) + { + return FORCE_LEVEL_3; + } + if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ ) + {//desann + return FORCE_LEVEL_4; + } + if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ ) + {//tavion + return FORCE_LEVEL_5; + } + if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ ) + {//dual + return SS_DUAL; + } + if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ ) + {//staff + return SS_STAFF; + } + return FORCE_LEVEL_0; +} + +int PM_PowerLevelForSaberAnim(playerState_t *ps, int saberNum) +{ + if (g_saberNewCombat->integer) + { //new code + int anim = ps->torsoAnim; + int animTimeElapsed = PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim) - ps->torsoAnimTimer; + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____) + { + //FIXME: these two need their own style + if (ps->saber[0].type == SABER_LANCE) + { + return FORCE_LEVEL_4; + } + else if (ps->saber[0].type == SABER_TRIDENT) + { + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_1; + } + if (anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____) + { + return FORCE_LEVEL_3; + } +<<<<<<< HEAD + if (anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____) + { + return FORCE_LEVEL_5; +======= + return FORCE_LEVEL_1; + } + if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ ) + { + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ ) + { + return FORCE_LEVEL_3; + } + if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ ) + {//desann + return FORCE_LEVEL_4; + } + if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ ) + {//tavion + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ ) + {//dual + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ ) + {//staff + return FORCE_LEVEL_2; + } + if ( ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR ) + || ( anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR ) + || ( anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR ) ) + {//parries + switch ( ps->saberAnimLevel ) + { + case SS_STRONG: + case SS_DESANN: + return FORCE_LEVEL_3; + break; + case SS_TAVION: + case SS_STAFF: + case SS_DUAL: + case SS_MEDIUM: + return FORCE_LEVEL_2; + break; + case SS_FAST: + return FORCE_LEVEL_1; + break; + default: + return FORCE_LEVEL_0; + break; + } + } + if ( ( anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR ) + || ( anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR ) + || ( anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR ) ) + {//knockaways + return FORCE_LEVEL_3; + } + if ( ( anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1 ) + || ( anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6 ) + || ( anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7 ) ) + {//knocked-away attacks + return FORCE_LEVEL_1; + } + if ( ( anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR ) + || ( anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR ) + || ( anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR ) ) + {//broken parries + return FORCE_LEVEL_0; + } + switch ( anim ) + { + case BOTH_A2_STABBACK1: + if ( ps->torsoAnimTimer < 450 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 400 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ATTACK_BACK: + if ( ps->torsoAnimTimer < 500 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_CROUCHATTACKBACK1: + if ( ps->torsoAnimTimer < 800 ) + {//end of anim + return FORCE_LEVEL_0; +>>>>>>> 38e3edc40da09b9c20f585a3b939ff493be8bf50 + } + if (anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____) + {//desann + return FORCE_LEVEL_5; + } + if (anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____) + {//tavion + return FORCE_LEVEL_2; + } + if (anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____) + {//dual + return FORCE_LEVEL_2; + } + if (anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____) + {//staff + return FORCE_LEVEL_4; + } + if ((anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR) + || (anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR) + || (anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR)) + {//parries + switch (ps->saberAnimLevel) + { + case SS_DESANN: + case SS_STRONG: + return FORCE_LEVEL_5; + break; + case SS_STAFF: + return FORCE_LEVEL_4; + break; + case SS_MEDIUM: + return FORCE_LEVEL_3; + break; + case SS_DUAL: + case SS_TAVION: + return FORCE_LEVEL_2; + break; + case SS_FAST: + return FORCE_LEVEL_1; + break; + default: + return FORCE_LEVEL_0; + break; + } + } + if ((anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR) + || (anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR) + || (anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR)) + {//knockaways + return FORCE_LEVEL_3; + } + if ((anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1) + || (anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6) + || (anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7)) + {//knocked-away attacks + return FORCE_LEVEL_1; + } + if ((anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR) + || (anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR) + || (anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR)) + {//broken parries + return FORCE_LEVEL_0; + } + switch (anim) + { + case BOTH_A2_STABBACK1: + if (ps->torsoAnimTimer < 450) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_ATTACK_BACK: + if (ps->torsoAnimTimer < 500) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_CROUCHATTACKBACK1: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + //FIXME: break up? + return FORCE_LEVEL_5; + break; + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + //FIXME: break up? + return FORCE_LEVEL_5; + break; + case BOTH_K1_S1_T_: //# knockaway saber top + case BOTH_K1_S1_TR: //# knockaway saber top right + case BOTH_K1_S1_TL: //# knockaway saber top left + case BOTH_K1_S1_BL: //# knockaway saber bottom left + case BOTH_K1_S1_B_: //# knockaway saber bottom + case BOTH_K1_S1_BR: //# knockaway saber bottom right + //FIXME: break up? + return FORCE_LEVEL_5; + break; + case BOTH_LUNGE2_B__T_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_FORCELEAP2_T__B_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + return FORCE_LEVEL_5;//??? + break; + case BOTH_JUMPFLIPSLASHDOWN1: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_JUMPFLIPSTABDOWN: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed <= 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_JUMPATTACK6: + /* + if (pm->ps) + { + if ( ( pm->ps->legsAnimTimer >= 1450 +<<<<<<< HEAD + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) + ||(pm->ps->legsAnimTimer >= 400 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) +======= + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) + ||(pm->ps->legsAnimTimer >= 400 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) +>>>>>>> 38e3edc40da09b9c20f585a3b939ff493be8bf50 + {//pretty much sideways + return FORCE_LEVEL_5; + } + } + */ + if ((ps->torsoAnimTimer >= 1450 + && animTimeElapsed >= 400) + || (ps->torsoAnimTimer >= 400 + && animTimeElapsed >= 1100)) + {//pretty much sideways + return FORCE_LEVEL_5; + } + return FORCE_LEVEL_0; +<<<<<<< HEAD + break; + case BOTH_JUMPATTACK7: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_SPINATTACK6: + if (animTimeElapsed <= 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_SPINATTACK7: + if (ps->torsoAnimTimer <= 500) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 500) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_FORCELONGLEAP_ATTACK: + if (animTimeElapsed <= 200) + {//1st four frames of anim + return FORCE_LEVEL_5; + } + break; + /* + case BOTH_A7_KICK_F://these kicks attack, too + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + //FIXME: break up + return FORCE_LEVEL_5; + break; + */ + case BOTH_STABDOWN: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_5; + } + break; + case BOTH_STABDOWN_STAFF: + if (ps->torsoAnimTimer <= 850) + {//end of anim + return FORCE_LEVEL_5; + } + break; + case BOTH_STABDOWN_DUAL: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_5; + } + break; + case BOTH_A6_SABERPROTECT: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A7_SOULCAL: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 600) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A1_SPECIAL: + if (ps->torsoAnimTimer < 600) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A2_SPECIAL: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A3_SPECIAL: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_FLIP_ATTACK7: + return FORCE_LEVEL_5; + break; + case BOTH_PULL_IMPALE_STAB: + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_PULL_IMPALE_SWING: + if (ps->torsoAnimTimer < 500)//750 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650)//600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_ALORA_SPIN_SLASH: + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A6_FB: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A6_LR: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A7_HILT: +======= + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_LR: + if ( ps->torsoAnimTimer < 250 ) + {//end of anim +>>>>>>> 38e3edc40da09b9c20f585a3b939ff493be8bf50 + return FORCE_LEVEL_0; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_LK_S_DL_T_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_S_SB_1_W: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + if (ps->torsoAnimTimer < 150) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_T_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + if (animTimeElapsed < 1000) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_ST_T_SB_1_W: + if (ps->torsoAnimTimer < 950) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_S_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 450) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_T_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_S_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_T_SB_1_W: + //special suberbreak - doesn't kill, just kicks them backwards + return FORCE_LEVEL_0; + break; + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 350) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + return FORCE_LEVEL_5; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_HANG_ATTACK: + //FIME: break up + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + else + {//sweet spot + return FORCE_LEVEL_5; + } + break; + case BOTH_ROLL_STAB: + if (animTimeElapsed > 400) + {//end of anim + return FORCE_LEVEL_0; + } + else + { + return FORCE_LEVEL_5; + } + break; + } + return FORCE_LEVEL_0; + } + else + { //old code + int anim = ps->torsoAnim; + int animTimeElapsed = PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim) - ps->torsoAnimTimer; + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____) + { + //FIXME: these two need their own style + if (ps->saber[0].type == SABER_LANCE) + { + return FORCE_LEVEL_4; + } + else if (ps->saber[0].type == SABER_TRIDENT) + { + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_1; + } + if (anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____) + { + return FORCE_LEVEL_3; + } + if (anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____) + { + return FORCE_LEVEL_5; + } + if (anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____) + {//desann + return FORCE_LEVEL_5; + } + if (anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____) + {//tavion + return FORCE_LEVEL_2; + } + if (anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____) + {//dual + return FORCE_LEVEL_2; + } + if (anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____) + {//staff + return FORCE_LEVEL_4; + } + if ((anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR) + || (anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR) + || (anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR)) + {//parries + switch (ps->saberAnimLevel) + { + case SS_STRONG: + case SS_DESANN: + return FORCE_LEVEL_3; + break; + case SS_TAVION: + case SS_STAFF: + case SS_DUAL: + case SS_MEDIUM: + return FORCE_LEVEL_2; + break; + case SS_FAST: + return FORCE_LEVEL_1; + break; + default: + return FORCE_LEVEL_0; + break; + } + } + if ((anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR) + || (anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR) + || (anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR)) + {//knockaways + return FORCE_LEVEL_3; + } + if ((anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1) + || (anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6) + || (anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7)) + {//knocked-away attacks + return FORCE_LEVEL_1; + } + if ((anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR) + || (anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR) + || (anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR)) + {//broken parries + return FORCE_LEVEL_0; + } + switch (anim) + { + case BOTH_A2_STABBACK1: + if (ps->torsoAnimTimer < 450) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ATTACK_BACK: + if (ps->torsoAnimTimer < 500) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_CROUCHATTACKBACK1: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_K1_S1_T_: //# knockaway saber top + case BOTH_K1_S1_TR: //# knockaway saber top right + case BOTH_K1_S1_TL: //# knockaway saber top left + case BOTH_K1_S1_BL: //# knockaway saber bottom left + case BOTH_K1_S1_B_: //# knockaway saber bottom + case BOTH_K1_S1_BR: //# knockaway saber bottom right + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_LUNGE2_B__T_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELEAP2_T__B_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + return FORCE_LEVEL_3;//??? + break; + case BOTH_JUMPFLIPSLASHDOWN1: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPFLIPSTABDOWN: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed <= 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPATTACK6: + /* + if (pm->ps) + { + if ( ( pm->ps->legsAnimTimer >= 1450 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) + ||(pm->ps->legsAnimTimer >= 400 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) + {//pretty much sideways + return FORCE_LEVEL_3; + } + } + */ + if ((ps->torsoAnimTimer >= 1450 + && animTimeElapsed >= 400) + || (ps->torsoAnimTimer >= 400 + && animTimeElapsed >= 1100)) + {//pretty much sideways + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_0; + break; + case BOTH_JUMPATTACK7: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK6: + if (animTimeElapsed <= 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK7: + if (ps->torsoAnimTimer <= 500) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 500) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELONGLEAP_ATTACK: + if (animTimeElapsed <= 200) + {//1st four frames of anim + return FORCE_LEVEL_3; + } + break; + /* + case BOTH_A7_KICK_F://these kicks attack, too + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + //FIXME: break up + return FORCE_LEVEL_3; + break; + */ + case BOTH_STABDOWN: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_STAFF: + if (ps->torsoAnimTimer <= 850) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_DUAL: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_A6_SABERPROTECT: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_SOULCAL: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 600) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A1_SPECIAL: + if (ps->torsoAnimTimer < 600) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A2_SPECIAL: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A3_SPECIAL: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FLIP_ATTACK7: + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_STAB: + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_SWING: + if (ps->torsoAnimTimer < 500)//750 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650)//600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ALORA_SPIN_SLASH: + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_FB: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_LR: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_HILT: + return FORCE_LEVEL_0; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_LK_S_DL_T_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_S_SB_1_W: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + if (ps->torsoAnimTimer < 150) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_T_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + if (animTimeElapsed < 1000) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_ST_T_SB_1_W: + if (ps->torsoAnimTimer < 950) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_S_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 450) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_T_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_S_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_T_SB_1_W: + //special suberbreak - doesn't kill, just kicks them backwards + return FORCE_LEVEL_0; + break; + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 350) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + return FORCE_LEVEL_5; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_HANG_ATTACK: + //FIME: break up + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + else + {//sweet spot + return FORCE_LEVEL_5; + } + break; + case BOTH_ROLL_STAB: + if (animTimeElapsed > 400) + {//end of anim + return FORCE_LEVEL_0; + } + else + { + return FORCE_LEVEL_3; + } + break; + } + return FORCE_LEVEL_0; + } +} + +qboolean PM_InAnimForSaberMove( int anim, int saberMove ) +{ + switch ( anim ) + {//special case anims + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qtrue; + } + if ( PM_SaberDrawPutawayAnim( anim ) ) + { + if ( saberMove == LS_DRAW || saberMove == LS_PUTAWAY ) + { + return qtrue; + } + return qfalse; + } + else if ( PM_SaberStanceAnim( anim ) ) + { + if ( saberMove == LS_READY ) + { + return qtrue; + } + return qfalse; + } + int animLevel = PM_AnimLevelForSaberAnim( anim ); + if ( animLevel <= 0 ) + {//NOTE: this will always return false for the ready poses and putaway/draw... + return qfalse; + } + //drop the anim to the first level and start the checks there + anim -= (animLevel-FORCE_LEVEL_1)*SABER_ANIM_GROUP_SIZE; + //check level 1 + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 2 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 3 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 4 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 5 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + if ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_H1_S1_BR ) + {//parries, knockaways and broken parries + return (anim==saberMoveData[saberMove].animToUse); + } + return qfalse; +} + +qboolean PM_SaberInIdle( int move ) +{ + switch ( move ) + { + case LS_NONE: + case LS_READY: + case LS_DRAW: + case LS_PUTAWAY: + return qtrue; + break; + } + return qfalse; +} +qboolean PM_SaberInSpecialAttack( int anim ) +{ + switch ( anim ) + { + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberInAttackPure( int move ) +{ + if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInAttack( int move ) +{ + if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + { + return qtrue; + } + switch ( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_ARIAL_LEFT: + case LS_JUMPATTACK_ARIAL_RIGHT: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qtrue; + break; + } + return qfalse; +} +qboolean PM_SaberInTransition( int move ) +{ + if ( move >= LS_T1_BR__R && move <= LS_T1_BL__L ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInStart( int move ) +{ + if ( move >= LS_S_TL2BR && move <= LS_S_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInReturn( int move ) +{ + if ( move >= LS_R_TL2BR && move <= LS_R_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInTransitionAny( int move ) +{ + if ( PM_SaberInStart( move ) ) + { + return qtrue; + } + else if ( PM_SaberInTransition( move ) ) + { + return qtrue; + } + else if ( PM_SaberInReturn( move ) ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInBounce( int move ) +{ + if ( move >= LS_B1_BR && move <= LS_B1_BL ) + { + return qtrue; + } + if ( move >= LS_D1_BR && move <= LS_D1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInBrokenParry( int move ) +{ + if ( move >= LS_V1_BR && move <= LS_V1_B_ ) + { + return qtrue; + } + if ( move >= LS_H1_T_ && move <= LS_H1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInDeflect( int move ) +{ + if ( move >= LS_D1_BR && move <= LS_D1_B_ ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInParry( int move ) +{ + if ( move >= LS_PARRY_UP && move <= LS_PARRY_LL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInKnockaway( int move ) +{ + if ( move >= LS_K1_T_ && move <= LS_K1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInReflect( int move ) +{ + if ( move >= LS_REFLECT_UP && move <= LS_REFLECT_LL ) + { + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberInSpecial( int move ) +{ + switch( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_ARIAL_LEFT: + case LS_JUMPATTACK_ARIAL_RIGHT: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qtrue; + } + return qfalse; +} + +qboolean PM_KickMove( int move ) +{ + switch( move ) + { + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_HILT_BASH: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberCanInterruptMove( int move, int anim ) +{ + if ( PM_InAnimForSaberMove( anim, move ) ) + { + switch( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qfalse; + } + + if ( PM_SaberInAttackPure( move ) ) + { + return qfalse; + } + if ( PM_SaberInStart( move ) ) + { + return qfalse; + } + if ( PM_SaberInTransition( move ) ) + { + return qfalse; + } + if ( PM_SaberInBounce( move ) ) + { + return qfalse; + } + if ( PM_SaberInBrokenParry( move ) ) + { + return qfalse; + } + if ( PM_SaberInDeflect( move ) ) + { + return qfalse; + } + if ( PM_SaberInParry( move ) ) + { + return qfalse; + } + if ( PM_SaberInKnockaway( move ) ) + { + return qfalse; + } + if ( PM_SaberInReflect( move ) ) + { + return qfalse; + } + } + switch ( anim ) + { + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qfalse; + } + return qtrue; +} + +saberMoveName_t PM_BrokenParryForAttack( int move ) +{ + //Our attack was knocked away by a knockaway parry + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( saberMoveData[move].startQuad ) + { + case Q_B: + return LS_V1_B_; + break; + case Q_BR: + return LS_V1_BR; + break; + case Q_R: + return LS_V1__R; + break; + case Q_TR: + return LS_V1_TR; + break; + case Q_T: + return LS_V1_T_; + break; + case Q_TL: + return LS_V1_TL; + break; + case Q_L: + return LS_V1__L; + break; + case Q_BL: + return LS_V1_BL; + break; + } + return LS_NONE; +} + +saberMoveName_t PM_BrokenParryForParry( int move ) +{ + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( move ) + { + case LS_PARRY_UP: + //Hmm... since we don't know what dir the hit came from, randomly pick knock down or knock back + if ( Q_irand( 0, 1 ) ) + { + return LS_H1_B_; + } + else + { + return LS_H1_T_; + } + break; + case LS_PARRY_UR: + return LS_H1_TR; + break; + case LS_PARRY_UL: + return LS_H1_TL; + break; + case LS_PARRY_LR: + return LS_H1_BR; + break; + case LS_PARRY_LL: + return LS_H1_BL; + break; + case LS_READY: + return LS_H1_B_;//??? + break; + } + return LS_NONE; +} + +saberMoveName_t PM_KnockawayForParry( int move ) +{ + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( move ) + { + case BLOCKED_TOP://LS_PARRY_UP: + return LS_K1_T_;//push up + break; + case BLOCKED_UPPER_RIGHT://LS_PARRY_UR: + default://case LS_READY: + return LS_K1_TR;//push up, slightly to right + break; + case BLOCKED_UPPER_LEFT://LS_PARRY_UL: + return LS_K1_TL;//push up and to left + break; + case BLOCKED_LOWER_RIGHT://LS_PARRY_LR: + return LS_K1_BR;//push down and to left + break; + case BLOCKED_LOWER_LEFT://LS_PARRY_LL: + return LS_K1_BL;//push down and to right + break; + } + //return LS_NONE; +} + +saberMoveName_t PM_SaberBounceForAttack( int move ) +{ + switch ( saberMoveData[move].startQuad ) + { + case Q_B: + case Q_BR: + return LS_B1_BR; + break; + case Q_R: + return LS_B1__R; + break; + case Q_TR: + return LS_B1_TR; + break; + case Q_T: + return LS_B1_T_; + break; + case Q_TL: + return LS_B1_TL; + break; + case Q_L: + return LS_B1__L; + break; + case Q_BL: + return LS_B1_BL; + break; + } + return LS_NONE; +} + +saberMoveName_t PM_AttackMoveForQuad( int quad ) +{ + switch ( quad ) + { + case Q_B: + case Q_BR: + return LS_A_BR2TL; + break; + case Q_R: + return LS_A_R2L; + break; + case Q_TR: + return LS_A_TR2BL; + break; + case Q_T: + return LS_A_T2B; + break; + case Q_TL: + return LS_A_TL2BR; + break; + case Q_L: + return LS_A_L2R; + break; + case Q_BL: + return LS_A_BL2TR; + break; + } + return LS_NONE; +} + +int saberMoveTransitionAngle[Q_NUM_QUADS][Q_NUM_QUADS] = +{ + { + 0,//Q_BR,Q_BR, + 45,//Q_BR,Q_R, + 90,//Q_BR,Q_TR, + 135,//Q_BR,Q_T, + 180,//Q_BR,Q_TL, + 215,//Q_BR,Q_L, + 270,//Q_BR,Q_BL, + 45,//Q_BR,Q_B, + }, + { + 45,//Q_R,Q_BR, + 0,//Q_R,Q_R, + 45,//Q_R,Q_TR, + 90,//Q_R,Q_T, + 135,//Q_R,Q_TL, + 180,//Q_R,Q_L, + 215,//Q_R,Q_BL, + 90,//Q_R,Q_B, + }, + { + 90,//Q_TR,Q_BR, + 45,//Q_TR,Q_R, + 0,//Q_TR,Q_TR, + 45,//Q_TR,Q_T, + 90,//Q_TR,Q_TL, + 135,//Q_TR,Q_L, + 180,//Q_TR,Q_BL, + 135,//Q_TR,Q_B, + }, + { + 135,//Q_T,Q_BR, + 90,//Q_T,Q_R, + 45,//Q_T,Q_TR, + 0,//Q_T,Q_T, + 45,//Q_T,Q_TL, + 90,//Q_T,Q_L, + 135,//Q_T,Q_BL, + 180,//Q_T,Q_B, + }, + { + 180,//Q_TL,Q_BR, + 135,//Q_TL,Q_R, + 90,//Q_TL,Q_TR, + 45,//Q_TL,Q_T, + 0,//Q_TL,Q_TL, + 45,//Q_TL,Q_L, + 90,//Q_TL,Q_BL, + 135,//Q_TL,Q_B, + }, + { + 215,//Q_L,Q_BR, + 180,//Q_L,Q_R, + 135,//Q_L,Q_TR, + 90,//Q_L,Q_T, + 45,//Q_L,Q_TL, + 0,//Q_L,Q_L, + 45,//Q_L,Q_BL, + 90,//Q_L,Q_B, + }, + { + 270,//Q_BL,Q_BR, + 215,//Q_BL,Q_R, + 180,//Q_BL,Q_TR, + 135,//Q_BL,Q_T, + 90,//Q_BL,Q_TL, + 45,//Q_BL,Q_L, + 0,//Q_BL,Q_BL, + 45,//Q_BL,Q_B, + }, + { + 45,//Q_B,Q_BR, + 90,//Q_B,Q_R, + 135,//Q_B,Q_TR, + 180,//Q_B,Q_T, + 135,//Q_B,Q_TL, + 90,//Q_B,Q_L, + 45,//Q_B,Q_BL, + 0//Q_B,Q_B, + } +}; + +int PM_SaberAttackChainAngle( int move1, int move2 ) +{ + if ( move1 == -1 || move2 == -1 ) + { + return -1; + } + return saberMoveTransitionAngle[saberMoveData[move1].endQuad][saberMoveData[move2].startQuad]; +} + +qboolean PM_SaberKataDone( int curmove = LS_NONE, int newmove = LS_NONE ) +{ + if ( pm->ps->forceRageRecoveryTime > level.time ) + {//rage recovery, only 1 swing at a time (tired) + if ( pm->ps->saberAttackChainCount > 0 ) + {//swung once + return qtrue; + } + else + {//allow one attack + return qfalse; + } + } + else if ( (pm->ps->forcePowersActive&(1<ps->saber[0].maxChain == -1 ) + { + return qfalse; + } + else if ( pm->ps->saber[0].maxChain != 0 ) + { + if ( pm->ps->saberAttackChainCount >= pm->ps->saber[0].maxChain ) + { + return qtrue; + } + else + { + return qfalse; + } + } + + if ( pm->ps->saberAnimLevel == SS_DESANN || pm->ps->saberAnimLevel == SS_TAVION ) + {//desann and tavion can link up as many attacks as they want + return qfalse; + } + //FIXME: instead of random, apply some sort of logical conditions to whether or + // not you can chain? Like if you were completely missed, you can't chain as much, or...? + // And/Or based on FP_SABER_OFFENSE level? So number of attacks you can chain + // increases with your FP_SABER_OFFENSE skill? + if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + //TEMP: for now, let staff attacks infinitely chain + return qfalse; + /* + if ( pm->ps->saberAttackChainCount > Q_irand( 3, 4 ) ) + { + return qtrue; + } + else if ( pm->ps->saberAttackChainCount > 0 ) + { + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 3 ) + { + return qtrue; + } + } + } + */ + } + else if ( pm->ps->saberAnimLevel == SS_DUAL ) + { + //TEMP: for now, let staff attacks infinitely chain + return qfalse; + } + else if ( pm->ps->saberAnimLevel == FORCE_LEVEL_3 ) + { + if ( curmove == LS_NONE || newmove == LS_NONE ) + { + if ( pm->ps->saberAnimLevel >= FORCE_LEVEL_3 && pm->ps->saberAttackChainCount > Q_irand( 0, 1 ) ) + { + return qtrue; + } + } + else if ( pm->ps->saberAttackChainCount > Q_irand( 2, 3 ) ) + { + return qtrue; + } + else if ( pm->ps->saberAttackChainCount > 0 ) + { + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + return qtrue; + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + } + } +<<<<<<< HEAD + else if ( g_saberNewCombat->integer ) //new code +======= + else +>>>>>>> 38e3edc40da09b9c20f585a3b939ff493be8bf50 + {//FIXME: have chainAngle influence fast and medium chains as well? + if ( (pm->ps->saberAnimLevel == FORCE_LEVEL_3 || pm->ps->saberAnimLevel == SS_DUAL) + && pm->ps->saberAttackChainCount > Q_irand( 2, 5 ) ) + { + return qtrue; + } + } + else //old code + {//FIXME: have chainAngle influence fast and medium chains as well? + if ((pm->ps->saberAnimLevel == FORCE_LEVEL_2 || pm->ps->saberAnimLevel == SS_DUAL) + && pm->ps->saberAttackChainCount > Q_irand(2, 5)) + { + return qtrue; + } + } + return qfalse; +} + +qboolean PM_CheckEnemyInBack( float backCheckDist ) +{ + if ( !pm->gent || !pm->gent->client ) + { + return qfalse; + } + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) + && !g_saberAutoAim->integer && pm->cmd.forwardmove >= 0 ) + {//don't auto-backstab + return qfalse; + } + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + {//only when on ground + return qfalse; + } + trace_t trace; + vec3_t end, fwd, fwdAngles = {0,pm->ps->viewangles[YAW],0}; + + AngleVectors( fwdAngles, fwd, NULL, NULL ); + VectorMA( pm->ps->origin, -backCheckDist, fwd, end ); + + pm->trace( &trace, pm->ps->origin, vec3_origin, vec3_origin, end, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 ); + if ( trace.fraction < 1.0f && trace.entityNum < ENTITYNUM_WORLD ) + { + gentity_t *traceEnt = &g_entities[trace.entityNum]; + if ( traceEnt + && traceEnt->health > 0 + && traceEnt->client + && traceEnt->client->playerTeam == pm->gent->client->enemyTeam + && traceEnt->client->ps.groundEntityNum != ENTITYNUM_NONE ) + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + {//player + if ( pm->gent ) + {//set player enemy to traceEnt so he auto-aims at him + pm->gent->enemy = traceEnt; + } + } + return qtrue; + } + } + return qfalse; +} + +saberMoveName_t PM_PickBackStab( void ) +{ + if ( !pm->gent || !pm->gent->client ) + { + return LS_READY; + } + if ( pm->ps->dualSabers + && pm->ps->saber[1].Active() ) + { + if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + if ( pm->gent->client->ps.saberAnimLevel == SS_TAVION ) + { + return LS_A_BACKSTAB; + } + else if ( pm->gent->client->ps.saberAnimLevel == SS_DESANN ) + { + if ( pm->ps->saberMove == LS_READY || !Q_irand( 0, 3 ) ) + { + return LS_A_BACKSTAB; + } + else if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } +<<<<<<< HEAD + else if ( (pm->ps->saberAnimLevel == FORCE_LEVEL_3 + || pm->ps->saberAnimLevel == SS_DUAL) && g_saberNewCombat->integer ) //new code +======= + else if ( pm->ps->saberAnimLevel == FORCE_LEVEL_2 + || pm->ps->saberAnimLevel == SS_DUAL ) +>>>>>>> 38e3edc40da09b9c20f585a3b939ff493be8bf50 + {//using medium attacks or dual sabers + if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + else if (pm->ps->saberAnimLevel == FORCE_LEVEL_2 + || pm->ps->saberAnimLevel == SS_DUAL) //old code + {//using medium attacks or dual sabers + if (pm->ps->pm_flags & PMF_DUCKED) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + else + { + return LS_A_BACKSTAB; + } +} + +saberMoveName_t PM_CheckStabDown( void ) +{ + if ( !pm->gent || !pm->gent->enemy || !pm->gent->enemy->client ) + { + return LS_NONE; + } + if ( (pm->ps->saber[0].saberFlags&SFL_NO_STABDOWN) ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_STABDOWN) ) + { + return LS_NONE; + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingKataAttack( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//want to try a special + return LS_NONE; + } + } + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + {//player + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + {//sorry must be on ground (or have just jumped) + if ( level.time-pm->ps->lastOnGround <= 50 && (pm->ps->pm_flags&PMF_JUMPING) ) + {//just jumped, it's okay + } + else + { + return LS_NONE; + } + } + /* + if ( pm->cmd.upmove > 0 ) + {//trying to jump + } + else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE //in air + && level.time-pm->ps->lastOnGround <= 250 //just left ground + && (pm->ps->pm_flags&PMF_JUMPING) )//jumped + {//just jumped + } + else + { + return LS_NONE; + } + */ + pm->ps->velocity[2] = 0; + pm->cmd.upmove = 0; + } + else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + {//NPC + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + {//sorry must be on ground (or have just jumped) + if ( level.time-pm->ps->lastOnGround <= 250 && (pm->ps->pm_flags&PMF_JUMPING) ) + {//just jumped, it's okay + } + else + { + return LS_NONE; + } + } + if ( !pm->gent->NPC ) + {//wtf??? + return LS_NONE; + } + if ( Q_irand( 0, RANK_CAPTAIN ) > pm->gent->NPC->rank ) + {//lower ranks do this less often + return LS_NONE; + } + } + vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, NULL, NULL ); + VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + float enemyZDiff = enemyDir[2]; + enemyDir[2] = 0; + float enemyHDist = VectorNormalize( enemyDir )-(pm->gent->maxs[0]+pm->gent->enemy->maxs[0]); + float dot = DotProduct( enemyDir, faceFwd ); + + if ( //(pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + dot > 0.65f + //&& enemyHDist >= 32 //was 48 + && enemyHDist <= 164//was 112 + && PM_InKnockDownOnGround( &pm->gent->enemy->client->ps )//still on ground + && !PM_InGetUpNoRoll( &pm->gent->enemy->client->ps )//not getting up yet + && enemyZDiff <= 20 ) + {//guy is on the ground below me, do a top-down attack + if ( pm->gent->enemy->s.number >= MAX_CLIENTS + || !G_ControlledByPlayer( pm->gent->enemy ) ) + {//don't get up while I'm doing this + //stop them from trying to get up for at least another 3 seconds + TIMER_Set( pm->gent->enemy, "noGetUpStraight", 3000 ); + } + //pick the right anim + if ( pm->ps->saberAnimLevel == SS_DUAL + || (pm->ps->dualSabers&&pm->ps->saber[1].Active()) ) + { + return LS_STABDOWN_DUAL; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + return LS_STABDOWN_STAFF; + } + else + { + return LS_STABDOWN; + } + } + return LS_NONE; +} + +extern saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ); +saberMoveName_t PM_SaberFlipOverAttackMove( void ); +saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ) +{ + saberMoveName_t autoMove = LS_INVALID; + + if( !pm->gent->enemy ) + { + return LS_NONE; + } + + vec3_t enemy_org, enemyDir, faceFwd, faceRight, faceUp, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, faceRight, faceUp ); + //FIXME: predict enemy position? + if ( pm->gent->enemy->client ) + { + //VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); + //HMM... using this will adjust for bbox size, so let's do that... + vec3_t size; + VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); + VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + + VectorSubtract( pm->gent->enemy->client->renderInfo.eyePoint, pm->ps->origin, enemyDir ); + } + else + { + if ( pm->gent->enemy->bmodel && VectorCompare( vec3_origin, pm->gent->enemy->currentOrigin ) ) + {//a brush model without an origin brush + vec3_t size; + VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); + VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + } + else + { + VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); + } + VectorSubtract( enemy_org, pm->ps->origin, enemyDir ); + } + float enemyZDiff = enemyDir[2]; + float enemyDist = VectorNormalize( enemyDir ); + float dot = DotProduct( enemyDir, faceFwd ); + if ( dot > 0 ) + {//enemy is in front + if ( allowStabDown ) + {//okay to try this + saberMoveName_t stabDownMove = PM_CheckStabDown(); + if ( stabDownMove != LS_NONE ) + { + return stabDownMove; + } + } + if ( (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + && dot > 0.65f + && enemyDist <= 64 && pm->gent->enemy->client + && (enemyZDiff <= 20 || PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) || PM_CrouchAnim( pm->gent->enemy->client->ps.legsAnim ) ) ) + {//swing down at them + return LS_A_T2B; + } + if ( allowFB ) + {//directly in front anim allowed + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( enemyDist > 200 || pm->gent->enemy->health <= 0 ) + {//hmm, look in back for an enemy + if ( pm->ps->clientNum && !PM_ControlledByPlayer() ) + {//player should never do this automatically + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 100 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + } + //this is the default only if they're *right* in front... + if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + {//NPC or player not in 1st person + if ( PM_CheckFlipOverAttackMove( qtrue ) ) + {//enemy must be close and in front + return PM_SaberFlipOverAttackMove(); + } + } + if ( PM_CheckLungeAttackMove() ) + {//NPC + autoMove = PM_SaberLungeAttackMove( qtrue ); + } + else + { + autoMove = LS_A_T2B; + } + } + else + {//pick a random one + if ( Q_irand( 0, 1 ) ) + { + autoMove = LS_A_TR2BL; + } + else + { + autoMove = LS_A_TL2BR; + } + } + float dotR = DotProduct( enemyDir, faceRight ); + if ( dotR > 0.35 ) + {//enemy is to far right + autoMove = LS_A_L2R; + } + else if ( dotR < -0.35 ) + {//far left + autoMove = LS_A_R2L; + } + else if ( dotR > 0.15 ) + {//enemy is to near right + autoMove = LS_A_TR2BL; + } + else if ( dotR < -0.15 ) + {//near left + autoMove = LS_A_TL2BR; + } + if ( DotProduct( enemyDir, faceUp ) > 0.5 ) + {//enemy is above me + if ( autoMove == LS_A_TR2BL ) + { + autoMove = LS_A_BL2TR; + } + else if ( autoMove == LS_A_TL2BR ) + { + autoMove = LS_A_BR2TL; + } + } + } + else if ( allowFB ) + {//back attack allowed + //if ( !PM_InKnockDown( pm->ps ) ) + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( !pm->gent->enemy->client || pm->gent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE ) + {//enemy not a client or is a client and on ground + if ( dot < -0.75f + && enemyDist < 128 + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,2))) ) + {//fast back-stab + if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + {//can't do it while ducked? + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + {//only fencers and above can do this + autoMove = LS_A_BACKSTAB; + } + } + } + else if ( pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF ) + {//higher level back spin-attacks + if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + { + if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + { + autoMove = LS_A_BACK_CR; + } + else + { + autoMove = LS_A_BACK; + } + } + } + } + } + } + } + return autoMove; +} + +qboolean PM_InSecondaryStyle( void ) +{ + if ( pm->ps->saber[0].numBlades > 1 + && pm->ps->saber[0].singleBladeStyle + && (pm->ps->saber[0].stylesForbidden&(1<ps->saber[0].singleBladeStyle)) + && pm->ps->saberAnimLevel == pm->ps->saber[0].singleBladeStyle ) + { + return qtrue; + } + + if ( pm->ps->dualSabers + && !pm->ps->saber[1].Active() )//pm->ps->saberAnimLevel != SS_DUAL ) + { + return qtrue; + } + return qfalse; +} + +saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ) +{ + G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER_FB ); + + //see if we have an overridden (or cancelled) lunge move + if ( pm->ps->saber[0].lungeAtkMove != LS_INVALID ) + { + if ( pm->ps->saber[0].lungeAtkMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].lungeAtkMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove != LS_INVALID ) + { + if ( pm->ps->saber[1].lungeAtkMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].lungeAtkMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand( 0, 3 ) ) + {//alora NPC + return LS_SPINATTACK_ALORA; + } + else + { + if ( pm->ps->dualSabers ) + { + return LS_SPINATTACK_DUAL; + } + switch ( pm->ps->saberAnimLevel ) + { + case SS_DUAL: + return LS_SPINATTACK_DUAL; + break; + case SS_STAFF: + return LS_SPINATTACK; + break; + default://normal lunge + if ( fallbackToNormalLunge ) + { + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + //do the lunge + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 150, pm->ps->velocity ); + pm->ps->velocity[2] = 50; + PM_AddEvent( EV_JUMP ); + + return LS_A_LUNGE; + } + break; + } + } + return LS_NONE; +} + +qboolean PM_CheckLungeAttackMove( void ) +{ + //check to see if it's cancelled? + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE + || pm->ps->saber[1].lungeAtkMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + { + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE + || pm->ps->saber[0].lungeAtkMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + if ( pm->ps->saberAnimLevel == SS_FAST//fast + || pm->ps->saberAnimLevel == SS_DUAL//dual + || pm->ps->saberAnimLevel == SS_STAFF //staff + || pm->ps->saberAnimLevel == SS_DESANN + || pm->ps->dualSabers ) + {//alt+back+attack using fast, dual or staff attacks + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove < 0 || (pm->ps->pm_flags&PMF_DUCKED) ) + {//ducking + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE + || pm->ps->legsAnim == BOTH_SABERDUAL_STANCE + || (level.time-pm->ps->lastStationary) <= 500 ) + {//standing or just stopped standing + if ( pm->gent + && pm->gent->NPC //NPC + && pm->gent->NPC->rank >= RANK_LT_JG //high rank + && ( pm->gent->NPC->rank == RANK_LT_JG || Q_irand( -3, pm->gent->NPC->rank ) >= RANK_LT_JG )//Q_irand( 0, pm->gent->NPC->rank ) >= RANK_LT_JG ) + && !Q_irand( 0, 3-g_spskill->integer ) ) + {//only fencer and higher can do this + if ( pm->ps->saberAnimLevel == SS_DESANN ) + { + if ( !Q_irand( 0, 4 ) ) + { + return qtrue; + } + } + else + { + return qtrue; + } + } + } + } + } + else + {//player + if ( G_TryingLungeAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + { + return qtrue; + } + } + } + + return qfalse; +} + +saberMoveName_t PM_SaberJumpForwardAttackMove( void ) +{ + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + } + if ( pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF ) + { + pm->cmd.upmove = 0;//no jump just yet + + if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + if ( Q_irand(0, 1) ) + { + return LS_JUMPATTACK_STAFF_LEFT; + } + else + { + return LS_JUMPATTACK_STAFF_RIGHT; + } + } + + return LS_JUMPATTACK_DUAL; + } + else + { + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 200, pm->ps->velocity ); + pm->ps->velocity[2] = 180; + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + + //FIXME: NPCs yell? + PM_AddEvent( EV_JUMP ); + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + pm->cmd.upmove = 0; + + return LS_A_JUMP_T__B_; + } +} + +qboolean PM_CheckJumpForwardAttackMove( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( pm->cmd.forwardmove > 0 //going forward + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped (if not player) + ) + { + if ( pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF ) + {//dual and staff + if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim + && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + && pm->ps->weaponTime <= 0//not busy + && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + { + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//jumping NPC + { + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + { + return qtrue; + } + } + } + else + {//PLAYER + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power to attack + { + return qtrue; + } + } + } + } + //check strong + else if ( pm->ps->saberAnimLevel == SS_STRONG //strong style + || pm->ps->saberAnimLevel == SS_DESANN )//desann + { + if ( //&& !PM_InKnockDown( pm->ps ) + !pm->ps->dualSabers + //&& (pm->ps->legsAnim == BOTH_STAND2||pm->ps->legsAnim == BOTH_SABERFAST_STANCE||pm->ps->legsAnim == BOTH_SABERSLOW_STANCE||level.time-pm->ps->lastStationary<=500)//standing or just started moving + ) + {//strong attack: jump-hack + /* + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || level.time-pm->ps->lastStationary <= 250 )//standing or just started moving + */ + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//NPC jumping + { + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + {//only acrobat or boss and higher can do this + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || level.time-pm->ps->lastStationary <= 250 ) + {//standing or just started moving + if ( pm->gent->client + && pm->gent->client->NPC_class == CLASS_DESANN ) + { + if ( !Q_irand( 0, 1 ) ) + { + return qtrue; + } + } + else + { + return qtrue; + } + } + } + } + } + else + {//player + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) ) + { + return qtrue; + } + } + } + } + } + return qfalse; +} + +saberMoveName_t PM_SaberFlipOverAttackMove( void ) +{ + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + } + //FIXME: check above for room enough to jump! + //FIXME: while in this jump, keep velocity[2] at a minimum until the end of the anim + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 150, pm->ps->velocity ); + pm->ps->velocity[2] = 250; + //250 is normalized for a standing enemy at your z level, about 64 tall... adjust for actual maxs[2]-mins[2] of enemy and for zdiff in origins + if ( pm->gent && pm->gent->enemy ) + { //go higher for taller enemies + pm->ps->velocity[2] *= (pm->gent->enemy->maxs[2]-pm->gent->enemy->mins[2])/64.0f; + //go higher for enemies higher than you, lower for those lower than you + float zDiff = pm->gent->enemy->currentOrigin[2] - pm->ps->origin[2]; + pm->ps->velocity[2] += (zDiff)*1.5f; + //clamp to decent-looking values + //FIXME: still jump too low sometimes + if ( zDiff <= 0 && pm->ps->velocity[2] < 200 ) + {//if we're on same level, don't let me jump so low, I clip into the ground + pm->ps->velocity[2] = 200; + } + else if ( pm->ps->velocity[2] < 50 ) + { + pm->ps->velocity[2] = 50; + } + else if ( pm->ps->velocity[2] > 400 ) + { + pm->ps->velocity[2] = 400; + } + } + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + + //FIXME: NPCs yell? + PM_AddEvent( EV_JUMP ); + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + pm->cmd.upmove = 0; + //FIXME: don't allow this to land on other people + + pm->gent->angle = pm->ps->viewangles[YAW];//so we know what yaw we started this at + + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + + if ( Q_irand( 0, 1 ) ) + { + return LS_A_FLIP_STAB; + } + else + { + return LS_A_FLIP_SLASH; + } +} + +qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( (pm->ps->saberAnimLevel == SS_MEDIUM //medium + || pm->ps->saberAnimLevel == SS_TAVION )//tavion + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + ) + { + qboolean tryMove = qfalse; + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0//want to jump + || (pm->ps->pm_flags&PMF_JUMPING) )//jumping + {//flip over-forward down-attack + if ( (pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) + && !Q_irand(0, 2) ) )//NPC who can do this, 33% chance + {//only player or acrobat or boss and higher can do this + tryMove = qtrue; + } + } + } + else + {//player + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power + { + if ( !pm->cmd.rightmove ) + { + if ( pm->ps->legsAnim == BOTH_JUMP1 + || pm->ps->legsAnim == BOTH_FORCEJUMP1 + || pm->ps->legsAnim == BOTH_INAIR1 + || pm->ps->legsAnim == BOTH_FORCEINAIR1 ) + {//in a non-flip forward jump + tryMove = qtrue; + } + } + } + } + + if ( tryMove ) + { + if ( !checkEnemy ) + {//based just on command input + return qtrue; + } + else + {//based on presence of enemy + if ( pm->gent->enemy )//have an enemy + { + vec3_t fwdAngles = {0,pm->ps->viewangles[YAW],0}; + if ( pm->gent->enemy->health > 0 + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->gent->enemy->maxs[2] > 12 + && (!pm->gent->enemy->client || !PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) ) + && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 10000 + && InFront( pm->gent->enemy->currentOrigin, pm->gent->currentOrigin, fwdAngles, 0.3f ) ) + {//enemy must be alive, not low to ground, close and in front + return qtrue; + } + } + return qfalse; + } + } + } + return qfalse; +} + +saberMoveName_t PM_SaberBackflipAttackMove( void ) +{ + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkBackMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkBackMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkBackMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkBackMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkBackMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + { + return LS_NONE; + } + } + pm->cmd.upmove = 0;//no jump just yet + return LS_A_BACKFLIP_ATK; +} + +qboolean PM_CheckBackflipAttackMove( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE + || pm->ps->saber[1].jumpAtkBackMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE + || pm->ps->saber[0].jumpAtkBackMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + //&& (pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE || level.time-pm->ps->lastStationary<=250)//standing or just started moving + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) )//on ground or just jumped (if not player) + { + if ( pm->cmd.forwardmove < 0 //moving backwards + && pm->ps->saberAnimLevel == SS_STAFF //using staff + && (pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING)) )//jumping + {//jumping backwards and using staff + if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim + && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + && pm->ps->weaponTime <= 0//not busy + && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + {//not already attacking + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + {//acrobat or boss and higher can do this + return qtrue; + } + } + else + {//player + return qtrue; + } + } + } + } + return qfalse; +} + +saberMoveName_t PM_CheckDualSpinProtect( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[0].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].kataMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[1].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].kataMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].kataMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->ps->saberMove == LS_READY//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? + //&& pm->ps->viewangles[0] > 30 //looking down + && pm->ps->saberAnimLevel == SS_DUAL//using dual saber style + && pm->ps->saber[0].Active() && pm->ps->saber[1].Active()//both sabers on + //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 + //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing + && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack + ) + {//FIXME: some NPC logic to do this? + /* + if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching + && g_crosshairEntNum >= ENTITYNUM_WORLD ) + */ + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_PUSH, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + } + return LS_DUAL_SPIN_PROTECT; + } + } + return LS_NONE; +} + +saberMoveName_t PM_CheckStaffKata( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[0].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].kataMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[1].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].kataMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].kataMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->ps->saberMove == LS_READY//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? + //&& pm->ps->viewangles[0] > 30 //looking down + && pm->ps->saberAnimLevel == SS_STAFF//using dual saber style + && pm->ps->saber[0].Active()//saber on + //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 + //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing + && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack + ) + {//FIXME: some NPC logic to do this? + /* + if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching + && g_crosshairEntNum >= ENTITYNUM_WORLD ) + */ + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + } + return LS_STAFF_SOULCAL; + } + } + return LS_NONE; +} + +extern qboolean WP_ForceThrowable( gentity_t *ent, gentity_t *forwardEnt, gentity_t *self, qboolean pull, float cone, float radius, vec3_t forward ); +saberMoveName_t PM_CheckPullAttack( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + if ( (pm->ps->saber[0].saberFlags&SFL_NO_PULL_ATTACK) ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_PULL_ATTACK) ) + { + return LS_NONE; + } + + if ( (pm->ps->saberMove == LS_READY||PM_SaberInReturn(pm->ps->saberMove)||PM_SaberInReflect(pm->ps->saberMove))//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY + && pm->ps->saberAnimLevel >= SS_FAST//single saber styles - FIXME: Tavion? + && pm->ps->saberAnimLevel <= SS_STRONG//single saber styles - FIXME: Tavion? + && G_TryingPullAttack( pm->gent, &pm->cmd, qfalse )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + //&& pm->cmd.forwardmove<0//pulling back + && (pm->cmd.buttons&BUTTON_ATTACK)//attacking + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB//have enough power + ) + {//FIXME: some NPC logic to do this? + qboolean doMove = g_saberNewControlScheme->integer?qtrue:qfalse;//in new control scheme, can always do this, even if there's no-one to do it to + if ( g_saberNewControlScheme->integer + || g_crosshairEntNum < ENTITYNUM_WORLD )//in old control scheme, there has to be someone there + { + saberMoveName_t pullAttackMove = LS_NONE; + if ( pm->ps->saberAnimLevel == SS_FAST ) + { + pullAttackMove = LS_PULL_ATTACK_STAB; + } + else + { + pullAttackMove = LS_PULL_ATTACK_SWING; + } + + if ( g_crosshairEntNum < ENTITYNUM_WORLD + && pm->gent && pm->gent->client ) + { + gentity_t *targEnt = &g_entities[g_crosshairEntNum]; + if ( targEnt->client + && targEnt->health > 0 + //FIXME: check other things like in knockdown, saberlock, uninterruptable anims, etc. + && !PM_InOnGroundAnim( &targEnt->client->ps ) + && !PM_LockedAnim( targEnt->client->ps.legsAnim ) + && !PM_SuperBreakLoseAnim( targEnt->client->ps.legsAnim ) + && !PM_SuperBreakWinAnim( targEnt->client->ps.legsAnim ) + && targEnt->client->ps.saberLockTime <= 0 + && WP_ForceThrowable( targEnt, targEnt, pm->gent, qtrue, 1.0f, 0.0f, NULL ) ) + { + if ( !g_saberNewControlScheme->integer ) + {//in old control scheme, make sure they're close or far enough away for the move we'll be doing + float targDist = Distance( targEnt->currentOrigin, pm->ps->origin ); + if ( pullAttackMove == LS_PULL_ATTACK_STAB ) + {//must be closer than 512 + if ( targDist > 384.0f ) + { + return LS_NONE; + } + } + else//if ( pullAttackMove == LS_PULL_ATTACK_SWING ) + {//must be farther than 256 + if ( targDist > 512.0f ) + { + return LS_NONE; + } + if ( targDist < 192.0f ) + { + return LS_NONE; + } + } + } + + vec3_t targAngles = {0,targEnt->client->ps.viewangles[YAW],0}; + if ( InFront( pm->ps->origin, targEnt->currentOrigin, targAngles ) ) + { + NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_F, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + } + else + { + NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_B, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + } + //hold the anim until I'm with done pull anim + targEnt->client->ps.legsAnimTimer = targEnt->client->ps.torsoAnimTimer = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, (animNumber_t)saberMoveData[pullAttackMove].animToUse ); + //set pullAttackTime + pm->gent->client->ps.pullAttackTime = targEnt->client->ps.pullAttackTime = level.time+targEnt->client->ps.legsAnimTimer; + //make us know about each other + pm->gent->client->ps.pullAttackEntNum = g_crosshairEntNum; + targEnt->client->ps.pullAttackEntNum = pm->ps->clientNum; + //do effect and sound on me + pm->ps->powerups[PW_FORCE_PUSH] = level.time + 1000; + if ( pm->gent ) + { + G_Sound( pm->gent, G_SoundIndex( "sound/weapons/force/pull.wav" ) ); + } + doMove = qtrue; + } + } + if ( doMove ) + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_PULL, SABER_ALT_ATTACK_POWER_FB ); + } + return pullAttackMove; + } + } + } + return LS_NONE; +} + +saberMoveName_t PM_CheckPlayerAttackFromParry( int curmove ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + { + if ( curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL ) + {//in a parry + switch ( saberMoveData[curmove].endQuad ) + { + case Q_T: + return LS_A_T2B; + break; + case Q_TR: + return LS_A_TR2BL; + break; + case Q_TL: + return LS_A_TL2BR; + break; + case Q_BR: + return LS_A_BR2TL; + break; + case Q_BL: + return LS_A_BL2TR; + break; + //shouldn't be a parry that ends at L, R or B + } + } + } + return LS_NONE; +} + + +saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int curmove ) +{ + qboolean noSpecials = qfalse; + + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + noSpecials = qtrue; + } + + saberMoveName_t overrideJumpRightAttackMove = LS_INVALID; + if ( pm->ps->saber[0].jumpAtkRightMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkRightMove != LS_NONE ) + {//actually overriding + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkRightMove; + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove > LS_NONE ) + {//would be cancelling it, but check the second saber, too + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; + } + else + {//nope, just cancel it + overrideJumpRightAttackMove = LS_NONE; + } + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove != LS_INVALID ) + {//first saber not overridden, check second + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; + } + + saberMoveName_t overrideJumpLeftAttackMove = LS_INVALID; + if ( pm->ps->saber[0].jumpAtkLeftMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkLeftMove != LS_NONE ) + {//actually overriding + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkLeftMove; + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove > LS_NONE ) + {//would be cancelling it, but check the second saber, too + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; + } + else + {//nope, just cancel it + overrideJumpLeftAttackMove = LS_NONE; + } + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove != LS_INVALID ) + {//first saber not overridden, check second + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; + } + if ( rightmove > 0 ) + {//moving right + if ( !noSpecials + && overrideJumpRightAttackMove != LS_NONE + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC + ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + {//cartwheel right + vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + } + pm->cmd.upmove = 0; + if ( overrideJumpRightAttackMove != LS_INVALID ) + {//overridden with another move + return overrideJumpRightAttackMove; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + return LS_BUTTERFLY_RIGHT; + } + else + { + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + {//okay to do cartwheels with this saber + /* + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//still on ground + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_CART_RIGHT; + } + else + */ + {//in air + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + PM_SetJumped( JUMP_VELOCITY, qtrue ); + return LS_JUMPATTACK_ARIAL_RIGHT; + } + } + } + } + else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_RIGHT + && pm->ps->legsAnim != BOTH_ARIAL_RIGHT ) + {//not in a cartwheel/arial + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( forwardmove > 0 ) + {//forward right = TL2BR slash + return LS_A_TL2BR; + } + else if ( forwardmove < 0 ) + {//backward right = BL2TR uppercut + return LS_A_BL2TR; + } + else + {//just right is a left slice + return LS_A_L2R; + } + } + } + else if ( rightmove < 0 ) + {//moving left + if ( !noSpecials + && overrideJumpLeftAttackMove != LS_NONE + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC + ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + {//cartwheel left + vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + } + pm->cmd.upmove = 0; + if ( overrideJumpRightAttackMove != LS_INVALID ) + {//overridden with another move + return overrideJumpRightAttackMove; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + return LS_BUTTERFLY_LEFT; + } + else + { + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + {//okay to do cartwheels with this saber + /* + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//still on ground + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_ARIAL_LEFT; + } + else + */ + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + PM_SetJumped( JUMP_VELOCITY, qtrue ); + return LS_JUMPATTACK_CART_LEFT; + } + } + } + } + else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_LEFT + && pm->ps->legsAnim != BOTH_ARIAL_LEFT ) + {//not in a left cartwheel/arial + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( forwardmove > 0 ) + {//forward left = TR2BL slash + return LS_A_TR2BL; + } + else if ( forwardmove < 0 ) + {//backward left = BR2TL uppercut + return LS_A_BR2TL; + } + else + {//just left is a right slice + return LS_A_R2L; + } + } + } + else + {//not moving left or right + if ( forwardmove > 0 ) + {//forward= T2B slash + saberMoveName_t stabDownMove = noSpecials?LS_NONE:PM_CheckStabDown(); + if ( stabDownMove != LS_NONE ) + { + return stabDownMove; + } + if ( ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zoomed in + {//player in thirdperson, not zoomed in + //flip-over attack logic + if ( !noSpecials && PM_CheckFlipOverAttackMove( qfalse ) ) + {//flip over-forward down-attack + return PM_SaberFlipOverAttackMove(); + } + //lunge attack logic + else if ( PM_CheckLungeAttackMove() ) + { + return PM_SaberLungeAttackMove( qtrue ); + } + //jump forward attack logic + else if ( !noSpecials && PM_CheckJumpForwardAttackMove() ) + { + return PM_SaberJumpForwardAttackMove(); + } + } + + //player NPC with enemy: autoMove logic + if ( pm->gent + && pm->gent->enemy + && pm->gent->enemy->client ) + {//I have an active enemy + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + {//a player who is running at an enemy + //if the enemy is not a jedi, don't use top-down, pick a diagonal or side attack + if ( pm->gent->enemy->s.weapon != WP_SABER + && pm->gent->enemy->client->NPC_class != CLASS_REMOTE//too small to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_SEEKER//too small to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_GONK//too short to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_HOWLER//too short to do auto-aiming accurately + && g_saberAutoAim->integer ) + { + saberMoveName_t autoMove = PM_AttackForEnemyPos( qfalse, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer()) ); + if ( autoMove != LS_INVALID ) + { + return autoMove; + } + } + } + + if ( pm->ps->clientNum>=MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + {//NPC + if ( PM_CheckFlipOverAttackMove( qtrue ) ) + { + return PM_SaberFlipOverAttackMove(); + } + } + } + + //Regular NPCs + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + {//NPC or player in third person, not zoomed in + //fwd jump attack logic + if ( PM_CheckJumpForwardAttackMove() ) + { + return PM_SaberJumpForwardAttackMove(); + } + //lunge attack logic + else if ( PM_CheckLungeAttackMove() ) + { + return PM_SaberLungeAttackMove( qtrue ); + } + } + + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + return LS_A_T2B; + } + else if ( forwardmove < 0 ) + {//backward= T2B slash//B2T uppercut? + if ( g_saberNewControlScheme->integer ) + { + saberMoveName_t pullAtk = PM_CheckPullAttack(); + if ( pullAtk != LS_NONE ) + { + return pullAtk; + } + } + + if ( g_saberNewControlScheme->integer + && (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + && (pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus, trying special backwards attacks + {//player lunge attack logic + if ( ( pm->ps->dualSabers //or dual + || pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->SaberStaff() )//or staff + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + {//alt+back+attack using fast, dual or staff attacks + PM_SaberLungeAttackMove( qfalse ); + } + } + else if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) //NPC + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zooomed + {//NPC or player in third person, not zoomed + if ( PM_CheckBackflipAttackMove() ) + { + return PM_SaberBackflipAttackMove();//backflip attack + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //if ( !PM_InKnockDown( pm->ps ) ) + //check backstabs + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( pm->gent && pm->gent->enemy ) + {//FIXME: or just trace for a valid enemy standing behind me? And no enemy in front? + vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, NULL, NULL ); + VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + float dot = DotProduct( enemyDir, faceFwd ); + if ( dot < 0 ) + {//enemy is behind me + if ( dot < -0.75f + && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 16384//128 squared + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,1))) ) + {//fast attacks and Tavion + if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + {//can't do it while ducked? + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + {//only fencers and above can do this + return LS_A_BACKSTAB; + } + } + } + else if ( pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF ) + {//medium and higher attacks + if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + } + else + {//enemy in front + float enemyDistSq = DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ); + if ( ((pm->ps->saberAnimLevel == FORCE_LEVEL_1 || + pm->ps->saberAnimLevel == SS_STAFF || + pm->gent->client->NPC_class == CLASS_TAVION || + pm->gent->client->NPC_class == CLASS_ALORA || + (pm->gent->client->NPC_class == CLASS_DESANN && !Q_irand(0,3))) && + enemyDistSq > 16384) || + pm->gent->enemy->health <= 0 )//128 squared + {//my enemy is pretty far in front of me and I'm using fast attacks + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || + ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 128 ) ) + { + return PM_PickBackStab(); + } + } + } + else if ( ((pm->ps->saberAnimLevel >= FORCE_LEVEL_2 || pm->gent->client->NPC_class == CLASS_DESANN) && enemyDistSq > 40000) || pm->gent->enemy->health <= 0 )//200 squared + {//enemy is very faw away and I'm using medium/strong attacks + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || + ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 164 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + else + {//no current enemy + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->gent && pm->gent->client ) + {//only player + if ( PM_CheckEnemyInBack( 128 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + } + + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + //else just swing down + return LS_A_T2B; + } + else + {//not moving in any direction + if ( PM_SaberInBounce( curmove ) ) + {//bounces should go to their default attack if you don't specify a direction but are attacking + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + saberMoveName_t newmove; + if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + } + else + {//player uses chain-attack + newmove = saberMoveData[curmove].chain_attack; + } + if ( PM_SaberKataDone( curmove, newmove ) ) + { + return saberMoveData[curmove].chain_idle; + } + else + { + return newmove; + } + } + else if ( PM_SaberInKnockaway( curmove ) ) + {//bounces should go to their default attack if you don't specify a direction but are attacking + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + saberMoveName_t newmove; + if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + } + else + { + if ( pm->ps->saberAnimLevel == SS_FAST || + pm->ps->saberAnimLevel == SS_TAVION ) + {//player is in fast attacks, so come right back down from the same spot + newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad ); + } + else + {//use a transition to wrap to another attack from a different dir + newmove = saberMoveData[curmove].chain_attack; + } + } + if ( PM_SaberKataDone( curmove, newmove ) ) + { + return saberMoveData[curmove].chain_idle; + } + else + { + return newmove; + } + } + else if ( curmove == LS_READY + || curmove == LS_A_FLIP_STAB + || curmove == LS_A_FLIP_SLASH + || ( curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL ) ) + {//Not moving at all, not too busy to attack + //push + lookdown + attack + dual sabers = LS_DUAL_SPIN_PROTECT + if ( g_saberNewControlScheme->integer ) + { + if ( PM_CheckDualSpinProtect() ) + { + return LS_DUAL_SPIN_PROTECT; + } + if ( PM_CheckStaffKata() ) + { + return LS_STAFF_SOULCAL; + } + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( pm->ps->clientNum || g_saberAutoAim->integer ) + {//auto-aim + if ( pm->gent && pm->gent->enemy ) + {//based on enemy position, pick a proper attack + saberMoveName_t autoMove = PM_AttackForEnemyPos( qtrue, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS) ); + if ( autoMove != LS_INVALID ) + { + return autoMove; + } + } + else if ( fabs(pm->ps->viewangles[0]) > 30 ) + {//looking far up or far down uses the top to bottom attack, presuming you want a vertical attack + return LS_A_T2B; + } + } + else + {//for now, just pick a random attack + return ((saberMoveName_t)Q_irand( LS_A_TL2BR, LS_A_T2B )); + } + } + } + } + //FIXME: pick a return? + return LS_NONE; +} + +saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveName_t newmove ) +{ + //FIXME: take FP_SABER_OFFENSE into account here somehow? + int retmove = newmove; + if ( curmove == LS_READY ) + {//just standing there + switch ( newmove ) + { + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + //transition is the start + retmove = LS_S_TL2BR + (newmove-LS_A_TL2BR); + break; + default: + break; + } + } + else + { + switch ( newmove ) + { + //transitioning to ready pose + case LS_READY: + switch ( curmove ) + { + //transitioning from an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + //transition is the return + retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + break; + default: + break; + } + break; + //transitioning to an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + if ( newmove == curmove ) + {//FIXME: need a spin or something or go to next level, but for now, just play the return + //going into another attack... + //allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3 + //FIXME: don't let strong attacks chain to an attack in the opposite direction ( > 45 degrees?) + if ( PM_SaberKataDone( curmove, newmove ) ) + {//done with this kata, must return to ready before attack again + retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + } + else + {//okay to chain to another attack + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + } + } + else if ( saberMoveData[curmove].endQuad == saberMoveData[newmove].startQuad ) + {//new move starts from same quadrant + retmove = newmove; + } + else + { + switch ( curmove ) + { + //transitioning from an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + case LS_D1_BR: + case LS_D1__R: + case LS_D1_TR: + case LS_D1_T_: + case LS_D1_TL: + case LS_D1__L: + case LS_D1_BL: + case LS_D1_B_: + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + break; + //transitioning from a return + case LS_R_TL2BR: + case LS_R_L2R: + case LS_R_BL2TR: + case LS_R_BR2TL: + case LS_R_R2L: + case LS_R_TR2BL: + case LS_R_T2B: + //transitioning from a bounce + /* + case LS_BOUNCE_UL2LL: + case LS_BOUNCE_LL2UL: + case LS_BOUNCE_L2LL: + case LS_BOUNCE_L2UL: + case LS_BOUNCE_UR2LR: + case LS_BOUNCE_LR2UR: + case LS_BOUNCE_R2LR: + case LS_BOUNCE_R2UR: + case LS_BOUNCE_TOP: + case LS_OVER_UR2UL: + case LS_OVER_UL2UR: + case LS_BOUNCE_UR: + case LS_BOUNCE_UL: + case LS_BOUNCE_LR: + case LS_BOUNCE_LL: + */ + //transitioning from a parry/reflection/knockaway/broken parry + case LS_PARRY_UP: + case LS_PARRY_UR: + case LS_PARRY_UL: + case LS_PARRY_LR: + case LS_PARRY_LL: + case LS_REFLECT_UP: + case LS_REFLECT_UR: + case LS_REFLECT_UL: + case LS_REFLECT_LR: + case LS_REFLECT_LL: + case LS_K1_T_: + case LS_K1_TR: + case LS_K1_TL: + case LS_K1_BR: + case LS_K1_BL: + case LS_V1_BR: + case LS_V1__R: + case LS_V1_TR: + case LS_V1_T_: + case LS_V1_TL: + case LS_V1__L: + case LS_V1_BL: + case LS_V1_B_: + case LS_H1_T_: + case LS_H1_TR: + case LS_H1_TL: + case LS_H1_BR: + case LS_H1_BL: + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + break; + //NB: transitioning from transitions is fine + default: + break; + } + } + break; + //transitioning to any other anim is not supported + default: + break; + } + } + + if ( retmove == LS_NONE ) + { + return newmove; + } + + return ((saberMoveName_t)retmove); +} + +/* +------------------------- +PM_LegsAnimForFrame +Returns animNumber for current frame +------------------------- +*/ +int PM_LegsAnimForFrame( gentity_t *ent, int legsFrame ) +{ + //Must be a valid client + if ( ent->client == NULL ) + return -1; + + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return -1; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); + + for ( int animation = 0; animation < BOTH_CIN_1; animation++ ) //first anim after last legs + { + if ( animation >= TORSO_DROPWEAP1 && animation < LEGS_TURN1 ) //first legs only anim + {//not a possible legs anim + continue; + } + + if ( animations[animation].glaIndex != glaIndex ) + { + continue; + } + + if ( animations[animation].firstFrame > legsFrame ) + {//This anim starts after this frame + continue; + } + + if ( animations[animation].firstFrame + animations[animation].numFrames < legsFrame ) + {//This anim ends before this frame + continue; + } + //else, must be in this anim! + return animation; + } + + //Not in ANY torsoAnim? SHOULD NEVER HAPPEN +// assert(0); + return -1; +} + +int PM_ValidateAnimRange( const int startFrame, const int endFrame, const float animSpeed ) +{//given a startframe and endframe, see if that lines up with any known animation + animation_t *animations = level.knownAnimFileSets[0].animations; + + for ( int anim = 0; anim < MAX_ANIMATIONS; anim++ ) + { + if ( animSpeed < 0 ) + {//playing backwards + if ( animations[anim].firstFrame == endFrame ) + { + if ( animations[anim].numFrames + animations[anim].firstFrame == startFrame ) + { + //Com_Printf( "valid reverse anim: %s\n", animTable[anim].name ); + return anim; + } + } + } + else + {//playing forwards + if ( animations[anim].firstFrame == startFrame ) + {//This anim starts on this frame + if ( animations[anim].firstFrame + animations[anim].numFrames == endFrame ) + {//This anim ends on this frame + //Com_Printf( "valid forward anim: %s\n", animTable[anim].name ); + return anim; + } + } + } + //else, must not be this anim! + } + + //Not in ANY anim? SHOULD NEVER HAPPEN + Com_Printf( "invalid anim range %d to %d, speed %4.2f\n", startFrame, endFrame, animSpeed ); + return -1; +} +/* +------------------------- +PM_TorsoAnimForFrame +Returns animNumber for current frame +------------------------- +*/ +int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame ) +{ + //Must be a valid client + if ( ent->client == NULL ) + return -1; + + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return -1; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); + + for ( int animation = 0; animation < LEGS_TURN1; animation++ ) //first legs only anim + { + if ( animations[animation].glaIndex != glaIndex ) + { + continue; + } + + if ( animations[animation].firstFrame > torsoFrame ) + {//This anim starts after this frame + continue; + } + + if ( animations[animation].firstFrame + animations[animation].numFrames < torsoFrame ) + {//This anim ends before this frame + continue; + } + //else, must be in this anim! + return animation; + } + + //Not in ANY torsoAnim? SHOULD NEVER HAPPEN +// assert(0); + return -1; +} + +qboolean PM_FinishedCurrentLegsAnim( gentity_t *self ) +{ + int junk, curFrame; + float currentFrame, animSpeed; + + if ( !self->client ) + { + return qtrue; + } + + gi.G2API_GetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &animSpeed, NULL ); + curFrame = floor( currentFrame ); + + int legsAnim = self->client->ps.legsAnim; + animation_t *animations = level.knownAnimFileSets[self->client->clientInfo.animFileIndex].animations; + + if ( curFrame >= animations[legsAnim].firstFrame + (animations[legsAnim].numFrames - 2) ) + { + return qtrue; + } + + return qfalse; +} + +/* +------------------------- +PM_HasAnimation +------------------------- +*/ + +qboolean PM_HasAnimation( gentity_t *ent, int animation ) +{ + //Must be a valid client + if ( !ent || ent->client == NULL ) + return qfalse; + + //must be a valid anim number + if ( animation < 0 || animation >= MAX_ANIMATIONS ) + { + return qfalse; + } + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return qfalse; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + + //No frames, no anim + if ( animations[animation].numFrames == 0 ) + return qfalse; + + //Has the sequence + return qtrue; +} + +int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim ) +{ + int anim; + int count = 0; + + if ( !self ) + { + return Q_irand(minAnim, maxAnim); + } + + do + { + anim = Q_irand(minAnim, maxAnim); + count++; + } + while ( !PM_HasAnimation( self, anim ) && count < 1000 ); + + return anim; +} + +/* +------------------------- +PM_AnimLength +------------------------- +*/ + +int PM_AnimLength( int index, animNumber_t anim ) +{ + if ( ValidAnimFileIndex( index ) == false ) + return 0; + + return level.knownAnimFileSets[index].animations[anim].numFrames * abs(level.knownAnimFileSets[index].animations[anim].frameLerp); +} + +/* +------------------------- +PM_SetLegsAnimTimer +------------------------- +*/ + +void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time ) +{ + *legsAnimTimer = time; + + if ( *legsAnimTimer < 0 && time != -1 ) + {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional + *legsAnimTimer = 0; + } + + if ( !*legsAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_LOWER ) ) + {//Waiting for legsAnimTimer to complete, and it just got set to zero + if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + {//Not waiting for top + Q3_TaskIDComplete( ent, TID_ANIM_LOWER ); + } + else + {//Waiting for both to finish before complete + Q3_TaskIDClear( &ent->taskID[TID_ANIM_LOWER] );//Bottom is done, regardless + if ( !Q3_TaskIDPending( ent, TID_ANIM_UPPER) ) + {//top is done and we're done + Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + } + } + } +} + +/* +------------------------- +PM_SetTorsoAnimTimer +------------------------- +*/ + +void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time ) +{ + *torsoAnimTimer = time; + + if ( *torsoAnimTimer < 0 && time != -1 ) + {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional + *torsoAnimTimer = 0; + } + + if ( !*torsoAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_UPPER ) ) + {//Waiting for torsoAnimTimer to complete, and it just got set to zero + if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + {//Not waiting for bottom + Q3_TaskIDComplete( ent, TID_ANIM_UPPER ); + } + else + {//Waiting for both to finish before complete + Q3_TaskIDClear( &ent->taskID[TID_ANIM_UPPER] );//Top is done, regardless + if ( !Q3_TaskIDPending( ent, TID_ANIM_LOWER) ) + {//lower is done and we're done + Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + } + } + } +} + +extern qboolean PM_SpinningSaberAnim( int anim ); +extern float saberAnimSpeedMod[NUM_FORCE_POWER_LEVELS]; +void PM_SaberStartTransAnim(int saberAnimLevel, int anim, float *animSpeed, gentity_t *gent) +{ + if (g_saberNewCombat->integer) //new code + { + if ( anim == BOTH_V1_BL_S1 + || anim == BOTH_V1_BR_S1 + || anim == BOTH_V1_TL_S1 + || anim == BOTH_V1_TR_S1 + || anim == BOTH_V1_T__S1 + || (anim >= BOTH_V6_BL_S6 && anim <= BOTH_V7__R_S7) ) + { //we're in a broken attack + //speed up recovery from broken attacks based on SO level + *animSpeed = saberAnimSpeedMod[gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]]; + } + if (g_saberAnimSpeed->value != 1.0f) + { + *animSpeed *= g_saberAnimSpeed->value; + } + else if (gent && gent->client && gent->client->ps.weapon == WP_SABER) + { + if (gent->client->ps.saber[0].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[0].animSpeedScale; + } + if (gent->client->ps.dualSabers + && gent->client->ps.saber[1].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[1].animSpeedScale; + } + } + } + else //old code + { + if (g_saberAnimSpeed->value != 1.0f) + { + *animSpeed *= g_saberAnimSpeed->value; + } + else if (gent && gent->client && gent->client->ps.weapon == WP_SABER) + { + if (gent->client->ps.saber[0].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[0].animSpeedScale; + } + if (gent->client->ps.dualSabers + && gent->client->ps.saber[1].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[1].animSpeedScale; + } + } + } + if ( gent + && gent->client + && gent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.dualSabers + && saberAnimLevel == SS_DUAL + && gent->weaponModel[1] ) + {//using a scepter and dual style, slow down anims + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H7_S7_BR ) + { + *animSpeed *= 0.75; + } + } + if ( gent && gent->client && gent->client->ps.forceRageRecoveryTime > level.time ) + {//rage recovery + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR ) + {//animate slower + *animSpeed *= 0.75; + } + } + else if ( gent && gent->NPC && gent->NPC->rank == RANK_CIVILIAN ) + {//grunt reborn + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + {//his fast attacks are slower + if ( !PM_SpinningSaberAnim( anim ) ) + { + *animSpeed *= 0.75; + } + return; + } + } + else if ( gent && gent->client ) + { + if ( gent->client->ps.saber[0].type == SABER_LANCE || gent->client->ps.saber[0].type == SABER_TRIDENT ) + {//FIXME: hack for now - these use the fast anims, but slowed down. Should have own style + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + {//his fast attacks are slower + if ( !PM_SpinningSaberAnim( anim ) ) + { + *animSpeed *= 0.75; + } + return; + } + } + } + + if ( ( anim >= BOTH_T1_BR__R && + anim <= BOTH_T1_BL_TL ) || + ( anim >= BOTH_T3_BR__R && + anim <= BOTH_T3_BL_TL ) || + ( anim >= BOTH_T5_BR__R && + anim <= BOTH_T5_BL_TL ) ) + { //what is this doing here exactly? + if ( g_saberNewCombat->integer ) //new code + { + if (saberAnimLevel == FORCE_LEVEL_1 /*|| saberAnimLevel == FORCE_LEVEL_5*/) + {//FIXME: should not be necc for FORCE_LEVEL_1's + *animSpeed *= 1.5; + } + else if (saberAnimLevel == FORCE_LEVEL_5) + { //both Desann and Strong styles suffer some transition speed penalty or something? + *animSpeed *= 0.75; + } + } + else //old code + { + if (saberAnimLevel == FORCE_LEVEL_1 || saberAnimLevel == FORCE_LEVEL_5) + {//FIXME: should not be necc for FORCE_LEVEL_1's + *animSpeed *= 1.5; + } + else if (saberAnimLevel == FORCE_LEVEL_3) + { + *animSpeed *= 0.75; + } + } + } +} +/* +void PM_SaberStartTransAnim( int anim, int entNum, int saberOffenseLevel, float *animSpeed ) +{ + //check starts + if ( ( anim >= BOTH_S1_S1_T_ && + anim <= BOTH_S1_S1_TR ) || + ( anim >= BOTH_S1_S1_T_ && + anim <= BOTH_S1_S1_TR ) || + ( anim >= BOTH_S3_S1_T_ && + anim <= BOTH_S3_S1_TR ) ) + { + if ( entNum == 0 ) + { + *animSpeed *= saberAnimSpeedMod[FORCE_LEVEL_3]; + } + else + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; + } + } + //Check transitions + else if ( PM_SpinningSaberAnim( anim ) ) + {//spins stay normal speed + return; + } + else if ( ( anim >= BOTH_T1_BR__R && + anim <= BOTH_T1_BL_TL ) || + ( anim >= BOTH_T2_BR__R && + anim <= BOTH_T2_BL_TL ) || + ( anim >= BOTH_T3_BR__R && + anim <= BOTH_T3_BL_TL ) ) + {//slow down the transitions + if ( entNum == 0 && saberOffenseLevel <= FORCE_LEVEL_2 ) + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; + } + else + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]/2.0f; + } + } + + return; +} +*/ +extern qboolean player_locked; +extern qboolean MatrixMode; +float PM_GetTimeScaleMod( gentity_t *gent ) +{ + if ( g_timescale->value ) + { + if ( !MatrixMode + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_START + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_ATTACK + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_LAND ) + { + if ( gent && gent->s.clientNum == 0 && !player_locked && gent->client->ps.forcePowersActive&(1<value); + } + else if ( gent && gent->client && gent->client->ps.forcePowersActive&(1<value); + } + } + } + return 1.0f; +} + +static inline qboolean PM_IsHumanoid( CGhoul2Info *ghlInfo ) +{ + char *GLAName; + GLAName = gi.G2API_GetGLAName( ghlInfo ); + assert(GLAName); + + if ( !Q_stricmp( "models/players/_humanoid/_humanoid", GLAName ) ) + { + return qtrue; + } + + return qfalse; +} + +/* +------------------------- +PM_SetAnimFinal +------------------------- +*/ +#define G2_DEBUG_TIMING (0) +void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, + int setAnimParts,int anim,int setAnimFlags, + int *torsoAnimTimer,int *legsAnimTimer, + gentity_t *gent,int blendTime) // default blendTime=350 +{ + +// BASIC SETUP AND SAFETY CHECKING +//================================= + + // If It Is A Busted Entity, Don't Do Anything Here. + //--------------------------------------------------- + if (!gent || !gent->client) + { + return; + } + + // Make Sure This Character Has Such An Anim And A Model + //------------------------------------------------------- + if (anim<0 || anim>=MAX_ANIMATIONS || !ValidAnimFileIndex(gent->client->clientInfo.animFileIndex)) + { + #ifndef FINAL_BUILD + if (g_AnimWarning->integer) + { + if (anim<0 || anim>=MAX_ANIMATIONS) + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim Index (%d)!\n", anim); + } + else + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim File Index (%d)!\n", gent->client->clientInfo.animFileIndex); + } + } + #endif + return; + } + + + // Get Global Time Properties + //---------------------------- + float timeScaleMod = PM_GetTimeScaleMod( gent ); + const int actualTime = (cg.time?cg.time:level.time); + const animation_t* animations = level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations; + const animation_t& curAnim = animations[anim]; + + // Make Sure This Character Has Such An Anim And A Model + //------------------------------------------------------- + if (animations[anim].numFrames==0) + { + #ifndef FINAL_BUILD + static int LastAnimWarningNum=0; + if (LastAnimWarningNum!=anim) + { + if ((cg_debugAnim.integer==3) || // 3 = do everyone + (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player + (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else + (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + ) + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Anim %s does not exist in this model (%s)!\n", animTable[anim].name, gent->NPC_type ); + } + } + LastAnimWarningNum = anim; + #endif + return; + } + + // If It's Not A Ghoul 2 Model, Just Remember The Anims And Stop, Because Everything Beyond This Is Ghoul2 + //--------------------------------------------------------------------------------------------------------- + if (!gi.G2API_HaveWeGhoul2Models(gent->ghoul2)) + { + if (setAnimParts&SETANIM_TORSO) + { + (*torsoAnim) = anim; + } + if (setAnimParts&SETANIM_LEGS) + { + (*legsAnim) = anim; + } + return; + } + + + // Lower Offensive Skill Slows Down The Saber Start Attack Animations + //-------------------------------------------------------------------- + PM_SaberStartTransAnim( gent->client->ps.saberAnimLevel, anim, &timeScaleMod, gent ); + + + +// SETUP VALUES FOR INCOMMING ANIMATION +//====================================== + const bool animFootMove = (PM_WalkingAnim(anim) || PM_RunningAnim(anim) || anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK); + const bool animHoldless = (setAnimFlags&SETANIM_FLAG_HOLDLESS)!=0; + const bool animHold = (setAnimFlags&SETANIM_FLAG_HOLD)!=0; + const bool animRestart = (setAnimFlags&SETANIM_FLAG_RESTART)!=0; + const bool animOverride = (setAnimFlags&SETANIM_FLAG_OVERRIDE)!=0; + const bool animSync = (g_synchSplitAnims->integer!=0 && !animRestart); + float animCurrent = (-1.0f); + float animSpeed = (50.0f / curAnim.frameLerp * timeScaleMod); // animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps). + const float animFPS = (fabsf(curAnim.frameLerp)); + const int animDurMSec = (int)(((curAnim.numFrames - 1) * animFPS) / timeScaleMod); + const int animHoldMSec = ((animHoldless && timeScaleMod==1.0f)?((animDurMSec>1)?(animDurMSec-1):(animFPS)):(animDurMSec)); + int animFlags = (curAnim.loopFrames!=-1)?(BONE_ANIM_OVERRIDE_LOOP):(BONE_ANIM_OVERRIDE_FREEZE); + int animStart = (curAnim.firstFrame); + int animEnd = (curAnim.firstFrame)+(animations[anim].numFrames); + + // If We Have A Blend Timer, Add The Blend Flag + //---------------------------------------------- + if (blendTime > 0) + { + animFlags |= BONE_ANIM_BLEND; + } + + // If Animation Is Going Backwards, Swap Last And First Frames + //------------------------------------------------------------- + if (animSpeed<0.0f) + { +// #ifndef FINAL_BUILD + #if 0 + if (g_AnimWarning->integer==1) + { + if (animFlags&BONE_ANIM_OVERRIDE_LOOP) + { + gi.Printf(S_COLOR_YELLOW"PM_SetAnimFinal: WARNING: Anim (%s) looping backwards!\n", animTable[anim].name); + } + } + #endif + + int temp = animEnd; + animEnd = animStart; + animStart = temp; + blendTime = 0; + } + + // If The Animation Is Walking Or Running, Attempt To Scale The Playback Speed To Match + //-------------------------------------------------------------------------------------- + if (g_noFootSlide->integer + && animFootMove + && !(animSpeed<0.0f) + //FIXME: either read speed from animation.cfg or only do this for NPCs + // for whom we've specifically determined the proper numbers! + && gent->client->NPC_class != CLASS_HOWLER + && gent->client->NPC_class != CLASS_WAMPA + && gent->client->NPC_class != CLASS_GONK + && gent->client->NPC_class != CLASS_HOWLER + && gent->client->NPC_class != CLASS_MOUSE + && gent->client->NPC_class != CLASS_PROBE + && gent->client->NPC_class != CLASS_PROTOCOL + && gent->client->NPC_class != CLASS_R2D2 + && gent->client->NPC_class != CLASS_R5D2 + && gent->client->NPC_class != CLASS_SEEKER) + { + bool Walking = !!PM_WalkingAnim(anim); + bool HasDual = (gent->client->ps.saberAnimLevel==SS_DUAL); + bool HasStaff = (gent->client->ps.saberAnimLevel==SS_STAFF); + float moveSpeedOfAnim = 150.0f;//g_noFootSlideRunScale->value; + + if (anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK) + { + moveSpeedOfAnim = 75.0f; + } + else + { + if (gent->client->NPC_class == CLASS_HAZARD_TROOPER) + { + moveSpeedOfAnim = 50.0f; + } + else if (gent->client->NPC_class == CLASS_RANCOR) + { + moveSpeedOfAnim = 173.0f; + } + else + { + if (Walking) + { + if (HasDual || HasStaff) + { + moveSpeedOfAnim = 100.0f; + } + else + { + moveSpeedOfAnim = 50.0f;// g_noFootSlideWalkScale->value; + } + } + else + { + if (HasStaff) + { + moveSpeedOfAnim = 250.0f; + } + else + { + moveSpeedOfAnim = 150.0f; + } + } + } + } + + + + + + + animSpeed *= (gent->resultspeed/moveSpeedOfAnim); + if (animSpeed<0.01f) + { + animSpeed = 0.01f; + } + + // Make Sure Not To Play Too Fast An Anim + //---------------------------------------- + float maxPlaybackSpeed = (1.5f * timeScaleMod); + if (animSpeed>maxPlaybackSpeed) + { + animSpeed = maxPlaybackSpeed; + } + } + + +// GET VALUES FOR EXISTING BODY ANIMATION +//========================================== + float bodySpeed = 0.0f; + float bodyCurrent = 0.0f; + int bodyStart = 0; + int bodyEnd = 0; + int bodyFlags = 0; + int bodyAnim = (*legsAnim); + int bodyBone = (gent->rootBone); + bool bodyTimerOn = ((*legsAnimTimer>0) || (*legsAnimTimer)==-1); + bool bodyPlay = ((setAnimParts&SETANIM_LEGS) && (bodyBone!=-1) && (animOverride || !bodyTimerOn)); + bool bodyAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, actualTime, &bodyCurrent, &bodyStart, &bodyEnd, &bodyFlags, &bodySpeed, NULL); + bool bodyOnAnimNow = (bodyAnimating && bodyAnim==anim && bodyStart==animStart && bodyEnd==animEnd); + bool bodyMatchTorsFrame = false; + + +// GET VALUES FOR EXISTING TORSO ANIMATION +//=========================================== + float torsSpeed = 0.0f; + float torsCurrent = 0.0f; + int torsStart = 0; + int torsEnd = 0; + int torsFlags = 0; + int torsAnim = (*torsoAnim); + int torsBone = (gent->lowerLumbarBone); + bool torsTimerOn = ((*torsoAnimTimer)>0 || (*torsoAnimTimer)==-1); + bool torsPlay = (gent->client->NPC_class!=CLASS_RANCOR && (setAnimParts&SETANIM_TORSO) && (torsBone!=-1) && (animOverride || !torsTimerOn)); + bool torsAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, actualTime, &torsCurrent, &torsStart, &torsEnd, &torsFlags, &torsSpeed, NULL); + bool torsOnAnimNow = (torsAnimating && torsAnim==anim && torsStart==animStart && torsEnd==animEnd); + bool torsMatchBodyFrame = false; + + +// APPLY SYNC TO TORSO +//===================== + if (animSync && torsPlay && !bodyPlay && bodyOnAnimNow && (!torsOnAnimNow || torsCurrent!=bodyCurrent)) + { + torsMatchBodyFrame = true; + animCurrent = bodyCurrent; + } + if (animSync && bodyPlay && !torsPlay && torsOnAnimNow && (!bodyOnAnimNow || bodyCurrent!=torsCurrent)) + { + bodyMatchTorsFrame = true; + animCurrent = torsCurrent; + } + + // If Already Doing These Exact Parameters, Then Don't Play + //---------------------------------------------------------- + if (!animRestart) + { + torsPlay &= !(torsOnAnimNow && torsSpeed==animSpeed && !torsMatchBodyFrame); + bodyPlay &= !(bodyOnAnimNow && bodySpeed==animSpeed && !bodyMatchTorsFrame); + } + +#ifndef FINAL_BUILD + if ((cg_debugAnim.integer==3) || // 3 = do everyone + (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player + (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else + (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + ) + { + if (bodyPlay || torsPlay) + { + char* entName = gent->targetname; + char* location; + + // Select Entity Name + //-------------------- + if (!entName || !entName[0]) + { + entName = gent->NPC_targetname; + } + if (!entName || !entName[0]) + { + entName = gent->NPC_type; + } + if (!entName || !entName[0]) + { + entName = gent->classname; + } + if (!entName || !entName[0]) + { + entName = "UNKNOWN"; + } + + // Select Play Location + //---------------------- + if (bodyPlay && torsPlay) + { + location = "BOTH "; + } + else if (bodyPlay) + { + location = "LEGS "; + } + else + { + location = "TORSO"; + } + + // Print It! + //----------- + Com_Printf("[%10d] ent[%3d-%18s] %s anim[%3d] - %s\n", + actualTime, + gent->s.number, + entName, + location, + anim, + animTable[anim].name ); + } + } +#endif + + +// PLAY ON THE TORSO +//======================== + if (torsPlay) + { + *torsoAnim = anim; + float oldAnimCurrent = animCurrent; + if (animCurrent!=bodyCurrent && torsOnAnimNow && !animRestart && !torsMatchBodyFrame) + { + animCurrent = torsCurrent; + } + + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + if (gent->motionBone!=-1) + { + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->motionBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + } + + animCurrent = oldAnimCurrent; + + // If This Animation Is To Be Locked And Held, Calculate The Duration And Set The Timer + //-------------------------------------------------------------------------------------- + if (animHold || animHoldless) + { + PM_SetTorsoAnimTimer(gent, torsoAnimTimer, animHoldMSec); + } + } + +// PLAY ON THE WHOLE BODY +//======================== + if (bodyPlay) + { + *legsAnim = anim; + + if (bodyOnAnimNow && !animRestart && !bodyMatchTorsFrame) + { + animCurrent = bodyCurrent; + } + + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, + animStart, + animEnd, + (bodyOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + // If This Animation Is To Be Locked And Held, Calculate The Duration And Set The Timer + //-------------------------------------------------------------------------------------- + if (animHold || animHoldless) + { + PM_SetLegsAnimTimer(gent, legsAnimTimer, animHoldMSec); + } + } + + + + + +// PRINT SOME DEBUG TEXT OF EXISTING VALUES +//========================================== + if (false) + { + gi.Printf("PLAYANIM: (%3d) Speed(%4.2f) ", anim, animSpeed); + if (bodyAnimating) + { + gi.Printf("BODY: (%4.2f) (%4.2f) ", bodyCurrent, bodySpeed); + } + else + { + gi.Printf(" "); + } + if (torsAnimating) + { + gi.Printf("TORS: (%4.2f) (%4.2f)\n", torsCurrent, torsSpeed); + } + else + { + gi.Printf("\n"); + } + } +} + + + +void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime) +{ // FIXME : once torsoAnim and legsAnim are in the same structure for NPC and Players + // rename PM_SetAnimFinal to PM_SetAnim and have both NPC and Players call PM_SetAnim + + if ( pm->ps->pm_type >= PM_DEAD ) + {//FIXME: sometimes we'll want to set anims when your dead... twitches, impacts, etc. + return; + } + + if ( pm->gent == NULL ) + { + return; + } + + if ( !pm->gent || pm->gent->health > 0 ) + {//don't lock anims if the guy is dead + if ( pm->ps->torsoAnimTimer + && PM_LockedAnim( pm->ps->torsoAnim ) + && !PM_LockedAnim( anim ) ) + {//nothing can override these special anims + setAnimParts &= ~SETANIM_TORSO; + } + + if ( pm->ps->legsAnimTimer + && PM_LockedAnim( pm->ps->legsAnim ) + && !PM_LockedAnim( anim ) ) + {//nothing can override these special anims + setAnimParts &= ~SETANIM_LEGS; + } + } + + if ( !setAnimParts ) + { + return; + } + + if (setAnimFlags&SETANIM_FLAG_OVERRIDE) + { +// pm->ps->animationTimer = 0; + + if (setAnimParts & SETANIM_TORSO) + { + if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->torsoAnim != anim ) + { + PM_SetTorsoAnimTimer( pm->gent, &pm->ps->torsoAnimTimer, 0 ); + } + } + if (setAnimParts & SETANIM_LEGS) + { + if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->legsAnim != anim ) + { + PM_SetLegsAnimTimer( pm->gent, &pm->ps->legsAnimTimer, 0 ); + } + } + } + + PM_SetAnimFinal(&pm->ps->torsoAnim,&pm->ps->legsAnim,setAnimParts,anim,setAnimFlags,&pm->ps->torsoAnimTimer,&pm->ps->legsAnimTimer,&g_entities[pm->ps->clientNum],blendTime);//was pm->gent +} + +bool TorsoAgainstWindTest( gentity_t* ent ) +{ + if (ent&&//valid ent + ent->client&&//a client + (ent->client->ps.weapon!=WP_SABER||ent->client->ps.saberMove==LS_READY)&&//either not holding a saber or the saber is in the ready pose + (ent->s.numbercurrentOrigin) && + gi.WE_IsOutside(ent->currentOrigin) ) + { + if (Q_stricmp(level.mapname, "t2_wedge")!=0) + { + vec3_t fwd; + vec3_t windDir; + if (gi.WE_GetWindVector(windDir, ent->currentOrigin)) + { + VectorScale(windDir, -1.0f, windDir); + AngleVectors(pm->gent->currentAngles, fwd, 0, 0); + if (DotProduct(fwd, windDir)>0.65f) + { + if (ent->client && ent->client->ps.torsoAnim!=BOTH_WIND) + { + NPC_SetAnim(ent, SETANIM_TORSO, BOTH_WIND, SETANIM_FLAG_NORMAL, 400); + } + return true; + } + } + } + } + return false; +} + +/* +------------------------- +PM_TorsoAnimLightsaber +------------------------- +*/ + + +// Note that this function is intended to set the animation for the player, but +// only does idle-ish anims. Anything that has a timer associated, such as attacks and blocks, +// are set by PM_WeaponLightsaber() + +extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt ); +extern qboolean PM_LandingAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +qboolean PM_InCartwheel( int anim ); +void PM_TorsoAnimLightsaber() +{ + // ********************************************************* + // WEAPON_READY + // ********************************************************* + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + {//holding an enemy aloft with force-grip + return; + } + + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + {//lightning + return; + } + + if ( pm->ps->forcePowersActive&(1<ps->saber[0].blade[0].active + && pm->ps->saber[0].blade[0].length < 3 + && !(pm->ps->saberEventFlags&SEF_HITWALL) + && pm->ps->weaponstate == WEAPON_RAISING ) + { + if (!G_IsRidingVehicle(pm->gent)) + { + PM_SetSaberMove(LS_DRAW); + } + return; + } + else if ( !pm->ps->SaberActive() && pm->ps->SaberLength() ) + { + if (!G_IsRidingVehicle(pm->gent)) + { + PM_SetSaberMove(LS_PUTAWAY); + } + return; + } + + if (pm->ps->weaponTime > 0) + { // weapon is already busy. + if ( pm->ps->torsoAnim == BOTH_TOSS1 + || pm->ps->torsoAnim == BOTH_TOSS2 ) + {//in toss + if ( !pm->ps->torsoAnimTimer ) + {//weird, get out of it, I guess + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + } + return; + } + + if ( pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + {//ready + if ( pm->ps->weapon == WP_SABER && (pm->ps->SaberLength()) ) + {//saber is on + // Select the proper idle Lightsaber attack move from the chart. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( (PM_RunningAnim( pm->ps->legsAnim ) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) + && pm->ps->saberBlockingTime < cg.time ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + /* + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + {//jumping, landing cartwheel, flipping + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetSaberMove( LS_READY ); + } + */ + } + else if (TorsoAgainstWindTest(pm->gent)) + { + } + else if( pm->ps->legsAnim == BOTH_RUN1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN2 )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN_DUAL ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK_DUAL ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + { + //??? Why nothing? What if you were running??? + //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_JUMP1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else + {//Used to default to both_stand1 which is an arms-down anim +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // Select the next proper pose for the lightsaber assuming that there are no attacks. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + } + + // ********************************************************* + // WEAPON_IDLE + // ********************************************************* + + else if ( pm->ps->weaponstate == WEAPON_IDLE ) + { + if (TorsoAgainstWindTest(pm->gent)) + { + } + else if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else + { +// This is now set in SetSaberMove. + // Idle for Lightsaber + if ( pm->gent && pm->gent->client ) + { +// pm->gent->client->saberTrail.inAction = qfalse; + } + + qboolean saberInAir = qtrue; + if ( pm->ps->saberInFlight ) + {//guiding saber + if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + {//we're stuck in a broken parry + saberInAir = qfalse; + } + if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + {// + if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + {//fell to the ground and we're not trying to pull it back + saberInAir = qfalse; + } + } + } + if ( pm->ps->saberInFlight + && saberInAir + && (!pm->ps->dualSabers || !pm->ps->saber[1].Active())) + { + if ( !PM_ForceAnim( pm->ps->torsoAnim ) + || pm->ps->torsoAnimTimer < 300 ) + {//don't interrupt a force power anim + if ( pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer ) + { + PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } + } + else + {//saber is on + // Idle for Lightsaber + if ( pm->gent && pm->gent->client ) + { + if ( !G_InCinematicSaberAnim( pm->gent ) ) + { + pm->gent->client->ps.SaberDeactivateTrail( 0 ); + } + } + // Idle for idle/ready Lightsaber +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONIDLE1 + // Select the proper idle Lightsaber attack move from the chart. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( (PM_RunningAnim( pm->ps->legsAnim ) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) + && pm->ps->saberBlockingTime < cg.time ) + {//running w/1-handed weapon uses full-body anim + int setFlags = SETANIM_FLAG_NORMAL; + if ( PM_LandingAnim( pm->ps->torsoAnim ) ) + { + setFlags = SETANIM_FLAG_OVERRIDE; + } + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,setFlags); + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + } + } + } +} + + + + +/* +------------------------- +PM_TorsoAnimation +------------------------- +*/ + +void PM_TorsoAnimation( void ) +{//FIXME: Write a much smarter and more appropriate anim picking routine logic... +// int oldAnim; + if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps )) + {//in knockdown + return; + } + + if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + { + return; + } + + if ( (pm->ps->eFlags&EF_FORCE_DRAINED) ) + {//being drained + //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + return; + } + if ( (pm->ps->forcePowersActive&(1<ps->forceDrainEntityNum < ENTITYNUM_WORLD ) + {//draining + //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGER1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + return; + } + + if( pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags & SCF_FORCED_MARCH) ) + { + return; + } + + if(pm->gent != NULL && pm->gent->client) + { + pm->gent->client->renderInfo.torsoFpsMod = 1.0f; + } + + if ( pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON ) + { + if ( pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use )//ugly way to tell, but... + {//full body + PM_SetAnim(pm,SETANIM_BOTH,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else + {//torso + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + return; + } +/* else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->ps->clientNum < MAX_CLIENTS && (m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].numHands == 2 || g_speederControlScheme->value == 2) ) + {//can't look around + PM_SetAnim(pm,SETANIM_TORSO,m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].riderAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + return; + }*/ + + if ( pm->ps->taunting > level.time ) + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_ALORA_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else if ( pm->ps->weapon == WP_SABER && pm->ps->saberAnimLevel == SS_DUAL && PM_HasAnimation( pm->gent, BOTH_DUAL_TAUNT ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_DUAL_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else if ( pm->ps->weapon == WP_SABER + && pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->saber[0].type == SABER_STAFF ) + {//turn on the blades + if ( PM_HasAnimation( pm->gent, BOTH_STAFF_TAUNT ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_STAFF_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + /* + else + { + if ( !pm->ps->saber[0].blade[0].active ) + {//first blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 0, qtrue ); + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is also off, extend time of this taunt so we have enough time to turn them both on + pm->ps->taunting = level.time + 3000; + } + } + else if ( (pm->ps->taunting - level.time) < 1500 ) + {//only 1500ms left in taunt + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 1, qtrue ); + } + } + //pose + PM_SetAnim(pm,SETANIM_BOTH,BOTH_SABERSTAFF_STANCE,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + pm->ps->torsoAnimTimer = pm->ps->legsAnimTimer = (pm->ps->taunting - level.time); + } + */ + } + else if ( PM_HasAnimation( pm->gent, BOTH_GESTURE1 ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_GESTURE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + pm->gent->client->ps.SaberActivateTrail( 100 ); + //FIXME: will this reset? + //FIXME: force-control (yellow glow) effect on hand and saber? + } + else + { + //PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL); + } + return; + } + + if (pm->ps->weapon == WP_SABER ) // WP_LIGHTSABER + { + qboolean saberInAir = qfalse; + if ( pm->ps->SaberLength() && !pm->ps->saberInFlight ) + { + PM_TorsoAnimLightsaber(); + } + else + { + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + {//holding an enemy aloft with force-grip + return; + } + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + {//lightning + return; + } + if ( pm->ps->forcePowersActive&(1<ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + {//we're stuck in a broken parry + PM_TorsoAnimLightsaber(); + } + else + { + if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + {// + if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + {//fell to the ground and we're not trying to pull it back + saberInAir = qfalse; + } + } + + if ( pm->ps->saberInFlight + && saberInAir + && (!pm->ps->dualSabers //not using 2 sabers + || !pm->ps->saber[1].Active() //left one off + || pm->ps->torsoAnim == BOTH_SABERDUAL_STANCE//not attacking + || pm->ps->torsoAnim == BOTH_SABERPULL//not attacking + || pm->ps->torsoAnim == BOTH_STAND1//not attacking + || PM_RunningAnim( pm->ps->torsoAnim ) //not attacking + || PM_WalkingAnim( pm->ps->torsoAnim ) //not attacking + || PM_JumpingAnim( pm->ps->torsoAnim )//not attacking + || PM_SwimmingAnim( pm->ps->torsoAnim ) )//not attacking + ) + { + if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 ) + {//don't interrupt a force power anim + if ( pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer ) + { + PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } + } + else + { + if ( PM_InSlopeAnim( pm->ps->legsAnim ) ) + {//HMM... this probably breaks the saber putaway and select anims + if ( pm->ps->SaberLength() > 0 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + } + } + } + } + + if (pm->ps->weaponTime<= 0 && (pm->ps->saberMove==LS_READY || pm->ps->SaberLength()==0) && !saberInAir) + { + TorsoAgainstWindTest(pm->gent); + } + return; + } + + if ( PM_ForceAnim( pm->ps->torsoAnim ) + && pm->ps->torsoAnimTimer > 0 ) + {//in a force anim, don't do a stand anim + return; + } + + + qboolean weaponBusy = qfalse; + + if ( pm->ps->weapon == WP_NONE ) + { + weaponBusy = qfalse; + } + else if ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->lastShotTime > level.time - 3000 ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->weaponTime > 0 ) + { + weaponBusy = qtrue; + } + else if ( pm->gent && pm->gent->client->fireDelay > 0 ) + { + weaponBusy = qtrue; + } + else if ( TorsoAgainstWindTest(pm->gent) ) + { + return; + } + else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.zoomTime > cg.time - 5000 ) + {//if we used binoculars recently, aim weapon + weaponBusy = qtrue; + pm->ps->weaponstate = WEAPON_IDLE; + } + else if ( pm->ps->pm_flags & PMF_DUCKED ) + {//ducking is considered on alert... plus looks stupid to have arms hanging down when crouched + weaponBusy = qtrue; + } + + if ( pm->ps->weapon == WP_NONE || + pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + { + if ( pm->ps->weapon == WP_SABER && pm->ps->SaberLength() ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + } + else if( pm->ps->legsAnim == BOTH_RUN1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN2 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN4 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN_STAFF && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN_DUAL && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK2 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK_STAFF && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK_DUAL&& !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + { + //??? Why nothing? What if you were running??? + //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_JUMP1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL, 100); // Only blend over 100ms + } + else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIMFORWARD && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + } + else if ( pm->ps->weapon == WP_NONE ) + { + int legsAnim = pm->ps->legsAnim; + /* + if ( PM_RollingAnim( legsAnim ) || + PM_FlippingAnim( legsAnim ) || + PM_JumpingAnim( legsAnim ) || + PM_PainAnim( legsAnim ) || + PM_SwimmingAnim( legsAnim ) ) + */ + { + PM_SetAnim(pm, SETANIM_TORSO, legsAnim, SETANIM_FLAG_NORMAL ); + } + } + else + {//Used to default to both_stand1 which is an arms-down anim + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else if ( pm->gent != NULL + && (pm->gent->s.numbergent)) + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT ) + {//PLayer- temp hack for weapon frame + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->ps->weapon == WP_MELEE ) + {//hehe + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + {//use legs anim + //FIXME: or just use whatever's currently playing? + //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); + } + else + { + switch(pm->ps->weapon) + { + // ******************************************************** + case WP_SABER: // WP_LIGHTSABER + // Ready pose for Lightsaber +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // Select the next proper pose for the lightsaber assuming that there are no attacks. + if (pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + break; + // ******************************************************** + + case WP_BRYAR_PISTOL: + //FIXME: if recently fired, hold the ready! + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + break; + case WP_BLASTER_PISTOL: + if ( pm->gent + && pm->gent->weaponModel[1] > 0 ) + {//dual pistols + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + } + else + {//single pistols + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + } + break; + case WP_NONE: + //NOTE: should never get here + break; + case WP_MELEE: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + break; + case WP_TUSKEN_STAFF: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND3, SETANIM_FLAG_NORMAL); + } + break; + + case WP_NOGHRI_STICK: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); + break; + + case WP_BLASTER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); + break; + case WP_DISRUPTOR: + case WP_TUSKEN_RIFLE: + if ( (pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running sniper weapon uses normal ready + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + } + else + { + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );//TORSO_WEAPONREADY4//SETANIM_FLAG_RESTART| + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + } + break; + case WP_BOT_LASER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + break; + case WP_THERMAL: + if ( pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT + && (PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim )) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && (pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT) ) + {//player pulling back to throw + if ( PM_StandingAnim( pm->ps->legsAnim ) ) + { + PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else if ( pm->ps->legsAnim == BOTH_THERMAL_READY ) + {//sigh... hold it so pm_footsteps doesn't override + if ( pm->ps->legsAnimTimer < 100 ) + { + pm->ps->legsAnimTimer = 100; + } + } + PM_SetAnim( pm, SETANIM_TORSO, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY10, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + } + break; + case WP_REPEATER: + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + {// + if ( pm->gent->alt_fire ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY1,SETANIM_FLAG_NORMAL); + } + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + default: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + break; + } + } + } + } + else if ( pm->ps->weaponstate == WEAPON_IDLE ) + { + if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIMFORWARD ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + } + else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + {//use legs anim + //FIXME: or just use whatever's currently playing? + //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); + } + else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( !weaponBusy + && pm->ps->weapon != WP_BOWCASTER + && pm->ps->weapon != WP_REPEATER + && pm->ps->weapon != WP_FLECHETTE + && pm->ps->weapon != WP_ROCKET_LAUNCHER + && pm->ps->weapon != WP_CONCUSSION + && ( PM_RunningAnim( pm->ps->legsAnim ) + || (PM_WalkingAnim( pm->ps->legsAnim ) && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) ) + {//running w/1-handed or light 2-handed weapon uses full-body anim if you're not using the weapon right now + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + switch ( pm->ps->weapon ) + { + // ******************************************************** + case WP_SABER: // WP_LIGHTSABER + // Shouldn't get here, should go to TorsoAnimLightsaber + break; + // ******************************************************** + + case WP_BRYAR_PISTOL: + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + } + break; + case WP_BLASTER_PISTOL: + if ( pm->gent + && pm->gent->weaponModel[1] > 0 ) + {//dual pistols + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else + {//single pistols + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + } + } + break; + + case WP_NONE: + //NOTE: should never get here + break; + + case WP_MELEE: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + break; + + case WP_TUSKEN_STAFF: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND3, SETANIM_FLAG_NORMAL); + } + break; + + case WP_NOGHRI_STICK: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_BLASTER: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_DISRUPTOR: + case WP_TUSKEN_RIFLE: + if ( (pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running sniper weapon uses normal ready + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + } + else + { + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + } + break; + + case WP_BOT_LASER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + break; + + case WP_THERMAL: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE10, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + + case WP_REPEATER: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + + default: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + } + } + } + } +} + +//========================================================================= +// Anim checking utils +//========================================================================= + +int PM_GetTurnAnim( gentity_t *gent, int anim ) +{ + if ( !gent ) + { + return -1; + } + + switch( anim ) + { + case BOTH_STAND1: //# Standing idle: no weapon: hands down + case BOTH_STAND1IDLE1: //# Random standing idle + case BOTH_STAND2: //# Standing idle with a weapon + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle + case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle + case BOTH_STAND4: //# two handed: gun down: relaxed stand + case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle + case BOTH_STAND6: //# one handed: gun at side: relaxed stand + case BOTH_STAND2TO4: //# Transition from stand2 to stand4 + case BOTH_STAND4TO2: //# Transition from stand4 to stand2 + case BOTH_GESTURE1: //# Generic gesture: non-specific + case BOTH_GESTURE2: //# Generic gesture: non-specific + case BOTH_TALK1: //# Generic talk anim + case BOTH_TALK2: //# Generic talk anim + if ( PM_HasAnimation( gent, LEGS_TURN1 ) ) + { + return LEGS_TURN1; + } + else + { + return -1; + } + break; + case BOTH_ATTACK1: //# Attack with generic 1-handed weapon + case BOTH_ATTACK2: //# Attack with generic 2-handed weapon + case BOTH_ATTACK3: //# Attack with heavy 2-handed weapon + case BOTH_ATTACK4: //# Attack with ??? + case BOTH_MELEE1: //# First melee attack + case BOTH_MELEE2: //# Second melee attack + case BOTH_GUARD_LOOKAROUND1: //# Cradling weapon and looking around + case BOTH_GUARD_IDLE1: //# Cradling weapon and standing + if ( PM_HasAnimation( gent, LEGS_TURN2 ) ) + { + return LEGS_TURN2; + } + else + { + return -1; + } + break; + default: + return -1; + break; + } +} + +int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) +{ + if ( !gent ) + { + return -1; + } + + switch( anim ) + { + case BOTH_STAND1: //# Standing idle: no weapon: hands down + case BOTH_STAND1IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND1 ) ) + { + return BOTH_TURNSTAND1; + } + else + { + return -1; + } + break; + case BOTH_STAND2: //# Standing idle with a weapon + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND2 ) ) + { + return BOTH_TURNSTAND2; + } + else + { + return -1; + } + break; + case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND3 ) ) + { + return BOTH_TURNSTAND3; + } + else + { + return -1; + } + break; + case BOTH_STAND4: //# two handed: gun down: relaxed stand + if ( PM_HasAnimation( gent, BOTH_TURNSTAND4 ) ) + { + return BOTH_TURNSTAND4; + } + else + { + return -1; + } + break; + case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND5 ) ) + { + return BOTH_TURNSTAND5; + } + else + { + return -1; + } + break; + case BOTH_CROUCH1: //# Transition from standing to crouch + case BOTH_CROUCH1IDLE: //# Crouching idle + /* + case BOTH_UNCROUCH1: //# Transition from crouch to standing + case BOTH_CROUCH2TOSTAND1: //# going from crouch2 to stand1 + case BOTH_CROUCH3: //# Desann crouching down to Kyle (cin 9) + case BOTH_UNCROUCH3: //# Desann uncrouching down to Kyle (cin 9) + case BOTH_CROUCH4: //# Slower version of crouch1 for cinematics + case BOTH_UNCROUCH4: //# Slower version of uncrouch1 for cinematics + */ + if ( PM_HasAnimation( gent, BOTH_TURNCROUCH1 ) ) + { + return BOTH_TURNCROUCH1; + } + else + { + return -1; + } + break; + default: + return -1; + break; + } +} + +qboolean PM_InOnGroundAnim ( playerState_t *ps ) +{ + switch( ps->legsAnim ) + { + case BOTH_DEAD1: + case BOTH_DEAD2: + case BOTH_DEAD3: + case BOTH_DEAD4: + case BOTH_DEAD5: + case BOTH_DEADFORWARD1: + case BOTH_DEADBACKWARD1: + case BOTH_DEADFORWARD2: + case BOTH_DEADBACKWARD2: + case BOTH_LYINGDEATH1: + case BOTH_LYINGDEAD1: + case BOTH_SLEEP1: //# laying on back-rknee up-rhand on torso + return qtrue; + break; + case BOTH_KNOCKDOWN1: //# + case BOTH_KNOCKDOWN2: //# + case BOTH_KNOCKDOWN3: //# + case BOTH_KNOCKDOWN4: //# + case BOTH_KNOCKDOWN5: //# + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_RELEASED: + if ( ps->legsAnimTimer < 500 ) + {//pretty much horizontal by this point + return qtrue; + } + break; + case BOTH_PLAYER_PA_3_FLY: + if ( ps->legsAnimTimer < 300 ) + {//pretty much horizontal by this point + return qtrue; + } + /* + else if ( ps->clientNum < MAX_CLIENTS + && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) + { + return qtrue; + } + */ + break; + case BOTH_GETUP1: + case BOTH_GETUP2: + case BOTH_GETUP3: + case BOTH_GETUP4: + case BOTH_GETUP5: + case BOTH_GETUP_CROUCH_F1: + case BOTH_GETUP_CROUCH_B1: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + if ( ps->legsAnimTimer > PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim )-400 ) + {//still pretty much horizontal at this point + return qtrue; + } + break; + } + + return qfalse; +} + +qboolean PM_InSpecialDeathAnim( int anim ) +{ + switch( pm->ps->legsAnim ) + { + case BOTH_DEATH_ROLL: //# Death anim from a roll + case BOTH_DEATH_FLIP: //# Death anim from a flip + case BOTH_DEATH_SPIN_90_R: //# Death anim when facing 90 degrees right + case BOTH_DEATH_SPIN_90_L: //# Death anim when facing 90 degrees left + case BOTH_DEATH_SPIN_180: //# Death anim when facing backwards + case BOTH_DEATH_LYING_UP: //# Death anim when lying on back + case BOTH_DEATH_LYING_DN: //# Death anim when lying on front + case BOTH_DEATH_FALLING_DN: //# Death anim when falling on face + case BOTH_DEATH_FALLING_UP: //# Death anim when falling on back + case BOTH_DEATH_CROUCHED: //# Death anim when crouched + return qtrue; + break; + default: + return qfalse; + break; + } +} + +qboolean PM_InDeathAnim ( void ) +{//Purposely does not cover stumbledeath and falldeath... + switch( pm->ps->legsAnim ) + { + case BOTH_DEATH1: //# First Death anim + case BOTH_DEATH2: //# Second Death anim + case BOTH_DEATH3: //# Third Death anim + case BOTH_DEATH4: //# Fourth Death anim + case BOTH_DEATH5: //# Fifth Death anim + case BOTH_DEATH6: //# Sixth Death anim + case BOTH_DEATH7: //# Seventh Death anim + case BOTH_DEATH8: //# + case BOTH_DEATH9: //# + case BOTH_DEATH10: //# + case BOTH_DEATH11: //# + case BOTH_DEATH12: //# + case BOTH_DEATH13: //# + case BOTH_DEATH14: //# + case BOTH_DEATH14_UNGRIP: //# Desann's end death (cin #35) + case BOTH_DEATH14_SITUP: //# Tavion sitting up after having been thrown (cin #23) + case BOTH_DEATH15: //# + case BOTH_DEATH16: //# + case BOTH_DEATH17: //# + case BOTH_DEATH18: //# + case BOTH_DEATH19: //# + case BOTH_DEATH20: //# + case BOTH_DEATH21: //# + case BOTH_DEATH22: //# + case BOTH_DEATH23: //# + case BOTH_DEATH24: //# + case BOTH_DEATH25: //# + + case BOTH_DEATHFORWARD1: //# First Death in which they get thrown forward + case BOTH_DEATHFORWARD2: //# Second Death in which they get thrown forward + case BOTH_DEATHFORWARD3: //# Tavion's falling in cin# 23 + case BOTH_DEATHBACKWARD1: //# First Death in which they get thrown backward + case BOTH_DEATHBACKWARD2: //# Second Death in which they get thrown backward + + case BOTH_DEATH1IDLE: //# Idle while close to death + case BOTH_LYINGDEATH1: //# Death to play when killed lying down + case BOTH_STUMBLEDEATH1: //# Stumble forward and fall face first death + case BOTH_FALLDEATH1: //# Fall forward off a high cliff and splat death - start + case BOTH_FALLDEATH1INAIR: //# Fall forward off a high cliff and splat death - loop + case BOTH_FALLDEATH1LAND: //# Fall forward off a high cliff and splat death - hit bottom + //# #sep case BOTH_ DEAD POSES # Should be last frame of corresponding previous anims + case BOTH_DEAD1: //# First Death finished pose + case BOTH_DEAD2: //# Second Death finished pose + case BOTH_DEAD3: //# Third Death finished pose + case BOTH_DEAD4: //# Fourth Death finished pose + case BOTH_DEAD5: //# Fifth Death finished pose + case BOTH_DEAD6: //# Sixth Death finished pose + case BOTH_DEAD7: //# Seventh Death finished pose + case BOTH_DEAD8: //# + case BOTH_DEAD9: //# + case BOTH_DEAD10: //# + case BOTH_DEAD11: //# + case BOTH_DEAD12: //# + case BOTH_DEAD13: //# + case BOTH_DEAD14: //# + case BOTH_DEAD15: //# + case BOTH_DEAD16: //# + case BOTH_DEAD17: //# + case BOTH_DEAD18: //# + case BOTH_DEAD19: //# + case BOTH_DEAD20: //# + case BOTH_DEAD21: //# + case BOTH_DEAD22: //# + case BOTH_DEAD23: //# + case BOTH_DEAD24: //# + case BOTH_DEAD25: //# + case BOTH_DEADFORWARD1: //# First thrown forward death finished pose + case BOTH_DEADFORWARD2: //# Second thrown forward death finished pose + case BOTH_DEADBACKWARD1: //# First thrown backward death finished pose + case BOTH_DEADBACKWARD2: //# Second thrown backward death finished pose + case BOTH_LYINGDEAD1: //# Killed lying down death finished pose + case BOTH_STUMBLEDEAD1: //# Stumble forward death finished pose + case BOTH_FALLDEAD1LAND: //# Fall forward and splat death finished pose + //# #sep case BOTH_ DEAD TWITCH/FLOP # React to being shot from death poses + case BOTH_DEADFLOP1: //# React to being shot from First Death finished pose + case BOTH_DEADFLOP2: //# React to being shot from Second Death finished pose + case BOTH_DISMEMBER_HEAD1: //# + case BOTH_DISMEMBER_TORSO1: //# + case BOTH_DISMEMBER_LLEG: //# + case BOTH_DISMEMBER_RLEG: //# + case BOTH_DISMEMBER_RARM: //# + case BOTH_DISMEMBER_LARM: //# + return qtrue; + break; + default: + return PM_InSpecialDeathAnim( pm->ps->legsAnim ); + break; + } +} + +qboolean PM_InCartwheel( int anim ) +{ + switch ( anim ) + { + case BOTH_ARIAL_LEFT: + case BOTH_ARIAL_RIGHT: + case BOTH_ARIAL_F1: + case BOTH_CARTWHEEL_LEFT: + case BOTH_CARTWHEEL_RIGHT: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_InButterfly( int anim ) +{ + switch ( anim ) + { + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_StandingAnim( int anim ) +{//NOTE: does not check idles or special (cinematic) stands + switch ( anim ) + { + case BOTH_STAND1: + case BOTH_STAND2: + case BOTH_STAND3: + case BOTH_STAND4: + case BOTH_ATTACK3: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_InAirKickingAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + return qtrue; + } + return qfalse; +} + +qboolean PM_KickingAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + //NOT a kick, but acts like one: + case BOTH_A7_HILT: + //NOT kicks, but do kick traces anyway + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + return qtrue; + break; + default: + return PM_InAirKickingAnim( anim ); + break; + } + //return qfalse; +} + +qboolean PM_StabDownAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + return qtrue; + } + return qfalse; +} + +qboolean PM_GoingToAttackDown( playerState_t *ps ) +{ + if ( PM_StabDownAnim( ps->torsoAnim )//stabbing downward + || ps->saberMove == LS_A_LUNGE//lunge + || ps->saberMove == LS_A_JUMP_T__B_//death from above + || ps->saberMove == LS_A_T2B//attacking top to bottom + || ps->saberMove == LS_S_T2B//starting at attack downward + || (PM_SaberInTransition( ps->saberMove ) && saberMoveData[ps->saberMove].endQuad == Q_T) )//transitioning to a top to bottom attack + { + return qtrue; + } + return qfalse; +} + +qboolean PM_ForceUsingSaberAnim( int anim ) +{//saber/acrobatic anims that should prevent you from recharging force power while you're in them... + switch ( anim ) + { + case BOTH_JUMPFLIPSLASHDOWN1: + case BOTH_JUMPFLIPSTABDOWN: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_FORCELONGLEAP_START: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_FORCEWALLRUNFLIP_START: + case BOTH_FORCEWALLRUNFLIP_END: + case BOTH_FORCEWALLRUNFLIP_ALT: + case BOTH_FORCEWALLREBOUND_FORWARD: + case BOTH_FORCEWALLREBOUND_LEFT: + case BOTH_FORCEWALLREBOUND_BACK: + case BOTH_FORCEWALLREBOUND_RIGHT: + case BOTH_FLIP_ATTACK7: + case BOTH_FLIP_HOLD7: + case BOTH_FLIP_LAND: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_ARIAL_LEFT: + case BOTH_ARIAL_RIGHT: + case BOTH_CARTWHEEL_LEFT: + case BOTH_CARTWHEEL_RIGHT: + case BOTH_FLIP_LEFT: + case BOTH_FLIP_BACK1: + case BOTH_FLIP_BACK2: + case BOTH_FLIP_BACK3: + case BOTH_ALORA_FLIP_B: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_WALL_RUN_RIGHT: + case BOTH_WALL_RUN_RIGHT_FLIP: + case BOTH_WALL_RUN_RIGHT_STOP: + case BOTH_WALL_RUN_LEFT: + case BOTH_WALL_RUN_LEFT_FLIP: + case BOTH_WALL_RUN_LEFT_STOP: + case BOTH_WALL_FLIP_RIGHT: + case BOTH_WALL_FLIP_LEFT: + case BOTH_FORCEJUMP1: + case BOTH_FORCEINAIR1: + case BOTH_FORCELAND1: + case BOTH_FORCEJUMPBACK1: + case BOTH_FORCEINAIRBACK1: + case BOTH_FORCELANDBACK1: + case BOTH_FORCEJUMPLEFT1: + case BOTH_FORCEINAIRLEFT1: + case BOTH_FORCELANDLEFT1: + case BOTH_FORCEJUMPRIGHT1: + case BOTH_FORCEINAIRRIGHT1: + case BOTH_FORCELANDRIGHT1: + case BOTH_FLIP_F: + case BOTH_FLIP_B: + case BOTH_FLIP_L: + case BOTH_FLIP_R: + case BOTH_ALORA_FLIP_1: + case BOTH_ALORA_FLIP_2: + case BOTH_ALORA_FLIP_3: + case BOTH_DODGE_FL: + case BOTH_DODGE_FR: + case BOTH_DODGE_BL: + case BOTH_DODGE_BR: + case BOTH_DODGE_L: + case BOTH_DODGE_R: + case BOTH_DODGE_HOLD_FL: + case BOTH_DODGE_HOLD_FR: + case BOTH_DODGE_HOLD_BL: + case BOTH_DODGE_HOLD_BR: + case BOTH_DODGE_HOLD_L: + case BOTH_DODGE_HOLD_R: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_BROLL_L: + case BOTH_GETUP_BROLL_R: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + case BOTH_GETUP_FROLL_L: + case BOTH_GETUP_FROLL_R: + case BOTH_WALL_FLIP_BACK1: + case BOTH_WALL_FLIP_BACK2: + case BOTH_SPIN1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_DEFLECTSLASH__R__L_FIN: + case BOTH_ARIAL_F1: + return qtrue; + } + return qfalse; +} + +qboolean G_HasKnockdownAnims( gentity_t *ent ) +{ + if ( PM_HasAnimation( ent, BOTH_KNOCKDOWN1 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN2 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN3 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN4 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN5 ) ) + { + return qtrue; + } + return qfalse; +} + +qboolean PM_InAttackRoll( int anim ) +{ + switch ( anim ) + { + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + return qtrue; + } + return qfalse; +} + +qboolean PM_LockedAnim( int anim ) +{//anims that can *NEVER* be overridden, regardless + switch ( anim ) + { + case BOTH_KYLE_PA_1: + case BOTH_KYLE_PA_2: + case BOTH_KYLE_PA_3: + case BOTH_PLAYER_PA_1: + case BOTH_PLAYER_PA_2: + case BOTH_PLAYER_PA_3: + case BOTH_PLAYER_PA_3_FLY: + case BOTH_TAVION_SCEPTERGROUND: + case BOTH_TAVION_SWORDPOWER: + case BOTH_SCEPTER_START: + case BOTH_SCEPTER_HOLD: + case BOTH_SCEPTER_STOP: + //grabbed by wampa + case BOTH_GRABBED: //# + case BOTH_RELEASED: //# when Wampa drops player, transitions into fall on back + case BOTH_HANG_IDLE: //# + case BOTH_HANG_ATTACK: //# + case BOTH_HANG_PAIN: //# + return qtrue; + } + return qfalse; +} + +qboolean PM_SuperBreakLoseAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_LK_S_DL_S_SB_1_L: //super break I lost + case BOTH_LK_S_DL_T_SB_1_L: //super break I lost + case BOTH_LK_S_ST_S_SB_1_L: //super break I lost + case BOTH_LK_S_ST_T_SB_1_L: //super break I lost + case BOTH_LK_S_S_S_SB_1_L: //super break I lost + case BOTH_LK_S_S_T_SB_1_L: //super break I lost + case BOTH_LK_DL_DL_S_SB_1_L: //super break I lost + case BOTH_LK_DL_DL_T_SB_1_L: //super break I lost + case BOTH_LK_DL_ST_S_SB_1_L: //super break I lost + case BOTH_LK_DL_ST_T_SB_1_L: //super break I lost + case BOTH_LK_DL_S_S_SB_1_L: //super break I lost + case BOTH_LK_DL_S_T_SB_1_L: //super break I lost + case BOTH_LK_ST_DL_S_SB_1_L: //super break I lost + case BOTH_LK_ST_DL_T_SB_1_L: //super break I lost + case BOTH_LK_ST_ST_S_SB_1_L: //super break I lost + case BOTH_LK_ST_ST_T_SB_1_L: //super break I lost + case BOTH_LK_ST_S_S_SB_1_L: //super break I lost + case BOTH_LK_ST_S_T_SB_1_L: //super break I lost + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_SuperBreakWinAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_LK_S_DL_S_SB_1_W: //super break I won + case BOTH_LK_S_DL_T_SB_1_W: //super break I won + case BOTH_LK_S_ST_S_SB_1_W: //super break I won + case BOTH_LK_S_ST_T_SB_1_W: //super break I won + case BOTH_LK_S_S_S_SB_1_W: //super break I won + case BOTH_LK_S_S_T_SB_1_W: //super break I won + case BOTH_LK_DL_DL_S_SB_1_W: //super break I won + case BOTH_LK_DL_DL_T_SB_1_W: //super break I won + case BOTH_LK_DL_ST_S_SB_1_W: //super break I won + case BOTH_LK_DL_ST_T_SB_1_W: //super break I won + case BOTH_LK_DL_S_S_SB_1_W: //super break I won + case BOTH_LK_DL_S_T_SB_1_W: //super break I won + case BOTH_LK_ST_DL_S_SB_1_W: //super break I won + case BOTH_LK_ST_DL_T_SB_1_W: //super break I won + case BOTH_LK_ST_ST_S_SB_1_W: //super break I won + case BOTH_LK_ST_ST_T_SB_1_W: //super break I won + case BOTH_LK_ST_S_S_SB_1_W: //super break I won + case BOTH_LK_ST_S_T_SB_1_W: //super break I won + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_SaberLockBreakAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_BF1BREAK: + case BOTH_BF2BREAK: + case BOTH_CWCIRCLEBREAK: + case BOTH_CCWCIRCLEBREAK: + case BOTH_LK_S_DL_S_B_1_L: //normal break I lost + case BOTH_LK_S_DL_S_B_1_W: //normal break I won + case BOTH_LK_S_DL_T_B_1_L: //normal break I lost + case BOTH_LK_S_DL_T_B_1_W: //normal break I won + case BOTH_LK_S_ST_S_B_1_L: //normal break I lost + case BOTH_LK_S_ST_S_B_1_W: //normal break I won + case BOTH_LK_S_ST_T_B_1_L: //normal break I lost + case BOTH_LK_S_ST_T_B_1_W: //normal break I won + case BOTH_LK_S_S_S_B_1_L: //normal break I lost + case BOTH_LK_S_S_S_B_1_W: //normal break I won + case BOTH_LK_S_S_T_B_1_L: //normal break I lost + case BOTH_LK_S_S_T_B_1_W: //normal break I won + case BOTH_LK_DL_DL_S_B_1_L: //normal break I lost + case BOTH_LK_DL_DL_S_B_1_W: //normal break I won + case BOTH_LK_DL_DL_T_B_1_L: //normal break I lost + case BOTH_LK_DL_DL_T_B_1_W: //normal break I won + case BOTH_LK_DL_ST_S_B_1_L: //normal break I lost + case BOTH_LK_DL_ST_S_B_1_W: //normal break I won + case BOTH_LK_DL_ST_T_B_1_L: //normal break I lost + case BOTH_LK_DL_ST_T_B_1_W: //normal break I won + case BOTH_LK_DL_S_S_B_1_L: //normal break I lost + case BOTH_LK_DL_S_S_B_1_W: //normal break I won + case BOTH_LK_DL_S_T_B_1_L: //normal break I lost + case BOTH_LK_DL_S_T_B_1_W: //normal break I won + case BOTH_LK_ST_DL_S_B_1_L: //normal break I lost + case BOTH_LK_ST_DL_S_B_1_W: //normal break I won + case BOTH_LK_ST_DL_T_B_1_L: //normal break I lost + case BOTH_LK_ST_DL_T_B_1_W: //normal break I won + case BOTH_LK_ST_ST_S_B_1_L: //normal break I lost + case BOTH_LK_ST_ST_S_B_1_W: //normal break I won + case BOTH_LK_ST_ST_T_B_1_L: //normal break I lost + case BOTH_LK_ST_ST_T_B_1_W: //normal break I won + case BOTH_LK_ST_S_S_B_1_L: //normal break I lost + case BOTH_LK_ST_S_S_B_1_W: //normal break I won + case BOTH_LK_ST_S_T_B_1_L: //normal break I lost + case BOTH_LK_ST_S_T_B_1_W: //normal break I won + return (PM_SuperBreakLoseAnim(anim)||PM_SuperBreakWinAnim(anim)); + break; + } + return qfalse; +} + +qboolean PM_GetupAnimNoMove( int legsAnim ) +{ + switch( legsAnim ) + { + case BOTH_GETUP1: + case BOTH_GETUP2: + case BOTH_GETUP3: + case BOTH_GETUP4: + case BOTH_GETUP5: + case BOTH_GETUP_CROUCH_F1: + case BOTH_GETUP_CROUCH_B1: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + return qtrue; + } + return qfalse; +} + +qboolean PM_KnockDownAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_KNOCKDOWN1: + case BOTH_KNOCKDOWN2: + case BOTH_KNOCKDOWN3: + case BOTH_KNOCKDOWN4: + case BOTH_KNOCKDOWN5: + /* + //special anims: + case BOTH_RELEASED: + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_PLAYER_PA_3_FLY: + */ + return qtrue; + break; + /* + default: + return PM_InGetUp( ps ); + break; + */ + } + return qfalse; +} + +qboolean PM_KnockDownAnimExtended( int anim ) +{ + switch ( anim ) + { + case BOTH_KNOCKDOWN1: + case BOTH_KNOCKDOWN2: + case BOTH_KNOCKDOWN3: + case BOTH_KNOCKDOWN4: + case BOTH_KNOCKDOWN5: + //special anims: + case BOTH_RELEASED: + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_PLAYER_PA_3_FLY: + return qtrue; + break; + /* + default: + return PM_InGetUp( ps ); + break; + */ + } + return qfalse; +} + +qboolean PM_SaberInKata( saberMoveName_t saberMove ) +{ + switch ( saberMove ) + { + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + return qtrue; + default: + break; + } + return qfalse; +} + +qboolean PM_CanRollFromSoulCal( playerState_t *ps ) +{ + if ( ps->legsAnim == BOTH_A7_SOULCAL + && ps->legsAnimTimer < 700 + && ps->legsAnimTimer > 250 ) + { + return qtrue; + } + return qfalse; +} + +qboolean BG_FullBodyTauntAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_GESTURE1: + case BOTH_DUAL_TAUNT: + case BOTH_STAFF_TAUNT: + case BOTH_BOW: + case BOTH_MEDITATE: + case BOTH_SHOWOFF_FAST: + case BOTH_SHOWOFF_MEDIUM: + case BOTH_SHOWOFF_STRONG: + case BOTH_SHOWOFF_DUAL: + case BOTH_SHOWOFF_STAFF: + case BOTH_VICTORY_FAST: + case BOTH_VICTORY_MEDIUM: + case BOTH_VICTORY_STRONG: + case BOTH_VICTORY_DUAL: + case BOTH_VICTORY_STAFF: + return qtrue; + break; + } + return qfalse; +} diff --git a/code/game/bg_panimate.cpp.BASE.8780.cpp b/code/game/bg_panimate.cpp.BASE.8780.cpp new file mode 100644 index 0000000000..41b0d561f0 --- /dev/null +++ b/code/game/bg_panimate.cpp.BASE.8780.cpp @@ -0,0 +1,7255 @@ +/* +This file is part of Jedi Academy. + + Jedi Academy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Jedi Academy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Jedi Academy. If not, see . +*/ +// Copyright 2001-2013 Raven Software + +// this include must remain at the top of every bg_xxxx CPP file +#include "common_headers.h" + + +// define GAME_INCLUDE so that g_public.h does not define the +// short, server-visible gclient_t and gentity_t structures, +// because we define the full size ones in this file +#define GAME_INCLUDE + +#include "../qcommon/q_shared.h" +#include "g_shared.h" +#include "bg_local.h" +#include "../cgame/cg_local.h" +#include "anims.h" +#include "Q3_Interface.h" +#include "g_local.h" +#include "wp_saber.h" +#include "g_vehicles.h" + +extern pmove_t *pm; +extern pml_t pml; +extern cvar_t *g_ICARUSDebug; +extern cvar_t *g_timescale; +extern cvar_t *g_synchSplitAnims; +extern cvar_t *g_AnimWarning; +extern cvar_t *g_noFootSlide; +extern cvar_t *g_noFootSlideRunScale; +extern cvar_t *g_noFootSlideWalkScale; +extern cvar_t *g_saberAnimSpeed; +extern cvar_t *g_saberAutoAim; +extern cvar_t *g_speederControlScheme; +extern cvar_t *g_saberNewControlScheme; + +extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); +extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); +extern qboolean ValidAnimFileIndex ( int index ); +extern qboolean PM_ControlledByPlayer( void ); +extern qboolean PM_DroidMelee( int npc_class ); +extern qboolean PM_PainAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +extern qboolean PM_FlippingAnim( int anim ); +extern qboolean PM_RollingAnim( int anim ); +extern qboolean PM_SwimmingAnim( int anim ); +extern qboolean PM_InKnockDown( playerState_t *ps ); +extern qboolean PM_InRoll( playerState_t *ps ); +extern qboolean PM_DodgeAnim( int anim ); +extern qboolean PM_InSlopeAnim( int anim ); +extern qboolean PM_ForceAnim( int anim ); +extern qboolean PM_InKnockDownOnGround( playerState_t *ps ); +extern qboolean PM_InSpecialJump( int anim ); +extern qboolean PM_RunningAnim( int anim ); +extern qboolean PM_WalkingAnim( int anim ); +extern qboolean PM_SwimmingAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +extern qboolean PM_SaberStanceAnim( int anim ); +extern qboolean PM_SaberDrawPutawayAnim( int anim ); +extern void PM_SetJumped( float height, qboolean force ); +extern qboolean PM_InGetUpNoRoll( playerState_t *ps ); +extern qboolean PM_CrouchAnim( int anim ); +extern qboolean G_TryingKataAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingCartwheel( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingSpecial( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingJumpAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingJumpForwardAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingLungeAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingPullAttack( gentity_t *self, usercmd_t *cmd, qboolean amPulling ); +extern qboolean G_InCinematicSaberAnim( gentity_t *self ); +extern qboolean G_ControlledByPlayer( gentity_t *self ); + +extern int g_crosshairEntNum; + +int PM_AnimLength( int index, animNumber_t anim ); +qboolean PM_LockedAnim( int anim ); +qboolean PM_StandingAnim( int anim ); +qboolean PM_InOnGroundAnim ( playerState_t *ps ); +qboolean PM_SuperBreakWinAnim( int anim ); +qboolean PM_SuperBreakLoseAnim( int anim ); +qboolean PM_LockedAnim( int anim ); +saberMoveName_t PM_SaberFlipOverAttackMove( void ); +qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ); +saberMoveName_t PM_SaberJumpForwardAttackMove( void ); +qboolean PM_CheckJumpForwardAttackMove( void ); +saberMoveName_t PM_SaberBackflipAttackMove( void ); +qboolean PM_CheckBackflipAttackMove( void ); +saberMoveName_t PM_SaberDualJumpAttackMove( void ); +qboolean PM_CheckDualJumpAttackMove( void ); +saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ); +qboolean PM_CheckLungeAttackMove( void ); +// Okay, here lies the much-dreaded Pat-created FSM movement chart... Heretic II strikes again! +// Why am I inflicting this on you? Well, it's better than hardcoded states. +// Ideally this will be replaced with an external file or more sophisticated move-picker +// once the game gets out of prototype stage. + +// Silly, but I'm replacing these macros so they are shorter! +#define AFLAG_IDLE (SETANIM_FLAG_NORMAL) +#define AFLAG_ACTIVE (SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS) +#define AFLAG_WAIT (SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS) +#define AFLAG_FINISH (SETANIM_FLAG_HOLD) + +//FIXME: add the alternate anims for each style? +saberMoveData_t saberMoveData[LS_MOVE_MAX] = {// NB:randomized + // name anim(do all styles?)startQ endQ setanimflag blend, blocking chain_idle chain_attack trailLen + {"None", BOTH_STAND1, Q_R, Q_R, AFLAG_IDLE, 350, BLK_NO, LS_NONE, LS_NONE, 0 }, // LS_NONE = 0, + + // General movements with saber + {"Ready", BOTH_STAND2, Q_R, Q_R, AFLAG_IDLE, 350, BLK_WIDE, LS_READY, LS_S_R2L, 0 }, // LS_READY, + {"Draw", BOTH_STAND1TO2, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_DRAW, + {"Putaway", BOTH_STAND2TO1, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_PUTAWAY, + + // Attacks + //UL2LR + {"TL2BR Att", BOTH_A1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TL2BR, LS_R_TL2BR, 200 }, // LS_A_TL2BR + //SLASH LEFT + {"L2R Att", BOTH_A1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_L2R, LS_R_L2R, 200 }, // LS_A_L2R + //LL2UR + {"BL2TR Att", BOTH_A1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_TIGHT, LS_R_BL2TR, LS_R_BL2TR, 200 }, // LS_A_BL2TR + //LR2UL + {"BR2TL Att", BOTH_A1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_BR2TL, LS_R_BR2TL, 200 }, // LS_A_BR2TL + //SLASH RIGHT + {"R2L Att", BOTH_A1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_R2L, LS_R_R2L, 200 },// LS_A_R2L + //UR2LL + {"TR2BL Att", BOTH_A1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TR2BL, LS_R_TR2BL, 200 }, // LS_A_TR2BL + //SLASH DOWN + {"T2B Att", BOTH_A1_T__B_, Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_T2B, LS_R_T2B, 200 }, // LS_A_T2B + //special attacks + {"Back Stab", BOTH_A2_STABBACK1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACKSTAB + {"Back Att", BOTH_ATTACK_BACK, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK + {"CR Back Att", BOTH_CROUCHATTACKBACK1,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK_CR + {"RollStab", BOTH_ROLL_STAB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_ROLL_STAB + {"Lunge Att", BOTH_LUNGE2_B__T_, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_LUNGE + {"Jump Att", BOTH_FORCELEAP2_T__B_,Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_JUMP_T__B_ + {"Flip Stab", BOTH_JUMPFLIPSTABDOWN,Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_FLIP_STAB + {"Flip Slash", BOTH_JUMPFLIPSLASHDOWN1,Q_L,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R_T_, 200 }, // LS_A_FLIP_SLASH + {"DualJump Atk",BOTH_JUMPATTACK6, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_BL_TR, 200 }, // LS_JUMPATTACK_DUAL + + {"DualJumpAtkL_A",BOTH_ARIAL_LEFT, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TL2BR, 200 }, // LS_JUMPATTACK_ARIAL_LEFT + {"DualJumpAtkR_A",BOTH_ARIAL_RIGHT, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TR2BL, 200 }, // LS_JUMPATTACK_ARIAL_RIGHT + + {"DualJumpAtkL_A",BOTH_CARTWHEEL_LEFT, Q_R,Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TL_BR, 200 }, // LS_JUMPATTACK_CART_LEFT + {"DualJumpAtkR_A",BOTH_CARTWHEEL_RIGHT, Q_R,Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TR_BL, 200 }, // LS_JUMPATTACK_CART_RIGHT + + {"DualJumpAtkLStaff", BOTH_BUTTERFLY_FL1,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_JUMPATTACK_STAFF_LEFT + {"DualJumpAtkRStaff", BOTH_BUTTERFLY_FR1,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_JUMPATTACK_STAFF_RIGHT + + {"ButterflyLeft", BOTH_BUTTERFLY_LEFT,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_BUTTERFLY_LEFT + {"ButterflyRight", BOTH_BUTTERFLY_RIGHT,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_BUTTERFLY_RIGHT + + {"BkFlip Atk", BOTH_JUMPATTACK7, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_BACKFLIP_ATK + {"DualSpinAtk", BOTH_SPINATTACK6, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_DUAL + {"StfSpinAtk", BOTH_SPINATTACK7, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK + {"LngLeapAtk", BOTH_FORCELONGLEAP_ATTACK,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_LEAP_ATTACK + {"SwoopAtkR", BOTH_VS_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_RIGHT + {"SwoopAtkL", BOTH_VS_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_LEFT + {"TauntaunAtkR",BOTH_VT_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_RIGHT + {"TauntaunAtkL",BOTH_VT_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_LEFT + {"StfKickFwd", BOTH_A7_KICK_F, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F + {"StfKickBack", BOTH_A7_KICK_B, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B + {"StfKickRight",BOTH_A7_KICK_R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R + {"StfKickLeft", BOTH_A7_KICK_L, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L + {"StfKickSpin", BOTH_A7_KICK_S, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_S + {"StfKickBkFwd",BOTH_A7_KICK_BF, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_BF + {"StfKickSplit",BOTH_A7_KICK_RL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_RL + {"StfKickFwdAir",BOTH_A7_KICK_F_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F_AIR + {"StfKickBackAir",BOTH_A7_KICK_B_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B_AIR + {"StfKickRightAir",BOTH_A7_KICK_R_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R_AIR + {"StfKickLeftAir",BOTH_A7_KICK_L_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L_AIR + {"StabDown", BOTH_STABDOWN, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN + {"StabDownStf", BOTH_STABDOWN_STAFF,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_STAFF + {"StabDownDual",BOTH_STABDOWN_DUAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_DUAL + {"dualspinprot",BOTH_A6_SABERPROTECT,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_DUAL_SPIN_PROTECT + {"StfSoulCal", BOTH_A7_SOULCAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_STAFF_SOULCAL + {"specialfast", BOTH_A1_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A1_SPECIAL + {"specialmed", BOTH_A2_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A2_SPECIAL + {"specialstr", BOTH_A3_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A3_SPECIAL + {"upsidedwnatk",BOTH_FLIP_ATTACK7, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_UPSIDE_DOWN_ATTACK + {"pullatkstab", BOTH_PULL_IMPALE_STAB,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_STAB + {"pullatkswing",BOTH_PULL_IMPALE_SWING,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_SWING + {"AloraSpinAtk",BOTH_ALORA_SPIN_SLASH,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_ALORA + {"Dual FB Atk", BOTH_A6_FB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_FB + {"Dual LR Atk", BOTH_A6_LR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_LR + {"StfHiltBash", BOTH_A7_HILT, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_HILT_BASH + + //starts + {"TL2BR St", BOTH_S1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TL2BR, LS_A_TL2BR, 200 }, // LS_S_TL2BR + {"L2R St", BOTH_S1_S1__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_L2R, LS_A_L2R, 200 }, // LS_S_L2R + {"BL2TR St", BOTH_S1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BL2TR, LS_A_BL2TR, 200 }, // LS_S_BL2TR + {"BR2TL St", BOTH_S1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BR2TL, LS_A_BR2TL, 200 }, // LS_S_BR2TL + {"R2L St", BOTH_S1_S1__R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_R2L, LS_A_R2L, 200 }, // LS_S_R2L + {"TR2BL St", BOTH_S1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TR2BL, LS_A_TR2BL, 200 }, // LS_S_TR2BL + {"T2B St", BOTH_S1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_T2B, LS_A_T2B, 200 }, // LS_S_T2B + + //returns + {"TL2BR Ret", BOTH_R1_BR_S1, Q_BR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TL2BR + {"L2R Ret", BOTH_R1__R_S1, Q_R, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_L2R + {"BL2TR Ret", BOTH_R1_TR_S1, Q_TR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BL2TR + {"BR2TL Ret", BOTH_R1_TL_S1, Q_TL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BR2TL + {"R2L Ret", BOTH_R1__L_S1, Q_L, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_R2L + {"TR2BL Ret", BOTH_R1_BL_S1, Q_BL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TR2BL + {"T2B Ret", BOTH_R1_B__S1, Q_B, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_T2B + + //Transitions + {"BR2R Trans", BOTH_T1_BR__R, Q_BR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc bottom right to right + {"BR2TR Trans", BOTH_T1_BR_TR, Q_BR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc bottom right to top right (use: BOTH_T1_TR_BR) + {"BR2T Trans", BOTH_T1_BR_T_, Q_BR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom right to top (use: BOTH_T1_T__BR) + {"BR2TL Trans", BOTH_T1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast weak spin bottom right to top left + {"BR2L Trans", BOTH_T1_BR__L, Q_BR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin bottom right to left + {"BR2BL Trans", BOTH_T1_BR_BL, Q_BR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin bottom right to bottom left + {"R2BR Trans", BOTH_T1__R_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc right to bottom right (use: BOTH_T1_BR__R) + {"R2TR Trans", BOTH_T1__R_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc right to top right + {"R2T Trans", BOTH_T1__R_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast ar right to top (use: BOTH_T1_T___R) + {"R2TL Trans", BOTH_T1__R_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc right to top left + {"R2L Trans", BOTH_T1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin right to left + {"R2BL Trans", BOTH_T1__R_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin right to bottom left + {"TR2BR Trans", BOTH_T1_TR_BR, Q_TR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top right to bottom right + {"TR2R Trans", BOTH_T1_TR__R, Q_TR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top right to right (use: BOTH_T1__R_TR) + {"TR2T Trans", BOTH_T1_TR_T_, Q_TR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top right to top (use: BOTH_T1_T__TR) + {"TR2TL Trans", BOTH_T1_TR_TL, Q_TR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top right to top left + {"TR2L Trans", BOTH_T1_TR__L, Q_TR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top right to left + {"TR2BL Trans", BOTH_T1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin top right to bottom left + {"T2BR Trans", BOTH_T1_T__BR, Q_T, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top to bottom right + {"T2R Trans", BOTH_T1_T___R, Q_T, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top to right + {"T2TR Trans", BOTH_T1_T__TR, Q_T, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top to top right + {"T2TL Trans", BOTH_T1_T__TL, Q_T, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top to top left + {"T2L Trans", BOTH_T1_T___L, Q_T, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top to left + {"T2BL Trans", BOTH_T1_T__BL, Q_T, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top to bottom left + {"TL2BR Trans", BOTH_T1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin top left to bottom right + {"TL2R Trans", BOTH_T1_TL__R, Q_TL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top left to right (use: BOTH_T1__R_TL) + {"TL2TR Trans", BOTH_T1_TL_TR, Q_TL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top left to top right (use: BOTH_T1_TR_TL) + {"TL2T Trans", BOTH_T1_TL_T_, Q_TL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top left to top (use: BOTH_T1_T__TL) + {"TL2L Trans", BOTH_T1_TL__L, Q_TL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top left to left (use: BOTH_T1__L_TL) + {"TL2BL Trans", BOTH_T1_TL_BL, Q_TL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top left to bottom left + {"L2BR Trans", BOTH_T1__L_BR, Q_L, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin left to bottom right + {"L2R Trans", BOTH_T1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin left to right + {"L2TR Trans", BOTH_T1__L_TR, Q_L, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc left to top right (use: BOTH_T1_TR__L) + {"L2T Trans", BOTH_T1__L_T_, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc left to top (use: BOTH_T1_T___L) + {"L2TL Trans", BOTH_T1__L_TL, Q_L, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc left to top left + {"L2BL Trans", BOTH_T1__L_BL, Q_L, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc left to bottom left (use: BOTH_T1_BL__L) + {"BL2BR Trans", BOTH_T1_BL_BR, Q_BL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin bottom left to bottom right + {"BL2R Trans", BOTH_T1_BL__R, Q_BL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin bottom left to right + {"BL2TR Trans", BOTH_T1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast weak spin bottom left to top right + {"BL2T Trans", BOTH_T1_BL_T_, Q_BL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom left to top (use: BOTH_T1_T__BL) + {"BL2TL Trans", BOTH_T1_BL_TL, Q_BL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc bottom left to top left (use: BOTH_T1_TL_BL) + {"BL2L Trans", BOTH_T1_BL__L, Q_BL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc bottom left to left + + //Bounces + {"Bounce BR", BOTH_B1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + {"Bounce R", BOTH_B1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + {"Bounce TR", BOTH_B1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + {"Bounce T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + {"Bounce TL", BOTH_B1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + {"Bounce L", BOTH_B1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + {"Bounce BL", BOTH_B1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + + //Deflected attacks (like bounces, but slide off enemy saber, not straight back) + {"Deflect BR", BOTH_D1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + {"Deflect R", BOTH_D1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + {"Deflect TR", BOTH_D1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + {"Deflect T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + {"Deflect TL", BOTH_D1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + {"Deflect L", BOTH_D1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + {"Deflect BL", BOTH_D1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + {"Deflect B", BOTH_D1_B____, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + + //Reflected attacks + {"Reflected BR",BOTH_V1_BR_S1, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BR + {"Reflected R", BOTH_V1__R_S1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__R + {"Reflected TR",BOTH_V1_TR_S1, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TR + {"Reflected T", BOTH_V1_T__S1, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_T_ + {"Reflected TL",BOTH_V1_TL_S1, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TL + {"Reflected L", BOTH_V1__L_S1, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__L + {"Reflected BL",BOTH_V1_BL_S1, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BL + {"Reflected B", BOTH_V1_B__S1, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_B_ + + // Broken parries + {"BParry Top", BOTH_H1_S1_T_, Q_T, Q_B, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UP, + {"BParry UR", BOTH_H1_S1_TR, Q_TR, Q_BL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UR, + {"BParry UL", BOTH_H1_S1_TL, Q_TL, Q_BR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UL, + {"BParry LR", BOTH_H1_S1_BL, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR, + {"BParry Bot", BOTH_H1_S1_B_, Q_B, Q_T, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + {"BParry LL", BOTH_H1_S1_BR, Q_BR, Q_TL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + + // Knockaways + {"Knock Top", BOTH_K1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_T__BR, 150 }, // LS_PARRY_UP, + {"Knock UR", BOTH_K1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_TR__R, 150 }, // LS_PARRY_UR, + {"Knock UL", BOTH_K1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_T1_TL__L, 150 }, // LS_PARRY_UL, + {"Knock LR", BOTH_K1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_T1_BL_TL, 150 }, // LS_PARRY_LR, + {"Knock LL", BOTH_K1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_T1_BR_TR, 150 }, // LS_PARRY_LL + + // Parry + {"Parry Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 150 }, // LS_PARRY_UP, + {"Parry UR", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 150 }, // LS_PARRY_UR, + {"Parry UL", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 150 }, // LS_PARRY_UL, + {"Parry LR", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 150 }, // LS_PARRY_LR, + {"Parry LL", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 150 }, // LS_PARRY_LL + + // Reflecting a missile + {"Reflect Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 300 }, // LS_PARRY_UP, + {"Reflect UR", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 300 }, // LS_PARRY_UR, + {"Reflect UL", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 300 }, // LS_PARRY_UL, + {"Reflect LR", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 300 }, // LS_PARRY_LR + {"Reflect LL", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 300 }, // LS_PARRY_LL, +}; + + +saberMoveName_t transitionMove[Q_NUM_QUADS][Q_NUM_QUADS] = +{ + { + LS_NONE, //Can't transition to same pos! + LS_T1_BR__R,//40 + LS_T1_BR_TR, + LS_T1_BR_T_, + LS_T1_BR_TL, + LS_T1_BR__L, + LS_T1_BR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1__R_BR,//46 + LS_NONE, //Can't transition to same pos! + LS_T1__R_TR, + LS_T1__R_T_, + LS_T1__R_TL, + LS_T1__R__L, + LS_T1__R_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_TR_BR,//52 + LS_T1_TR__R, + LS_NONE, //Can't transition to same pos! + LS_T1_TR_T_, + LS_T1_TR_TL, + LS_T1_TR__L, + LS_T1_TR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_T__BR,//58 + LS_T1_T___R, + LS_T1_T__TR, + LS_NONE, //Can't transition to same pos! + LS_T1_T__TL, + LS_T1_T___L, + LS_T1_T__BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_TL_BR,//64 + LS_T1_TL__R, + LS_T1_TL_TR, + LS_T1_TL_T_, + LS_NONE, //Can't transition to same pos! + LS_T1_TL__L, + LS_T1_TL_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1__L_BR,//70 + LS_T1__L__R, + LS_T1__L_TR, + LS_T1__L_T_, + LS_T1__L_TL, + LS_NONE, //Can't transition to same pos! + LS_T1__L_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_BL_BR,//76 + LS_T1_BL__R, + LS_T1_BL_TR, + LS_T1_BL_T_, + LS_T1_BL_TL, + LS_T1_BL__L, + LS_NONE, //Can't transition to same pos! + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_BL_BR,//NOTE: there are no transitions from bottom, so re-use the bottom right transitions + LS_T1_BR__R, + LS_T1_BR_TR, + LS_T1_BR_T_, + LS_T1_BR_TL, + LS_T1_BR__L, + LS_T1_BR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + } +}; + +void PM_VelocityForSaberMove( playerState_t *ps, vec3_t throwDir ) +{ + vec3_t vForward, vRight, vUp, startQ, endQ; + + AngleVectors( ps->viewangles, vForward, vRight, vUp ); + + switch ( saberMoveData[ps->saberMove].startQuad ) + { + case Q_BR: + VectorScale( vRight, 1, startQ ); + VectorMA( startQ, -1, vUp, startQ ); + break; + case Q_R: + VectorScale( vRight, 2, startQ ); + break; + case Q_TR: + VectorScale( vRight, 1, startQ ); + VectorMA( startQ, 1, vUp, startQ ); + break; + case Q_T: + VectorScale( vUp, 2, startQ ); + break; + case Q_TL: + VectorScale( vRight, -1, startQ ); + VectorMA( startQ, 1, vUp, startQ ); + break; + case Q_L: + VectorScale( vRight, -2, startQ ); + break; + case Q_BL: + VectorScale( vRight, -1, startQ ); + VectorMA( startQ, -1, vUp, startQ ); + break; + case Q_B: + VectorScale( vUp, -2, startQ ); + break; + } + switch ( saberMoveData[ps->saberMove].endQuad ) + { + case Q_BR: + VectorScale( vRight, 1, endQ ); + VectorMA( endQ, -1, vUp, endQ ); + break; + case Q_R: + VectorScale( vRight, 2, endQ ); + break; + case Q_TR: + VectorScale( vRight, 1, endQ ); + VectorMA( endQ, 1, vUp, endQ ); + break; + case Q_T: + VectorScale( vUp, 2, endQ ); + break; + case Q_TL: + VectorScale( vRight, -1, endQ ); + VectorMA( endQ, 1, vUp, endQ ); + break; + case Q_L: + VectorScale( vRight, -2, endQ ); + break; + case Q_BL: + VectorScale( vRight, -1, endQ ); + VectorMA( endQ, -1, vUp, endQ ); + break; + case Q_B: + VectorScale( vUp, -2, endQ ); + break; + } + VectorMA( endQ, 2, vForward, endQ ); + VectorScale( throwDir, 125, throwDir );//FIXME: pass in the throw strength? + VectorSubtract( endQ, startQ, throwDir ); +} + +qboolean PM_VelocityForBlockedMove( playerState_t *ps, vec3_t throwDir ) +{ + vec3_t vForward, vRight, vUp; + AngleVectors( ps->viewangles, vForward, vRight, vUp ); + switch ( ps->saberBlocked ) + { + case BLOCKED_UPPER_RIGHT: + VectorScale( vRight, 1, throwDir ); + VectorMA( throwDir, 1, vUp, throwDir ); + break; + case BLOCKED_UPPER_LEFT: + VectorScale( vRight, -1, throwDir ); + VectorMA( throwDir, 1, vUp, throwDir ); + break; + case BLOCKED_LOWER_RIGHT: + VectorScale( vRight, 1, throwDir ); + VectorMA( throwDir, -1, vUp, throwDir ); + break; + case BLOCKED_LOWER_LEFT: + VectorScale( vRight, -1, throwDir ); + VectorMA( throwDir, -1, vUp, throwDir ); + break; + case BLOCKED_TOP: + VectorScale( vUp, 2, throwDir ); + break; + default: + return qfalse; + break; + } + VectorMA( throwDir, 2, vForward, throwDir ); + VectorScale( throwDir, 250, throwDir );//FIXME: pass in the throw strength? + return qtrue; +} + +int PM_AnimLevelForSaberAnim( int anim ) +{ + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ ) + { + return FORCE_LEVEL_1; + } + if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ ) + { + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ ) + { + return FORCE_LEVEL_3; + } + if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ ) + {//desann + return FORCE_LEVEL_4; + } + if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ ) + {//tavion + return FORCE_LEVEL_5; + } + if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ ) + {//dual + return SS_DUAL; + } + if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ ) + {//staff + return SS_STAFF; + } + return FORCE_LEVEL_0; +} + +int PM_PowerLevelForSaberAnim( playerState_t *ps, int saberNum ) +{ + int anim = ps->torsoAnim; + int animTimeElapsed = PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim ) - ps->torsoAnimTimer; + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ ) + { + //FIXME: these two need their own style + if ( ps->saber[0].type == SABER_LANCE ) + { + return FORCE_LEVEL_4; + } + else if ( ps->saber[0].type == SABER_TRIDENT ) + { + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_1; + } + if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ ) + { + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ ) + { + return FORCE_LEVEL_3; + } + if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ ) + {//desann + return FORCE_LEVEL_4; + } + if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ ) + {//tavion + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ ) + {//dual + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ ) + {//staff + return FORCE_LEVEL_2; + } + if ( ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR ) + || ( anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR ) + || ( anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR ) ) + {//parries + switch ( ps->saberAnimLevel ) + { + case SS_STRONG: + case SS_DESANN: + return FORCE_LEVEL_3; + break; + case SS_TAVION: + case SS_STAFF: + case SS_DUAL: + case SS_MEDIUM: + return FORCE_LEVEL_2; + break; + case SS_FAST: + return FORCE_LEVEL_1; + break; + default: + return FORCE_LEVEL_0; + break; + } + } + if ( ( anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR ) + || ( anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR ) + || ( anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR ) ) + {//knockaways + return FORCE_LEVEL_3; + } + if ( ( anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1 ) + || ( anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6 ) + || ( anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7 ) ) + {//knocked-away attacks + return FORCE_LEVEL_1; + } + if ( ( anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR ) + || ( anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR ) + || ( anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR ) ) + {//broken parries + return FORCE_LEVEL_0; + } + switch ( anim ) + { + case BOTH_A2_STABBACK1: + if ( ps->torsoAnimTimer < 450 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 400 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ATTACK_BACK: + if ( ps->torsoAnimTimer < 500 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_CROUCHATTACKBACK1: + if ( ps->torsoAnimTimer < 800 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_K1_S1_T_: //# knockaway saber top + case BOTH_K1_S1_TR: //# knockaway saber top right + case BOTH_K1_S1_TL: //# knockaway saber top left + case BOTH_K1_S1_BL: //# knockaway saber bottom left + case BOTH_K1_S1_B_: //# knockaway saber bottom + case BOTH_K1_S1_BR: //# knockaway saber bottom right + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_LUNGE2_B__T_: + if ( ps->torsoAnimTimer < 400 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 150 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELEAP2_T__B_: + if ( ps->torsoAnimTimer < 400 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 550 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + return FORCE_LEVEL_3;//??? + break; + case BOTH_JUMPFLIPSLASHDOWN1: + if ( ps->torsoAnimTimer <= 900 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 550 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPFLIPSTABDOWN: + if ( ps->torsoAnimTimer <= 1200 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed <= 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPATTACK6: + /* + if (pm->ps) + { + if ( ( pm->ps->legsAnimTimer >= 1450 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) + ||(pm->ps->legsAnimTimer >= 400 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) + {//pretty much sideways + return FORCE_LEVEL_3; + } + } + */ + if ( ( ps->torsoAnimTimer >= 1450 + && animTimeElapsed >= 400 ) + ||(ps->torsoAnimTimer >= 400 + && animTimeElapsed >= 1100 ) ) + {//pretty much sideways + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_0; + break; + case BOTH_JUMPATTACK7: + if ( ps->torsoAnimTimer <= 1200 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK6: + if ( animTimeElapsed <= 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK7: + if ( ps->torsoAnimTimer <= 500 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 500 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELONGLEAP_ATTACK: + if ( animTimeElapsed <= 200 ) + {//1st four frames of anim + return FORCE_LEVEL_3; + } + break; + /* + case BOTH_A7_KICK_F://these kicks attack, too + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + //FIXME: break up + return FORCE_LEVEL_3; + break; + */ + case BOTH_STABDOWN: + if ( ps->torsoAnimTimer <= 900 ) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_STAFF: + if ( ps->torsoAnimTimer <= 850 ) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_DUAL: + if ( ps->torsoAnimTimer <= 900 ) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_A6_SABERPROTECT: + if ( ps->torsoAnimTimer < 650 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_SOULCAL: + if ( ps->torsoAnimTimer < 650 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A1_SPECIAL: + if ( ps->torsoAnimTimer < 600 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A2_SPECIAL: + if ( ps->torsoAnimTimer < 300 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A3_SPECIAL: + if ( ps->torsoAnimTimer < 700 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FLIP_ATTACK7: + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_STAB: + if ( ps->torsoAnimTimer < 1000 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_SWING: + if ( ps->torsoAnimTimer < 500 )//750 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 650 )//600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ALORA_SPIN_SLASH: + if ( ps->torsoAnimTimer < 900 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_FB: + if ( ps->torsoAnimTimer < 250 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_LR: + if ( ps->torsoAnimTimer < 250 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_HILT: + return FORCE_LEVEL_0; + break; +//===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_LK_S_DL_T_SB_1_W: + if ( ps->torsoAnimTimer < 700 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_S_SB_1_W: + if ( ps->torsoAnimTimer < 300 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + if ( ps->torsoAnimTimer < 700 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 400 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + if ( ps->torsoAnimTimer < 150 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 400 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_T_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + if ( animTimeElapsed < 1000 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_ST_T_SB_1_W: + if ( ps->torsoAnimTimer < 950 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 650 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_S_SB_1_W: + if ( saberNum != 0 ) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if ( ps->torsoAnimTimer < 900 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 450 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_T_SB_1_W: + if ( saberNum != 0 ) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if ( ps->torsoAnimTimer < 250 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 150 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_S_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_T_SB_1_W: + //special suberbreak - doesn't kill, just kicks them backwards + return FORCE_LEVEL_0; + break; + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + if ( ps->torsoAnimTimer < 800 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 350 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + return FORCE_LEVEL_5; + break; +//===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_HANG_ATTACK: + //FIME: break up + if ( ps->torsoAnimTimer < 1000 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + else + {//sweet spot + return FORCE_LEVEL_5; + } + break; + case BOTH_ROLL_STAB: + if ( animTimeElapsed > 400 ) + {//end of anim + return FORCE_LEVEL_0; + } + else + { + return FORCE_LEVEL_3; + } + break; + } + return FORCE_LEVEL_0; +} + +qboolean PM_InAnimForSaberMove( int anim, int saberMove ) +{ + switch ( anim ) + {//special case anims + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qtrue; + } + if ( PM_SaberDrawPutawayAnim( anim ) ) + { + if ( saberMove == LS_DRAW || saberMove == LS_PUTAWAY ) + { + return qtrue; + } + return qfalse; + } + else if ( PM_SaberStanceAnim( anim ) ) + { + if ( saberMove == LS_READY ) + { + return qtrue; + } + return qfalse; + } + int animLevel = PM_AnimLevelForSaberAnim( anim ); + if ( animLevel <= 0 ) + {//NOTE: this will always return false for the ready poses and putaway/draw... + return qfalse; + } + //drop the anim to the first level and start the checks there + anim -= (animLevel-FORCE_LEVEL_1)*SABER_ANIM_GROUP_SIZE; + //check level 1 + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 2 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 3 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 4 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 5 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + if ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_H1_S1_BR ) + {//parries, knockaways and broken parries + return (anim==saberMoveData[saberMove].animToUse); + } + return qfalse; +} + +qboolean PM_SaberInIdle( int move ) +{ + switch ( move ) + { + case LS_NONE: + case LS_READY: + case LS_DRAW: + case LS_PUTAWAY: + return qtrue; + break; + } + return qfalse; +} +qboolean PM_SaberInSpecialAttack( int anim ) +{ + switch ( anim ) + { + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberInAttackPure( int move ) +{ + if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInAttack( int move ) +{ + if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + { + return qtrue; + } + switch ( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_ARIAL_LEFT: + case LS_JUMPATTACK_ARIAL_RIGHT: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qtrue; + break; + } + return qfalse; +} +qboolean PM_SaberInTransition( int move ) +{ + if ( move >= LS_T1_BR__R && move <= LS_T1_BL__L ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInStart( int move ) +{ + if ( move >= LS_S_TL2BR && move <= LS_S_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInReturn( int move ) +{ + if ( move >= LS_R_TL2BR && move <= LS_R_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInTransitionAny( int move ) +{ + if ( PM_SaberInStart( move ) ) + { + return qtrue; + } + else if ( PM_SaberInTransition( move ) ) + { + return qtrue; + } + else if ( PM_SaberInReturn( move ) ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInBounce( int move ) +{ + if ( move >= LS_B1_BR && move <= LS_B1_BL ) + { + return qtrue; + } + if ( move >= LS_D1_BR && move <= LS_D1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInBrokenParry( int move ) +{ + if ( move >= LS_V1_BR && move <= LS_V1_B_ ) + { + return qtrue; + } + if ( move >= LS_H1_T_ && move <= LS_H1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInDeflect( int move ) +{ + if ( move >= LS_D1_BR && move <= LS_D1_B_ ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInParry( int move ) +{ + if ( move >= LS_PARRY_UP && move <= LS_PARRY_LL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInKnockaway( int move ) +{ + if ( move >= LS_K1_T_ && move <= LS_K1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInReflect( int move ) +{ + if ( move >= LS_REFLECT_UP && move <= LS_REFLECT_LL ) + { + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberInSpecial( int move ) +{ + switch( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_ARIAL_LEFT: + case LS_JUMPATTACK_ARIAL_RIGHT: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qtrue; + } + return qfalse; +} + +qboolean PM_KickMove( int move ) +{ + switch( move ) + { + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_HILT_BASH: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberCanInterruptMove( int move, int anim ) +{ + if ( PM_InAnimForSaberMove( anim, move ) ) + { + switch( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qfalse; + } + + if ( PM_SaberInAttackPure( move ) ) + { + return qfalse; + } + if ( PM_SaberInStart( move ) ) + { + return qfalse; + } + if ( PM_SaberInTransition( move ) ) + { + return qfalse; + } + if ( PM_SaberInBounce( move ) ) + { + return qfalse; + } + if ( PM_SaberInBrokenParry( move ) ) + { + return qfalse; + } + if ( PM_SaberInDeflect( move ) ) + { + return qfalse; + } + if ( PM_SaberInParry( move ) ) + { + return qfalse; + } + if ( PM_SaberInKnockaway( move ) ) + { + return qfalse; + } + if ( PM_SaberInReflect( move ) ) + { + return qfalse; + } + } + switch ( anim ) + { + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qfalse; + } + return qtrue; +} + +saberMoveName_t PM_BrokenParryForAttack( int move ) +{ + //Our attack was knocked away by a knockaway parry + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( saberMoveData[move].startQuad ) + { + case Q_B: + return LS_V1_B_; + break; + case Q_BR: + return LS_V1_BR; + break; + case Q_R: + return LS_V1__R; + break; + case Q_TR: + return LS_V1_TR; + break; + case Q_T: + return LS_V1_T_; + break; + case Q_TL: + return LS_V1_TL; + break; + case Q_L: + return LS_V1__L; + break; + case Q_BL: + return LS_V1_BL; + break; + } + return LS_NONE; +} + +saberMoveName_t PM_BrokenParryForParry( int move ) +{ + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( move ) + { + case LS_PARRY_UP: + //Hmm... since we don't know what dir the hit came from, randomly pick knock down or knock back + if ( Q_irand( 0, 1 ) ) + { + return LS_H1_B_; + } + else + { + return LS_H1_T_; + } + break; + case LS_PARRY_UR: + return LS_H1_TR; + break; + case LS_PARRY_UL: + return LS_H1_TL; + break; + case LS_PARRY_LR: + return LS_H1_BR; + break; + case LS_PARRY_LL: + return LS_H1_BL; + break; + case LS_READY: + return LS_H1_B_;//??? + break; + } + return LS_NONE; +} + +saberMoveName_t PM_KnockawayForParry( int move ) +{ + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( move ) + { + case BLOCKED_TOP://LS_PARRY_UP: + return LS_K1_T_;//push up + break; + case BLOCKED_UPPER_RIGHT://LS_PARRY_UR: + default://case LS_READY: + return LS_K1_TR;//push up, slightly to right + break; + case BLOCKED_UPPER_LEFT://LS_PARRY_UL: + return LS_K1_TL;//push up and to left + break; + case BLOCKED_LOWER_RIGHT://LS_PARRY_LR: + return LS_K1_BR;//push down and to left + break; + case BLOCKED_LOWER_LEFT://LS_PARRY_LL: + return LS_K1_BL;//push down and to right + break; + } + //return LS_NONE; +} + +saberMoveName_t PM_SaberBounceForAttack( int move ) +{ + switch ( saberMoveData[move].startQuad ) + { + case Q_B: + case Q_BR: + return LS_B1_BR; + break; + case Q_R: + return LS_B1__R; + break; + case Q_TR: + return LS_B1_TR; + break; + case Q_T: + return LS_B1_T_; + break; + case Q_TL: + return LS_B1_TL; + break; + case Q_L: + return LS_B1__L; + break; + case Q_BL: + return LS_B1_BL; + break; + } + return LS_NONE; +} + +saberMoveName_t PM_AttackMoveForQuad( int quad ) +{ + switch ( quad ) + { + case Q_B: + case Q_BR: + return LS_A_BR2TL; + break; + case Q_R: + return LS_A_R2L; + break; + case Q_TR: + return LS_A_TR2BL; + break; + case Q_T: + return LS_A_T2B; + break; + case Q_TL: + return LS_A_TL2BR; + break; + case Q_L: + return LS_A_L2R; + break; + case Q_BL: + return LS_A_BL2TR; + break; + } + return LS_NONE; +} + +int saberMoveTransitionAngle[Q_NUM_QUADS][Q_NUM_QUADS] = +{ + { + 0,//Q_BR,Q_BR, + 45,//Q_BR,Q_R, + 90,//Q_BR,Q_TR, + 135,//Q_BR,Q_T, + 180,//Q_BR,Q_TL, + 215,//Q_BR,Q_L, + 270,//Q_BR,Q_BL, + 45,//Q_BR,Q_B, + }, + { + 45,//Q_R,Q_BR, + 0,//Q_R,Q_R, + 45,//Q_R,Q_TR, + 90,//Q_R,Q_T, + 135,//Q_R,Q_TL, + 180,//Q_R,Q_L, + 215,//Q_R,Q_BL, + 90,//Q_R,Q_B, + }, + { + 90,//Q_TR,Q_BR, + 45,//Q_TR,Q_R, + 0,//Q_TR,Q_TR, + 45,//Q_TR,Q_T, + 90,//Q_TR,Q_TL, + 135,//Q_TR,Q_L, + 180,//Q_TR,Q_BL, + 135,//Q_TR,Q_B, + }, + { + 135,//Q_T,Q_BR, + 90,//Q_T,Q_R, + 45,//Q_T,Q_TR, + 0,//Q_T,Q_T, + 45,//Q_T,Q_TL, + 90,//Q_T,Q_L, + 135,//Q_T,Q_BL, + 180,//Q_T,Q_B, + }, + { + 180,//Q_TL,Q_BR, + 135,//Q_TL,Q_R, + 90,//Q_TL,Q_TR, + 45,//Q_TL,Q_T, + 0,//Q_TL,Q_TL, + 45,//Q_TL,Q_L, + 90,//Q_TL,Q_BL, + 135,//Q_TL,Q_B, + }, + { + 215,//Q_L,Q_BR, + 180,//Q_L,Q_R, + 135,//Q_L,Q_TR, + 90,//Q_L,Q_T, + 45,//Q_L,Q_TL, + 0,//Q_L,Q_L, + 45,//Q_L,Q_BL, + 90,//Q_L,Q_B, + }, + { + 270,//Q_BL,Q_BR, + 215,//Q_BL,Q_R, + 180,//Q_BL,Q_TR, + 135,//Q_BL,Q_T, + 90,//Q_BL,Q_TL, + 45,//Q_BL,Q_L, + 0,//Q_BL,Q_BL, + 45,//Q_BL,Q_B, + }, + { + 45,//Q_B,Q_BR, + 90,//Q_B,Q_R, + 135,//Q_B,Q_TR, + 180,//Q_B,Q_T, + 135,//Q_B,Q_TL, + 90,//Q_B,Q_L, + 45,//Q_B,Q_BL, + 0//Q_B,Q_B, + } +}; + +int PM_SaberAttackChainAngle( int move1, int move2 ) +{ + if ( move1 == -1 || move2 == -1 ) + { + return -1; + } + return saberMoveTransitionAngle[saberMoveData[move1].endQuad][saberMoveData[move2].startQuad]; +} + +qboolean PM_SaberKataDone( int curmove = LS_NONE, int newmove = LS_NONE ) +{ + if ( pm->ps->forceRageRecoveryTime > level.time ) + {//rage recovery, only 1 swing at a time (tired) + if ( pm->ps->saberAttackChainCount > 0 ) + {//swung once + return qtrue; + } + else + {//allow one attack + return qfalse; + } + } + else if ( (pm->ps->forcePowersActive&(1<ps->saber[0].maxChain == -1 ) + { + return qfalse; + } + else if ( pm->ps->saber[0].maxChain != 0 ) + { + if ( pm->ps->saberAttackChainCount >= pm->ps->saber[0].maxChain ) + { + return qtrue; + } + else + { + return qfalse; + } + } + + if ( pm->ps->saberAnimLevel == SS_DESANN || pm->ps->saberAnimLevel == SS_TAVION ) + {//desann and tavion can link up as many attacks as they want + return qfalse; + } + //FIXME: instead of random, apply some sort of logical conditions to whether or + // not you can chain? Like if you were completely missed, you can't chain as much, or...? + // And/Or based on FP_SABER_OFFENSE level? So number of attacks you can chain + // increases with your FP_SABER_OFFENSE skill? + if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + //TEMP: for now, let staff attacks infinitely chain + return qfalse; + /* + if ( pm->ps->saberAttackChainCount > Q_irand( 3, 4 ) ) + { + return qtrue; + } + else if ( pm->ps->saberAttackChainCount > 0 ) + { + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 3 ) + { + return qtrue; + } + } + } + */ + } + else if ( pm->ps->saberAnimLevel == SS_DUAL ) + { + //TEMP: for now, let staff attacks infinitely chain + return qfalse; + } + else if ( pm->ps->saberAnimLevel == FORCE_LEVEL_3 ) + { + if ( curmove == LS_NONE || newmove == LS_NONE ) + { + if ( pm->ps->saberAnimLevel >= FORCE_LEVEL_3 && pm->ps->saberAttackChainCount > Q_irand( 0, 1 ) ) + { + return qtrue; + } + } + else if ( pm->ps->saberAttackChainCount > Q_irand( 2, 3 ) ) + { + return qtrue; + } + else if ( pm->ps->saberAttackChainCount > 0 ) + { + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + return qtrue; + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + } + } + else + {//FIXME: have chainAngle influence fast and medium chains as well? + if ( (pm->ps->saberAnimLevel == FORCE_LEVEL_2 || pm->ps->saberAnimLevel == SS_DUAL) + && pm->ps->saberAttackChainCount > Q_irand( 2, 5 ) ) + { + return qtrue; + } + } + return qfalse; +} + +qboolean PM_CheckEnemyInBack( float backCheckDist ) +{ + if ( !pm->gent || !pm->gent->client ) + { + return qfalse; + } + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) + && !g_saberAutoAim->integer && pm->cmd.forwardmove >= 0 ) + {//don't auto-backstab + return qfalse; + } + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + {//only when on ground + return qfalse; + } + trace_t trace; + vec3_t end, fwd, fwdAngles = {0,pm->ps->viewangles[YAW],0}; + + AngleVectors( fwdAngles, fwd, NULL, NULL ); + VectorMA( pm->ps->origin, -backCheckDist, fwd, end ); + + pm->trace( &trace, pm->ps->origin, vec3_origin, vec3_origin, end, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 ); + if ( trace.fraction < 1.0f && trace.entityNum < ENTITYNUM_WORLD ) + { + gentity_t *traceEnt = &g_entities[trace.entityNum]; + if ( traceEnt + && traceEnt->health > 0 + && traceEnt->client + && traceEnt->client->playerTeam == pm->gent->client->enemyTeam + && traceEnt->client->ps.groundEntityNum != ENTITYNUM_NONE ) + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + {//player + if ( pm->gent ) + {//set player enemy to traceEnt so he auto-aims at him + pm->gent->enemy = traceEnt; + } + } + return qtrue; + } + } + return qfalse; +} + +saberMoveName_t PM_PickBackStab( void ) +{ + if ( !pm->gent || !pm->gent->client ) + { + return LS_READY; + } + if ( pm->ps->dualSabers + && pm->ps->saber[1].Active() ) + { + if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + if ( pm->gent->client->ps.saberAnimLevel == SS_TAVION ) + { + return LS_A_BACKSTAB; + } + else if ( pm->gent->client->ps.saberAnimLevel == SS_DESANN ) + { + if ( pm->ps->saberMove == LS_READY || !Q_irand( 0, 3 ) ) + { + return LS_A_BACKSTAB; + } + else if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + else if ( pm->ps->saberAnimLevel == FORCE_LEVEL_2 + || pm->ps->saberAnimLevel == SS_DUAL ) + {//using medium attacks or dual sabers + if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + else + { + return LS_A_BACKSTAB; + } +} + +saberMoveName_t PM_CheckStabDown( void ) +{ + if ( !pm->gent || !pm->gent->enemy || !pm->gent->enemy->client ) + { + return LS_NONE; + } + if ( (pm->ps->saber[0].saberFlags&SFL_NO_STABDOWN) ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_STABDOWN) ) + { + return LS_NONE; + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingKataAttack( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//want to try a special + return LS_NONE; + } + } + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + {//player + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + {//sorry must be on ground (or have just jumped) + if ( level.time-pm->ps->lastOnGround <= 50 && (pm->ps->pm_flags&PMF_JUMPING) ) + {//just jumped, it's okay + } + else + { + return LS_NONE; + } + } + /* + if ( pm->cmd.upmove > 0 ) + {//trying to jump + } + else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE //in air + && level.time-pm->ps->lastOnGround <= 250 //just left ground + && (pm->ps->pm_flags&PMF_JUMPING) )//jumped + {//just jumped + } + else + { + return LS_NONE; + } + */ + pm->ps->velocity[2] = 0; + pm->cmd.upmove = 0; + } + else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + {//NPC + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + {//sorry must be on ground (or have just jumped) + if ( level.time-pm->ps->lastOnGround <= 250 && (pm->ps->pm_flags&PMF_JUMPING) ) + {//just jumped, it's okay + } + else + { + return LS_NONE; + } + } + if ( !pm->gent->NPC ) + {//wtf??? + return LS_NONE; + } + if ( Q_irand( 0, RANK_CAPTAIN ) > pm->gent->NPC->rank ) + {//lower ranks do this less often + return LS_NONE; + } + } + vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, NULL, NULL ); + VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + float enemyZDiff = enemyDir[2]; + enemyDir[2] = 0; + float enemyHDist = VectorNormalize( enemyDir )-(pm->gent->maxs[0]+pm->gent->enemy->maxs[0]); + float dot = DotProduct( enemyDir, faceFwd ); + + if ( //(pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + dot > 0.65f + //&& enemyHDist >= 32 //was 48 + && enemyHDist <= 164//was 112 + && PM_InKnockDownOnGround( &pm->gent->enemy->client->ps )//still on ground + && !PM_InGetUpNoRoll( &pm->gent->enemy->client->ps )//not getting up yet + && enemyZDiff <= 20 ) + {//guy is on the ground below me, do a top-down attack + if ( pm->gent->enemy->s.number >= MAX_CLIENTS + || !G_ControlledByPlayer( pm->gent->enemy ) ) + {//don't get up while I'm doing this + //stop them from trying to get up for at least another 3 seconds + TIMER_Set( pm->gent->enemy, "noGetUpStraight", 3000 ); + } + //pick the right anim + if ( pm->ps->saberAnimLevel == SS_DUAL + || (pm->ps->dualSabers&&pm->ps->saber[1].Active()) ) + { + return LS_STABDOWN_DUAL; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + return LS_STABDOWN_STAFF; + } + else + { + return LS_STABDOWN; + } + } + return LS_NONE; +} + +extern saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ); +saberMoveName_t PM_SaberFlipOverAttackMove( void ); +saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ) +{ + saberMoveName_t autoMove = LS_INVALID; + + if( !pm->gent->enemy ) + { + return LS_NONE; + } + + vec3_t enemy_org, enemyDir, faceFwd, faceRight, faceUp, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, faceRight, faceUp ); + //FIXME: predict enemy position? + if ( pm->gent->enemy->client ) + { + //VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); + //HMM... using this will adjust for bbox size, so let's do that... + vec3_t size; + VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); + VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + + VectorSubtract( pm->gent->enemy->client->renderInfo.eyePoint, pm->ps->origin, enemyDir ); + } + else + { + if ( pm->gent->enemy->bmodel && VectorCompare( vec3_origin, pm->gent->enemy->currentOrigin ) ) + {//a brush model without an origin brush + vec3_t size; + VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); + VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + } + else + { + VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); + } + VectorSubtract( enemy_org, pm->ps->origin, enemyDir ); + } + float enemyZDiff = enemyDir[2]; + float enemyDist = VectorNormalize( enemyDir ); + float dot = DotProduct( enemyDir, faceFwd ); + if ( dot > 0 ) + {//enemy is in front + if ( allowStabDown ) + {//okay to try this + saberMoveName_t stabDownMove = PM_CheckStabDown(); + if ( stabDownMove != LS_NONE ) + { + return stabDownMove; + } + } + if ( (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + && dot > 0.65f + && enemyDist <= 64 && pm->gent->enemy->client + && (enemyZDiff <= 20 || PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) || PM_CrouchAnim( pm->gent->enemy->client->ps.legsAnim ) ) ) + {//swing down at them + return LS_A_T2B; + } + if ( allowFB ) + {//directly in front anim allowed + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( enemyDist > 200 || pm->gent->enemy->health <= 0 ) + {//hmm, look in back for an enemy + if ( pm->ps->clientNum && !PM_ControlledByPlayer() ) + {//player should never do this automatically + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 100 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + } + //this is the default only if they're *right* in front... + if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + {//NPC or player not in 1st person + if ( PM_CheckFlipOverAttackMove( qtrue ) ) + {//enemy must be close and in front + return PM_SaberFlipOverAttackMove(); + } + } + if ( PM_CheckLungeAttackMove() ) + {//NPC + autoMove = PM_SaberLungeAttackMove( qtrue ); + } + else + { + autoMove = LS_A_T2B; + } + } + else + {//pick a random one + if ( Q_irand( 0, 1 ) ) + { + autoMove = LS_A_TR2BL; + } + else + { + autoMove = LS_A_TL2BR; + } + } + float dotR = DotProduct( enemyDir, faceRight ); + if ( dotR > 0.35 ) + {//enemy is to far right + autoMove = LS_A_L2R; + } + else if ( dotR < -0.35 ) + {//far left + autoMove = LS_A_R2L; + } + else if ( dotR > 0.15 ) + {//enemy is to near right + autoMove = LS_A_TR2BL; + } + else if ( dotR < -0.15 ) + {//near left + autoMove = LS_A_TL2BR; + } + if ( DotProduct( enemyDir, faceUp ) > 0.5 ) + {//enemy is above me + if ( autoMove == LS_A_TR2BL ) + { + autoMove = LS_A_BL2TR; + } + else if ( autoMove == LS_A_TL2BR ) + { + autoMove = LS_A_BR2TL; + } + } + } + else if ( allowFB ) + {//back attack allowed + //if ( !PM_InKnockDown( pm->ps ) ) + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( !pm->gent->enemy->client || pm->gent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE ) + {//enemy not a client or is a client and on ground + if ( dot < -0.75f + && enemyDist < 128 + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,2))) ) + {//fast back-stab + if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + {//can't do it while ducked? + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + {//only fencers and above can do this + autoMove = LS_A_BACKSTAB; + } + } + } + else if ( pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF ) + {//higher level back spin-attacks + if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + { + if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + { + autoMove = LS_A_BACK_CR; + } + else + { + autoMove = LS_A_BACK; + } + } + } + } + } + } + } + return autoMove; +} + +qboolean PM_InSecondaryStyle( void ) +{ + if ( pm->ps->saber[0].numBlades > 1 + && pm->ps->saber[0].singleBladeStyle + && (pm->ps->saber[0].stylesForbidden&(1<ps->saber[0].singleBladeStyle)) + && pm->ps->saberAnimLevel == pm->ps->saber[0].singleBladeStyle ) + { + return qtrue; + } + + if ( pm->ps->dualSabers + && !pm->ps->saber[1].Active() )//pm->ps->saberAnimLevel != SS_DUAL ) + { + return qtrue; + } + return qfalse; +} + +saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ) +{ + G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER_FB ); + + //see if we have an overridden (or cancelled) lunge move + if ( pm->ps->saber[0].lungeAtkMove != LS_INVALID ) + { + if ( pm->ps->saber[0].lungeAtkMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].lungeAtkMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove != LS_INVALID ) + { + if ( pm->ps->saber[1].lungeAtkMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].lungeAtkMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand( 0, 3 ) ) + {//alora NPC + return LS_SPINATTACK_ALORA; + } + else + { + if ( pm->ps->dualSabers ) + { + return LS_SPINATTACK_DUAL; + } + switch ( pm->ps->saberAnimLevel ) + { + case SS_DUAL: + return LS_SPINATTACK_DUAL; + break; + case SS_STAFF: + return LS_SPINATTACK; + break; + default://normal lunge + if ( fallbackToNormalLunge ) + { + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + //do the lunge + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 150, pm->ps->velocity ); + pm->ps->velocity[2] = 50; + PM_AddEvent( EV_JUMP ); + + return LS_A_LUNGE; + } + break; + } + } + return LS_NONE; +} + +qboolean PM_CheckLungeAttackMove( void ) +{ + //check to see if it's cancelled? + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE + || pm->ps->saber[1].lungeAtkMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + { + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE + || pm->ps->saber[0].lungeAtkMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + if ( pm->ps->saberAnimLevel == SS_FAST//fast + || pm->ps->saberAnimLevel == SS_DUAL//dual + || pm->ps->saberAnimLevel == SS_STAFF //staff + || pm->ps->saberAnimLevel == SS_DESANN + || pm->ps->dualSabers ) + {//alt+back+attack using fast, dual or staff attacks + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove < 0 || (pm->ps->pm_flags&PMF_DUCKED) ) + {//ducking + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE + || pm->ps->legsAnim == BOTH_SABERDUAL_STANCE + || (level.time-pm->ps->lastStationary) <= 500 ) + {//standing or just stopped standing + if ( pm->gent + && pm->gent->NPC //NPC + && pm->gent->NPC->rank >= RANK_LT_JG //high rank + && ( pm->gent->NPC->rank == RANK_LT_JG || Q_irand( -3, pm->gent->NPC->rank ) >= RANK_LT_JG )//Q_irand( 0, pm->gent->NPC->rank ) >= RANK_LT_JG ) + && !Q_irand( 0, 3-g_spskill->integer ) ) + {//only fencer and higher can do this + if ( pm->ps->saberAnimLevel == SS_DESANN ) + { + if ( !Q_irand( 0, 4 ) ) + { + return qtrue; + } + } + else + { + return qtrue; + } + } + } + } + } + else + {//player + if ( G_TryingLungeAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + { + return qtrue; + } + } + } + + return qfalse; +} + +saberMoveName_t PM_SaberJumpForwardAttackMove( void ) +{ + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + } + if ( pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF ) + { + pm->cmd.upmove = 0;//no jump just yet + + if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + if ( Q_irand(0, 1) ) + { + return LS_JUMPATTACK_STAFF_LEFT; + } + else + { + return LS_JUMPATTACK_STAFF_RIGHT; + } + } + + return LS_JUMPATTACK_DUAL; + } + else + { + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 200, pm->ps->velocity ); + pm->ps->velocity[2] = 180; + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + + //FIXME: NPCs yell? + PM_AddEvent( EV_JUMP ); + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + pm->cmd.upmove = 0; + + return LS_A_JUMP_T__B_; + } +} + +qboolean PM_CheckJumpForwardAttackMove( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( pm->cmd.forwardmove > 0 //going forward + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped (if not player) + ) + { + if ( pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF ) + {//dual and staff + if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim + && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + && pm->ps->weaponTime <= 0//not busy + && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + { + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//jumping NPC + { + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + { + return qtrue; + } + } + } + else + {//PLAYER + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power to attack + { + return qtrue; + } + } + } + } + //check strong + else if ( pm->ps->saberAnimLevel == SS_STRONG //strong style + || pm->ps->saberAnimLevel == SS_DESANN )//desann + { + if ( //&& !PM_InKnockDown( pm->ps ) + !pm->ps->dualSabers + //&& (pm->ps->legsAnim == BOTH_STAND2||pm->ps->legsAnim == BOTH_SABERFAST_STANCE||pm->ps->legsAnim == BOTH_SABERSLOW_STANCE||level.time-pm->ps->lastStationary<=500)//standing or just started moving + ) + {//strong attack: jump-hack + /* + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || level.time-pm->ps->lastStationary <= 250 )//standing or just started moving + */ + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//NPC jumping + { + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + {//only acrobat or boss and higher can do this + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || level.time-pm->ps->lastStationary <= 250 ) + {//standing or just started moving + if ( pm->gent->client + && pm->gent->client->NPC_class == CLASS_DESANN ) + { + if ( !Q_irand( 0, 1 ) ) + { + return qtrue; + } + } + else + { + return qtrue; + } + } + } + } + } + else + {//player + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) ) + { + return qtrue; + } + } + } + } + } + return qfalse; +} + +saberMoveName_t PM_SaberFlipOverAttackMove( void ) +{ + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + } + //FIXME: check above for room enough to jump! + //FIXME: while in this jump, keep velocity[2] at a minimum until the end of the anim + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 150, pm->ps->velocity ); + pm->ps->velocity[2] = 250; + //250 is normalized for a standing enemy at your z level, about 64 tall... adjust for actual maxs[2]-mins[2] of enemy and for zdiff in origins + if ( pm->gent && pm->gent->enemy ) + { //go higher for taller enemies + pm->ps->velocity[2] *= (pm->gent->enemy->maxs[2]-pm->gent->enemy->mins[2])/64.0f; + //go higher for enemies higher than you, lower for those lower than you + float zDiff = pm->gent->enemy->currentOrigin[2] - pm->ps->origin[2]; + pm->ps->velocity[2] += (zDiff)*1.5f; + //clamp to decent-looking values + //FIXME: still jump too low sometimes + if ( zDiff <= 0 && pm->ps->velocity[2] < 200 ) + {//if we're on same level, don't let me jump so low, I clip into the ground + pm->ps->velocity[2] = 200; + } + else if ( pm->ps->velocity[2] < 50 ) + { + pm->ps->velocity[2] = 50; + } + else if ( pm->ps->velocity[2] > 400 ) + { + pm->ps->velocity[2] = 400; + } + } + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + + //FIXME: NPCs yell? + PM_AddEvent( EV_JUMP ); + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + pm->cmd.upmove = 0; + //FIXME: don't allow this to land on other people + + pm->gent->angle = pm->ps->viewangles[YAW];//so we know what yaw we started this at + + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + + if ( Q_irand( 0, 1 ) ) + { + return LS_A_FLIP_STAB; + } + else + { + return LS_A_FLIP_SLASH; + } +} + +qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( (pm->ps->saberAnimLevel == SS_MEDIUM //medium + || pm->ps->saberAnimLevel == SS_TAVION )//tavion + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + ) + { + qboolean tryMove = qfalse; + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0//want to jump + || (pm->ps->pm_flags&PMF_JUMPING) )//jumping + {//flip over-forward down-attack + if ( (pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) + && !Q_irand(0, 2) ) )//NPC who can do this, 33% chance + {//only player or acrobat or boss and higher can do this + tryMove = qtrue; + } + } + } + else + {//player + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power + { + if ( !pm->cmd.rightmove ) + { + if ( pm->ps->legsAnim == BOTH_JUMP1 + || pm->ps->legsAnim == BOTH_FORCEJUMP1 + || pm->ps->legsAnim == BOTH_INAIR1 + || pm->ps->legsAnim == BOTH_FORCEINAIR1 ) + {//in a non-flip forward jump + tryMove = qtrue; + } + } + } + } + + if ( tryMove ) + { + if ( !checkEnemy ) + {//based just on command input + return qtrue; + } + else + {//based on presence of enemy + if ( pm->gent->enemy )//have an enemy + { + vec3_t fwdAngles = {0,pm->ps->viewangles[YAW],0}; + if ( pm->gent->enemy->health > 0 + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->gent->enemy->maxs[2] > 12 + && (!pm->gent->enemy->client || !PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) ) + && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 10000 + && InFront( pm->gent->enemy->currentOrigin, pm->gent->currentOrigin, fwdAngles, 0.3f ) ) + {//enemy must be alive, not low to ground, close and in front + return qtrue; + } + } + return qfalse; + } + } + } + return qfalse; +} + +saberMoveName_t PM_SaberBackflipAttackMove( void ) +{ + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkBackMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkBackMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkBackMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkBackMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkBackMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + { + return LS_NONE; + } + } + pm->cmd.upmove = 0;//no jump just yet + return LS_A_BACKFLIP_ATK; +} + +qboolean PM_CheckBackflipAttackMove( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE + || pm->ps->saber[1].jumpAtkBackMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE + || pm->ps->saber[0].jumpAtkBackMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + //&& (pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE || level.time-pm->ps->lastStationary<=250)//standing or just started moving + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) )//on ground or just jumped (if not player) + { + if ( pm->cmd.forwardmove < 0 //moving backwards + && pm->ps->saberAnimLevel == SS_STAFF //using staff + && (pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING)) )//jumping + {//jumping backwards and using staff + if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim + && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + && pm->ps->weaponTime <= 0//not busy + && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + {//not already attacking + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + {//acrobat or boss and higher can do this + return qtrue; + } + } + else + {//player + return qtrue; + } + } + } + } + return qfalse; +} + +saberMoveName_t PM_CheckDualSpinProtect( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[0].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].kataMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[1].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].kataMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].kataMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->ps->saberMove == LS_READY//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? + //&& pm->ps->viewangles[0] > 30 //looking down + && pm->ps->saberAnimLevel == SS_DUAL//using dual saber style + && pm->ps->saber[0].Active() && pm->ps->saber[1].Active()//both sabers on + //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 + //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing + && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack + ) + {//FIXME: some NPC logic to do this? + /* + if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching + && g_crosshairEntNum >= ENTITYNUM_WORLD ) + */ + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_PUSH, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + } + return LS_DUAL_SPIN_PROTECT; + } + } + return LS_NONE; +} + +saberMoveName_t PM_CheckStaffKata( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[0].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].kataMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[1].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].kataMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].kataMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->ps->saberMove == LS_READY//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? + //&& pm->ps->viewangles[0] > 30 //looking down + && pm->ps->saberAnimLevel == SS_STAFF//using dual saber style + && pm->ps->saber[0].Active()//saber on + //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 + //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing + && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack + ) + {//FIXME: some NPC logic to do this? + /* + if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching + && g_crosshairEntNum >= ENTITYNUM_WORLD ) + */ + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + } + return LS_STAFF_SOULCAL; + } + } + return LS_NONE; +} + +extern qboolean WP_ForceThrowable( gentity_t *ent, gentity_t *forwardEnt, gentity_t *self, qboolean pull, float cone, float radius, vec3_t forward ); +saberMoveName_t PM_CheckPullAttack( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + if ( (pm->ps->saber[0].saberFlags&SFL_NO_PULL_ATTACK) ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_PULL_ATTACK) ) + { + return LS_NONE; + } + + if ( (pm->ps->saberMove == LS_READY||PM_SaberInReturn(pm->ps->saberMove)||PM_SaberInReflect(pm->ps->saberMove))//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY + && pm->ps->saberAnimLevel >= SS_FAST//single saber styles - FIXME: Tavion? + && pm->ps->saberAnimLevel <= SS_STRONG//single saber styles - FIXME: Tavion? + && G_TryingPullAttack( pm->gent, &pm->cmd, qfalse )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + //&& pm->cmd.forwardmove<0//pulling back + && (pm->cmd.buttons&BUTTON_ATTACK)//attacking + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB//have enough power + ) + {//FIXME: some NPC logic to do this? + qboolean doMove = g_saberNewControlScheme->integer?qtrue:qfalse;//in new control scheme, can always do this, even if there's no-one to do it to + if ( g_saberNewControlScheme->integer + || g_crosshairEntNum < ENTITYNUM_WORLD )//in old control scheme, there has to be someone there + { + saberMoveName_t pullAttackMove = LS_NONE; + if ( pm->ps->saberAnimLevel == SS_FAST ) + { + pullAttackMove = LS_PULL_ATTACK_STAB; + } + else + { + pullAttackMove = LS_PULL_ATTACK_SWING; + } + + if ( g_crosshairEntNum < ENTITYNUM_WORLD + && pm->gent && pm->gent->client ) + { + gentity_t *targEnt = &g_entities[g_crosshairEntNum]; + if ( targEnt->client + && targEnt->health > 0 + //FIXME: check other things like in knockdown, saberlock, uninterruptable anims, etc. + && !PM_InOnGroundAnim( &targEnt->client->ps ) + && !PM_LockedAnim( targEnt->client->ps.legsAnim ) + && !PM_SuperBreakLoseAnim( targEnt->client->ps.legsAnim ) + && !PM_SuperBreakWinAnim( targEnt->client->ps.legsAnim ) + && targEnt->client->ps.saberLockTime <= 0 + && WP_ForceThrowable( targEnt, targEnt, pm->gent, qtrue, 1.0f, 0.0f, NULL ) ) + { + if ( !g_saberNewControlScheme->integer ) + {//in old control scheme, make sure they're close or far enough away for the move we'll be doing + float targDist = Distance( targEnt->currentOrigin, pm->ps->origin ); + if ( pullAttackMove == LS_PULL_ATTACK_STAB ) + {//must be closer than 512 + if ( targDist > 384.0f ) + { + return LS_NONE; + } + } + else//if ( pullAttackMove == LS_PULL_ATTACK_SWING ) + {//must be farther than 256 + if ( targDist > 512.0f ) + { + return LS_NONE; + } + if ( targDist < 192.0f ) + { + return LS_NONE; + } + } + } + + vec3_t targAngles = {0,targEnt->client->ps.viewangles[YAW],0}; + if ( InFront( pm->ps->origin, targEnt->currentOrigin, targAngles ) ) + { + NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_F, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + } + else + { + NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_B, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + } + //hold the anim until I'm with done pull anim + targEnt->client->ps.legsAnimTimer = targEnt->client->ps.torsoAnimTimer = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, (animNumber_t)saberMoveData[pullAttackMove].animToUse ); + //set pullAttackTime + pm->gent->client->ps.pullAttackTime = targEnt->client->ps.pullAttackTime = level.time+targEnt->client->ps.legsAnimTimer; + //make us know about each other + pm->gent->client->ps.pullAttackEntNum = g_crosshairEntNum; + targEnt->client->ps.pullAttackEntNum = pm->ps->clientNum; + //do effect and sound on me + pm->ps->powerups[PW_FORCE_PUSH] = level.time + 1000; + if ( pm->gent ) + { + G_Sound( pm->gent, G_SoundIndex( "sound/weapons/force/pull.wav" ) ); + } + doMove = qtrue; + } + } + if ( doMove ) + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_PULL, SABER_ALT_ATTACK_POWER_FB ); + } + return pullAttackMove; + } + } + } + return LS_NONE; +} + +saberMoveName_t PM_CheckPlayerAttackFromParry( int curmove ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + { + if ( curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL ) + {//in a parry + switch ( saberMoveData[curmove].endQuad ) + { + case Q_T: + return LS_A_T2B; + break; + case Q_TR: + return LS_A_TR2BL; + break; + case Q_TL: + return LS_A_TL2BR; + break; + case Q_BR: + return LS_A_BR2TL; + break; + case Q_BL: + return LS_A_BL2TR; + break; + //shouldn't be a parry that ends at L, R or B + } + } + } + return LS_NONE; +} + + +saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int curmove ) +{ + qboolean noSpecials = qfalse; + + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + noSpecials = qtrue; + } + + saberMoveName_t overrideJumpRightAttackMove = LS_INVALID; + if ( pm->ps->saber[0].jumpAtkRightMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkRightMove != LS_NONE ) + {//actually overriding + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkRightMove; + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove > LS_NONE ) + {//would be cancelling it, but check the second saber, too + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; + } + else + {//nope, just cancel it + overrideJumpRightAttackMove = LS_NONE; + } + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove != LS_INVALID ) + {//first saber not overridden, check second + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; + } + + saberMoveName_t overrideJumpLeftAttackMove = LS_INVALID; + if ( pm->ps->saber[0].jumpAtkLeftMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkLeftMove != LS_NONE ) + {//actually overriding + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkLeftMove; + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove > LS_NONE ) + {//would be cancelling it, but check the second saber, too + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; + } + else + {//nope, just cancel it + overrideJumpLeftAttackMove = LS_NONE; + } + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove != LS_INVALID ) + {//first saber not overridden, check second + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; + } + if ( rightmove > 0 ) + {//moving right + if ( !noSpecials + && overrideJumpRightAttackMove != LS_NONE + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC + ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + {//cartwheel right + vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + } + pm->cmd.upmove = 0; + if ( overrideJumpRightAttackMove != LS_INVALID ) + {//overridden with another move + return overrideJumpRightAttackMove; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + return LS_BUTTERFLY_RIGHT; + } + else + { + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + {//okay to do cartwheels with this saber + /* + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//still on ground + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_CART_RIGHT; + } + else + */ + {//in air + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + PM_SetJumped( JUMP_VELOCITY, qtrue ); + return LS_JUMPATTACK_ARIAL_RIGHT; + } + } + } + } + else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_RIGHT + && pm->ps->legsAnim != BOTH_ARIAL_RIGHT ) + {//not in a cartwheel/arial + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( forwardmove > 0 ) + {//forward right = TL2BR slash + return LS_A_TL2BR; + } + else if ( forwardmove < 0 ) + {//backward right = BL2TR uppercut + return LS_A_BL2TR; + } + else + {//just right is a left slice + return LS_A_L2R; + } + } + } + else if ( rightmove < 0 ) + {//moving left + if ( !noSpecials + && overrideJumpLeftAttackMove != LS_NONE + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC + ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + {//cartwheel left + vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + } + pm->cmd.upmove = 0; + if ( overrideJumpRightAttackMove != LS_INVALID ) + {//overridden with another move + return overrideJumpRightAttackMove; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + return LS_BUTTERFLY_LEFT; + } + else + { + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + {//okay to do cartwheels with this saber + /* + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//still on ground + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_ARIAL_LEFT; + } + else + */ + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + PM_SetJumped( JUMP_VELOCITY, qtrue ); + return LS_JUMPATTACK_CART_LEFT; + } + } + } + } + else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_LEFT + && pm->ps->legsAnim != BOTH_ARIAL_LEFT ) + {//not in a left cartwheel/arial + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( forwardmove > 0 ) + {//forward left = TR2BL slash + return LS_A_TR2BL; + } + else if ( forwardmove < 0 ) + {//backward left = BR2TL uppercut + return LS_A_BR2TL; + } + else + {//just left is a right slice + return LS_A_R2L; + } + } + } + else + {//not moving left or right + if ( forwardmove > 0 ) + {//forward= T2B slash + saberMoveName_t stabDownMove = noSpecials?LS_NONE:PM_CheckStabDown(); + if ( stabDownMove != LS_NONE ) + { + return stabDownMove; + } + if ( ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zoomed in + {//player in thirdperson, not zoomed in + //flip-over attack logic + if ( !noSpecials && PM_CheckFlipOverAttackMove( qfalse ) ) + {//flip over-forward down-attack + return PM_SaberFlipOverAttackMove(); + } + //lunge attack logic + else if ( PM_CheckLungeAttackMove() ) + { + return PM_SaberLungeAttackMove( qtrue ); + } + //jump forward attack logic + else if ( !noSpecials && PM_CheckJumpForwardAttackMove() ) + { + return PM_SaberJumpForwardAttackMove(); + } + } + + //player NPC with enemy: autoMove logic + if ( pm->gent + && pm->gent->enemy + && pm->gent->enemy->client ) + {//I have an active enemy + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + {//a player who is running at an enemy + //if the enemy is not a jedi, don't use top-down, pick a diagonal or side attack + if ( pm->gent->enemy->s.weapon != WP_SABER + && pm->gent->enemy->client->NPC_class != CLASS_REMOTE//too small to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_SEEKER//too small to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_GONK//too short to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_HOWLER//too short to do auto-aiming accurately + && g_saberAutoAim->integer ) + { + saberMoveName_t autoMove = PM_AttackForEnemyPos( qfalse, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer()) ); + if ( autoMove != LS_INVALID ) + { + return autoMove; + } + } + } + + if ( pm->ps->clientNum>=MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + {//NPC + if ( PM_CheckFlipOverAttackMove( qtrue ) ) + { + return PM_SaberFlipOverAttackMove(); + } + } + } + + //Regular NPCs + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + {//NPC or player in third person, not zoomed in + //fwd jump attack logic + if ( PM_CheckJumpForwardAttackMove() ) + { + return PM_SaberJumpForwardAttackMove(); + } + //lunge attack logic + else if ( PM_CheckLungeAttackMove() ) + { + return PM_SaberLungeAttackMove( qtrue ); + } + } + + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + return LS_A_T2B; + } + else if ( forwardmove < 0 ) + {//backward= T2B slash//B2T uppercut? + if ( g_saberNewControlScheme->integer ) + { + saberMoveName_t pullAtk = PM_CheckPullAttack(); + if ( pullAtk != LS_NONE ) + { + return pullAtk; + } + } + + if ( g_saberNewControlScheme->integer + && (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + && (pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus, trying special backwards attacks + {//player lunge attack logic + if ( ( pm->ps->dualSabers //or dual + || pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->SaberStaff() )//or staff + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + {//alt+back+attack using fast, dual or staff attacks + PM_SaberLungeAttackMove( qfalse ); + } + } + else if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) //NPC + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zooomed + {//NPC or player in third person, not zoomed + if ( PM_CheckBackflipAttackMove() ) + { + return PM_SaberBackflipAttackMove();//backflip attack + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //if ( !PM_InKnockDown( pm->ps ) ) + //check backstabs + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( pm->gent && pm->gent->enemy ) + {//FIXME: or just trace for a valid enemy standing behind me? And no enemy in front? + vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, NULL, NULL ); + VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + float dot = DotProduct( enemyDir, faceFwd ); + if ( dot < 0 ) + {//enemy is behind me + if ( dot < -0.75f + && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 16384//128 squared + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,1))) ) + {//fast attacks and Tavion + if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + {//can't do it while ducked? + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + {//only fencers and above can do this + return LS_A_BACKSTAB; + } + } + } + else if ( pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF ) + {//medium and higher attacks + if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + } + else + {//enemy in front + float enemyDistSq = DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ); + if ( ((pm->ps->saberAnimLevel == FORCE_LEVEL_1 || + pm->ps->saberAnimLevel == SS_STAFF || + pm->gent->client->NPC_class == CLASS_TAVION || + pm->gent->client->NPC_class == CLASS_ALORA || + (pm->gent->client->NPC_class == CLASS_DESANN && !Q_irand(0,3))) && + enemyDistSq > 16384) || + pm->gent->enemy->health <= 0 )//128 squared + {//my enemy is pretty far in front of me and I'm using fast attacks + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || + ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 128 ) ) + { + return PM_PickBackStab(); + } + } + } + else if ( ((pm->ps->saberAnimLevel >= FORCE_LEVEL_2 || pm->gent->client->NPC_class == CLASS_DESANN) && enemyDistSq > 40000) || pm->gent->enemy->health <= 0 )//200 squared + {//enemy is very faw away and I'm using medium/strong attacks + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || + ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 164 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + else + {//no current enemy + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->gent && pm->gent->client ) + {//only player + if ( PM_CheckEnemyInBack( 128 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + } + + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + //else just swing down + return LS_A_T2B; + } + else + {//not moving in any direction + if ( PM_SaberInBounce( curmove ) ) + {//bounces should go to their default attack if you don't specify a direction but are attacking + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + saberMoveName_t newmove; + if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + } + else + {//player uses chain-attack + newmove = saberMoveData[curmove].chain_attack; + } + if ( PM_SaberKataDone( curmove, newmove ) ) + { + return saberMoveData[curmove].chain_idle; + } + else + { + return newmove; + } + } + else if ( PM_SaberInKnockaway( curmove ) ) + {//bounces should go to their default attack if you don't specify a direction but are attacking + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + saberMoveName_t newmove; + if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + } + else + { + if ( pm->ps->saberAnimLevel == SS_FAST || + pm->ps->saberAnimLevel == SS_TAVION ) + {//player is in fast attacks, so come right back down from the same spot + newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad ); + } + else + {//use a transition to wrap to another attack from a different dir + newmove = saberMoveData[curmove].chain_attack; + } + } + if ( PM_SaberKataDone( curmove, newmove ) ) + { + return saberMoveData[curmove].chain_idle; + } + else + { + return newmove; + } + } + else if ( curmove == LS_READY + || curmove == LS_A_FLIP_STAB + || curmove == LS_A_FLIP_SLASH + || ( curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL ) ) + {//Not moving at all, not too busy to attack + //push + lookdown + attack + dual sabers = LS_DUAL_SPIN_PROTECT + if ( g_saberNewControlScheme->integer ) + { + if ( PM_CheckDualSpinProtect() ) + { + return LS_DUAL_SPIN_PROTECT; + } + if ( PM_CheckStaffKata() ) + { + return LS_STAFF_SOULCAL; + } + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( pm->ps->clientNum || g_saberAutoAim->integer ) + {//auto-aim + if ( pm->gent && pm->gent->enemy ) + {//based on enemy position, pick a proper attack + saberMoveName_t autoMove = PM_AttackForEnemyPos( qtrue, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS) ); + if ( autoMove != LS_INVALID ) + { + return autoMove; + } + } + else if ( fabs(pm->ps->viewangles[0]) > 30 ) + {//looking far up or far down uses the top to bottom attack, presuming you want a vertical attack + return LS_A_T2B; + } + } + else + {//for now, just pick a random attack + return ((saberMoveName_t)Q_irand( LS_A_TL2BR, LS_A_T2B )); + } + } + } + } + //FIXME: pick a return? + return LS_NONE; +} + +saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveName_t newmove ) +{ + //FIXME: take FP_SABER_OFFENSE into account here somehow? + int retmove = newmove; + if ( curmove == LS_READY ) + {//just standing there + switch ( newmove ) + { + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + //transition is the start + retmove = LS_S_TL2BR + (newmove-LS_A_TL2BR); + break; + default: + break; + } + } + else + { + switch ( newmove ) + { + //transitioning to ready pose + case LS_READY: + switch ( curmove ) + { + //transitioning from an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + //transition is the return + retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + break; + default: + break; + } + break; + //transitioning to an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + if ( newmove == curmove ) + {//FIXME: need a spin or something or go to next level, but for now, just play the return + //going into another attack... + //allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3 + //FIXME: don't let strong attacks chain to an attack in the opposite direction ( > 45 degrees?) + if ( PM_SaberKataDone( curmove, newmove ) ) + {//done with this kata, must return to ready before attack again + retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + } + else + {//okay to chain to another attack + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + } + } + else if ( saberMoveData[curmove].endQuad == saberMoveData[newmove].startQuad ) + {//new move starts from same quadrant + retmove = newmove; + } + else + { + switch ( curmove ) + { + //transitioning from an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + case LS_D1_BR: + case LS_D1__R: + case LS_D1_TR: + case LS_D1_T_: + case LS_D1_TL: + case LS_D1__L: + case LS_D1_BL: + case LS_D1_B_: + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + break; + //transitioning from a return + case LS_R_TL2BR: + case LS_R_L2R: + case LS_R_BL2TR: + case LS_R_BR2TL: + case LS_R_R2L: + case LS_R_TR2BL: + case LS_R_T2B: + //transitioning from a bounce + /* + case LS_BOUNCE_UL2LL: + case LS_BOUNCE_LL2UL: + case LS_BOUNCE_L2LL: + case LS_BOUNCE_L2UL: + case LS_BOUNCE_UR2LR: + case LS_BOUNCE_LR2UR: + case LS_BOUNCE_R2LR: + case LS_BOUNCE_R2UR: + case LS_BOUNCE_TOP: + case LS_OVER_UR2UL: + case LS_OVER_UL2UR: + case LS_BOUNCE_UR: + case LS_BOUNCE_UL: + case LS_BOUNCE_LR: + case LS_BOUNCE_LL: + */ + //transitioning from a parry/reflection/knockaway/broken parry + case LS_PARRY_UP: + case LS_PARRY_UR: + case LS_PARRY_UL: + case LS_PARRY_LR: + case LS_PARRY_LL: + case LS_REFLECT_UP: + case LS_REFLECT_UR: + case LS_REFLECT_UL: + case LS_REFLECT_LR: + case LS_REFLECT_LL: + case LS_K1_T_: + case LS_K1_TR: + case LS_K1_TL: + case LS_K1_BR: + case LS_K1_BL: + case LS_V1_BR: + case LS_V1__R: + case LS_V1_TR: + case LS_V1_T_: + case LS_V1_TL: + case LS_V1__L: + case LS_V1_BL: + case LS_V1_B_: + case LS_H1_T_: + case LS_H1_TR: + case LS_H1_TL: + case LS_H1_BR: + case LS_H1_BL: + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + break; + //NB: transitioning from transitions is fine + default: + break; + } + } + break; + //transitioning to any other anim is not supported + default: + break; + } + } + + if ( retmove == LS_NONE ) + { + return newmove; + } + + return ((saberMoveName_t)retmove); +} + +/* +------------------------- +PM_LegsAnimForFrame +Returns animNumber for current frame +------------------------- +*/ +int PM_LegsAnimForFrame( gentity_t *ent, int legsFrame ) +{ + //Must be a valid client + if ( ent->client == NULL ) + return -1; + + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return -1; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); + + for ( int animation = 0; animation < BOTH_CIN_1; animation++ ) //first anim after last legs + { + if ( animation >= TORSO_DROPWEAP1 && animation < LEGS_TURN1 ) //first legs only anim + {//not a possible legs anim + continue; + } + + if ( animations[animation].glaIndex != glaIndex ) + { + continue; + } + + if ( animations[animation].firstFrame > legsFrame ) + {//This anim starts after this frame + continue; + } + + if ( animations[animation].firstFrame + animations[animation].numFrames < legsFrame ) + {//This anim ends before this frame + continue; + } + //else, must be in this anim! + return animation; + } + + //Not in ANY torsoAnim? SHOULD NEVER HAPPEN +// assert(0); + return -1; +} + +int PM_ValidateAnimRange( const int startFrame, const int endFrame, const float animSpeed ) +{//given a startframe and endframe, see if that lines up with any known animation + animation_t *animations = level.knownAnimFileSets[0].animations; + + for ( int anim = 0; anim < MAX_ANIMATIONS; anim++ ) + { + if ( animSpeed < 0 ) + {//playing backwards + if ( animations[anim].firstFrame == endFrame ) + { + if ( animations[anim].numFrames + animations[anim].firstFrame == startFrame ) + { + //Com_Printf( "valid reverse anim: %s\n", animTable[anim].name ); + return anim; + } + } + } + else + {//playing forwards + if ( animations[anim].firstFrame == startFrame ) + {//This anim starts on this frame + if ( animations[anim].firstFrame + animations[anim].numFrames == endFrame ) + {//This anim ends on this frame + //Com_Printf( "valid forward anim: %s\n", animTable[anim].name ); + return anim; + } + } + } + //else, must not be this anim! + } + + //Not in ANY anim? SHOULD NEVER HAPPEN + Com_Printf( "invalid anim range %d to %d, speed %4.2f\n", startFrame, endFrame, animSpeed ); + return -1; +} +/* +------------------------- +PM_TorsoAnimForFrame +Returns animNumber for current frame +------------------------- +*/ +int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame ) +{ + //Must be a valid client + if ( ent->client == NULL ) + return -1; + + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return -1; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); + + for ( int animation = 0; animation < LEGS_TURN1; animation++ ) //first legs only anim + { + if ( animations[animation].glaIndex != glaIndex ) + { + continue; + } + + if ( animations[animation].firstFrame > torsoFrame ) + {//This anim starts after this frame + continue; + } + + if ( animations[animation].firstFrame + animations[animation].numFrames < torsoFrame ) + {//This anim ends before this frame + continue; + } + //else, must be in this anim! + return animation; + } + + //Not in ANY torsoAnim? SHOULD NEVER HAPPEN +// assert(0); + return -1; +} + +qboolean PM_FinishedCurrentLegsAnim( gentity_t *self ) +{ + int junk, curFrame; + float currentFrame, animSpeed; + + if ( !self->client ) + { + return qtrue; + } + + gi.G2API_GetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &animSpeed, NULL ); + curFrame = floor( currentFrame ); + + int legsAnim = self->client->ps.legsAnim; + animation_t *animations = level.knownAnimFileSets[self->client->clientInfo.animFileIndex].animations; + + if ( curFrame >= animations[legsAnim].firstFrame + (animations[legsAnim].numFrames - 2) ) + { + return qtrue; + } + + return qfalse; +} + +/* +------------------------- +PM_HasAnimation +------------------------- +*/ + +qboolean PM_HasAnimation( gentity_t *ent, int animation ) +{ + //Must be a valid client + if ( !ent || ent->client == NULL ) + return qfalse; + + //must be a valid anim number + if ( animation < 0 || animation >= MAX_ANIMATIONS ) + { + return qfalse; + } + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return qfalse; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + + //No frames, no anim + if ( animations[animation].numFrames == 0 ) + return qfalse; + + //Has the sequence + return qtrue; +} + +int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim ) +{ + int anim; + int count = 0; + + if ( !self ) + { + return Q_irand(minAnim, maxAnim); + } + + do + { + anim = Q_irand(minAnim, maxAnim); + count++; + } + while ( !PM_HasAnimation( self, anim ) && count < 1000 ); + + return anim; +} + +/* +------------------------- +PM_AnimLength +------------------------- +*/ + +int PM_AnimLength( int index, animNumber_t anim ) +{ + if ( ValidAnimFileIndex( index ) == false ) + return 0; + + return level.knownAnimFileSets[index].animations[anim].numFrames * abs(level.knownAnimFileSets[index].animations[anim].frameLerp); +} + +/* +------------------------- +PM_SetLegsAnimTimer +------------------------- +*/ + +void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time ) +{ + *legsAnimTimer = time; + + if ( *legsAnimTimer < 0 && time != -1 ) + {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional + *legsAnimTimer = 0; + } + + if ( !*legsAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_LOWER ) ) + {//Waiting for legsAnimTimer to complete, and it just got set to zero + if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + {//Not waiting for top + Q3_TaskIDComplete( ent, TID_ANIM_LOWER ); + } + else + {//Waiting for both to finish before complete + Q3_TaskIDClear( &ent->taskID[TID_ANIM_LOWER] );//Bottom is done, regardless + if ( !Q3_TaskIDPending( ent, TID_ANIM_UPPER) ) + {//top is done and we're done + Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + } + } + } +} + +/* +------------------------- +PM_SetTorsoAnimTimer +------------------------- +*/ + +void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time ) +{ + *torsoAnimTimer = time; + + if ( *torsoAnimTimer < 0 && time != -1 ) + {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional + *torsoAnimTimer = 0; + } + + if ( !*torsoAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_UPPER ) ) + {//Waiting for torsoAnimTimer to complete, and it just got set to zero + if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + {//Not waiting for bottom + Q3_TaskIDComplete( ent, TID_ANIM_UPPER ); + } + else + {//Waiting for both to finish before complete + Q3_TaskIDClear( &ent->taskID[TID_ANIM_UPPER] );//Top is done, regardless + if ( !Q3_TaskIDPending( ent, TID_ANIM_LOWER) ) + {//lower is done and we're done + Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + } + } + } +} + +extern qboolean PM_SpinningSaberAnim( int anim ); +extern float saberAnimSpeedMod[NUM_FORCE_POWER_LEVELS]; +void PM_SaberStartTransAnim( int saberAnimLevel, int anim, float *animSpeed, gentity_t *gent ) +{ + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_ROLL_STAB ) + { + if ( g_saberAnimSpeed->value != 1.0f ) + { + *animSpeed *= g_saberAnimSpeed->value; + } + else if ( gent && gent->client && gent->client->ps.weapon == WP_SABER ) + { + if ( gent->client->ps.saber[0].animSpeedScale != 1.0f ) + { + *animSpeed *= gent->client->ps.saber[0].animSpeedScale; + } + if ( gent->client->ps.dualSabers + && gent->client->ps.saber[1].animSpeedScale != 1.0f ) + { + *animSpeed *= gent->client->ps.saber[1].animSpeedScale; + } + } + } + if ( gent + && gent->client + && gent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.dualSabers + && saberAnimLevel == SS_DUAL + && gent->weaponModel[1] ) + {//using a scepter and dual style, slow down anims + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H7_S7_BR ) + { + *animSpeed *= 0.75; + } + } + if ( gent && gent->client && gent->client->ps.forceRageRecoveryTime > level.time ) + {//rage recovery + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR ) + {//animate slower + *animSpeed *= 0.75; + } + } + else if ( gent && gent->NPC && gent->NPC->rank == RANK_CIVILIAN ) + {//grunt reborn + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + {//his fast attacks are slower + if ( !PM_SpinningSaberAnim( anim ) ) + { + *animSpeed *= 0.75; + } + return; + } + } + else if ( gent && gent->client ) + { + if ( gent->client->ps.saber[0].type == SABER_LANCE || gent->client->ps.saber[0].type == SABER_TRIDENT ) + {//FIXME: hack for now - these use the fast anims, but slowed down. Should have own style + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + {//his fast attacks are slower + if ( !PM_SpinningSaberAnim( anim ) ) + { + *animSpeed *= 0.75; + } + return; + } + } + } + + if ( ( anim >= BOTH_T1_BR__R && + anim <= BOTH_T1_BL_TL ) || + ( anim >= BOTH_T3_BR__R && + anim <= BOTH_T3_BL_TL ) || + ( anim >= BOTH_T5_BR__R && + anim <= BOTH_T5_BL_TL ) ) + { + if ( saberAnimLevel == FORCE_LEVEL_1 || saberAnimLevel == FORCE_LEVEL_5 ) + {//FIXME: should not be necc for FORCE_LEVEL_1's + *animSpeed *= 1.5; + } + else if ( saberAnimLevel == FORCE_LEVEL_3 ) + { + *animSpeed *= 0.75; + } + } +} +/* +void PM_SaberStartTransAnim( int anim, int entNum, int saberOffenseLevel, float *animSpeed ) +{ + //check starts + if ( ( anim >= BOTH_S1_S1_T_ && + anim <= BOTH_S1_S1_TR ) || + ( anim >= BOTH_S1_S1_T_ && + anim <= BOTH_S1_S1_TR ) || + ( anim >= BOTH_S3_S1_T_ && + anim <= BOTH_S3_S1_TR ) ) + { + if ( entNum == 0 ) + { + *animSpeed *= saberAnimSpeedMod[FORCE_LEVEL_3]; + } + else + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; + } + } + //Check transitions + else if ( PM_SpinningSaberAnim( anim ) ) + {//spins stay normal speed + return; + } + else if ( ( anim >= BOTH_T1_BR__R && + anim <= BOTH_T1_BL_TL ) || + ( anim >= BOTH_T2_BR__R && + anim <= BOTH_T2_BL_TL ) || + ( anim >= BOTH_T3_BR__R && + anim <= BOTH_T3_BL_TL ) ) + {//slow down the transitions + if ( entNum == 0 && saberOffenseLevel <= FORCE_LEVEL_2 ) + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; + } + else + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]/2.0f; + } + } + + return; +} +*/ +extern qboolean player_locked; +extern qboolean MatrixMode; +float PM_GetTimeScaleMod( gentity_t *gent ) +{ + if ( g_timescale->value ) + { + if ( !MatrixMode + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_START + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_ATTACK + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_LAND ) + { + if ( gent && gent->s.clientNum == 0 && !player_locked && gent->client->ps.forcePowersActive&(1<value); + } + else if ( gent && gent->client && gent->client->ps.forcePowersActive&(1<value); + } + } + } + return 1.0f; +} + +static inline qboolean PM_IsHumanoid( CGhoul2Info *ghlInfo ) +{ + char *GLAName; + GLAName = gi.G2API_GetGLAName( ghlInfo ); + assert(GLAName); + + if ( !Q_stricmp( "models/players/_humanoid/_humanoid", GLAName ) ) + { + return qtrue; + } + + return qfalse; +} + +/* +------------------------- +PM_SetAnimFinal +------------------------- +*/ +#define G2_DEBUG_TIMING (0) +void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, + int setAnimParts,int anim,int setAnimFlags, + int *torsoAnimTimer,int *legsAnimTimer, + gentity_t *gent,int blendTime) // default blendTime=350 +{ + +// BASIC SETUP AND SAFETY CHECKING +//================================= + + // If It Is A Busted Entity, Don't Do Anything Here. + //--------------------------------------------------- + if (!gent || !gent->client) + { + return; + } + + // Make Sure This Character Has Such An Anim And A Model + //------------------------------------------------------- + if (anim<0 || anim>=MAX_ANIMATIONS || !ValidAnimFileIndex(gent->client->clientInfo.animFileIndex)) + { + #ifndef FINAL_BUILD + if (g_AnimWarning->integer) + { + if (anim<0 || anim>=MAX_ANIMATIONS) + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim Index (%d)!\n", anim); + } + else + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim File Index (%d)!\n", gent->client->clientInfo.animFileIndex); + } + } + #endif + return; + } + + + // Get Global Time Properties + //---------------------------- + float timeScaleMod = PM_GetTimeScaleMod( gent ); + const int actualTime = (cg.time?cg.time:level.time); + const animation_t* animations = level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations; + const animation_t& curAnim = animations[anim]; + + // Make Sure This Character Has Such An Anim And A Model + //------------------------------------------------------- + if (animations[anim].numFrames==0) + { + #ifndef FINAL_BUILD + static int LastAnimWarningNum=0; + if (LastAnimWarningNum!=anim) + { + if ((cg_debugAnim.integer==3) || // 3 = do everyone + (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player + (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else + (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + ) + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Anim %s does not exist in this model (%s)!\n", animTable[anim].name, gent->NPC_type ); + } + } + LastAnimWarningNum = anim; + #endif + return; + } + + // If It's Not A Ghoul 2 Model, Just Remember The Anims And Stop, Because Everything Beyond This Is Ghoul2 + //--------------------------------------------------------------------------------------------------------- + if (!gi.G2API_HaveWeGhoul2Models(gent->ghoul2)) + { + if (setAnimParts&SETANIM_TORSO) + { + (*torsoAnim) = anim; + } + if (setAnimParts&SETANIM_LEGS) + { + (*legsAnim) = anim; + } + return; + } + + + // Lower Offensive Skill Slows Down The Saber Start Attack Animations + //-------------------------------------------------------------------- + PM_SaberStartTransAnim( gent->client->ps.saberAnimLevel, anim, &timeScaleMod, gent ); + + + +// SETUP VALUES FOR INCOMMING ANIMATION +//====================================== + const bool animFootMove = (PM_WalkingAnim(anim) || PM_RunningAnim(anim) || anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK); + const bool animHoldless = (setAnimFlags&SETANIM_FLAG_HOLDLESS)!=0; + const bool animHold = (setAnimFlags&SETANIM_FLAG_HOLD)!=0; + const bool animRestart = (setAnimFlags&SETANIM_FLAG_RESTART)!=0; + const bool animOverride = (setAnimFlags&SETANIM_FLAG_OVERRIDE)!=0; + const bool animSync = (g_synchSplitAnims->integer!=0 && !animRestart); + float animCurrent = (-1.0f); + float animSpeed = (50.0f / curAnim.frameLerp * timeScaleMod); // animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps). + const float animFPS = (fabsf(curAnim.frameLerp)); + const int animDurMSec = (int)(((curAnim.numFrames - 1) * animFPS) / timeScaleMod); + const int animHoldMSec = ((animHoldless && timeScaleMod==1.0f)?((animDurMSec>1)?(animDurMSec-1):(animFPS)):(animDurMSec)); + int animFlags = (curAnim.loopFrames!=-1)?(BONE_ANIM_OVERRIDE_LOOP):(BONE_ANIM_OVERRIDE_FREEZE); + int animStart = (curAnim.firstFrame); + int animEnd = (curAnim.firstFrame)+(animations[anim].numFrames); + + // If We Have A Blend Timer, Add The Blend Flag + //---------------------------------------------- + if (blendTime > 0) + { + animFlags |= BONE_ANIM_BLEND; + } + + // If Animation Is Going Backwards, Swap Last And First Frames + //------------------------------------------------------------- + if (animSpeed<0.0f) + { +// #ifndef FINAL_BUILD + #if 0 + if (g_AnimWarning->integer==1) + { + if (animFlags&BONE_ANIM_OVERRIDE_LOOP) + { + gi.Printf(S_COLOR_YELLOW"PM_SetAnimFinal: WARNING: Anim (%s) looping backwards!\n", animTable[anim].name); + } + } + #endif + + int temp = animEnd; + animEnd = animStart; + animStart = temp; + blendTime = 0; + } + + // If The Animation Is Walking Or Running, Attempt To Scale The Playback Speed To Match + //-------------------------------------------------------------------------------------- + if (g_noFootSlide->integer + && animFootMove + && !(animSpeed<0.0f) + //FIXME: either read speed from animation.cfg or only do this for NPCs + // for whom we've specifically determined the proper numbers! + && gent->client->NPC_class != CLASS_HOWLER + && gent->client->NPC_class != CLASS_WAMPA + && gent->client->NPC_class != CLASS_GONK + && gent->client->NPC_class != CLASS_HOWLER + && gent->client->NPC_class != CLASS_MOUSE + && gent->client->NPC_class != CLASS_PROBE + && gent->client->NPC_class != CLASS_PROTOCOL + && gent->client->NPC_class != CLASS_R2D2 + && gent->client->NPC_class != CLASS_R5D2 + && gent->client->NPC_class != CLASS_SEEKER) + { + bool Walking = !!PM_WalkingAnim(anim); + bool HasDual = (gent->client->ps.saberAnimLevel==SS_DUAL); + bool HasStaff = (gent->client->ps.saberAnimLevel==SS_STAFF); + float moveSpeedOfAnim = 150.0f;//g_noFootSlideRunScale->value; + + if (anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK) + { + moveSpeedOfAnim = 75.0f; + } + else + { + if (gent->client->NPC_class == CLASS_HAZARD_TROOPER) + { + moveSpeedOfAnim = 50.0f; + } + else if (gent->client->NPC_class == CLASS_RANCOR) + { + moveSpeedOfAnim = 173.0f; + } + else + { + if (Walking) + { + if (HasDual || HasStaff) + { + moveSpeedOfAnim = 100.0f; + } + else + { + moveSpeedOfAnim = 50.0f;// g_noFootSlideWalkScale->value; + } + } + else + { + if (HasStaff) + { + moveSpeedOfAnim = 250.0f; + } + else + { + moveSpeedOfAnim = 150.0f; + } + } + } + } + + + + + + + animSpeed *= (gent->resultspeed/moveSpeedOfAnim); + if (animSpeed<0.01f) + { + animSpeed = 0.01f; + } + + // Make Sure Not To Play Too Fast An Anim + //---------------------------------------- + float maxPlaybackSpeed = (1.5f * timeScaleMod); + if (animSpeed>maxPlaybackSpeed) + { + animSpeed = maxPlaybackSpeed; + } + } + + +// GET VALUES FOR EXISTING BODY ANIMATION +//========================================== + float bodySpeed = 0.0f; + float bodyCurrent = 0.0f; + int bodyStart = 0; + int bodyEnd = 0; + int bodyFlags = 0; + int bodyAnim = (*legsAnim); + int bodyBone = (gent->rootBone); + bool bodyTimerOn = ((*legsAnimTimer>0) || (*legsAnimTimer)==-1); + bool bodyPlay = ((setAnimParts&SETANIM_LEGS) && (bodyBone!=-1) && (animOverride || !bodyTimerOn)); + bool bodyAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, actualTime, &bodyCurrent, &bodyStart, &bodyEnd, &bodyFlags, &bodySpeed, NULL); + bool bodyOnAnimNow = (bodyAnimating && bodyAnim==anim && bodyStart==animStart && bodyEnd==animEnd); + bool bodyMatchTorsFrame = false; + + +// GET VALUES FOR EXISTING TORSO ANIMATION +//=========================================== + float torsSpeed = 0.0f; + float torsCurrent = 0.0f; + int torsStart = 0; + int torsEnd = 0; + int torsFlags = 0; + int torsAnim = (*torsoAnim); + int torsBone = (gent->lowerLumbarBone); + bool torsTimerOn = ((*torsoAnimTimer)>0 || (*torsoAnimTimer)==-1); + bool torsPlay = (gent->client->NPC_class!=CLASS_RANCOR && (setAnimParts&SETANIM_TORSO) && (torsBone!=-1) && (animOverride || !torsTimerOn)); + bool torsAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, actualTime, &torsCurrent, &torsStart, &torsEnd, &torsFlags, &torsSpeed, NULL); + bool torsOnAnimNow = (torsAnimating && torsAnim==anim && torsStart==animStart && torsEnd==animEnd); + bool torsMatchBodyFrame = false; + + +// APPLY SYNC TO TORSO +//===================== + if (animSync && torsPlay && !bodyPlay && bodyOnAnimNow && (!torsOnAnimNow || torsCurrent!=bodyCurrent)) + { + torsMatchBodyFrame = true; + animCurrent = bodyCurrent; + } + if (animSync && bodyPlay && !torsPlay && torsOnAnimNow && (!bodyOnAnimNow || bodyCurrent!=torsCurrent)) + { + bodyMatchTorsFrame = true; + animCurrent = torsCurrent; + } + + // If Already Doing These Exact Parameters, Then Don't Play + //---------------------------------------------------------- + if (!animRestart) + { + torsPlay &= !(torsOnAnimNow && torsSpeed==animSpeed && !torsMatchBodyFrame); + bodyPlay &= !(bodyOnAnimNow && bodySpeed==animSpeed && !bodyMatchTorsFrame); + } + +#ifndef FINAL_BUILD + if ((cg_debugAnim.integer==3) || // 3 = do everyone + (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player + (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else + (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + ) + { + if (bodyPlay || torsPlay) + { + char* entName = gent->targetname; + char* location; + + // Select Entity Name + //-------------------- + if (!entName || !entName[0]) + { + entName = gent->NPC_targetname; + } + if (!entName || !entName[0]) + { + entName = gent->NPC_type; + } + if (!entName || !entName[0]) + { + entName = gent->classname; + } + if (!entName || !entName[0]) + { + entName = "UNKNOWN"; + } + + // Select Play Location + //---------------------- + if (bodyPlay && torsPlay) + { + location = "BOTH "; + } + else if (bodyPlay) + { + location = "LEGS "; + } + else + { + location = "TORSO"; + } + + // Print It! + //----------- + Com_Printf("[%10d] ent[%3d-%18s] %s anim[%3d] - %s\n", + actualTime, + gent->s.number, + entName, + location, + anim, + animTable[anim].name ); + } + } +#endif + + +// PLAY ON THE TORSO +//======================== + if (torsPlay) + { + *torsoAnim = anim; + float oldAnimCurrent = animCurrent; + if (animCurrent!=bodyCurrent && torsOnAnimNow && !animRestart && !torsMatchBodyFrame) + { + animCurrent = torsCurrent; + } + + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + if (gent->motionBone!=-1) + { + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->motionBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + } + + animCurrent = oldAnimCurrent; + + // If This Animation Is To Be Locked And Held, Calculate The Duration And Set The Timer + //-------------------------------------------------------------------------------------- + if (animHold || animHoldless) + { + PM_SetTorsoAnimTimer(gent, torsoAnimTimer, animHoldMSec); + } + } + +// PLAY ON THE WHOLE BODY +//======================== + if (bodyPlay) + { + *legsAnim = anim; + + if (bodyOnAnimNow && !animRestart && !bodyMatchTorsFrame) + { + animCurrent = bodyCurrent; + } + + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, + animStart, + animEnd, + (bodyOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + // If This Animation Is To Be Locked And Held, Calculate The Duration And Set The Timer + //-------------------------------------------------------------------------------------- + if (animHold || animHoldless) + { + PM_SetLegsAnimTimer(gent, legsAnimTimer, animHoldMSec); + } + } + + + + + +// PRINT SOME DEBUG TEXT OF EXISTING VALUES +//========================================== + if (false) + { + gi.Printf("PLAYANIM: (%3d) Speed(%4.2f) ", anim, animSpeed); + if (bodyAnimating) + { + gi.Printf("BODY: (%4.2f) (%4.2f) ", bodyCurrent, bodySpeed); + } + else + { + gi.Printf(" "); + } + if (torsAnimating) + { + gi.Printf("TORS: (%4.2f) (%4.2f)\n", torsCurrent, torsSpeed); + } + else + { + gi.Printf("\n"); + } + } +} + + + +void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime) +{ // FIXME : once torsoAnim and legsAnim are in the same structure for NPC and Players + // rename PM_SetAnimFinal to PM_SetAnim and have both NPC and Players call PM_SetAnim + + if ( pm->ps->pm_type >= PM_DEAD ) + {//FIXME: sometimes we'll want to set anims when your dead... twitches, impacts, etc. + return; + } + + if ( pm->gent == NULL ) + { + return; + } + + if ( !pm->gent || pm->gent->health > 0 ) + {//don't lock anims if the guy is dead + if ( pm->ps->torsoAnimTimer + && PM_LockedAnim( pm->ps->torsoAnim ) + && !PM_LockedAnim( anim ) ) + {//nothing can override these special anims + setAnimParts &= ~SETANIM_TORSO; + } + + if ( pm->ps->legsAnimTimer + && PM_LockedAnim( pm->ps->legsAnim ) + && !PM_LockedAnim( anim ) ) + {//nothing can override these special anims + setAnimParts &= ~SETANIM_LEGS; + } + } + + if ( !setAnimParts ) + { + return; + } + + if (setAnimFlags&SETANIM_FLAG_OVERRIDE) + { +// pm->ps->animationTimer = 0; + + if (setAnimParts & SETANIM_TORSO) + { + if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->torsoAnim != anim ) + { + PM_SetTorsoAnimTimer( pm->gent, &pm->ps->torsoAnimTimer, 0 ); + } + } + if (setAnimParts & SETANIM_LEGS) + { + if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->legsAnim != anim ) + { + PM_SetLegsAnimTimer( pm->gent, &pm->ps->legsAnimTimer, 0 ); + } + } + } + + PM_SetAnimFinal(&pm->ps->torsoAnim,&pm->ps->legsAnim,setAnimParts,anim,setAnimFlags,&pm->ps->torsoAnimTimer,&pm->ps->legsAnimTimer,&g_entities[pm->ps->clientNum],blendTime);//was pm->gent +} + +bool TorsoAgainstWindTest( gentity_t* ent ) +{ + if (ent&&//valid ent + ent->client&&//a client + (ent->client->ps.weapon!=WP_SABER||ent->client->ps.saberMove==LS_READY)&&//either not holding a saber or the saber is in the ready pose + (ent->s.numbercurrentOrigin) && + gi.WE_IsOutside(ent->currentOrigin) ) + { + if (Q_stricmp(level.mapname, "t2_wedge")!=0) + { + vec3_t fwd; + vec3_t windDir; + if (gi.WE_GetWindVector(windDir, ent->currentOrigin)) + { + VectorScale(windDir, -1.0f, windDir); + AngleVectors(pm->gent->currentAngles, fwd, 0, 0); + if (DotProduct(fwd, windDir)>0.65f) + { + if (ent->client && ent->client->ps.torsoAnim!=BOTH_WIND) + { + NPC_SetAnim(ent, SETANIM_TORSO, BOTH_WIND, SETANIM_FLAG_NORMAL, 400); + } + return true; + } + } + } + } + return false; +} + +/* +------------------------- +PM_TorsoAnimLightsaber +------------------------- +*/ + + +// Note that this function is intended to set the animation for the player, but +// only does idle-ish anims. Anything that has a timer associated, such as attacks and blocks, +// are set by PM_WeaponLightsaber() + +extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt ); +extern qboolean PM_LandingAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +qboolean PM_InCartwheel( int anim ); +void PM_TorsoAnimLightsaber() +{ + // ********************************************************* + // WEAPON_READY + // ********************************************************* + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + {//holding an enemy aloft with force-grip + return; + } + + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + {//lightning + return; + } + + if ( pm->ps->forcePowersActive&(1<ps->saber[0].blade[0].active + && pm->ps->saber[0].blade[0].length < 3 + && !(pm->ps->saberEventFlags&SEF_HITWALL) + && pm->ps->weaponstate == WEAPON_RAISING ) + { + if (!G_IsRidingVehicle(pm->gent)) + { + PM_SetSaberMove(LS_DRAW); + } + return; + } + else if ( !pm->ps->SaberActive() && pm->ps->SaberLength() ) + { + if (!G_IsRidingVehicle(pm->gent)) + { + PM_SetSaberMove(LS_PUTAWAY); + } + return; + } + + if (pm->ps->weaponTime > 0) + { // weapon is already busy. + if ( pm->ps->torsoAnim == BOTH_TOSS1 + || pm->ps->torsoAnim == BOTH_TOSS2 ) + {//in toss + if ( !pm->ps->torsoAnimTimer ) + {//weird, get out of it, I guess + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + } + return; + } + + if ( pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + {//ready + if ( pm->ps->weapon == WP_SABER && (pm->ps->SaberLength()) ) + {//saber is on + // Select the proper idle Lightsaber attack move from the chart. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( (PM_RunningAnim( pm->ps->legsAnim ) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) + && pm->ps->saberBlockingTime < cg.time ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + /* + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + {//jumping, landing cartwheel, flipping + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetSaberMove( LS_READY ); + } + */ + } + else if (TorsoAgainstWindTest(pm->gent)) + { + } + else if( pm->ps->legsAnim == BOTH_RUN1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN2 )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN_DUAL ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK_DUAL ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + { + //??? Why nothing? What if you were running??? + //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_JUMP1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else + {//Used to default to both_stand1 which is an arms-down anim +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // Select the next proper pose for the lightsaber assuming that there are no attacks. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + } + + // ********************************************************* + // WEAPON_IDLE + // ********************************************************* + + else if ( pm->ps->weaponstate == WEAPON_IDLE ) + { + if (TorsoAgainstWindTest(pm->gent)) + { + } + else if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else + { +// This is now set in SetSaberMove. + // Idle for Lightsaber + if ( pm->gent && pm->gent->client ) + { +// pm->gent->client->saberTrail.inAction = qfalse; + } + + qboolean saberInAir = qtrue; + if ( pm->ps->saberInFlight ) + {//guiding saber + if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + {//we're stuck in a broken parry + saberInAir = qfalse; + } + if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + {// + if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + {//fell to the ground and we're not trying to pull it back + saberInAir = qfalse; + } + } + } + if ( pm->ps->saberInFlight + && saberInAir + && (!pm->ps->dualSabers || !pm->ps->saber[1].Active())) + { + if ( !PM_ForceAnim( pm->ps->torsoAnim ) + || pm->ps->torsoAnimTimer < 300 ) + {//don't interrupt a force power anim + if ( pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer ) + { + PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } + } + else + {//saber is on + // Idle for Lightsaber + if ( pm->gent && pm->gent->client ) + { + if ( !G_InCinematicSaberAnim( pm->gent ) ) + { + pm->gent->client->ps.SaberDeactivateTrail( 0 ); + } + } + // Idle for idle/ready Lightsaber +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONIDLE1 + // Select the proper idle Lightsaber attack move from the chart. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( (PM_RunningAnim( pm->ps->legsAnim ) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) + && pm->ps->saberBlockingTime < cg.time ) + {//running w/1-handed weapon uses full-body anim + int setFlags = SETANIM_FLAG_NORMAL; + if ( PM_LandingAnim( pm->ps->torsoAnim ) ) + { + setFlags = SETANIM_FLAG_OVERRIDE; + } + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,setFlags); + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + } + } + } +} + + + + +/* +------------------------- +PM_TorsoAnimation +------------------------- +*/ + +void PM_TorsoAnimation( void ) +{//FIXME: Write a much smarter and more appropriate anim picking routine logic... +// int oldAnim; + if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps )) + {//in knockdown + return; + } + + if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + { + return; + } + + if ( (pm->ps->eFlags&EF_FORCE_DRAINED) ) + {//being drained + //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + return; + } + if ( (pm->ps->forcePowersActive&(1<ps->forceDrainEntityNum < ENTITYNUM_WORLD ) + {//draining + //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGER1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + return; + } + + if( pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags & SCF_FORCED_MARCH) ) + { + return; + } + + if(pm->gent != NULL && pm->gent->client) + { + pm->gent->client->renderInfo.torsoFpsMod = 1.0f; + } + + if ( pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON ) + { + if ( pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use )//ugly way to tell, but... + {//full body + PM_SetAnim(pm,SETANIM_BOTH,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else + {//torso + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + return; + } +/* else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->ps->clientNum < MAX_CLIENTS && (m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].numHands == 2 || g_speederControlScheme->value == 2) ) + {//can't look around + PM_SetAnim(pm,SETANIM_TORSO,m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].riderAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + return; + }*/ + + if ( pm->ps->taunting > level.time ) + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_ALORA_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else if ( pm->ps->weapon == WP_SABER && pm->ps->saberAnimLevel == SS_DUAL && PM_HasAnimation( pm->gent, BOTH_DUAL_TAUNT ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_DUAL_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else if ( pm->ps->weapon == WP_SABER + && pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->saber[0].type == SABER_STAFF ) + {//turn on the blades + if ( PM_HasAnimation( pm->gent, BOTH_STAFF_TAUNT ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_STAFF_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + /* + else + { + if ( !pm->ps->saber[0].blade[0].active ) + {//first blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 0, qtrue ); + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is also off, extend time of this taunt so we have enough time to turn them both on + pm->ps->taunting = level.time + 3000; + } + } + else if ( (pm->ps->taunting - level.time) < 1500 ) + {//only 1500ms left in taunt + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 1, qtrue ); + } + } + //pose + PM_SetAnim(pm,SETANIM_BOTH,BOTH_SABERSTAFF_STANCE,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + pm->ps->torsoAnimTimer = pm->ps->legsAnimTimer = (pm->ps->taunting - level.time); + } + */ + } + else if ( PM_HasAnimation( pm->gent, BOTH_GESTURE1 ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_GESTURE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + pm->gent->client->ps.SaberActivateTrail( 100 ); + //FIXME: will this reset? + //FIXME: force-control (yellow glow) effect on hand and saber? + } + else + { + //PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL); + } + return; + } + + if (pm->ps->weapon == WP_SABER ) // WP_LIGHTSABER + { + qboolean saberInAir = qfalse; + if ( pm->ps->SaberLength() && !pm->ps->saberInFlight ) + { + PM_TorsoAnimLightsaber(); + } + else + { + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + {//holding an enemy aloft with force-grip + return; + } + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + {//lightning + return; + } + if ( pm->ps->forcePowersActive&(1<ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + {//we're stuck in a broken parry + PM_TorsoAnimLightsaber(); + } + else + { + if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + {// + if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + {//fell to the ground and we're not trying to pull it back + saberInAir = qfalse; + } + } + + if ( pm->ps->saberInFlight + && saberInAir + && (!pm->ps->dualSabers //not using 2 sabers + || !pm->ps->saber[1].Active() //left one off + || pm->ps->torsoAnim == BOTH_SABERDUAL_STANCE//not attacking + || pm->ps->torsoAnim == BOTH_SABERPULL//not attacking + || pm->ps->torsoAnim == BOTH_STAND1//not attacking + || PM_RunningAnim( pm->ps->torsoAnim ) //not attacking + || PM_WalkingAnim( pm->ps->torsoAnim ) //not attacking + || PM_JumpingAnim( pm->ps->torsoAnim )//not attacking + || PM_SwimmingAnim( pm->ps->torsoAnim ) )//not attacking + ) + { + if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 ) + {//don't interrupt a force power anim + if ( pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer ) + { + PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } + } + else + { + if ( PM_InSlopeAnim( pm->ps->legsAnim ) ) + {//HMM... this probably breaks the saber putaway and select anims + if ( pm->ps->SaberLength() > 0 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + } + } + } + } + + if (pm->ps->weaponTime<= 0 && (pm->ps->saberMove==LS_READY || pm->ps->SaberLength()==0) && !saberInAir) + { + TorsoAgainstWindTest(pm->gent); + } + return; + } + + if ( PM_ForceAnim( pm->ps->torsoAnim ) + && pm->ps->torsoAnimTimer > 0 ) + {//in a force anim, don't do a stand anim + return; + } + + + qboolean weaponBusy = qfalse; + + if ( pm->ps->weapon == WP_NONE ) + { + weaponBusy = qfalse; + } + else if ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->lastShotTime > level.time - 3000 ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->weaponTime > 0 ) + { + weaponBusy = qtrue; + } + else if ( pm->gent && pm->gent->client->fireDelay > 0 ) + { + weaponBusy = qtrue; + } + else if ( TorsoAgainstWindTest(pm->gent) ) + { + return; + } + else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.zoomTime > cg.time - 5000 ) + {//if we used binoculars recently, aim weapon + weaponBusy = qtrue; + pm->ps->weaponstate = WEAPON_IDLE; + } + else if ( pm->ps->pm_flags & PMF_DUCKED ) + {//ducking is considered on alert... plus looks stupid to have arms hanging down when crouched + weaponBusy = qtrue; + } + + if ( pm->ps->weapon == WP_NONE || + pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + { + if ( pm->ps->weapon == WP_SABER && pm->ps->SaberLength() ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + } + else if( pm->ps->legsAnim == BOTH_RUN1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN2 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN4 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN_STAFF && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN_DUAL && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK2 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK_STAFF && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK_DUAL&& !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + { + //??? Why nothing? What if you were running??? + //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_JUMP1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL, 100); // Only blend over 100ms + } + else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIMFORWARD && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + } + else if ( pm->ps->weapon == WP_NONE ) + { + int legsAnim = pm->ps->legsAnim; + /* + if ( PM_RollingAnim( legsAnim ) || + PM_FlippingAnim( legsAnim ) || + PM_JumpingAnim( legsAnim ) || + PM_PainAnim( legsAnim ) || + PM_SwimmingAnim( legsAnim ) ) + */ + { + PM_SetAnim(pm, SETANIM_TORSO, legsAnim, SETANIM_FLAG_NORMAL ); + } + } + else + {//Used to default to both_stand1 which is an arms-down anim + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else if ( pm->gent != NULL + && (pm->gent->s.numbergent)) + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT ) + {//PLayer- temp hack for weapon frame + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->ps->weapon == WP_MELEE ) + {//hehe + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + {//use legs anim + //FIXME: or just use whatever's currently playing? + //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); + } + else + { + switch(pm->ps->weapon) + { + // ******************************************************** + case WP_SABER: // WP_LIGHTSABER + // Ready pose for Lightsaber +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // Select the next proper pose for the lightsaber assuming that there are no attacks. + if (pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + break; + // ******************************************************** + + case WP_BRYAR_PISTOL: + //FIXME: if recently fired, hold the ready! + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + break; + case WP_BLASTER_PISTOL: + if ( pm->gent + && pm->gent->weaponModel[1] > 0 ) + {//dual pistols + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + } + else + {//single pistols + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + } + break; + case WP_NONE: + //NOTE: should never get here + break; + case WP_MELEE: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + break; + case WP_TUSKEN_STAFF: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND3, SETANIM_FLAG_NORMAL); + } + break; + + case WP_NOGHRI_STICK: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); + break; + + case WP_BLASTER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); + break; + case WP_DISRUPTOR: + case WP_TUSKEN_RIFLE: + if ( (pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running sniper weapon uses normal ready + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + } + else + { + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );//TORSO_WEAPONREADY4//SETANIM_FLAG_RESTART| + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + } + break; + case WP_BOT_LASER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + break; + case WP_THERMAL: + if ( pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT + && (PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim )) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && (pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT) ) + {//player pulling back to throw + if ( PM_StandingAnim( pm->ps->legsAnim ) ) + { + PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else if ( pm->ps->legsAnim == BOTH_THERMAL_READY ) + {//sigh... hold it so pm_footsteps doesn't override + if ( pm->ps->legsAnimTimer < 100 ) + { + pm->ps->legsAnimTimer = 100; + } + } + PM_SetAnim( pm, SETANIM_TORSO, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY10, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + } + break; + case WP_REPEATER: + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + {// + if ( pm->gent->alt_fire ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY1,SETANIM_FLAG_NORMAL); + } + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + default: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + break; + } + } + } + } + else if ( pm->ps->weaponstate == WEAPON_IDLE ) + { + if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIMFORWARD ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + } + else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + {//use legs anim + //FIXME: or just use whatever's currently playing? + //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); + } + else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( !weaponBusy + && pm->ps->weapon != WP_BOWCASTER + && pm->ps->weapon != WP_REPEATER + && pm->ps->weapon != WP_FLECHETTE + && pm->ps->weapon != WP_ROCKET_LAUNCHER + && pm->ps->weapon != WP_CONCUSSION + && ( PM_RunningAnim( pm->ps->legsAnim ) + || (PM_WalkingAnim( pm->ps->legsAnim ) && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) ) + {//running w/1-handed or light 2-handed weapon uses full-body anim if you're not using the weapon right now + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + switch ( pm->ps->weapon ) + { + // ******************************************************** + case WP_SABER: // WP_LIGHTSABER + // Shouldn't get here, should go to TorsoAnimLightsaber + break; + // ******************************************************** + + case WP_BRYAR_PISTOL: + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + } + break; + case WP_BLASTER_PISTOL: + if ( pm->gent + && pm->gent->weaponModel[1] > 0 ) + {//dual pistols + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else + {//single pistols + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + } + } + break; + + case WP_NONE: + //NOTE: should never get here + break; + + case WP_MELEE: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + break; + + case WP_TUSKEN_STAFF: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND3, SETANIM_FLAG_NORMAL); + } + break; + + case WP_NOGHRI_STICK: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_BLASTER: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_DISRUPTOR: + case WP_TUSKEN_RIFLE: + if ( (pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running sniper weapon uses normal ready + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + } + else + { + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + } + break; + + case WP_BOT_LASER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + break; + + case WP_THERMAL: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE10, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + + case WP_REPEATER: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + + default: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + } + } + } + } +} + +//========================================================================= +// Anim checking utils +//========================================================================= + +int PM_GetTurnAnim( gentity_t *gent, int anim ) +{ + if ( !gent ) + { + return -1; + } + + switch( anim ) + { + case BOTH_STAND1: //# Standing idle: no weapon: hands down + case BOTH_STAND1IDLE1: //# Random standing idle + case BOTH_STAND2: //# Standing idle with a weapon + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle + case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle + case BOTH_STAND4: //# two handed: gun down: relaxed stand + case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle + case BOTH_STAND6: //# one handed: gun at side: relaxed stand + case BOTH_STAND2TO4: //# Transition from stand2 to stand4 + case BOTH_STAND4TO2: //# Transition from stand4 to stand2 + case BOTH_GESTURE1: //# Generic gesture: non-specific + case BOTH_GESTURE2: //# Generic gesture: non-specific + case BOTH_TALK1: //# Generic talk anim + case BOTH_TALK2: //# Generic talk anim + if ( PM_HasAnimation( gent, LEGS_TURN1 ) ) + { + return LEGS_TURN1; + } + else + { + return -1; + } + break; + case BOTH_ATTACK1: //# Attack with generic 1-handed weapon + case BOTH_ATTACK2: //# Attack with generic 2-handed weapon + case BOTH_ATTACK3: //# Attack with heavy 2-handed weapon + case BOTH_ATTACK4: //# Attack with ??? + case BOTH_MELEE1: //# First melee attack + case BOTH_MELEE2: //# Second melee attack + case BOTH_GUARD_LOOKAROUND1: //# Cradling weapon and looking around + case BOTH_GUARD_IDLE1: //# Cradling weapon and standing + if ( PM_HasAnimation( gent, LEGS_TURN2 ) ) + { + return LEGS_TURN2; + } + else + { + return -1; + } + break; + default: + return -1; + break; + } +} + +int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) +{ + if ( !gent ) + { + return -1; + } + + switch( anim ) + { + case BOTH_STAND1: //# Standing idle: no weapon: hands down + case BOTH_STAND1IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND1 ) ) + { + return BOTH_TURNSTAND1; + } + else + { + return -1; + } + break; + case BOTH_STAND2: //# Standing idle with a weapon + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND2 ) ) + { + return BOTH_TURNSTAND2; + } + else + { + return -1; + } + break; + case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND3 ) ) + { + return BOTH_TURNSTAND3; + } + else + { + return -1; + } + break; + case BOTH_STAND4: //# two handed: gun down: relaxed stand + if ( PM_HasAnimation( gent, BOTH_TURNSTAND4 ) ) + { + return BOTH_TURNSTAND4; + } + else + { + return -1; + } + break; + case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND5 ) ) + { + return BOTH_TURNSTAND5; + } + else + { + return -1; + } + break; + case BOTH_CROUCH1: //# Transition from standing to crouch + case BOTH_CROUCH1IDLE: //# Crouching idle + /* + case BOTH_UNCROUCH1: //# Transition from crouch to standing + case BOTH_CROUCH2TOSTAND1: //# going from crouch2 to stand1 + case BOTH_CROUCH3: //# Desann crouching down to Kyle (cin 9) + case BOTH_UNCROUCH3: //# Desann uncrouching down to Kyle (cin 9) + case BOTH_CROUCH4: //# Slower version of crouch1 for cinematics + case BOTH_UNCROUCH4: //# Slower version of uncrouch1 for cinematics + */ + if ( PM_HasAnimation( gent, BOTH_TURNCROUCH1 ) ) + { + return BOTH_TURNCROUCH1; + } + else + { + return -1; + } + break; + default: + return -1; + break; + } +} + +qboolean PM_InOnGroundAnim ( playerState_t *ps ) +{ + switch( ps->legsAnim ) + { + case BOTH_DEAD1: + case BOTH_DEAD2: + case BOTH_DEAD3: + case BOTH_DEAD4: + case BOTH_DEAD5: + case BOTH_DEADFORWARD1: + case BOTH_DEADBACKWARD1: + case BOTH_DEADFORWARD2: + case BOTH_DEADBACKWARD2: + case BOTH_LYINGDEATH1: + case BOTH_LYINGDEAD1: + case BOTH_SLEEP1: //# laying on back-rknee up-rhand on torso + return qtrue; + break; + case BOTH_KNOCKDOWN1: //# + case BOTH_KNOCKDOWN2: //# + case BOTH_KNOCKDOWN3: //# + case BOTH_KNOCKDOWN4: //# + case BOTH_KNOCKDOWN5: //# + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_RELEASED: + if ( ps->legsAnimTimer < 500 ) + {//pretty much horizontal by this point + return qtrue; + } + break; + case BOTH_PLAYER_PA_3_FLY: + if ( ps->legsAnimTimer < 300 ) + {//pretty much horizontal by this point + return qtrue; + } + /* + else if ( ps->clientNum < MAX_CLIENTS + && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) + { + return qtrue; + } + */ + break; + case BOTH_GETUP1: + case BOTH_GETUP2: + case BOTH_GETUP3: + case BOTH_GETUP4: + case BOTH_GETUP5: + case BOTH_GETUP_CROUCH_F1: + case BOTH_GETUP_CROUCH_B1: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + if ( ps->legsAnimTimer > PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim )-400 ) + {//still pretty much horizontal at this point + return qtrue; + } + break; + } + + return qfalse; +} + +qboolean PM_InSpecialDeathAnim( int anim ) +{ + switch( pm->ps->legsAnim ) + { + case BOTH_DEATH_ROLL: //# Death anim from a roll + case BOTH_DEATH_FLIP: //# Death anim from a flip + case BOTH_DEATH_SPIN_90_R: //# Death anim when facing 90 degrees right + case BOTH_DEATH_SPIN_90_L: //# Death anim when facing 90 degrees left + case BOTH_DEATH_SPIN_180: //# Death anim when facing backwards + case BOTH_DEATH_LYING_UP: //# Death anim when lying on back + case BOTH_DEATH_LYING_DN: //# Death anim when lying on front + case BOTH_DEATH_FALLING_DN: //# Death anim when falling on face + case BOTH_DEATH_FALLING_UP: //# Death anim when falling on back + case BOTH_DEATH_CROUCHED: //# Death anim when crouched + return qtrue; + break; + default: + return qfalse; + break; + } +} + +qboolean PM_InDeathAnim ( void ) +{//Purposely does not cover stumbledeath and falldeath... + switch( pm->ps->legsAnim ) + { + case BOTH_DEATH1: //# First Death anim + case BOTH_DEATH2: //# Second Death anim + case BOTH_DEATH3: //# Third Death anim + case BOTH_DEATH4: //# Fourth Death anim + case BOTH_DEATH5: //# Fifth Death anim + case BOTH_DEATH6: //# Sixth Death anim + case BOTH_DEATH7: //# Seventh Death anim + case BOTH_DEATH8: //# + case BOTH_DEATH9: //# + case BOTH_DEATH10: //# + case BOTH_DEATH11: //# + case BOTH_DEATH12: //# + case BOTH_DEATH13: //# + case BOTH_DEATH14: //# + case BOTH_DEATH14_UNGRIP: //# Desann's end death (cin #35) + case BOTH_DEATH14_SITUP: //# Tavion sitting up after having been thrown (cin #23) + case BOTH_DEATH15: //# + case BOTH_DEATH16: //# + case BOTH_DEATH17: //# + case BOTH_DEATH18: //# + case BOTH_DEATH19: //# + case BOTH_DEATH20: //# + case BOTH_DEATH21: //# + case BOTH_DEATH22: //# + case BOTH_DEATH23: //# + case BOTH_DEATH24: //# + case BOTH_DEATH25: //# + + case BOTH_DEATHFORWARD1: //# First Death in which they get thrown forward + case BOTH_DEATHFORWARD2: //# Second Death in which they get thrown forward + case BOTH_DEATHFORWARD3: //# Tavion's falling in cin# 23 + case BOTH_DEATHBACKWARD1: //# First Death in which they get thrown backward + case BOTH_DEATHBACKWARD2: //# Second Death in which they get thrown backward + + case BOTH_DEATH1IDLE: //# Idle while close to death + case BOTH_LYINGDEATH1: //# Death to play when killed lying down + case BOTH_STUMBLEDEATH1: //# Stumble forward and fall face first death + case BOTH_FALLDEATH1: //# Fall forward off a high cliff and splat death - start + case BOTH_FALLDEATH1INAIR: //# Fall forward off a high cliff and splat death - loop + case BOTH_FALLDEATH1LAND: //# Fall forward off a high cliff and splat death - hit bottom + //# #sep case BOTH_ DEAD POSES # Should be last frame of corresponding previous anims + case BOTH_DEAD1: //# First Death finished pose + case BOTH_DEAD2: //# Second Death finished pose + case BOTH_DEAD3: //# Third Death finished pose + case BOTH_DEAD4: //# Fourth Death finished pose + case BOTH_DEAD5: //# Fifth Death finished pose + case BOTH_DEAD6: //# Sixth Death finished pose + case BOTH_DEAD7: //# Seventh Death finished pose + case BOTH_DEAD8: //# + case BOTH_DEAD9: //# + case BOTH_DEAD10: //# + case BOTH_DEAD11: //# + case BOTH_DEAD12: //# + case BOTH_DEAD13: //# + case BOTH_DEAD14: //# + case BOTH_DEAD15: //# + case BOTH_DEAD16: //# + case BOTH_DEAD17: //# + case BOTH_DEAD18: //# + case BOTH_DEAD19: //# + case BOTH_DEAD20: //# + case BOTH_DEAD21: //# + case BOTH_DEAD22: //# + case BOTH_DEAD23: //# + case BOTH_DEAD24: //# + case BOTH_DEAD25: //# + case BOTH_DEADFORWARD1: //# First thrown forward death finished pose + case BOTH_DEADFORWARD2: //# Second thrown forward death finished pose + case BOTH_DEADBACKWARD1: //# First thrown backward death finished pose + case BOTH_DEADBACKWARD2: //# Second thrown backward death finished pose + case BOTH_LYINGDEAD1: //# Killed lying down death finished pose + case BOTH_STUMBLEDEAD1: //# Stumble forward death finished pose + case BOTH_FALLDEAD1LAND: //# Fall forward and splat death finished pose + //# #sep case BOTH_ DEAD TWITCH/FLOP # React to being shot from death poses + case BOTH_DEADFLOP1: //# React to being shot from First Death finished pose + case BOTH_DEADFLOP2: //# React to being shot from Second Death finished pose + case BOTH_DISMEMBER_HEAD1: //# + case BOTH_DISMEMBER_TORSO1: //# + case BOTH_DISMEMBER_LLEG: //# + case BOTH_DISMEMBER_RLEG: //# + case BOTH_DISMEMBER_RARM: //# + case BOTH_DISMEMBER_LARM: //# + return qtrue; + break; + default: + return PM_InSpecialDeathAnim( pm->ps->legsAnim ); + break; + } +} + +qboolean PM_InCartwheel( int anim ) +{ + switch ( anim ) + { + case BOTH_ARIAL_LEFT: + case BOTH_ARIAL_RIGHT: + case BOTH_ARIAL_F1: + case BOTH_CARTWHEEL_LEFT: + case BOTH_CARTWHEEL_RIGHT: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_InButterfly( int anim ) +{ + switch ( anim ) + { + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_StandingAnim( int anim ) +{//NOTE: does not check idles or special (cinematic) stands + switch ( anim ) + { + case BOTH_STAND1: + case BOTH_STAND2: + case BOTH_STAND3: + case BOTH_STAND4: + case BOTH_ATTACK3: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_InAirKickingAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + return qtrue; + } + return qfalse; +} + +qboolean PM_KickingAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + //NOT a kick, but acts like one: + case BOTH_A7_HILT: + //NOT kicks, but do kick traces anyway + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + return qtrue; + break; + default: + return PM_InAirKickingAnim( anim ); + break; + } + //return qfalse; +} + +qboolean PM_StabDownAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + return qtrue; + } + return qfalse; +} + +qboolean PM_GoingToAttackDown( playerState_t *ps ) +{ + if ( PM_StabDownAnim( ps->torsoAnim )//stabbing downward + || ps->saberMove == LS_A_LUNGE//lunge + || ps->saberMove == LS_A_JUMP_T__B_//death from above + || ps->saberMove == LS_A_T2B//attacking top to bottom + || ps->saberMove == LS_S_T2B//starting at attack downward + || (PM_SaberInTransition( ps->saberMove ) && saberMoveData[ps->saberMove].endQuad == Q_T) )//transitioning to a top to bottom attack + { + return qtrue; + } + return qfalse; +} + +qboolean PM_ForceUsingSaberAnim( int anim ) +{//saber/acrobatic anims that should prevent you from recharging force power while you're in them... + switch ( anim ) + { + case BOTH_JUMPFLIPSLASHDOWN1: + case BOTH_JUMPFLIPSTABDOWN: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_FORCELONGLEAP_START: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_FORCEWALLRUNFLIP_START: + case BOTH_FORCEWALLRUNFLIP_END: + case BOTH_FORCEWALLRUNFLIP_ALT: + case BOTH_FORCEWALLREBOUND_FORWARD: + case BOTH_FORCEWALLREBOUND_LEFT: + case BOTH_FORCEWALLREBOUND_BACK: + case BOTH_FORCEWALLREBOUND_RIGHT: + case BOTH_FLIP_ATTACK7: + case BOTH_FLIP_HOLD7: + case BOTH_FLIP_LAND: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_ARIAL_LEFT: + case BOTH_ARIAL_RIGHT: + case BOTH_CARTWHEEL_LEFT: + case BOTH_CARTWHEEL_RIGHT: + case BOTH_FLIP_LEFT: + case BOTH_FLIP_BACK1: + case BOTH_FLIP_BACK2: + case BOTH_FLIP_BACK3: + case BOTH_ALORA_FLIP_B: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_WALL_RUN_RIGHT: + case BOTH_WALL_RUN_RIGHT_FLIP: + case BOTH_WALL_RUN_RIGHT_STOP: + case BOTH_WALL_RUN_LEFT: + case BOTH_WALL_RUN_LEFT_FLIP: + case BOTH_WALL_RUN_LEFT_STOP: + case BOTH_WALL_FLIP_RIGHT: + case BOTH_WALL_FLIP_LEFT: + case BOTH_FORCEJUMP1: + case BOTH_FORCEINAIR1: + case BOTH_FORCELAND1: + case BOTH_FORCEJUMPBACK1: + case BOTH_FORCEINAIRBACK1: + case BOTH_FORCELANDBACK1: + case BOTH_FORCEJUMPLEFT1: + case BOTH_FORCEINAIRLEFT1: + case BOTH_FORCELANDLEFT1: + case BOTH_FORCEJUMPRIGHT1: + case BOTH_FORCEINAIRRIGHT1: + case BOTH_FORCELANDRIGHT1: + case BOTH_FLIP_F: + case BOTH_FLIP_B: + case BOTH_FLIP_L: + case BOTH_FLIP_R: + case BOTH_ALORA_FLIP_1: + case BOTH_ALORA_FLIP_2: + case BOTH_ALORA_FLIP_3: + case BOTH_DODGE_FL: + case BOTH_DODGE_FR: + case BOTH_DODGE_BL: + case BOTH_DODGE_BR: + case BOTH_DODGE_L: + case BOTH_DODGE_R: + case BOTH_DODGE_HOLD_FL: + case BOTH_DODGE_HOLD_FR: + case BOTH_DODGE_HOLD_BL: + case BOTH_DODGE_HOLD_BR: + case BOTH_DODGE_HOLD_L: + case BOTH_DODGE_HOLD_R: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_BROLL_L: + case BOTH_GETUP_BROLL_R: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + case BOTH_GETUP_FROLL_L: + case BOTH_GETUP_FROLL_R: + case BOTH_WALL_FLIP_BACK1: + case BOTH_WALL_FLIP_BACK2: + case BOTH_SPIN1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_DEFLECTSLASH__R__L_FIN: + case BOTH_ARIAL_F1: + return qtrue; + } + return qfalse; +} + +qboolean G_HasKnockdownAnims( gentity_t *ent ) +{ + if ( PM_HasAnimation( ent, BOTH_KNOCKDOWN1 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN2 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN3 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN4 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN5 ) ) + { + return qtrue; + } + return qfalse; +} + +qboolean PM_InAttackRoll( int anim ) +{ + switch ( anim ) + { + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + return qtrue; + } + return qfalse; +} + +qboolean PM_LockedAnim( int anim ) +{//anims that can *NEVER* be overridden, regardless + switch ( anim ) + { + case BOTH_KYLE_PA_1: + case BOTH_KYLE_PA_2: + case BOTH_KYLE_PA_3: + case BOTH_PLAYER_PA_1: + case BOTH_PLAYER_PA_2: + case BOTH_PLAYER_PA_3: + case BOTH_PLAYER_PA_3_FLY: + case BOTH_TAVION_SCEPTERGROUND: + case BOTH_TAVION_SWORDPOWER: + case BOTH_SCEPTER_START: + case BOTH_SCEPTER_HOLD: + case BOTH_SCEPTER_STOP: + //grabbed by wampa + case BOTH_GRABBED: //# + case BOTH_RELEASED: //# when Wampa drops player, transitions into fall on back + case BOTH_HANG_IDLE: //# + case BOTH_HANG_ATTACK: //# + case BOTH_HANG_PAIN: //# + return qtrue; + } + return qfalse; +} + +qboolean PM_SuperBreakLoseAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_LK_S_DL_S_SB_1_L: //super break I lost + case BOTH_LK_S_DL_T_SB_1_L: //super break I lost + case BOTH_LK_S_ST_S_SB_1_L: //super break I lost + case BOTH_LK_S_ST_T_SB_1_L: //super break I lost + case BOTH_LK_S_S_S_SB_1_L: //super break I lost + case BOTH_LK_S_S_T_SB_1_L: //super break I lost + case BOTH_LK_DL_DL_S_SB_1_L: //super break I lost + case BOTH_LK_DL_DL_T_SB_1_L: //super break I lost + case BOTH_LK_DL_ST_S_SB_1_L: //super break I lost + case BOTH_LK_DL_ST_T_SB_1_L: //super break I lost + case BOTH_LK_DL_S_S_SB_1_L: //super break I lost + case BOTH_LK_DL_S_T_SB_1_L: //super break I lost + case BOTH_LK_ST_DL_S_SB_1_L: //super break I lost + case BOTH_LK_ST_DL_T_SB_1_L: //super break I lost + case BOTH_LK_ST_ST_S_SB_1_L: //super break I lost + case BOTH_LK_ST_ST_T_SB_1_L: //super break I lost + case BOTH_LK_ST_S_S_SB_1_L: //super break I lost + case BOTH_LK_ST_S_T_SB_1_L: //super break I lost + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_SuperBreakWinAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_LK_S_DL_S_SB_1_W: //super break I won + case BOTH_LK_S_DL_T_SB_1_W: //super break I won + case BOTH_LK_S_ST_S_SB_1_W: //super break I won + case BOTH_LK_S_ST_T_SB_1_W: //super break I won + case BOTH_LK_S_S_S_SB_1_W: //super break I won + case BOTH_LK_S_S_T_SB_1_W: //super break I won + case BOTH_LK_DL_DL_S_SB_1_W: //super break I won + case BOTH_LK_DL_DL_T_SB_1_W: //super break I won + case BOTH_LK_DL_ST_S_SB_1_W: //super break I won + case BOTH_LK_DL_ST_T_SB_1_W: //super break I won + case BOTH_LK_DL_S_S_SB_1_W: //super break I won + case BOTH_LK_DL_S_T_SB_1_W: //super break I won + case BOTH_LK_ST_DL_S_SB_1_W: //super break I won + case BOTH_LK_ST_DL_T_SB_1_W: //super break I won + case BOTH_LK_ST_ST_S_SB_1_W: //super break I won + case BOTH_LK_ST_ST_T_SB_1_W: //super break I won + case BOTH_LK_ST_S_S_SB_1_W: //super break I won + case BOTH_LK_ST_S_T_SB_1_W: //super break I won + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_SaberLockBreakAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_BF1BREAK: + case BOTH_BF2BREAK: + case BOTH_CWCIRCLEBREAK: + case BOTH_CCWCIRCLEBREAK: + case BOTH_LK_S_DL_S_B_1_L: //normal break I lost + case BOTH_LK_S_DL_S_B_1_W: //normal break I won + case BOTH_LK_S_DL_T_B_1_L: //normal break I lost + case BOTH_LK_S_DL_T_B_1_W: //normal break I won + case BOTH_LK_S_ST_S_B_1_L: //normal break I lost + case BOTH_LK_S_ST_S_B_1_W: //normal break I won + case BOTH_LK_S_ST_T_B_1_L: //normal break I lost + case BOTH_LK_S_ST_T_B_1_W: //normal break I won + case BOTH_LK_S_S_S_B_1_L: //normal break I lost + case BOTH_LK_S_S_S_B_1_W: //normal break I won + case BOTH_LK_S_S_T_B_1_L: //normal break I lost + case BOTH_LK_S_S_T_B_1_W: //normal break I won + case BOTH_LK_DL_DL_S_B_1_L: //normal break I lost + case BOTH_LK_DL_DL_S_B_1_W: //normal break I won + case BOTH_LK_DL_DL_T_B_1_L: //normal break I lost + case BOTH_LK_DL_DL_T_B_1_W: //normal break I won + case BOTH_LK_DL_ST_S_B_1_L: //normal break I lost + case BOTH_LK_DL_ST_S_B_1_W: //normal break I won + case BOTH_LK_DL_ST_T_B_1_L: //normal break I lost + case BOTH_LK_DL_ST_T_B_1_W: //normal break I won + case BOTH_LK_DL_S_S_B_1_L: //normal break I lost + case BOTH_LK_DL_S_S_B_1_W: //normal break I won + case BOTH_LK_DL_S_T_B_1_L: //normal break I lost + case BOTH_LK_DL_S_T_B_1_W: //normal break I won + case BOTH_LK_ST_DL_S_B_1_L: //normal break I lost + case BOTH_LK_ST_DL_S_B_1_W: //normal break I won + case BOTH_LK_ST_DL_T_B_1_L: //normal break I lost + case BOTH_LK_ST_DL_T_B_1_W: //normal break I won + case BOTH_LK_ST_ST_S_B_1_L: //normal break I lost + case BOTH_LK_ST_ST_S_B_1_W: //normal break I won + case BOTH_LK_ST_ST_T_B_1_L: //normal break I lost + case BOTH_LK_ST_ST_T_B_1_W: //normal break I won + case BOTH_LK_ST_S_S_B_1_L: //normal break I lost + case BOTH_LK_ST_S_S_B_1_W: //normal break I won + case BOTH_LK_ST_S_T_B_1_L: //normal break I lost + case BOTH_LK_ST_S_T_B_1_W: //normal break I won + return (PM_SuperBreakLoseAnim(anim)||PM_SuperBreakWinAnim(anim)); + break; + } + return qfalse; +} + +qboolean PM_GetupAnimNoMove( int legsAnim ) +{ + switch( legsAnim ) + { + case BOTH_GETUP1: + case BOTH_GETUP2: + case BOTH_GETUP3: + case BOTH_GETUP4: + case BOTH_GETUP5: + case BOTH_GETUP_CROUCH_F1: + case BOTH_GETUP_CROUCH_B1: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + return qtrue; + } + return qfalse; +} + +qboolean PM_KnockDownAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_KNOCKDOWN1: + case BOTH_KNOCKDOWN2: + case BOTH_KNOCKDOWN3: + case BOTH_KNOCKDOWN4: + case BOTH_KNOCKDOWN5: + /* + //special anims: + case BOTH_RELEASED: + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_PLAYER_PA_3_FLY: + */ + return qtrue; + break; + /* + default: + return PM_InGetUp( ps ); + break; + */ + } + return qfalse; +} + +qboolean PM_KnockDownAnimExtended( int anim ) +{ + switch ( anim ) + { + case BOTH_KNOCKDOWN1: + case BOTH_KNOCKDOWN2: + case BOTH_KNOCKDOWN3: + case BOTH_KNOCKDOWN4: + case BOTH_KNOCKDOWN5: + //special anims: + case BOTH_RELEASED: + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_PLAYER_PA_3_FLY: + return qtrue; + break; + /* + default: + return PM_InGetUp( ps ); + break; + */ + } + return qfalse; +} + +qboolean PM_SaberInKata( saberMoveName_t saberMove ) +{ + switch ( saberMove ) + { + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + return qtrue; + default: + break; + } + return qfalse; +} + +qboolean PM_CanRollFromSoulCal( playerState_t *ps ) +{ + if ( ps->legsAnim == BOTH_A7_SOULCAL + && ps->legsAnimTimer < 700 + && ps->legsAnimTimer > 250 ) + { + return qtrue; + } + return qfalse; +} + +qboolean BG_FullBodyTauntAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_GESTURE1: + case BOTH_DUAL_TAUNT: + case BOTH_STAFF_TAUNT: + case BOTH_BOW: + case BOTH_MEDITATE: + case BOTH_SHOWOFF_FAST: + case BOTH_SHOWOFF_MEDIUM: + case BOTH_SHOWOFF_STRONG: + case BOTH_SHOWOFF_DUAL: + case BOTH_SHOWOFF_STAFF: + case BOTH_VICTORY_FAST: + case BOTH_VICTORY_MEDIUM: + case BOTH_VICTORY_STRONG: + case BOTH_VICTORY_DUAL: + case BOTH_VICTORY_STAFF: + return qtrue; + break; + } + return qfalse; +} diff --git a/code/game/bg_panimate.cpp.LOCAL.8780.cpp b/code/game/bg_panimate.cpp.LOCAL.8780.cpp new file mode 100644 index 0000000000..0af60a639d --- /dev/null +++ b/code/game/bg_panimate.cpp.LOCAL.8780.cpp @@ -0,0 +1,7842 @@ +/* +This file is part of Jedi Academy. + + Jedi Academy is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Jedi Academy is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Jedi Academy. If not, see . +*/ +// Copyright 2001-2013 Raven Software + +// this include must remain at the top of every bg_xxxx CPP file +#include "common_headers.h" + + +// define GAME_INCLUDE so that g_public.h does not define the +// short, server-visible gclient_t and gentity_t structures, +// because we define the full size ones in this file +#define GAME_INCLUDE + +#include "../qcommon/q_shared.h" +#include "g_shared.h" +#include "bg_local.h" +#include "../cgame/cg_local.h" +#include "anims.h" +#include "Q3_Interface.h" +#include "g_local.h" +#include "wp_saber.h" +#include "g_vehicles.h" + +extern pmove_t *pm; +extern pml_t pml; +extern cvar_t *g_ICARUSDebug; +extern cvar_t *g_timescale; +extern cvar_t *g_synchSplitAnims; +extern cvar_t *g_AnimWarning; +extern cvar_t *g_noFootSlide; +extern cvar_t *g_noFootSlideRunScale; +extern cvar_t *g_noFootSlideWalkScale; +extern cvar_t *g_saberAnimSpeed; +extern cvar_t *g_saberAutoAim; +extern cvar_t *g_speederControlScheme; +extern cvar_t *g_saberNewControlScheme; +extern cvar_t *g_saberNewCombat; + +extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); +extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); +extern qboolean ValidAnimFileIndex ( int index ); +extern qboolean PM_ControlledByPlayer( void ); +extern qboolean PM_DroidMelee( int npc_class ); +extern qboolean PM_PainAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +extern qboolean PM_FlippingAnim( int anim ); +extern qboolean PM_RollingAnim( int anim ); +extern qboolean PM_SwimmingAnim( int anim ); +extern qboolean PM_InKnockDown( playerState_t *ps ); +extern qboolean PM_InRoll( playerState_t *ps ); +extern qboolean PM_DodgeAnim( int anim ); +extern qboolean PM_InSlopeAnim( int anim ); +extern qboolean PM_ForceAnim( int anim ); +extern qboolean PM_InKnockDownOnGround( playerState_t *ps ); +extern qboolean PM_InSpecialJump( int anim ); +extern qboolean PM_RunningAnim( int anim ); +extern qboolean PM_WalkingAnim( int anim ); +extern qboolean PM_SwimmingAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +extern qboolean PM_SaberStanceAnim( int anim ); +extern qboolean PM_SaberDrawPutawayAnim( int anim ); +extern void PM_SetJumped( float height, qboolean force ); +extern qboolean PM_InGetUpNoRoll( playerState_t *ps ); +extern qboolean PM_CrouchAnim( int anim ); +extern qboolean G_TryingKataAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingCartwheel( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingSpecial( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingJumpAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingJumpForwardAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingLungeAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingPullAttack( gentity_t *self, usercmd_t *cmd, qboolean amPulling ); +extern qboolean G_InCinematicSaberAnim( gentity_t *self ); +extern qboolean G_ControlledByPlayer( gentity_t *self ); + +extern int g_crosshairEntNum; + +int PM_AnimLength( int index, animNumber_t anim ); +qboolean PM_LockedAnim( int anim ); +qboolean PM_StandingAnim( int anim ); +qboolean PM_InOnGroundAnim ( playerState_t *ps ); +qboolean PM_SuperBreakWinAnim( int anim ); +qboolean PM_SuperBreakLoseAnim( int anim ); +qboolean PM_LockedAnim( int anim ); +saberMoveName_t PM_SaberFlipOverAttackMove( void ); +qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ); +saberMoveName_t PM_SaberJumpForwardAttackMove( void ); +qboolean PM_CheckJumpForwardAttackMove( void ); +saberMoveName_t PM_SaberBackflipAttackMove( void ); +qboolean PM_CheckBackflipAttackMove( void ); +saberMoveName_t PM_SaberDualJumpAttackMove( void ); +qboolean PM_CheckDualJumpAttackMove( void ); +saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ); +qboolean PM_CheckLungeAttackMove( void ); +// Okay, here lies the much-dreaded Pat-created FSM movement chart... Heretic II strikes again! +// Why am I inflicting this on you? Well, it's better than hardcoded states. +// Ideally this will be replaced with an external file or more sophisticated move-picker +// once the game gets out of prototype stage. + +// Silly, but I'm replacing these macros so they are shorter! +#define AFLAG_IDLE (SETANIM_FLAG_NORMAL) +#define AFLAG_ACTIVE (SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS) +#define AFLAG_WAIT (SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS) +#define AFLAG_FINISH (SETANIM_FLAG_HOLD) + +//FIXME: add the alternate anims for each style? +saberMoveData_t saberMoveData[LS_MOVE_MAX] = {// NB:randomized + // name anim(do all styles?)startQ endQ setanimflag blend, blocking chain_idle chain_attack trailLen + {"None", BOTH_STAND1, Q_R, Q_R, AFLAG_IDLE, 350, BLK_NO, LS_NONE, LS_NONE, 0 }, // LS_NONE = 0, + + // General movements with saber + {"Ready", BOTH_STAND2, Q_R, Q_R, AFLAG_IDLE, 350, BLK_WIDE, LS_READY, LS_S_R2L, 0 }, // LS_READY, + {"Draw", BOTH_STAND1TO2, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_DRAW, + {"Putaway", BOTH_STAND2TO1, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_PUTAWAY, + + // Attacks + //UL2LR + {"TL2BR Att", BOTH_A1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TL2BR, LS_R_TL2BR, 200 }, // LS_A_TL2BR + //SLASH LEFT + {"L2R Att", BOTH_A1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_L2R, LS_R_L2R, 200 }, // LS_A_L2R + //LL2UR + {"BL2TR Att", BOTH_A1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_TIGHT, LS_R_BL2TR, LS_R_BL2TR, 200 }, // LS_A_BL2TR + //LR2UL + {"BR2TL Att", BOTH_A1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_BR2TL, LS_R_BR2TL, 200 }, // LS_A_BR2TL + //SLASH RIGHT + {"R2L Att", BOTH_A1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_R2L, LS_R_R2L, 200 },// LS_A_R2L + //UR2LL + {"TR2BL Att", BOTH_A1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TR2BL, LS_R_TR2BL, 200 }, // LS_A_TR2BL + //SLASH DOWN + {"T2B Att", BOTH_A1_T__B_, Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_T2B, LS_R_T2B, 200 }, // LS_A_T2B + //special attacks + {"Back Stab", BOTH_A2_STABBACK1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACKSTAB + {"Back Att", BOTH_ATTACK_BACK, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK + {"CR Back Att", BOTH_CROUCHATTACKBACK1,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK_CR + {"RollStab", BOTH_ROLL_STAB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_ROLL_STAB + {"Lunge Att", BOTH_LUNGE2_B__T_, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_LUNGE + {"Jump Att", BOTH_FORCELEAP2_T__B_,Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_JUMP_T__B_ + {"Flip Stab", BOTH_JUMPFLIPSTABDOWN,Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_FLIP_STAB + {"Flip Slash", BOTH_JUMPFLIPSLASHDOWN1,Q_L,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R_T_, 200 }, // LS_A_FLIP_SLASH + {"DualJump Atk",BOTH_JUMPATTACK6, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_BL_TR, 200 }, // LS_JUMPATTACK_DUAL + + {"DualJumpAtkL_A",BOTH_ARIAL_LEFT, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TL2BR, 200 }, // LS_JUMPATTACK_ARIAL_LEFT + {"DualJumpAtkR_A",BOTH_ARIAL_RIGHT, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TR2BL, 200 }, // LS_JUMPATTACK_ARIAL_RIGHT + + {"DualJumpAtkL_A",BOTH_CARTWHEEL_LEFT, Q_R,Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TL_BR, 200 }, // LS_JUMPATTACK_CART_LEFT + {"DualJumpAtkR_A",BOTH_CARTWHEEL_RIGHT, Q_R,Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TR_BL, 200 }, // LS_JUMPATTACK_CART_RIGHT + + {"DualJumpAtkLStaff", BOTH_BUTTERFLY_FL1,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_JUMPATTACK_STAFF_LEFT + {"DualJumpAtkRStaff", BOTH_BUTTERFLY_FR1,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_JUMPATTACK_STAFF_RIGHT + + {"ButterflyLeft", BOTH_BUTTERFLY_LEFT,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_BUTTERFLY_LEFT + {"ButterflyRight", BOTH_BUTTERFLY_RIGHT,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_BUTTERFLY_RIGHT + + {"BkFlip Atk", BOTH_JUMPATTACK7, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_BACKFLIP_ATK + {"DualSpinAtk", BOTH_SPINATTACK6, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_DUAL + {"StfSpinAtk", BOTH_SPINATTACK7, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK + {"LngLeapAtk", BOTH_FORCELONGLEAP_ATTACK,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_LEAP_ATTACK + {"SwoopAtkR", BOTH_VS_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_RIGHT + {"SwoopAtkL", BOTH_VS_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_LEFT + {"TauntaunAtkR",BOTH_VT_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_RIGHT + {"TauntaunAtkL",BOTH_VT_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_LEFT + {"StfKickFwd", BOTH_A7_KICK_F, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F + {"StfKickBack", BOTH_A7_KICK_B, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B + {"StfKickRight",BOTH_A7_KICK_R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R + {"StfKickLeft", BOTH_A7_KICK_L, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L + {"StfKickSpin", BOTH_A7_KICK_S, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_S + {"StfKickBkFwd",BOTH_A7_KICK_BF, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_BF + {"StfKickSplit",BOTH_A7_KICK_RL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_RL + {"StfKickFwdAir",BOTH_A7_KICK_F_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F_AIR + {"StfKickBackAir",BOTH_A7_KICK_B_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B_AIR + {"StfKickRightAir",BOTH_A7_KICK_R_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R_AIR + {"StfKickLeftAir",BOTH_A7_KICK_L_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L_AIR + {"StabDown", BOTH_STABDOWN, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN + {"StabDownStf", BOTH_STABDOWN_STAFF,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_STAFF + {"StabDownDual",BOTH_STABDOWN_DUAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_DUAL + {"dualspinprot",BOTH_A6_SABERPROTECT,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_DUAL_SPIN_PROTECT + {"StfSoulCal", BOTH_A7_SOULCAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_STAFF_SOULCAL + {"specialfast", BOTH_A1_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A1_SPECIAL + {"specialmed", BOTH_A2_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A2_SPECIAL + {"specialstr", BOTH_A3_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A3_SPECIAL + {"upsidedwnatk",BOTH_FLIP_ATTACK7, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_UPSIDE_DOWN_ATTACK + {"pullatkstab", BOTH_PULL_IMPALE_STAB,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_STAB + {"pullatkswing",BOTH_PULL_IMPALE_SWING,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_SWING + {"AloraSpinAtk",BOTH_ALORA_SPIN_SLASH,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_ALORA + {"Dual FB Atk", BOTH_A6_FB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_FB + {"Dual LR Atk", BOTH_A6_LR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_LR + {"StfHiltBash", BOTH_A7_HILT, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_HILT_BASH + + //starts + {"TL2BR St", BOTH_S1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TL2BR, LS_A_TL2BR, 200 }, // LS_S_TL2BR + {"L2R St", BOTH_S1_S1__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_L2R, LS_A_L2R, 200 }, // LS_S_L2R + {"BL2TR St", BOTH_S1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BL2TR, LS_A_BL2TR, 200 }, // LS_S_BL2TR + {"BR2TL St", BOTH_S1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BR2TL, LS_A_BR2TL, 200 }, // LS_S_BR2TL + {"R2L St", BOTH_S1_S1__R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_R2L, LS_A_R2L, 200 }, // LS_S_R2L + {"TR2BL St", BOTH_S1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TR2BL, LS_A_TR2BL, 200 }, // LS_S_TR2BL + {"T2B St", BOTH_S1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_T2B, LS_A_T2B, 200 }, // LS_S_T2B + + //returns + {"TL2BR Ret", BOTH_R1_BR_S1, Q_BR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TL2BR + {"L2R Ret", BOTH_R1__R_S1, Q_R, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_L2R + {"BL2TR Ret", BOTH_R1_TR_S1, Q_TR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BL2TR + {"BR2TL Ret", BOTH_R1_TL_S1, Q_TL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BR2TL + {"R2L Ret", BOTH_R1__L_S1, Q_L, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_R2L + {"TR2BL Ret", BOTH_R1_BL_S1, Q_BL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TR2BL + {"T2B Ret", BOTH_R1_B__S1, Q_B, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_T2B + + //Transitions + {"BR2R Trans", BOTH_T1_BR__R, Q_BR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc bottom right to right + {"BR2TR Trans", BOTH_T1_BR_TR, Q_BR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc bottom right to top right (use: BOTH_T1_TR_BR) + {"BR2T Trans", BOTH_T1_BR_T_, Q_BR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom right to top (use: BOTH_T1_T__BR) + {"BR2TL Trans", BOTH_T1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast weak spin bottom right to top left + {"BR2L Trans", BOTH_T1_BR__L, Q_BR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin bottom right to left + {"BR2BL Trans", BOTH_T1_BR_BL, Q_BR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin bottom right to bottom left + {"R2BR Trans", BOTH_T1__R_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc right to bottom right (use: BOTH_T1_BR__R) + {"R2TR Trans", BOTH_T1__R_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc right to top right + {"R2T Trans", BOTH_T1__R_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast ar right to top (use: BOTH_T1_T___R) + {"R2TL Trans", BOTH_T1__R_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc right to top left + {"R2L Trans", BOTH_T1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin right to left + {"R2BL Trans", BOTH_T1__R_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin right to bottom left + {"TR2BR Trans", BOTH_T1_TR_BR, Q_TR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top right to bottom right + {"TR2R Trans", BOTH_T1_TR__R, Q_TR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top right to right (use: BOTH_T1__R_TR) + {"TR2T Trans", BOTH_T1_TR_T_, Q_TR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top right to top (use: BOTH_T1_T__TR) + {"TR2TL Trans", BOTH_T1_TR_TL, Q_TR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top right to top left + {"TR2L Trans", BOTH_T1_TR__L, Q_TR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top right to left + {"TR2BL Trans", BOTH_T1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin top right to bottom left + {"T2BR Trans", BOTH_T1_T__BR, Q_T, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top to bottom right + {"T2R Trans", BOTH_T1_T___R, Q_T, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top to right + {"T2TR Trans", BOTH_T1_T__TR, Q_T, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top to top right + {"T2TL Trans", BOTH_T1_T__TL, Q_T, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top to top left + {"T2L Trans", BOTH_T1_T___L, Q_T, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top to left + {"T2BL Trans", BOTH_T1_T__BL, Q_T, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top to bottom left + {"TL2BR Trans", BOTH_T1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin top left to bottom right + {"TL2R Trans", BOTH_T1_TL__R, Q_TL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top left to right (use: BOTH_T1__R_TL) + {"TL2TR Trans", BOTH_T1_TL_TR, Q_TL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top left to top right (use: BOTH_T1_TR_TL) + {"TL2T Trans", BOTH_T1_TL_T_, Q_TL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top left to top (use: BOTH_T1_T__TL) + {"TL2L Trans", BOTH_T1_TL__L, Q_TL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top left to left (use: BOTH_T1__L_TL) + {"TL2BL Trans", BOTH_T1_TL_BL, Q_TL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top left to bottom left + {"L2BR Trans", BOTH_T1__L_BR, Q_L, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin left to bottom right + {"L2R Trans", BOTH_T1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin left to right + {"L2TR Trans", BOTH_T1__L_TR, Q_L, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc left to top right (use: BOTH_T1_TR__L) + {"L2T Trans", BOTH_T1__L_T_, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc left to top (use: BOTH_T1_T___L) + {"L2TL Trans", BOTH_T1__L_TL, Q_L, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc left to top left + {"L2BL Trans", BOTH_T1__L_BL, Q_L, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc left to bottom left (use: BOTH_T1_BL__L) + {"BL2BR Trans", BOTH_T1_BL_BR, Q_BL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin bottom left to bottom right + {"BL2R Trans", BOTH_T1_BL__R, Q_BL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin bottom left to right + {"BL2TR Trans", BOTH_T1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast weak spin bottom left to top right + {"BL2T Trans", BOTH_T1_BL_T_, Q_BL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom left to top (use: BOTH_T1_T__BL) + {"BL2TL Trans", BOTH_T1_BL_TL, Q_BL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc bottom left to top left (use: BOTH_T1_TL_BL) + {"BL2L Trans", BOTH_T1_BL__L, Q_BL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc bottom left to left + + //Bounces + {"Bounce BR", BOTH_B1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + {"Bounce R", BOTH_B1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + {"Bounce TR", BOTH_B1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + {"Bounce T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + {"Bounce TL", BOTH_B1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + {"Bounce L", BOTH_B1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + {"Bounce BL", BOTH_B1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + + //Deflected attacks (like bounces, but slide off enemy saber, not straight back) + {"Deflect BR", BOTH_D1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + {"Deflect R", BOTH_D1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + {"Deflect TR", BOTH_D1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + {"Deflect T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + {"Deflect TL", BOTH_D1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + {"Deflect L", BOTH_D1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + {"Deflect BL", BOTH_D1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + {"Deflect B", BOTH_D1_B____, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + + //Reflected attacks + {"Reflected BR",BOTH_V1_BR_S1, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BR + {"Reflected R", BOTH_V1__R_S1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__R + {"Reflected TR",BOTH_V1_TR_S1, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TR + {"Reflected T", BOTH_V1_T__S1, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_T_ + {"Reflected TL",BOTH_V1_TL_S1, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TL + {"Reflected L", BOTH_V1__L_S1, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__L + {"Reflected BL",BOTH_V1_BL_S1, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BL + {"Reflected B", BOTH_V1_B__S1, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_B_ + + // Broken parries + {"BParry Top", BOTH_H1_S1_T_, Q_T, Q_B, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UP, + {"BParry UR", BOTH_H1_S1_TR, Q_TR, Q_BL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UR, + {"BParry UL", BOTH_H1_S1_TL, Q_TL, Q_BR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UL, + {"BParry LR", BOTH_H1_S1_BL, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR, + {"BParry Bot", BOTH_H1_S1_B_, Q_B, Q_T, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + {"BParry LL", BOTH_H1_S1_BR, Q_BR, Q_TL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + + // Knockaways + {"Knock Top", BOTH_K1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_T__BR, 150 }, // LS_PARRY_UP, + {"Knock UR", BOTH_K1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_TR__R, 150 }, // LS_PARRY_UR, + {"Knock UL", BOTH_K1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_T1_TL__L, 150 }, // LS_PARRY_UL, + {"Knock LR", BOTH_K1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_T1_BL_TL, 150 }, // LS_PARRY_LR, + {"Knock LL", BOTH_K1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_T1_BR_TR, 150 }, // LS_PARRY_LL + + // Parry + {"Parry Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 150 }, // LS_PARRY_UP, + {"Parry UR", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 150 }, // LS_PARRY_UR, + {"Parry UL", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 150 }, // LS_PARRY_UL, + {"Parry LR", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 150 }, // LS_PARRY_LR, + {"Parry LL", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 150 }, // LS_PARRY_LL + + // Reflecting a missile + {"Reflect Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 300 }, // LS_PARRY_UP, + {"Reflect UR", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 300 }, // LS_PARRY_UR, + {"Reflect UL", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 300 }, // LS_PARRY_UL, + {"Reflect LR", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 300 }, // LS_PARRY_LR + {"Reflect LL", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 300 }, // LS_PARRY_LL, +}; + + +saberMoveName_t transitionMove[Q_NUM_QUADS][Q_NUM_QUADS] = +{ + { + LS_NONE, //Can't transition to same pos! + LS_T1_BR__R,//40 + LS_T1_BR_TR, + LS_T1_BR_T_, + LS_T1_BR_TL, + LS_T1_BR__L, + LS_T1_BR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1__R_BR,//46 + LS_NONE, //Can't transition to same pos! + LS_T1__R_TR, + LS_T1__R_T_, + LS_T1__R_TL, + LS_T1__R__L, + LS_T1__R_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_TR_BR,//52 + LS_T1_TR__R, + LS_NONE, //Can't transition to same pos! + LS_T1_TR_T_, + LS_T1_TR_TL, + LS_T1_TR__L, + LS_T1_TR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_T__BR,//58 + LS_T1_T___R, + LS_T1_T__TR, + LS_NONE, //Can't transition to same pos! + LS_T1_T__TL, + LS_T1_T___L, + LS_T1_T__BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_TL_BR,//64 + LS_T1_TL__R, + LS_T1_TL_TR, + LS_T1_TL_T_, + LS_NONE, //Can't transition to same pos! + LS_T1_TL__L, + LS_T1_TL_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1__L_BR,//70 + LS_T1__L__R, + LS_T1__L_TR, + LS_T1__L_T_, + LS_T1__L_TL, + LS_NONE, //Can't transition to same pos! + LS_T1__L_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_BL_BR,//76 + LS_T1_BL__R, + LS_T1_BL_TR, + LS_T1_BL_T_, + LS_T1_BL_TL, + LS_T1_BL__L, + LS_NONE, //Can't transition to same pos! + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_BL_BR,//NOTE: there are no transitions from bottom, so re-use the bottom right transitions + LS_T1_BR__R, + LS_T1_BR_TR, + LS_T1_BR_T_, + LS_T1_BR_TL, + LS_T1_BR__L, + LS_T1_BR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + } +}; + +void PM_VelocityForSaberMove( playerState_t *ps, vec3_t throwDir ) +{ + vec3_t vForward, vRight, vUp, startQ, endQ; + + AngleVectors( ps->viewangles, vForward, vRight, vUp ); + + switch ( saberMoveData[ps->saberMove].startQuad ) + { + case Q_BR: + VectorScale( vRight, 1, startQ ); + VectorMA( startQ, -1, vUp, startQ ); + break; + case Q_R: + VectorScale( vRight, 2, startQ ); + break; + case Q_TR: + VectorScale( vRight, 1, startQ ); + VectorMA( startQ, 1, vUp, startQ ); + break; + case Q_T: + VectorScale( vUp, 2, startQ ); + break; + case Q_TL: + VectorScale( vRight, -1, startQ ); + VectorMA( startQ, 1, vUp, startQ ); + break; + case Q_L: + VectorScale( vRight, -2, startQ ); + break; + case Q_BL: + VectorScale( vRight, -1, startQ ); + VectorMA( startQ, -1, vUp, startQ ); + break; + case Q_B: + VectorScale( vUp, -2, startQ ); + break; + } + switch ( saberMoveData[ps->saberMove].endQuad ) + { + case Q_BR: + VectorScale( vRight, 1, endQ ); + VectorMA( endQ, -1, vUp, endQ ); + break; + case Q_R: + VectorScale( vRight, 2, endQ ); + break; + case Q_TR: + VectorScale( vRight, 1, endQ ); + VectorMA( endQ, 1, vUp, endQ ); + break; + case Q_T: + VectorScale( vUp, 2, endQ ); + break; + case Q_TL: + VectorScale( vRight, -1, endQ ); + VectorMA( endQ, 1, vUp, endQ ); + break; + case Q_L: + VectorScale( vRight, -2, endQ ); + break; + case Q_BL: + VectorScale( vRight, -1, endQ ); + VectorMA( endQ, -1, vUp, endQ ); + break; + case Q_B: + VectorScale( vUp, -2, endQ ); + break; + } + VectorMA( endQ, 2, vForward, endQ ); + VectorScale( throwDir, 125, throwDir );//FIXME: pass in the throw strength? + VectorSubtract( endQ, startQ, throwDir ); +} + +qboolean PM_VelocityForBlockedMove( playerState_t *ps, vec3_t throwDir ) +{ + vec3_t vForward, vRight, vUp; + AngleVectors( ps->viewangles, vForward, vRight, vUp ); + switch ( ps->saberBlocked ) + { + case BLOCKED_UPPER_RIGHT: + VectorScale( vRight, 1, throwDir ); + VectorMA( throwDir, 1, vUp, throwDir ); + break; + case BLOCKED_UPPER_LEFT: + VectorScale( vRight, -1, throwDir ); + VectorMA( throwDir, 1, vUp, throwDir ); + break; + case BLOCKED_LOWER_RIGHT: + VectorScale( vRight, 1, throwDir ); + VectorMA( throwDir, -1, vUp, throwDir ); + break; + case BLOCKED_LOWER_LEFT: + VectorScale( vRight, -1, throwDir ); + VectorMA( throwDir, -1, vUp, throwDir ); + break; + case BLOCKED_TOP: + VectorScale( vUp, 2, throwDir ); + break; + default: + return qfalse; + break; + } + VectorMA( throwDir, 2, vForward, throwDir ); + VectorScale( throwDir, 250, throwDir );//FIXME: pass in the throw strength? + return qtrue; +} + +int PM_AnimLevelForSaberAnim( int anim ) +{ + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ ) + { + return FORCE_LEVEL_1; + } + if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ ) + { + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ ) + { + return FORCE_LEVEL_3; + } + if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ ) + {//desann + return FORCE_LEVEL_4; + } + if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ ) + {//tavion + return FORCE_LEVEL_5; + } + if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ ) + {//dual + return SS_DUAL; + } + if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ ) + {//staff + return SS_STAFF; + } + return FORCE_LEVEL_0; +} + +int PM_PowerLevelForSaberAnim(playerState_t *ps, int saberNum) +{ + if (g_saberNewCombat->integer) + { //new code + int anim = ps->torsoAnim; + int animTimeElapsed = PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim) - ps->torsoAnimTimer; + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____) + { + //FIXME: these two need their own style + if (ps->saber[0].type == SABER_LANCE) + { + return FORCE_LEVEL_4; + } + else if (ps->saber[0].type == SABER_TRIDENT) + { + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_1; + } + if (anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____) + { + return FORCE_LEVEL_3; + } + if (anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____) + { + return FORCE_LEVEL_5; + } + if (anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____) + {//desann + return FORCE_LEVEL_5; + } + if (anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____) + {//tavion + return FORCE_LEVEL_2; + } + if (anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____) + {//dual + return FORCE_LEVEL_2; + } + if (anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____) + {//staff + return FORCE_LEVEL_4; + } + if ((anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR) + || (anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR) + || (anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR)) + {//parries + switch (ps->saberAnimLevel) + { + case SS_DESANN: + case SS_STRONG: + return FORCE_LEVEL_5; + break; + case SS_STAFF: + return FORCE_LEVEL_4; + break; + case SS_MEDIUM: + return FORCE_LEVEL_3; + break; + case SS_DUAL: + case SS_TAVION: + return FORCE_LEVEL_2; + break; + case SS_FAST: + return FORCE_LEVEL_1; + break; + default: + return FORCE_LEVEL_0; + break; + } + } + if ((anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR) + || (anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR) + || (anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR)) + {//knockaways + return FORCE_LEVEL_3; + } + if ((anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1) + || (anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6) + || (anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7)) + {//knocked-away attacks + return FORCE_LEVEL_1; + } + if ((anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR) + || (anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR) + || (anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR)) + {//broken parries + return FORCE_LEVEL_0; + } + switch (anim) + { + case BOTH_A2_STABBACK1: + if (ps->torsoAnimTimer < 450) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_ATTACK_BACK: + if (ps->torsoAnimTimer < 500) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_CROUCHATTACKBACK1: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + //FIXME: break up? + return FORCE_LEVEL_5; + break; + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + //FIXME: break up? + return FORCE_LEVEL_5; + break; + case BOTH_K1_S1_T_: //# knockaway saber top + case BOTH_K1_S1_TR: //# knockaway saber top right + case BOTH_K1_S1_TL: //# knockaway saber top left + case BOTH_K1_S1_BL: //# knockaway saber bottom left + case BOTH_K1_S1_B_: //# knockaway saber bottom + case BOTH_K1_S1_BR: //# knockaway saber bottom right + //FIXME: break up? + return FORCE_LEVEL_5; + break; + case BOTH_LUNGE2_B__T_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_FORCELEAP2_T__B_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + return FORCE_LEVEL_5;//??? + break; + case BOTH_JUMPFLIPSLASHDOWN1: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_JUMPFLIPSTABDOWN: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed <= 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_JUMPATTACK6: + /* + if (pm->ps) + { + if ( ( pm->ps->legsAnimTimer >= 1450 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) + ||(pm->ps->legsAnimTimer >= 400 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) + {//pretty much sideways + return FORCE_LEVEL_5; + } + } + */ + if ((ps->torsoAnimTimer >= 1450 + && animTimeElapsed >= 400) + || (ps->torsoAnimTimer >= 400 + && animTimeElapsed >= 1100)) + {//pretty much sideways + return FORCE_LEVEL_5; + } + return FORCE_LEVEL_0; + break; + case BOTH_JUMPATTACK7: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_SPINATTACK6: + if (animTimeElapsed <= 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_SPINATTACK7: + if (ps->torsoAnimTimer <= 500) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 500) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_FORCELONGLEAP_ATTACK: + if (animTimeElapsed <= 200) + {//1st four frames of anim + return FORCE_LEVEL_5; + } + break; + /* + case BOTH_A7_KICK_F://these kicks attack, too + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + //FIXME: break up + return FORCE_LEVEL_5; + break; + */ + case BOTH_STABDOWN: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_5; + } + break; + case BOTH_STABDOWN_STAFF: + if (ps->torsoAnimTimer <= 850) + {//end of anim + return FORCE_LEVEL_5; + } + break; + case BOTH_STABDOWN_DUAL: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_5; + } + break; + case BOTH_A6_SABERPROTECT: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A7_SOULCAL: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 600) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A1_SPECIAL: + if (ps->torsoAnimTimer < 600) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A2_SPECIAL: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A3_SPECIAL: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_FLIP_ATTACK7: + return FORCE_LEVEL_5; + break; + case BOTH_PULL_IMPALE_STAB: + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_PULL_IMPALE_SWING: + if (ps->torsoAnimTimer < 500)//750 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650)//600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_ALORA_SPIN_SLASH: + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A6_FB: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A6_LR: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_A7_HILT: + return FORCE_LEVEL_0; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_LK_S_DL_T_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_S_SB_1_W: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + if (ps->torsoAnimTimer < 150) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_T_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + if (animTimeElapsed < 1000) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_ST_T_SB_1_W: + if (ps->torsoAnimTimer < 950) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_S_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 450) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_T_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_S_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_T_SB_1_W: + //special suberbreak - doesn't kill, just kicks them backwards + return FORCE_LEVEL_0; + break; + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 350) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + return FORCE_LEVEL_5; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_HANG_ATTACK: + //FIME: break up + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + else + {//sweet spot + return FORCE_LEVEL_5; + } + break; + case BOTH_ROLL_STAB: + if (animTimeElapsed > 400) + {//end of anim + return FORCE_LEVEL_0; + } + else + { + return FORCE_LEVEL_5; + } + break; + } + return FORCE_LEVEL_0; + } + else + { //old code + int anim = ps->torsoAnim; + int animTimeElapsed = PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim) - ps->torsoAnimTimer; + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____) + { + //FIXME: these two need their own style + if (ps->saber[0].type == SABER_LANCE) + { + return FORCE_LEVEL_4; + } + else if (ps->saber[0].type == SABER_TRIDENT) + { + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_1; + } + if (anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____) + { + return FORCE_LEVEL_3; + } + if (anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____) + { + return FORCE_LEVEL_5; + } + if (anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____) + {//desann + return FORCE_LEVEL_5; + } + if (anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____) + {//tavion + return FORCE_LEVEL_2; + } + if (anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____) + {//dual + return FORCE_LEVEL_2; + } + if (anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____) + {//staff + return FORCE_LEVEL_4; + } + if ((anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR) + || (anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR) + || (anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR)) + {//parries + switch (ps->saberAnimLevel) + { + case SS_STRONG: + case SS_DESANN: + return FORCE_LEVEL_3; + break; + case SS_TAVION: + case SS_STAFF: + case SS_DUAL: + case SS_MEDIUM: + return FORCE_LEVEL_2; + break; + case SS_FAST: + return FORCE_LEVEL_1; + break; + default: + return FORCE_LEVEL_0; + break; + } + } + if ((anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR) + || (anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR) + || (anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR)) + {//knockaways + return FORCE_LEVEL_3; + } + if ((anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1) + || (anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6) + || (anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7)) + {//knocked-away attacks + return FORCE_LEVEL_1; + } + if ((anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR) + || (anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR) + || (anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR)) + {//broken parries + return FORCE_LEVEL_0; + } + switch (anim) + { + case BOTH_A2_STABBACK1: + if (ps->torsoAnimTimer < 450) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ATTACK_BACK: + if (ps->torsoAnimTimer < 500) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_CROUCHATTACKBACK1: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_K1_S1_T_: //# knockaway saber top + case BOTH_K1_S1_TR: //# knockaway saber top right + case BOTH_K1_S1_TL: //# knockaway saber top left + case BOTH_K1_S1_BL: //# knockaway saber bottom left + case BOTH_K1_S1_B_: //# knockaway saber bottom + case BOTH_K1_S1_BR: //# knockaway saber bottom right + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_LUNGE2_B__T_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELEAP2_T__B_: + if (ps->torsoAnimTimer < 400) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + return FORCE_LEVEL_3;//??? + break; + case BOTH_JUMPFLIPSLASHDOWN1: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 550) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPFLIPSTABDOWN: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed <= 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPATTACK6: + /* + if (pm->ps) + { + if ( ( pm->ps->legsAnimTimer >= 1450 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) + ||(pm->ps->legsAnimTimer >= 400 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) + {//pretty much sideways + return FORCE_LEVEL_3; + } + } + */ + if ((ps->torsoAnimTimer >= 1450 + && animTimeElapsed >= 400) + || (ps->torsoAnimTimer >= 400 + && animTimeElapsed >= 1100)) + {//pretty much sideways + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_0; + break; + case BOTH_JUMPATTACK7: + if (ps->torsoAnimTimer <= 1200) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK6: + if (animTimeElapsed <= 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK7: + if (ps->torsoAnimTimer <= 500) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 500) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELONGLEAP_ATTACK: + if (animTimeElapsed <= 200) + {//1st four frames of anim + return FORCE_LEVEL_3; + } + break; + /* + case BOTH_A7_KICK_F://these kicks attack, too + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + //FIXME: break up + return FORCE_LEVEL_3; + break; + */ + case BOTH_STABDOWN: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_STAFF: + if (ps->torsoAnimTimer <= 850) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_DUAL: + if (ps->torsoAnimTimer <= 900) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_A6_SABERPROTECT: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_SOULCAL: + if (ps->torsoAnimTimer < 650) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 600) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A1_SPECIAL: + if (ps->torsoAnimTimer < 600) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A2_SPECIAL: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A3_SPECIAL: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 200) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FLIP_ATTACK7: + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_STAB: + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_SWING: + if (ps->torsoAnimTimer < 500)//750 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650)//600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ALORA_SPIN_SLASH: + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_FB: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_LR: + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_HILT: + return FORCE_LEVEL_0; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_LK_S_DL_T_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_S_SB_1_W: + if (ps->torsoAnimTimer < 300) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + if (ps->torsoAnimTimer < 700) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + if (ps->torsoAnimTimer < 150) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 400) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_T_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + if (animTimeElapsed < 1000) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_ST_T_SB_1_W: + if (ps->torsoAnimTimer < 950) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 650) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_S_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 900) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 450) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_T_SB_1_W: + if (saberNum != 0) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if (ps->torsoAnimTimer < 250) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 150) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_S_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_T_SB_1_W: + //special suberbreak - doesn't kill, just kicks them backwards + return FORCE_LEVEL_0; + break; + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + if (ps->torsoAnimTimer < 800) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 350) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + return FORCE_LEVEL_5; + break; + //===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_HANG_ATTACK: + //FIME: break up + if (ps->torsoAnimTimer < 1000) + {//end of anim + return FORCE_LEVEL_0; + } + else if (animTimeElapsed < 250) + {//beginning of anim + return FORCE_LEVEL_0; + } + else + {//sweet spot + return FORCE_LEVEL_5; + } + break; + case BOTH_ROLL_STAB: + if (animTimeElapsed > 400) + {//end of anim + return FORCE_LEVEL_0; + } + else + { + return FORCE_LEVEL_3; + } + break; + } + return FORCE_LEVEL_0; + } +} + +qboolean PM_InAnimForSaberMove( int anim, int saberMove ) +{ + switch ( anim ) + {//special case anims + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qtrue; + } + if ( PM_SaberDrawPutawayAnim( anim ) ) + { + if ( saberMove == LS_DRAW || saberMove == LS_PUTAWAY ) + { + return qtrue; + } + return qfalse; + } + else if ( PM_SaberStanceAnim( anim ) ) + { + if ( saberMove == LS_READY ) + { + return qtrue; + } + return qfalse; + } + int animLevel = PM_AnimLevelForSaberAnim( anim ); + if ( animLevel <= 0 ) + {//NOTE: this will always return false for the ready poses and putaway/draw... + return qfalse; + } + //drop the anim to the first level and start the checks there + anim -= (animLevel-FORCE_LEVEL_1)*SABER_ANIM_GROUP_SIZE; + //check level 1 + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 2 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 3 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 4 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 5 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + if ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_H1_S1_BR ) + {//parries, knockaways and broken parries + return (anim==saberMoveData[saberMove].animToUse); + } + return qfalse; +} + +qboolean PM_SaberInIdle( int move ) +{ + switch ( move ) + { + case LS_NONE: + case LS_READY: + case LS_DRAW: + case LS_PUTAWAY: + return qtrue; + break; + } + return qfalse; +} +qboolean PM_SaberInSpecialAttack( int anim ) +{ + switch ( anim ) + { + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberInAttackPure( int move ) +{ + if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInAttack( int move ) +{ + if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + { + return qtrue; + } + switch ( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_ARIAL_LEFT: + case LS_JUMPATTACK_ARIAL_RIGHT: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qtrue; + break; + } + return qfalse; +} +qboolean PM_SaberInTransition( int move ) +{ + if ( move >= LS_T1_BR__R && move <= LS_T1_BL__L ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInStart( int move ) +{ + if ( move >= LS_S_TL2BR && move <= LS_S_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInReturn( int move ) +{ + if ( move >= LS_R_TL2BR && move <= LS_R_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInTransitionAny( int move ) +{ + if ( PM_SaberInStart( move ) ) + { + return qtrue; + } + else if ( PM_SaberInTransition( move ) ) + { + return qtrue; + } + else if ( PM_SaberInReturn( move ) ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInBounce( int move ) +{ + if ( move >= LS_B1_BR && move <= LS_B1_BL ) + { + return qtrue; + } + if ( move >= LS_D1_BR && move <= LS_D1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInBrokenParry( int move ) +{ + if ( move >= LS_V1_BR && move <= LS_V1_B_ ) + { + return qtrue; + } + if ( move >= LS_H1_T_ && move <= LS_H1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInDeflect( int move ) +{ + if ( move >= LS_D1_BR && move <= LS_D1_B_ ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInParry( int move ) +{ + if ( move >= LS_PARRY_UP && move <= LS_PARRY_LL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInKnockaway( int move ) +{ + if ( move >= LS_K1_T_ && move <= LS_K1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInReflect( int move ) +{ + if ( move >= LS_REFLECT_UP && move <= LS_REFLECT_LL ) + { + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberInSpecial( int move ) +{ + switch( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_ARIAL_LEFT: + case LS_JUMPATTACK_ARIAL_RIGHT: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qtrue; + } + return qfalse; +} + +qboolean PM_KickMove( int move ) +{ + switch( move ) + { + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_HILT_BASH: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberCanInterruptMove( int move, int anim ) +{ + if ( PM_InAnimForSaberMove( anim, move ) ) + { + switch( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qfalse; + } + + if ( PM_SaberInAttackPure( move ) ) + { + return qfalse; + } + if ( PM_SaberInStart( move ) ) + { + return qfalse; + } + if ( PM_SaberInTransition( move ) ) + { + return qfalse; + } + if ( PM_SaberInBounce( move ) ) + { + return qfalse; + } + if ( PM_SaberInBrokenParry( move ) ) + { + return qfalse; + } + if ( PM_SaberInDeflect( move ) ) + { + return qfalse; + } + if ( PM_SaberInParry( move ) ) + { + return qfalse; + } + if ( PM_SaberInKnockaway( move ) ) + { + return qfalse; + } + if ( PM_SaberInReflect( move ) ) + { + return qfalse; + } + } + switch ( anim ) + { + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qfalse; + } + return qtrue; +} + +saberMoveName_t PM_BrokenParryForAttack( int move ) +{ + //Our attack was knocked away by a knockaway parry + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( saberMoveData[move].startQuad ) + { + case Q_B: + return LS_V1_B_; + break; + case Q_BR: + return LS_V1_BR; + break; + case Q_R: + return LS_V1__R; + break; + case Q_TR: + return LS_V1_TR; + break; + case Q_T: + return LS_V1_T_; + break; + case Q_TL: + return LS_V1_TL; + break; + case Q_L: + return LS_V1__L; + break; + case Q_BL: + return LS_V1_BL; + break; + } + return LS_NONE; +} + +saberMoveName_t PM_BrokenParryForParry( int move ) +{ + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( move ) + { + case LS_PARRY_UP: + //Hmm... since we don't know what dir the hit came from, randomly pick knock down or knock back + if ( Q_irand( 0, 1 ) ) + { + return LS_H1_B_; + } + else + { + return LS_H1_T_; + } + break; + case LS_PARRY_UR: + return LS_H1_TR; + break; + case LS_PARRY_UL: + return LS_H1_TL; + break; + case LS_PARRY_LR: + return LS_H1_BR; + break; + case LS_PARRY_LL: + return LS_H1_BL; + break; + case LS_READY: + return LS_H1_B_;//??? + break; + } + return LS_NONE; +} + +saberMoveName_t PM_KnockawayForParry( int move ) +{ + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( move ) + { + case BLOCKED_TOP://LS_PARRY_UP: + return LS_K1_T_;//push up + break; + case BLOCKED_UPPER_RIGHT://LS_PARRY_UR: + default://case LS_READY: + return LS_K1_TR;//push up, slightly to right + break; + case BLOCKED_UPPER_LEFT://LS_PARRY_UL: + return LS_K1_TL;//push up and to left + break; + case BLOCKED_LOWER_RIGHT://LS_PARRY_LR: + return LS_K1_BR;//push down and to left + break; + case BLOCKED_LOWER_LEFT://LS_PARRY_LL: + return LS_K1_BL;//push down and to right + break; + } + //return LS_NONE; +} + +saberMoveName_t PM_SaberBounceForAttack( int move ) +{ + switch ( saberMoveData[move].startQuad ) + { + case Q_B: + case Q_BR: + return LS_B1_BR; + break; + case Q_R: + return LS_B1__R; + break; + case Q_TR: + return LS_B1_TR; + break; + case Q_T: + return LS_B1_T_; + break; + case Q_TL: + return LS_B1_TL; + break; + case Q_L: + return LS_B1__L; + break; + case Q_BL: + return LS_B1_BL; + break; + } + return LS_NONE; +} + +saberMoveName_t PM_AttackMoveForQuad( int quad ) +{ + switch ( quad ) + { + case Q_B: + case Q_BR: + return LS_A_BR2TL; + break; + case Q_R: + return LS_A_R2L; + break; + case Q_TR: + return LS_A_TR2BL; + break; + case Q_T: + return LS_A_T2B; + break; + case Q_TL: + return LS_A_TL2BR; + break; + case Q_L: + return LS_A_L2R; + break; + case Q_BL: + return LS_A_BL2TR; + break; + } + return LS_NONE; +} + +int saberMoveTransitionAngle[Q_NUM_QUADS][Q_NUM_QUADS] = +{ + { + 0,//Q_BR,Q_BR, + 45,//Q_BR,Q_R, + 90,//Q_BR,Q_TR, + 135,//Q_BR,Q_T, + 180,//Q_BR,Q_TL, + 215,//Q_BR,Q_L, + 270,//Q_BR,Q_BL, + 45,//Q_BR,Q_B, + }, + { + 45,//Q_R,Q_BR, + 0,//Q_R,Q_R, + 45,//Q_R,Q_TR, + 90,//Q_R,Q_T, + 135,//Q_R,Q_TL, + 180,//Q_R,Q_L, + 215,//Q_R,Q_BL, + 90,//Q_R,Q_B, + }, + { + 90,//Q_TR,Q_BR, + 45,//Q_TR,Q_R, + 0,//Q_TR,Q_TR, + 45,//Q_TR,Q_T, + 90,//Q_TR,Q_TL, + 135,//Q_TR,Q_L, + 180,//Q_TR,Q_BL, + 135,//Q_TR,Q_B, + }, + { + 135,//Q_T,Q_BR, + 90,//Q_T,Q_R, + 45,//Q_T,Q_TR, + 0,//Q_T,Q_T, + 45,//Q_T,Q_TL, + 90,//Q_T,Q_L, + 135,//Q_T,Q_BL, + 180,//Q_T,Q_B, + }, + { + 180,//Q_TL,Q_BR, + 135,//Q_TL,Q_R, + 90,//Q_TL,Q_TR, + 45,//Q_TL,Q_T, + 0,//Q_TL,Q_TL, + 45,//Q_TL,Q_L, + 90,//Q_TL,Q_BL, + 135,//Q_TL,Q_B, + }, + { + 215,//Q_L,Q_BR, + 180,//Q_L,Q_R, + 135,//Q_L,Q_TR, + 90,//Q_L,Q_T, + 45,//Q_L,Q_TL, + 0,//Q_L,Q_L, + 45,//Q_L,Q_BL, + 90,//Q_L,Q_B, + }, + { + 270,//Q_BL,Q_BR, + 215,//Q_BL,Q_R, + 180,//Q_BL,Q_TR, + 135,//Q_BL,Q_T, + 90,//Q_BL,Q_TL, + 45,//Q_BL,Q_L, + 0,//Q_BL,Q_BL, + 45,//Q_BL,Q_B, + }, + { + 45,//Q_B,Q_BR, + 90,//Q_B,Q_R, + 135,//Q_B,Q_TR, + 180,//Q_B,Q_T, + 135,//Q_B,Q_TL, + 90,//Q_B,Q_L, + 45,//Q_B,Q_BL, + 0//Q_B,Q_B, + } +}; + +int PM_SaberAttackChainAngle( int move1, int move2 ) +{ + if ( move1 == -1 || move2 == -1 ) + { + return -1; + } + return saberMoveTransitionAngle[saberMoveData[move1].endQuad][saberMoveData[move2].startQuad]; +} + +qboolean PM_SaberKataDone( int curmove = LS_NONE, int newmove = LS_NONE ) +{ + if ( pm->ps->forceRageRecoveryTime > level.time ) + {//rage recovery, only 1 swing at a time (tired) + if ( pm->ps->saberAttackChainCount > 0 ) + {//swung once + return qtrue; + } + else + {//allow one attack + return qfalse; + } + } + else if ( (pm->ps->forcePowersActive&(1<ps->saber[0].maxChain == -1 ) + { + return qfalse; + } + else if ( pm->ps->saber[0].maxChain != 0 ) + { + if ( pm->ps->saberAttackChainCount >= pm->ps->saber[0].maxChain ) + { + return qtrue; + } + else + { + return qfalse; + } + } + + if ( pm->ps->saberAnimLevel == SS_DESANN || pm->ps->saberAnimLevel == SS_TAVION ) + {//desann and tavion can link up as many attacks as they want + return qfalse; + } + //FIXME: instead of random, apply some sort of logical conditions to whether or + // not you can chain? Like if you were completely missed, you can't chain as much, or...? + // And/Or based on FP_SABER_OFFENSE level? So number of attacks you can chain + // increases with your FP_SABER_OFFENSE skill? + if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + //TEMP: for now, let staff attacks infinitely chain + return qfalse; + /* + if ( pm->ps->saberAttackChainCount > Q_irand( 3, 4 ) ) + { + return qtrue; + } + else if ( pm->ps->saberAttackChainCount > 0 ) + { + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 3 ) + { + return qtrue; + } + } + } + */ + } + else if ( pm->ps->saberAnimLevel == SS_DUAL ) + { + //TEMP: for now, let staff attacks infinitely chain + return qfalse; + } + else if ( pm->ps->saberAnimLevel == FORCE_LEVEL_3 ) + { + if ( curmove == LS_NONE || newmove == LS_NONE ) + { + if ( pm->ps->saberAnimLevel >= FORCE_LEVEL_3 && pm->ps->saberAttackChainCount > Q_irand( 0, 1 ) ) + { + return qtrue; + } + } + else if ( pm->ps->saberAttackChainCount > Q_irand( 2, 3 ) ) + { + return qtrue; + } + else if ( pm->ps->saberAttackChainCount > 0 ) + { + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + return qtrue; + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + } + } + else if ( g_saberNewCombat->integer ) //new code + {//FIXME: have chainAngle influence fast and medium chains as well? + if ( (pm->ps->saberAnimLevel == FORCE_LEVEL_3 || pm->ps->saberAnimLevel == SS_DUAL) + && pm->ps->saberAttackChainCount > Q_irand( 2, 5 ) ) + { + return qtrue; + } + } + else //old code + {//FIXME: have chainAngle influence fast and medium chains as well? + if ((pm->ps->saberAnimLevel == FORCE_LEVEL_2 || pm->ps->saberAnimLevel == SS_DUAL) + && pm->ps->saberAttackChainCount > Q_irand(2, 5)) + { + return qtrue; + } + } + return qfalse; +} + +qboolean PM_CheckEnemyInBack( float backCheckDist ) +{ + if ( !pm->gent || !pm->gent->client ) + { + return qfalse; + } + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) + && !g_saberAutoAim->integer && pm->cmd.forwardmove >= 0 ) + {//don't auto-backstab + return qfalse; + } + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + {//only when on ground + return qfalse; + } + trace_t trace; + vec3_t end, fwd, fwdAngles = {0,pm->ps->viewangles[YAW],0}; + + AngleVectors( fwdAngles, fwd, NULL, NULL ); + VectorMA( pm->ps->origin, -backCheckDist, fwd, end ); + + pm->trace( &trace, pm->ps->origin, vec3_origin, vec3_origin, end, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 ); + if ( trace.fraction < 1.0f && trace.entityNum < ENTITYNUM_WORLD ) + { + gentity_t *traceEnt = &g_entities[trace.entityNum]; + if ( traceEnt + && traceEnt->health > 0 + && traceEnt->client + && traceEnt->client->playerTeam == pm->gent->client->enemyTeam + && traceEnt->client->ps.groundEntityNum != ENTITYNUM_NONE ) + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + {//player + if ( pm->gent ) + {//set player enemy to traceEnt so he auto-aims at him + pm->gent->enemy = traceEnt; + } + } + return qtrue; + } + } + return qfalse; +} + +saberMoveName_t PM_PickBackStab( void ) +{ + if ( !pm->gent || !pm->gent->client ) + { + return LS_READY; + } + if ( pm->ps->dualSabers + && pm->ps->saber[1].Active() ) + { + if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + if ( pm->gent->client->ps.saberAnimLevel == SS_TAVION ) + { + return LS_A_BACKSTAB; + } + else if ( pm->gent->client->ps.saberAnimLevel == SS_DESANN ) + { + if ( pm->ps->saberMove == LS_READY || !Q_irand( 0, 3 ) ) + { + return LS_A_BACKSTAB; + } + else if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + else if ( (pm->ps->saberAnimLevel == FORCE_LEVEL_3 + || pm->ps->saberAnimLevel == SS_DUAL) && g_saberNewCombat->integer ) //new code + {//using medium attacks or dual sabers + if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + else if (pm->ps->saberAnimLevel == FORCE_LEVEL_2 + || pm->ps->saberAnimLevel == SS_DUAL) //old code + {//using medium attacks or dual sabers + if (pm->ps->pm_flags & PMF_DUCKED) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + else + { + return LS_A_BACKSTAB; + } +} + +saberMoveName_t PM_CheckStabDown( void ) +{ + if ( !pm->gent || !pm->gent->enemy || !pm->gent->enemy->client ) + { + return LS_NONE; + } + if ( (pm->ps->saber[0].saberFlags&SFL_NO_STABDOWN) ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_STABDOWN) ) + { + return LS_NONE; + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingKataAttack( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//want to try a special + return LS_NONE; + } + } + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + {//player + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + {//sorry must be on ground (or have just jumped) + if ( level.time-pm->ps->lastOnGround <= 50 && (pm->ps->pm_flags&PMF_JUMPING) ) + {//just jumped, it's okay + } + else + { + return LS_NONE; + } + } + /* + if ( pm->cmd.upmove > 0 ) + {//trying to jump + } + else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE //in air + && level.time-pm->ps->lastOnGround <= 250 //just left ground + && (pm->ps->pm_flags&PMF_JUMPING) )//jumped + {//just jumped + } + else + { + return LS_NONE; + } + */ + pm->ps->velocity[2] = 0; + pm->cmd.upmove = 0; + } + else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + {//NPC + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + {//sorry must be on ground (or have just jumped) + if ( level.time-pm->ps->lastOnGround <= 250 && (pm->ps->pm_flags&PMF_JUMPING) ) + {//just jumped, it's okay + } + else + { + return LS_NONE; + } + } + if ( !pm->gent->NPC ) + {//wtf??? + return LS_NONE; + } + if ( Q_irand( 0, RANK_CAPTAIN ) > pm->gent->NPC->rank ) + {//lower ranks do this less often + return LS_NONE; + } + } + vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, NULL, NULL ); + VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + float enemyZDiff = enemyDir[2]; + enemyDir[2] = 0; + float enemyHDist = VectorNormalize( enemyDir )-(pm->gent->maxs[0]+pm->gent->enemy->maxs[0]); + float dot = DotProduct( enemyDir, faceFwd ); + + if ( //(pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + dot > 0.65f + //&& enemyHDist >= 32 //was 48 + && enemyHDist <= 164//was 112 + && PM_InKnockDownOnGround( &pm->gent->enemy->client->ps )//still on ground + && !PM_InGetUpNoRoll( &pm->gent->enemy->client->ps )//not getting up yet + && enemyZDiff <= 20 ) + {//guy is on the ground below me, do a top-down attack + if ( pm->gent->enemy->s.number >= MAX_CLIENTS + || !G_ControlledByPlayer( pm->gent->enemy ) ) + {//don't get up while I'm doing this + //stop them from trying to get up for at least another 3 seconds + TIMER_Set( pm->gent->enemy, "noGetUpStraight", 3000 ); + } + //pick the right anim + if ( pm->ps->saberAnimLevel == SS_DUAL + || (pm->ps->dualSabers&&pm->ps->saber[1].Active()) ) + { + return LS_STABDOWN_DUAL; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + return LS_STABDOWN_STAFF; + } + else + { + return LS_STABDOWN; + } + } + return LS_NONE; +} + +extern saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ); +saberMoveName_t PM_SaberFlipOverAttackMove( void ); +saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ) +{ + saberMoveName_t autoMove = LS_INVALID; + + if( !pm->gent->enemy ) + { + return LS_NONE; + } + + vec3_t enemy_org, enemyDir, faceFwd, faceRight, faceUp, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, faceRight, faceUp ); + //FIXME: predict enemy position? + if ( pm->gent->enemy->client ) + { + //VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); + //HMM... using this will adjust for bbox size, so let's do that... + vec3_t size; + VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); + VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + + VectorSubtract( pm->gent->enemy->client->renderInfo.eyePoint, pm->ps->origin, enemyDir ); + } + else + { + if ( pm->gent->enemy->bmodel && VectorCompare( vec3_origin, pm->gent->enemy->currentOrigin ) ) + {//a brush model without an origin brush + vec3_t size; + VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); + VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + } + else + { + VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); + } + VectorSubtract( enemy_org, pm->ps->origin, enemyDir ); + } + float enemyZDiff = enemyDir[2]; + float enemyDist = VectorNormalize( enemyDir ); + float dot = DotProduct( enemyDir, faceFwd ); + if ( dot > 0 ) + {//enemy is in front + if ( allowStabDown ) + {//okay to try this + saberMoveName_t stabDownMove = PM_CheckStabDown(); + if ( stabDownMove != LS_NONE ) + { + return stabDownMove; + } + } + if ( (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + && dot > 0.65f + && enemyDist <= 64 && pm->gent->enemy->client + && (enemyZDiff <= 20 || PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) || PM_CrouchAnim( pm->gent->enemy->client->ps.legsAnim ) ) ) + {//swing down at them + return LS_A_T2B; + } + if ( allowFB ) + {//directly in front anim allowed + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( enemyDist > 200 || pm->gent->enemy->health <= 0 ) + {//hmm, look in back for an enemy + if ( pm->ps->clientNum && !PM_ControlledByPlayer() ) + {//player should never do this automatically + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 100 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + } + //this is the default only if they're *right* in front... + if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + {//NPC or player not in 1st person + if ( PM_CheckFlipOverAttackMove( qtrue ) ) + {//enemy must be close and in front + return PM_SaberFlipOverAttackMove(); + } + } + if ( PM_CheckLungeAttackMove() ) + {//NPC + autoMove = PM_SaberLungeAttackMove( qtrue ); + } + else + { + autoMove = LS_A_T2B; + } + } + else + {//pick a random one + if ( Q_irand( 0, 1 ) ) + { + autoMove = LS_A_TR2BL; + } + else + { + autoMove = LS_A_TL2BR; + } + } + float dotR = DotProduct( enemyDir, faceRight ); + if ( dotR > 0.35 ) + {//enemy is to far right + autoMove = LS_A_L2R; + } + else if ( dotR < -0.35 ) + {//far left + autoMove = LS_A_R2L; + } + else if ( dotR > 0.15 ) + {//enemy is to near right + autoMove = LS_A_TR2BL; + } + else if ( dotR < -0.15 ) + {//near left + autoMove = LS_A_TL2BR; + } + if ( DotProduct( enemyDir, faceUp ) > 0.5 ) + {//enemy is above me + if ( autoMove == LS_A_TR2BL ) + { + autoMove = LS_A_BL2TR; + } + else if ( autoMove == LS_A_TL2BR ) + { + autoMove = LS_A_BR2TL; + } + } + } + else if ( allowFB ) + {//back attack allowed + //if ( !PM_InKnockDown( pm->ps ) ) + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( !pm->gent->enemy->client || pm->gent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE ) + {//enemy not a client or is a client and on ground + if ( dot < -0.75f + && enemyDist < 128 + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,2))) ) + {//fast back-stab + if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + {//can't do it while ducked? + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + {//only fencers and above can do this + autoMove = LS_A_BACKSTAB; + } + } + } + else if ( pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF ) + {//higher level back spin-attacks + if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + { + if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + { + autoMove = LS_A_BACK_CR; + } + else + { + autoMove = LS_A_BACK; + } + } + } + } + } + } + } + return autoMove; +} + +qboolean PM_InSecondaryStyle( void ) +{ + if ( pm->ps->saber[0].numBlades > 1 + && pm->ps->saber[0].singleBladeStyle + && (pm->ps->saber[0].stylesForbidden&(1<ps->saber[0].singleBladeStyle)) + && pm->ps->saberAnimLevel == pm->ps->saber[0].singleBladeStyle ) + { + return qtrue; + } + + if ( pm->ps->dualSabers + && !pm->ps->saber[1].Active() )//pm->ps->saberAnimLevel != SS_DUAL ) + { + return qtrue; + } + return qfalse; +} + +saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ) +{ + G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER_FB ); + + //see if we have an overridden (or cancelled) lunge move + if ( pm->ps->saber[0].lungeAtkMove != LS_INVALID ) + { + if ( pm->ps->saber[0].lungeAtkMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].lungeAtkMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove != LS_INVALID ) + { + if ( pm->ps->saber[1].lungeAtkMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].lungeAtkMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand( 0, 3 ) ) + {//alora NPC + return LS_SPINATTACK_ALORA; + } + else + { + if ( pm->ps->dualSabers ) + { + return LS_SPINATTACK_DUAL; + } + switch ( pm->ps->saberAnimLevel ) + { + case SS_DUAL: + return LS_SPINATTACK_DUAL; + break; + case SS_STAFF: + return LS_SPINATTACK; + break; + default://normal lunge + if ( fallbackToNormalLunge ) + { + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + //do the lunge + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 150, pm->ps->velocity ); + pm->ps->velocity[2] = 50; + PM_AddEvent( EV_JUMP ); + + return LS_A_LUNGE; + } + break; + } + } + return LS_NONE; +} + +qboolean PM_CheckLungeAttackMove( void ) +{ + //check to see if it's cancelled? + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE + || pm->ps->saber[1].lungeAtkMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + { + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE + || pm->ps->saber[0].lungeAtkMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + if ( pm->ps->saberAnimLevel == SS_FAST//fast + || pm->ps->saberAnimLevel == SS_DUAL//dual + || pm->ps->saberAnimLevel == SS_STAFF //staff + || pm->ps->saberAnimLevel == SS_DESANN + || pm->ps->dualSabers ) + {//alt+back+attack using fast, dual or staff attacks + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove < 0 || (pm->ps->pm_flags&PMF_DUCKED) ) + {//ducking + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE + || pm->ps->legsAnim == BOTH_SABERDUAL_STANCE + || (level.time-pm->ps->lastStationary) <= 500 ) + {//standing or just stopped standing + if ( pm->gent + && pm->gent->NPC //NPC + && pm->gent->NPC->rank >= RANK_LT_JG //high rank + && ( pm->gent->NPC->rank == RANK_LT_JG || Q_irand( -3, pm->gent->NPC->rank ) >= RANK_LT_JG )//Q_irand( 0, pm->gent->NPC->rank ) >= RANK_LT_JG ) + && !Q_irand( 0, 3-g_spskill->integer ) ) + {//only fencer and higher can do this + if ( pm->ps->saberAnimLevel == SS_DESANN ) + { + if ( !Q_irand( 0, 4 ) ) + { + return qtrue; + } + } + else + { + return qtrue; + } + } + } + } + } + else + {//player + if ( G_TryingLungeAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + { + return qtrue; + } + } + } + + return qfalse; +} + +saberMoveName_t PM_SaberJumpForwardAttackMove( void ) +{ + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + } + if ( pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF ) + { + pm->cmd.upmove = 0;//no jump just yet + + if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + if ( Q_irand(0, 1) ) + { + return LS_JUMPATTACK_STAFF_LEFT; + } + else + { + return LS_JUMPATTACK_STAFF_RIGHT; + } + } + + return LS_JUMPATTACK_DUAL; + } + else + { + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 200, pm->ps->velocity ); + pm->ps->velocity[2] = 180; + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + + //FIXME: NPCs yell? + PM_AddEvent( EV_JUMP ); + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + pm->cmd.upmove = 0; + + return LS_A_JUMP_T__B_; + } +} + +qboolean PM_CheckJumpForwardAttackMove( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( pm->cmd.forwardmove > 0 //going forward + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped (if not player) + ) + { + if ( pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF ) + {//dual and staff + if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim + && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + && pm->ps->weaponTime <= 0//not busy + && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + { + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//jumping NPC + { + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + { + return qtrue; + } + } + } + else + {//PLAYER + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power to attack + { + return qtrue; + } + } + } + } + //check strong + else if ( pm->ps->saberAnimLevel == SS_STRONG //strong style + || pm->ps->saberAnimLevel == SS_DESANN )//desann + { + if ( //&& !PM_InKnockDown( pm->ps ) + !pm->ps->dualSabers + //&& (pm->ps->legsAnim == BOTH_STAND2||pm->ps->legsAnim == BOTH_SABERFAST_STANCE||pm->ps->legsAnim == BOTH_SABERSLOW_STANCE||level.time-pm->ps->lastStationary<=500)//standing or just started moving + ) + {//strong attack: jump-hack + /* + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || level.time-pm->ps->lastStationary <= 250 )//standing or just started moving + */ + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//NPC jumping + { + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + {//only acrobat or boss and higher can do this + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || level.time-pm->ps->lastStationary <= 250 ) + {//standing or just started moving + if ( pm->gent->client + && pm->gent->client->NPC_class == CLASS_DESANN ) + { + if ( !Q_irand( 0, 1 ) ) + { + return qtrue; + } + } + else + { + return qtrue; + } + } + } + } + } + else + {//player + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) ) + { + return qtrue; + } + } + } + } + } + return qfalse; +} + +saberMoveName_t PM_SaberFlipOverAttackMove( void ) +{ + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + } + //FIXME: check above for room enough to jump! + //FIXME: while in this jump, keep velocity[2] at a minimum until the end of the anim + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 150, pm->ps->velocity ); + pm->ps->velocity[2] = 250; + //250 is normalized for a standing enemy at your z level, about 64 tall... adjust for actual maxs[2]-mins[2] of enemy and for zdiff in origins + if ( pm->gent && pm->gent->enemy ) + { //go higher for taller enemies + pm->ps->velocity[2] *= (pm->gent->enemy->maxs[2]-pm->gent->enemy->mins[2])/64.0f; + //go higher for enemies higher than you, lower for those lower than you + float zDiff = pm->gent->enemy->currentOrigin[2] - pm->ps->origin[2]; + pm->ps->velocity[2] += (zDiff)*1.5f; + //clamp to decent-looking values + //FIXME: still jump too low sometimes + if ( zDiff <= 0 && pm->ps->velocity[2] < 200 ) + {//if we're on same level, don't let me jump so low, I clip into the ground + pm->ps->velocity[2] = 200; + } + else if ( pm->ps->velocity[2] < 50 ) + { + pm->ps->velocity[2] = 50; + } + else if ( pm->ps->velocity[2] > 400 ) + { + pm->ps->velocity[2] = 400; + } + } + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + + //FIXME: NPCs yell? + PM_AddEvent( EV_JUMP ); + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + pm->cmd.upmove = 0; + //FIXME: don't allow this to land on other people + + pm->gent->angle = pm->ps->viewangles[YAW];//so we know what yaw we started this at + + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + + if ( Q_irand( 0, 1 ) ) + { + return LS_A_FLIP_STAB; + } + else + { + return LS_A_FLIP_SLASH; + } +} + +qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( (pm->ps->saberAnimLevel == SS_MEDIUM //medium + || pm->ps->saberAnimLevel == SS_TAVION )//tavion + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + ) + { + qboolean tryMove = qfalse; + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0//want to jump + || (pm->ps->pm_flags&PMF_JUMPING) )//jumping + {//flip over-forward down-attack + if ( (pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) + && !Q_irand(0, 2) ) )//NPC who can do this, 33% chance + {//only player or acrobat or boss and higher can do this + tryMove = qtrue; + } + } + } + else + {//player + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power + { + if ( !pm->cmd.rightmove ) + { + if ( pm->ps->legsAnim == BOTH_JUMP1 + || pm->ps->legsAnim == BOTH_FORCEJUMP1 + || pm->ps->legsAnim == BOTH_INAIR1 + || pm->ps->legsAnim == BOTH_FORCEINAIR1 ) + {//in a non-flip forward jump + tryMove = qtrue; + } + } + } + } + + if ( tryMove ) + { + if ( !checkEnemy ) + {//based just on command input + return qtrue; + } + else + {//based on presence of enemy + if ( pm->gent->enemy )//have an enemy + { + vec3_t fwdAngles = {0,pm->ps->viewangles[YAW],0}; + if ( pm->gent->enemy->health > 0 + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->gent->enemy->maxs[2] > 12 + && (!pm->gent->enemy->client || !PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) ) + && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 10000 + && InFront( pm->gent->enemy->currentOrigin, pm->gent->currentOrigin, fwdAngles, 0.3f ) ) + {//enemy must be alive, not low to ground, close and in front + return qtrue; + } + } + return qfalse; + } + } + } + return qfalse; +} + +saberMoveName_t PM_SaberBackflipAttackMove( void ) +{ + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkBackMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkBackMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkBackMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkBackMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkBackMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + { + return LS_NONE; + } + } + pm->cmd.upmove = 0;//no jump just yet + return LS_A_BACKFLIP_ATK; +} + +qboolean PM_CheckBackflipAttackMove( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE + || pm->ps->saber[1].jumpAtkBackMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE + || pm->ps->saber[0].jumpAtkBackMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + //&& (pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE || level.time-pm->ps->lastStationary<=250)//standing or just started moving + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) )//on ground or just jumped (if not player) + { + if ( pm->cmd.forwardmove < 0 //moving backwards + && pm->ps->saberAnimLevel == SS_STAFF //using staff + && (pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING)) )//jumping + {//jumping backwards and using staff + if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim + && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + && pm->ps->weaponTime <= 0//not busy + && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + {//not already attacking + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + {//acrobat or boss and higher can do this + return qtrue; + } + } + else + {//player + return qtrue; + } + } + } + } + return qfalse; +} + +saberMoveName_t PM_CheckDualSpinProtect( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[0].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].kataMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[1].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].kataMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].kataMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->ps->saberMove == LS_READY//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? + //&& pm->ps->viewangles[0] > 30 //looking down + && pm->ps->saberAnimLevel == SS_DUAL//using dual saber style + && pm->ps->saber[0].Active() && pm->ps->saber[1].Active()//both sabers on + //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 + //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing + && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack + ) + {//FIXME: some NPC logic to do this? + /* + if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching + && g_crosshairEntNum >= ENTITYNUM_WORLD ) + */ + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_PUSH, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + } + return LS_DUAL_SPIN_PROTECT; + } + } + return LS_NONE; +} + +saberMoveName_t PM_CheckStaffKata( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[0].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].kataMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[1].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].kataMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].kataMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->ps->saberMove == LS_READY//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? + //&& pm->ps->viewangles[0] > 30 //looking down + && pm->ps->saberAnimLevel == SS_STAFF//using dual saber style + && pm->ps->saber[0].Active()//saber on + //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 + //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing + && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack + ) + {//FIXME: some NPC logic to do this? + /* + if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching + && g_crosshairEntNum >= ENTITYNUM_WORLD ) + */ + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + } + return LS_STAFF_SOULCAL; + } + } + return LS_NONE; +} + +extern qboolean WP_ForceThrowable( gentity_t *ent, gentity_t *forwardEnt, gentity_t *self, qboolean pull, float cone, float radius, vec3_t forward ); +saberMoveName_t PM_CheckPullAttack( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + if ( (pm->ps->saber[0].saberFlags&SFL_NO_PULL_ATTACK) ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_PULL_ATTACK) ) + { + return LS_NONE; + } + + if ( (pm->ps->saberMove == LS_READY||PM_SaberInReturn(pm->ps->saberMove)||PM_SaberInReflect(pm->ps->saberMove))//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY + && pm->ps->saberAnimLevel >= SS_FAST//single saber styles - FIXME: Tavion? + && pm->ps->saberAnimLevel <= SS_STRONG//single saber styles - FIXME: Tavion? + && G_TryingPullAttack( pm->gent, &pm->cmd, qfalse )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + //&& pm->cmd.forwardmove<0//pulling back + && (pm->cmd.buttons&BUTTON_ATTACK)//attacking + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB//have enough power + ) + {//FIXME: some NPC logic to do this? + qboolean doMove = g_saberNewControlScheme->integer?qtrue:qfalse;//in new control scheme, can always do this, even if there's no-one to do it to + if ( g_saberNewControlScheme->integer + || g_crosshairEntNum < ENTITYNUM_WORLD )//in old control scheme, there has to be someone there + { + saberMoveName_t pullAttackMove = LS_NONE; + if ( pm->ps->saberAnimLevel == SS_FAST ) + { + pullAttackMove = LS_PULL_ATTACK_STAB; + } + else + { + pullAttackMove = LS_PULL_ATTACK_SWING; + } + + if ( g_crosshairEntNum < ENTITYNUM_WORLD + && pm->gent && pm->gent->client ) + { + gentity_t *targEnt = &g_entities[g_crosshairEntNum]; + if ( targEnt->client + && targEnt->health > 0 + //FIXME: check other things like in knockdown, saberlock, uninterruptable anims, etc. + && !PM_InOnGroundAnim( &targEnt->client->ps ) + && !PM_LockedAnim( targEnt->client->ps.legsAnim ) + && !PM_SuperBreakLoseAnim( targEnt->client->ps.legsAnim ) + && !PM_SuperBreakWinAnim( targEnt->client->ps.legsAnim ) + && targEnt->client->ps.saberLockTime <= 0 + && WP_ForceThrowable( targEnt, targEnt, pm->gent, qtrue, 1.0f, 0.0f, NULL ) ) + { + if ( !g_saberNewControlScheme->integer ) + {//in old control scheme, make sure they're close or far enough away for the move we'll be doing + float targDist = Distance( targEnt->currentOrigin, pm->ps->origin ); + if ( pullAttackMove == LS_PULL_ATTACK_STAB ) + {//must be closer than 512 + if ( targDist > 384.0f ) + { + return LS_NONE; + } + } + else//if ( pullAttackMove == LS_PULL_ATTACK_SWING ) + {//must be farther than 256 + if ( targDist > 512.0f ) + { + return LS_NONE; + } + if ( targDist < 192.0f ) + { + return LS_NONE; + } + } + } + + vec3_t targAngles = {0,targEnt->client->ps.viewangles[YAW],0}; + if ( InFront( pm->ps->origin, targEnt->currentOrigin, targAngles ) ) + { + NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_F, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + } + else + { + NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_B, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + } + //hold the anim until I'm with done pull anim + targEnt->client->ps.legsAnimTimer = targEnt->client->ps.torsoAnimTimer = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, (animNumber_t)saberMoveData[pullAttackMove].animToUse ); + //set pullAttackTime + pm->gent->client->ps.pullAttackTime = targEnt->client->ps.pullAttackTime = level.time+targEnt->client->ps.legsAnimTimer; + //make us know about each other + pm->gent->client->ps.pullAttackEntNum = g_crosshairEntNum; + targEnt->client->ps.pullAttackEntNum = pm->ps->clientNum; + //do effect and sound on me + pm->ps->powerups[PW_FORCE_PUSH] = level.time + 1000; + if ( pm->gent ) + { + G_Sound( pm->gent, G_SoundIndex( "sound/weapons/force/pull.wav" ) ); + } + doMove = qtrue; + } + } + if ( doMove ) + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_PULL, SABER_ALT_ATTACK_POWER_FB ); + } + return pullAttackMove; + } + } + } + return LS_NONE; +} + +saberMoveName_t PM_CheckPlayerAttackFromParry( int curmove ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + { + if ( curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL ) + {//in a parry + switch ( saberMoveData[curmove].endQuad ) + { + case Q_T: + return LS_A_T2B; + break; + case Q_TR: + return LS_A_TR2BL; + break; + case Q_TL: + return LS_A_TL2BR; + break; + case Q_BR: + return LS_A_BR2TL; + break; + case Q_BL: + return LS_A_BL2TR; + break; + //shouldn't be a parry that ends at L, R or B + } + } + } + return LS_NONE; +} + + +saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int curmove ) +{ + qboolean noSpecials = qfalse; + + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + noSpecials = qtrue; + } + + saberMoveName_t overrideJumpRightAttackMove = LS_INVALID; + if ( pm->ps->saber[0].jumpAtkRightMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkRightMove != LS_NONE ) + {//actually overriding + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkRightMove; + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove > LS_NONE ) + {//would be cancelling it, but check the second saber, too + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; + } + else + {//nope, just cancel it + overrideJumpRightAttackMove = LS_NONE; + } + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove != LS_INVALID ) + {//first saber not overridden, check second + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; + } + + saberMoveName_t overrideJumpLeftAttackMove = LS_INVALID; + if ( pm->ps->saber[0].jumpAtkLeftMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkLeftMove != LS_NONE ) + {//actually overriding + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkLeftMove; + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove > LS_NONE ) + {//would be cancelling it, but check the second saber, too + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; + } + else + {//nope, just cancel it + overrideJumpLeftAttackMove = LS_NONE; + } + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove != LS_INVALID ) + {//first saber not overridden, check second + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; + } + if ( rightmove > 0 ) + {//moving right + if ( !noSpecials + && overrideJumpRightAttackMove != LS_NONE + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC + ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + {//cartwheel right + vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + } + pm->cmd.upmove = 0; + if ( overrideJumpRightAttackMove != LS_INVALID ) + {//overridden with another move + return overrideJumpRightAttackMove; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + return LS_BUTTERFLY_RIGHT; + } + else + { + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + {//okay to do cartwheels with this saber + /* + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//still on ground + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_CART_RIGHT; + } + else + */ + {//in air + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + PM_SetJumped( JUMP_VELOCITY, qtrue ); + return LS_JUMPATTACK_ARIAL_RIGHT; + } + } + } + } + else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_RIGHT + && pm->ps->legsAnim != BOTH_ARIAL_RIGHT ) + {//not in a cartwheel/arial + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( forwardmove > 0 ) + {//forward right = TL2BR slash + return LS_A_TL2BR; + } + else if ( forwardmove < 0 ) + {//backward right = BL2TR uppercut + return LS_A_BL2TR; + } + else + {//just right is a left slice + return LS_A_L2R; + } + } + } + else if ( rightmove < 0 ) + {//moving left + if ( !noSpecials + && overrideJumpLeftAttackMove != LS_NONE + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC + ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + {//cartwheel left + vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + } + pm->cmd.upmove = 0; + if ( overrideJumpRightAttackMove != LS_INVALID ) + {//overridden with another move + return overrideJumpRightAttackMove; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + return LS_BUTTERFLY_LEFT; + } + else + { + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + {//okay to do cartwheels with this saber + /* + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//still on ground + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_ARIAL_LEFT; + } + else + */ + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + PM_SetJumped( JUMP_VELOCITY, qtrue ); + return LS_JUMPATTACK_CART_LEFT; + } + } + } + } + else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_LEFT + && pm->ps->legsAnim != BOTH_ARIAL_LEFT ) + {//not in a left cartwheel/arial + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( forwardmove > 0 ) + {//forward left = TR2BL slash + return LS_A_TR2BL; + } + else if ( forwardmove < 0 ) + {//backward left = BR2TL uppercut + return LS_A_BR2TL; + } + else + {//just left is a right slice + return LS_A_R2L; + } + } + } + else + {//not moving left or right + if ( forwardmove > 0 ) + {//forward= T2B slash + saberMoveName_t stabDownMove = noSpecials?LS_NONE:PM_CheckStabDown(); + if ( stabDownMove != LS_NONE ) + { + return stabDownMove; + } + if ( ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zoomed in + {//player in thirdperson, not zoomed in + //flip-over attack logic + if ( !noSpecials && PM_CheckFlipOverAttackMove( qfalse ) ) + {//flip over-forward down-attack + return PM_SaberFlipOverAttackMove(); + } + //lunge attack logic + else if ( PM_CheckLungeAttackMove() ) + { + return PM_SaberLungeAttackMove( qtrue ); + } + //jump forward attack logic + else if ( !noSpecials && PM_CheckJumpForwardAttackMove() ) + { + return PM_SaberJumpForwardAttackMove(); + } + } + + //player NPC with enemy: autoMove logic + if ( pm->gent + && pm->gent->enemy + && pm->gent->enemy->client ) + {//I have an active enemy + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + {//a player who is running at an enemy + //if the enemy is not a jedi, don't use top-down, pick a diagonal or side attack + if ( pm->gent->enemy->s.weapon != WP_SABER + && pm->gent->enemy->client->NPC_class != CLASS_REMOTE//too small to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_SEEKER//too small to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_GONK//too short to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_HOWLER//too short to do auto-aiming accurately + && g_saberAutoAim->integer ) + { + saberMoveName_t autoMove = PM_AttackForEnemyPos( qfalse, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer()) ); + if ( autoMove != LS_INVALID ) + { + return autoMove; + } + } + } + + if ( pm->ps->clientNum>=MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + {//NPC + if ( PM_CheckFlipOverAttackMove( qtrue ) ) + { + return PM_SaberFlipOverAttackMove(); + } + } + } + + //Regular NPCs + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + {//NPC or player in third person, not zoomed in + //fwd jump attack logic + if ( PM_CheckJumpForwardAttackMove() ) + { + return PM_SaberJumpForwardAttackMove(); + } + //lunge attack logic + else if ( PM_CheckLungeAttackMove() ) + { + return PM_SaberLungeAttackMove( qtrue ); + } + } + + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + return LS_A_T2B; + } + else if ( forwardmove < 0 ) + {//backward= T2B slash//B2T uppercut? + if ( g_saberNewControlScheme->integer ) + { + saberMoveName_t pullAtk = PM_CheckPullAttack(); + if ( pullAtk != LS_NONE ) + { + return pullAtk; + } + } + + if ( g_saberNewControlScheme->integer + && (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + && (pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus, trying special backwards attacks + {//player lunge attack logic + if ( ( pm->ps->dualSabers //or dual + || pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->SaberStaff() )//or staff + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + {//alt+back+attack using fast, dual or staff attacks + PM_SaberLungeAttackMove( qfalse ); + } + } + else if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) //NPC + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zooomed + {//NPC or player in third person, not zoomed + if ( PM_CheckBackflipAttackMove() ) + { + return PM_SaberBackflipAttackMove();//backflip attack + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //if ( !PM_InKnockDown( pm->ps ) ) + //check backstabs + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( pm->gent && pm->gent->enemy ) + {//FIXME: or just trace for a valid enemy standing behind me? And no enemy in front? + vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, NULL, NULL ); + VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + float dot = DotProduct( enemyDir, faceFwd ); + if ( dot < 0 ) + {//enemy is behind me + if ( dot < -0.75f + && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 16384//128 squared + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,1))) ) + {//fast attacks and Tavion + if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + {//can't do it while ducked? + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + {//only fencers and above can do this + return LS_A_BACKSTAB; + } + } + } + else if ( pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF ) + {//medium and higher attacks + if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + } + else + {//enemy in front + float enemyDistSq = DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ); + if ( ((pm->ps->saberAnimLevel == FORCE_LEVEL_1 || + pm->ps->saberAnimLevel == SS_STAFF || + pm->gent->client->NPC_class == CLASS_TAVION || + pm->gent->client->NPC_class == CLASS_ALORA || + (pm->gent->client->NPC_class == CLASS_DESANN && !Q_irand(0,3))) && + enemyDistSq > 16384) || + pm->gent->enemy->health <= 0 )//128 squared + {//my enemy is pretty far in front of me and I'm using fast attacks + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || + ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 128 ) ) + { + return PM_PickBackStab(); + } + } + } + else if ( ((pm->ps->saberAnimLevel >= FORCE_LEVEL_2 || pm->gent->client->NPC_class == CLASS_DESANN) && enemyDistSq > 40000) || pm->gent->enemy->health <= 0 )//200 squared + {//enemy is very faw away and I'm using medium/strong attacks + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || + ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 164 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + else + {//no current enemy + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->gent && pm->gent->client ) + {//only player + if ( PM_CheckEnemyInBack( 128 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + } + + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + //else just swing down + return LS_A_T2B; + } + else + {//not moving in any direction + if ( PM_SaberInBounce( curmove ) ) + {//bounces should go to their default attack if you don't specify a direction but are attacking + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + saberMoveName_t newmove; + if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + } + else + {//player uses chain-attack + newmove = saberMoveData[curmove].chain_attack; + } + if ( PM_SaberKataDone( curmove, newmove ) ) + { + return saberMoveData[curmove].chain_idle; + } + else + { + return newmove; + } + } + else if ( PM_SaberInKnockaway( curmove ) ) + {//bounces should go to their default attack if you don't specify a direction but are attacking + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + saberMoveName_t newmove; + if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + } + else + { + if ( pm->ps->saberAnimLevel == SS_FAST || + pm->ps->saberAnimLevel == SS_TAVION ) + {//player is in fast attacks, so come right back down from the same spot + newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad ); + } + else + {//use a transition to wrap to another attack from a different dir + newmove = saberMoveData[curmove].chain_attack; + } + } + if ( PM_SaberKataDone( curmove, newmove ) ) + { + return saberMoveData[curmove].chain_idle; + } + else + { + return newmove; + } + } + else if ( curmove == LS_READY + || curmove == LS_A_FLIP_STAB + || curmove == LS_A_FLIP_SLASH + || ( curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL ) ) + {//Not moving at all, not too busy to attack + //push + lookdown + attack + dual sabers = LS_DUAL_SPIN_PROTECT + if ( g_saberNewControlScheme->integer ) + { + if ( PM_CheckDualSpinProtect() ) + { + return LS_DUAL_SPIN_PROTECT; + } + if ( PM_CheckStaffKata() ) + { + return LS_STAFF_SOULCAL; + } + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( pm->ps->clientNum || g_saberAutoAim->integer ) + {//auto-aim + if ( pm->gent && pm->gent->enemy ) + {//based on enemy position, pick a proper attack + saberMoveName_t autoMove = PM_AttackForEnemyPos( qtrue, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS) ); + if ( autoMove != LS_INVALID ) + { + return autoMove; + } + } + else if ( fabs(pm->ps->viewangles[0]) > 30 ) + {//looking far up or far down uses the top to bottom attack, presuming you want a vertical attack + return LS_A_T2B; + } + } + else + {//for now, just pick a random attack + return ((saberMoveName_t)Q_irand( LS_A_TL2BR, LS_A_T2B )); + } + } + } + } + //FIXME: pick a return? + return LS_NONE; +} + +saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveName_t newmove ) +{ + //FIXME: take FP_SABER_OFFENSE into account here somehow? + int retmove = newmove; + if ( curmove == LS_READY ) + {//just standing there + switch ( newmove ) + { + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + //transition is the start + retmove = LS_S_TL2BR + (newmove-LS_A_TL2BR); + break; + default: + break; + } + } + else + { + switch ( newmove ) + { + //transitioning to ready pose + case LS_READY: + switch ( curmove ) + { + //transitioning from an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + //transition is the return + retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + break; + default: + break; + } + break; + //transitioning to an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + if ( newmove == curmove ) + {//FIXME: need a spin or something or go to next level, but for now, just play the return + //going into another attack... + //allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3 + //FIXME: don't let strong attacks chain to an attack in the opposite direction ( > 45 degrees?) + if ( PM_SaberKataDone( curmove, newmove ) ) + {//done with this kata, must return to ready before attack again + retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + } + else + {//okay to chain to another attack + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + } + } + else if ( saberMoveData[curmove].endQuad == saberMoveData[newmove].startQuad ) + {//new move starts from same quadrant + retmove = newmove; + } + else + { + switch ( curmove ) + { + //transitioning from an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + case LS_D1_BR: + case LS_D1__R: + case LS_D1_TR: + case LS_D1_T_: + case LS_D1_TL: + case LS_D1__L: + case LS_D1_BL: + case LS_D1_B_: + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + break; + //transitioning from a return + case LS_R_TL2BR: + case LS_R_L2R: + case LS_R_BL2TR: + case LS_R_BR2TL: + case LS_R_R2L: + case LS_R_TR2BL: + case LS_R_T2B: + //transitioning from a bounce + /* + case LS_BOUNCE_UL2LL: + case LS_BOUNCE_LL2UL: + case LS_BOUNCE_L2LL: + case LS_BOUNCE_L2UL: + case LS_BOUNCE_UR2LR: + case LS_BOUNCE_LR2UR: + case LS_BOUNCE_R2LR: + case LS_BOUNCE_R2UR: + case LS_BOUNCE_TOP: + case LS_OVER_UR2UL: + case LS_OVER_UL2UR: + case LS_BOUNCE_UR: + case LS_BOUNCE_UL: + case LS_BOUNCE_LR: + case LS_BOUNCE_LL: + */ + //transitioning from a parry/reflection/knockaway/broken parry + case LS_PARRY_UP: + case LS_PARRY_UR: + case LS_PARRY_UL: + case LS_PARRY_LR: + case LS_PARRY_LL: + case LS_REFLECT_UP: + case LS_REFLECT_UR: + case LS_REFLECT_UL: + case LS_REFLECT_LR: + case LS_REFLECT_LL: + case LS_K1_T_: + case LS_K1_TR: + case LS_K1_TL: + case LS_K1_BR: + case LS_K1_BL: + case LS_V1_BR: + case LS_V1__R: + case LS_V1_TR: + case LS_V1_T_: + case LS_V1_TL: + case LS_V1__L: + case LS_V1_BL: + case LS_V1_B_: + case LS_H1_T_: + case LS_H1_TR: + case LS_H1_TL: + case LS_H1_BR: + case LS_H1_BL: + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + break; + //NB: transitioning from transitions is fine + default: + break; + } + } + break; + //transitioning to any other anim is not supported + default: + break; + } + } + + if ( retmove == LS_NONE ) + { + return newmove; + } + + return ((saberMoveName_t)retmove); +} + +/* +------------------------- +PM_LegsAnimForFrame +Returns animNumber for current frame +------------------------- +*/ +int PM_LegsAnimForFrame( gentity_t *ent, int legsFrame ) +{ + //Must be a valid client + if ( ent->client == NULL ) + return -1; + + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return -1; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); + + for ( int animation = 0; animation < BOTH_CIN_1; animation++ ) //first anim after last legs + { + if ( animation >= TORSO_DROPWEAP1 && animation < LEGS_TURN1 ) //first legs only anim + {//not a possible legs anim + continue; + } + + if ( animations[animation].glaIndex != glaIndex ) + { + continue; + } + + if ( animations[animation].firstFrame > legsFrame ) + {//This anim starts after this frame + continue; + } + + if ( animations[animation].firstFrame + animations[animation].numFrames < legsFrame ) + {//This anim ends before this frame + continue; + } + //else, must be in this anim! + return animation; + } + + //Not in ANY torsoAnim? SHOULD NEVER HAPPEN +// assert(0); + return -1; +} + +int PM_ValidateAnimRange( const int startFrame, const int endFrame, const float animSpeed ) +{//given a startframe and endframe, see if that lines up with any known animation + animation_t *animations = level.knownAnimFileSets[0].animations; + + for ( int anim = 0; anim < MAX_ANIMATIONS; anim++ ) + { + if ( animSpeed < 0 ) + {//playing backwards + if ( animations[anim].firstFrame == endFrame ) + { + if ( animations[anim].numFrames + animations[anim].firstFrame == startFrame ) + { + //Com_Printf( "valid reverse anim: %s\n", animTable[anim].name ); + return anim; + } + } + } + else + {//playing forwards + if ( animations[anim].firstFrame == startFrame ) + {//This anim starts on this frame + if ( animations[anim].firstFrame + animations[anim].numFrames == endFrame ) + {//This anim ends on this frame + //Com_Printf( "valid forward anim: %s\n", animTable[anim].name ); + return anim; + } + } + } + //else, must not be this anim! + } + + //Not in ANY anim? SHOULD NEVER HAPPEN + Com_Printf( "invalid anim range %d to %d, speed %4.2f\n", startFrame, endFrame, animSpeed ); + return -1; +} +/* +------------------------- +PM_TorsoAnimForFrame +Returns animNumber for current frame +------------------------- +*/ +int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame ) +{ + //Must be a valid client + if ( ent->client == NULL ) + return -1; + + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return -1; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); + + for ( int animation = 0; animation < LEGS_TURN1; animation++ ) //first legs only anim + { + if ( animations[animation].glaIndex != glaIndex ) + { + continue; + } + + if ( animations[animation].firstFrame > torsoFrame ) + {//This anim starts after this frame + continue; + } + + if ( animations[animation].firstFrame + animations[animation].numFrames < torsoFrame ) + {//This anim ends before this frame + continue; + } + //else, must be in this anim! + return animation; + } + + //Not in ANY torsoAnim? SHOULD NEVER HAPPEN +// assert(0); + return -1; +} + +qboolean PM_FinishedCurrentLegsAnim( gentity_t *self ) +{ + int junk, curFrame; + float currentFrame, animSpeed; + + if ( !self->client ) + { + return qtrue; + } + + gi.G2API_GetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &animSpeed, NULL ); + curFrame = floor( currentFrame ); + + int legsAnim = self->client->ps.legsAnim; + animation_t *animations = level.knownAnimFileSets[self->client->clientInfo.animFileIndex].animations; + + if ( curFrame >= animations[legsAnim].firstFrame + (animations[legsAnim].numFrames - 2) ) + { + return qtrue; + } + + return qfalse; +} + +/* +------------------------- +PM_HasAnimation +------------------------- +*/ + +qboolean PM_HasAnimation( gentity_t *ent, int animation ) +{ + //Must be a valid client + if ( !ent || ent->client == NULL ) + return qfalse; + + //must be a valid anim number + if ( animation < 0 || animation >= MAX_ANIMATIONS ) + { + return qfalse; + } + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return qfalse; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + + //No frames, no anim + if ( animations[animation].numFrames == 0 ) + return qfalse; + + //Has the sequence + return qtrue; +} + +int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim ) +{ + int anim; + int count = 0; + + if ( !self ) + { + return Q_irand(minAnim, maxAnim); + } + + do + { + anim = Q_irand(minAnim, maxAnim); + count++; + } + while ( !PM_HasAnimation( self, anim ) && count < 1000 ); + + return anim; +} + +/* +------------------------- +PM_AnimLength +------------------------- +*/ + +int PM_AnimLength( int index, animNumber_t anim ) +{ + if ( ValidAnimFileIndex( index ) == false ) + return 0; + + return level.knownAnimFileSets[index].animations[anim].numFrames * abs(level.knownAnimFileSets[index].animations[anim].frameLerp); +} + +/* +------------------------- +PM_SetLegsAnimTimer +------------------------- +*/ + +void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time ) +{ + *legsAnimTimer = time; + + if ( *legsAnimTimer < 0 && time != -1 ) + {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional + *legsAnimTimer = 0; + } + + if ( !*legsAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_LOWER ) ) + {//Waiting for legsAnimTimer to complete, and it just got set to zero + if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + {//Not waiting for top + Q3_TaskIDComplete( ent, TID_ANIM_LOWER ); + } + else + {//Waiting for both to finish before complete + Q3_TaskIDClear( &ent->taskID[TID_ANIM_LOWER] );//Bottom is done, regardless + if ( !Q3_TaskIDPending( ent, TID_ANIM_UPPER) ) + {//top is done and we're done + Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + } + } + } +} + +/* +------------------------- +PM_SetTorsoAnimTimer +------------------------- +*/ + +void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time ) +{ + *torsoAnimTimer = time; + + if ( *torsoAnimTimer < 0 && time != -1 ) + {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional + *torsoAnimTimer = 0; + } + + if ( !*torsoAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_UPPER ) ) + {//Waiting for torsoAnimTimer to complete, and it just got set to zero + if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + {//Not waiting for bottom + Q3_TaskIDComplete( ent, TID_ANIM_UPPER ); + } + else + {//Waiting for both to finish before complete + Q3_TaskIDClear( &ent->taskID[TID_ANIM_UPPER] );//Top is done, regardless + if ( !Q3_TaskIDPending( ent, TID_ANIM_LOWER) ) + {//lower is done and we're done + Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + } + } + } +} + +extern qboolean PM_SpinningSaberAnim( int anim ); +extern float saberAnimSpeedMod[NUM_FORCE_POWER_LEVELS]; +void PM_SaberStartTransAnim(int saberAnimLevel, int anim, float *animSpeed, gentity_t *gent) +{ + if (g_saberNewCombat->integer) //new code + { + if ( anim == BOTH_V1_BL_S1 + || anim == BOTH_V1_BR_S1 + || anim == BOTH_V1_TL_S1 + || anim == BOTH_V1_TR_S1 + || anim == BOTH_V1_T__S1 + || (anim >= BOTH_V6_BL_S6 && anim <= BOTH_V7__R_S7) ) + { //we're in a broken attack + //speed up recovery from broken attacks based on SO level + *animSpeed = saberAnimSpeedMod[gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]]; + } + if (g_saberAnimSpeed->value != 1.0f) + { + *animSpeed *= g_saberAnimSpeed->value; + } + else if (gent && gent->client && gent->client->ps.weapon == WP_SABER) + { + if (gent->client->ps.saber[0].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[0].animSpeedScale; + } + if (gent->client->ps.dualSabers + && gent->client->ps.saber[1].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[1].animSpeedScale; + } + } + } + else //old code + { + if (g_saberAnimSpeed->value != 1.0f) + { + *animSpeed *= g_saberAnimSpeed->value; + } + else if (gent && gent->client && gent->client->ps.weapon == WP_SABER) + { + if (gent->client->ps.saber[0].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[0].animSpeedScale; + } + if (gent->client->ps.dualSabers + && gent->client->ps.saber[1].animSpeedScale != 1.0f) + { + *animSpeed *= gent->client->ps.saber[1].animSpeedScale; + } + } + } + if ( gent + && gent->client + && gent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.dualSabers + && saberAnimLevel == SS_DUAL + && gent->weaponModel[1] ) + {//using a scepter and dual style, slow down anims + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H7_S7_BR ) + { + *animSpeed *= 0.75; + } + } + if ( gent && gent->client && gent->client->ps.forceRageRecoveryTime > level.time ) + {//rage recovery + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR ) + {//animate slower + *animSpeed *= 0.75; + } + } + else if ( gent && gent->NPC && gent->NPC->rank == RANK_CIVILIAN ) + {//grunt reborn + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + {//his fast attacks are slower + if ( !PM_SpinningSaberAnim( anim ) ) + { + *animSpeed *= 0.75; + } + return; + } + } + else if ( gent && gent->client ) + { + if ( gent->client->ps.saber[0].type == SABER_LANCE || gent->client->ps.saber[0].type == SABER_TRIDENT ) + {//FIXME: hack for now - these use the fast anims, but slowed down. Should have own style + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + {//his fast attacks are slower + if ( !PM_SpinningSaberAnim( anim ) ) + { + *animSpeed *= 0.75; + } + return; + } + } + } + + if ( ( anim >= BOTH_T1_BR__R && + anim <= BOTH_T1_BL_TL ) || + ( anim >= BOTH_T3_BR__R && + anim <= BOTH_T3_BL_TL ) || + ( anim >= BOTH_T5_BR__R && + anim <= BOTH_T5_BL_TL ) ) + { //what is this doing here exactly? + if ( g_saberNewCombat->integer ) //new code + { + if (saberAnimLevel == FORCE_LEVEL_1 /*|| saberAnimLevel == FORCE_LEVEL_5*/) + {//FIXME: should not be necc for FORCE_LEVEL_1's + *animSpeed *= 1.5; + } + else if (saberAnimLevel == FORCE_LEVEL_5) + { //both Desann and Strong styles suffer some transition speed penalty or something? + *animSpeed *= 0.75; + } + } + else //old code + { + if (saberAnimLevel == FORCE_LEVEL_1 || saberAnimLevel == FORCE_LEVEL_5) + {//FIXME: should not be necc for FORCE_LEVEL_1's + *animSpeed *= 1.5; + } + else if (saberAnimLevel == FORCE_LEVEL_3) + { + *animSpeed *= 0.75; + } + } + } +} +/* +void PM_SaberStartTransAnim( int anim, int entNum, int saberOffenseLevel, float *animSpeed ) +{ + //check starts + if ( ( anim >= BOTH_S1_S1_T_ && + anim <= BOTH_S1_S1_TR ) || + ( anim >= BOTH_S1_S1_T_ && + anim <= BOTH_S1_S1_TR ) || + ( anim >= BOTH_S3_S1_T_ && + anim <= BOTH_S3_S1_TR ) ) + { + if ( entNum == 0 ) + { + *animSpeed *= saberAnimSpeedMod[FORCE_LEVEL_3]; + } + else + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; + } + } + //Check transitions + else if ( PM_SpinningSaberAnim( anim ) ) + {//spins stay normal speed + return; + } + else if ( ( anim >= BOTH_T1_BR__R && + anim <= BOTH_T1_BL_TL ) || + ( anim >= BOTH_T2_BR__R && + anim <= BOTH_T2_BL_TL ) || + ( anim >= BOTH_T3_BR__R && + anim <= BOTH_T3_BL_TL ) ) + {//slow down the transitions + if ( entNum == 0 && saberOffenseLevel <= FORCE_LEVEL_2 ) + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; + } + else + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]/2.0f; + } + } + + return; +} +*/ +extern qboolean player_locked; +extern qboolean MatrixMode; +float PM_GetTimeScaleMod( gentity_t *gent ) +{ + if ( g_timescale->value ) + { + if ( !MatrixMode + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_START + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_ATTACK + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_LAND ) + { + if ( gent && gent->s.clientNum == 0 && !player_locked && gent->client->ps.forcePowersActive&(1<value); + } + else if ( gent && gent->client && gent->client->ps.forcePowersActive&(1<value); + } + } + } + return 1.0f; +} + +static inline qboolean PM_IsHumanoid( CGhoul2Info *ghlInfo ) +{ + char *GLAName; + GLAName = gi.G2API_GetGLAName( ghlInfo ); + assert(GLAName); + + if ( !Q_stricmp( "models/players/_humanoid/_humanoid", GLAName ) ) + { + return qtrue; + } + + return qfalse; +} + +/* +------------------------- +PM_SetAnimFinal +------------------------- +*/ +#define G2_DEBUG_TIMING (0) +void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, + int setAnimParts,int anim,int setAnimFlags, + int *torsoAnimTimer,int *legsAnimTimer, + gentity_t *gent,int blendTime) // default blendTime=350 +{ + +// BASIC SETUP AND SAFETY CHECKING +//================================= + + // If It Is A Busted Entity, Don't Do Anything Here. + //--------------------------------------------------- + if (!gent || !gent->client) + { + return; + } + + // Make Sure This Character Has Such An Anim And A Model + //------------------------------------------------------- + if (anim<0 || anim>=MAX_ANIMATIONS || !ValidAnimFileIndex(gent->client->clientInfo.animFileIndex)) + { + #ifndef FINAL_BUILD + if (g_AnimWarning->integer) + { + if (anim<0 || anim>=MAX_ANIMATIONS) + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim Index (%d)!\n", anim); + } + else + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim File Index (%d)!\n", gent->client->clientInfo.animFileIndex); + } + } + #endif + return; + } + + + // Get Global Time Properties + //---------------------------- + float timeScaleMod = PM_GetTimeScaleMod( gent ); + const int actualTime = (cg.time?cg.time:level.time); + const animation_t* animations = level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations; + const animation_t& curAnim = animations[anim]; + + // Make Sure This Character Has Such An Anim And A Model + //------------------------------------------------------- + if (animations[anim].numFrames==0) + { + #ifndef FINAL_BUILD + static int LastAnimWarningNum=0; + if (LastAnimWarningNum!=anim) + { + if ((cg_debugAnim.integer==3) || // 3 = do everyone + (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player + (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else + (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + ) + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Anim %s does not exist in this model (%s)!\n", animTable[anim].name, gent->NPC_type ); + } + } + LastAnimWarningNum = anim; + #endif + return; + } + + // If It's Not A Ghoul 2 Model, Just Remember The Anims And Stop, Because Everything Beyond This Is Ghoul2 + //--------------------------------------------------------------------------------------------------------- + if (!gi.G2API_HaveWeGhoul2Models(gent->ghoul2)) + { + if (setAnimParts&SETANIM_TORSO) + { + (*torsoAnim) = anim; + } + if (setAnimParts&SETANIM_LEGS) + { + (*legsAnim) = anim; + } + return; + } + + + // Lower Offensive Skill Slows Down The Saber Start Attack Animations + //-------------------------------------------------------------------- + PM_SaberStartTransAnim( gent->client->ps.saberAnimLevel, anim, &timeScaleMod, gent ); + + + +// SETUP VALUES FOR INCOMMING ANIMATION +//====================================== + const bool animFootMove = (PM_WalkingAnim(anim) || PM_RunningAnim(anim) || anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK); + const bool animHoldless = (setAnimFlags&SETANIM_FLAG_HOLDLESS)!=0; + const bool animHold = (setAnimFlags&SETANIM_FLAG_HOLD)!=0; + const bool animRestart = (setAnimFlags&SETANIM_FLAG_RESTART)!=0; + const bool animOverride = (setAnimFlags&SETANIM_FLAG_OVERRIDE)!=0; + const bool animSync = (g_synchSplitAnims->integer!=0 && !animRestart); + float animCurrent = (-1.0f); + float animSpeed = (50.0f / curAnim.frameLerp * timeScaleMod); // animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps). + const float animFPS = (fabsf(curAnim.frameLerp)); + const int animDurMSec = (int)(((curAnim.numFrames - 1) * animFPS) / timeScaleMod); + const int animHoldMSec = ((animHoldless && timeScaleMod==1.0f)?((animDurMSec>1)?(animDurMSec-1):(animFPS)):(animDurMSec)); + int animFlags = (curAnim.loopFrames!=-1)?(BONE_ANIM_OVERRIDE_LOOP):(BONE_ANIM_OVERRIDE_FREEZE); + int animStart = (curAnim.firstFrame); + int animEnd = (curAnim.firstFrame)+(animations[anim].numFrames); + + // If We Have A Blend Timer, Add The Blend Flag + //---------------------------------------------- + if (blendTime > 0) + { + animFlags |= BONE_ANIM_BLEND; + } + + // If Animation Is Going Backwards, Swap Last And First Frames + //------------------------------------------------------------- + if (animSpeed<0.0f) + { +// #ifndef FINAL_BUILD + #if 0 + if (g_AnimWarning->integer==1) + { + if (animFlags&BONE_ANIM_OVERRIDE_LOOP) + { + gi.Printf(S_COLOR_YELLOW"PM_SetAnimFinal: WARNING: Anim (%s) looping backwards!\n", animTable[anim].name); + } + } + #endif + + int temp = animEnd; + animEnd = animStart; + animStart = temp; + blendTime = 0; + } + + // If The Animation Is Walking Or Running, Attempt To Scale The Playback Speed To Match + //-------------------------------------------------------------------------------------- + if (g_noFootSlide->integer + && animFootMove + && !(animSpeed<0.0f) + //FIXME: either read speed from animation.cfg or only do this for NPCs + // for whom we've specifically determined the proper numbers! + && gent->client->NPC_class != CLASS_HOWLER + && gent->client->NPC_class != CLASS_WAMPA + && gent->client->NPC_class != CLASS_GONK + && gent->client->NPC_class != CLASS_HOWLER + && gent->client->NPC_class != CLASS_MOUSE + && gent->client->NPC_class != CLASS_PROBE + && gent->client->NPC_class != CLASS_PROTOCOL + && gent->client->NPC_class != CLASS_R2D2 + && gent->client->NPC_class != CLASS_R5D2 + && gent->client->NPC_class != CLASS_SEEKER) + { + bool Walking = !!PM_WalkingAnim(anim); + bool HasDual = (gent->client->ps.saberAnimLevel==SS_DUAL); + bool HasStaff = (gent->client->ps.saberAnimLevel==SS_STAFF); + float moveSpeedOfAnim = 150.0f;//g_noFootSlideRunScale->value; + + if (anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK) + { + moveSpeedOfAnim = 75.0f; + } + else + { + if (gent->client->NPC_class == CLASS_HAZARD_TROOPER) + { + moveSpeedOfAnim = 50.0f; + } + else if (gent->client->NPC_class == CLASS_RANCOR) + { + moveSpeedOfAnim = 173.0f; + } + else + { + if (Walking) + { + if (HasDual || HasStaff) + { + moveSpeedOfAnim = 100.0f; + } + else + { + moveSpeedOfAnim = 50.0f;// g_noFootSlideWalkScale->value; + } + } + else + { + if (HasStaff) + { + moveSpeedOfAnim = 250.0f; + } + else + { + moveSpeedOfAnim = 150.0f; + } + } + } + } + + + + + + + animSpeed *= (gent->resultspeed/moveSpeedOfAnim); + if (animSpeed<0.01f) + { + animSpeed = 0.01f; + } + + // Make Sure Not To Play Too Fast An Anim + //---------------------------------------- + float maxPlaybackSpeed = (1.5f * timeScaleMod); + if (animSpeed>maxPlaybackSpeed) + { + animSpeed = maxPlaybackSpeed; + } + } + + +// GET VALUES FOR EXISTING BODY ANIMATION +//========================================== + float bodySpeed = 0.0f; + float bodyCurrent = 0.0f; + int bodyStart = 0; + int bodyEnd = 0; + int bodyFlags = 0; + int bodyAnim = (*legsAnim); + int bodyBone = (gent->rootBone); + bool bodyTimerOn = ((*legsAnimTimer>0) || (*legsAnimTimer)==-1); + bool bodyPlay = ((setAnimParts&SETANIM_LEGS) && (bodyBone!=-1) && (animOverride || !bodyTimerOn)); + bool bodyAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, actualTime, &bodyCurrent, &bodyStart, &bodyEnd, &bodyFlags, &bodySpeed, NULL); + bool bodyOnAnimNow = (bodyAnimating && bodyAnim==anim && bodyStart==animStart && bodyEnd==animEnd); + bool bodyMatchTorsFrame = false; + + +// GET VALUES FOR EXISTING TORSO ANIMATION +//=========================================== + float torsSpeed = 0.0f; + float torsCurrent = 0.0f; + int torsStart = 0; + int torsEnd = 0; + int torsFlags = 0; + int torsAnim = (*torsoAnim); + int torsBone = (gent->lowerLumbarBone); + bool torsTimerOn = ((*torsoAnimTimer)>0 || (*torsoAnimTimer)==-1); + bool torsPlay = (gent->client->NPC_class!=CLASS_RANCOR && (setAnimParts&SETANIM_TORSO) && (torsBone!=-1) && (animOverride || !torsTimerOn)); + bool torsAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, actualTime, &torsCurrent, &torsStart, &torsEnd, &torsFlags, &torsSpeed, NULL); + bool torsOnAnimNow = (torsAnimating && torsAnim==anim && torsStart==animStart && torsEnd==animEnd); + bool torsMatchBodyFrame = false; + + +// APPLY SYNC TO TORSO +//===================== + if (animSync && torsPlay && !bodyPlay && bodyOnAnimNow && (!torsOnAnimNow || torsCurrent!=bodyCurrent)) + { + torsMatchBodyFrame = true; + animCurrent = bodyCurrent; + } + if (animSync && bodyPlay && !torsPlay && torsOnAnimNow && (!bodyOnAnimNow || bodyCurrent!=torsCurrent)) + { + bodyMatchTorsFrame = true; + animCurrent = torsCurrent; + } + + // If Already Doing These Exact Parameters, Then Don't Play + //---------------------------------------------------------- + if (!animRestart) + { + torsPlay &= !(torsOnAnimNow && torsSpeed==animSpeed && !torsMatchBodyFrame); + bodyPlay &= !(bodyOnAnimNow && bodySpeed==animSpeed && !bodyMatchTorsFrame); + } + +#ifndef FINAL_BUILD + if ((cg_debugAnim.integer==3) || // 3 = do everyone + (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player + (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else + (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + ) + { + if (bodyPlay || torsPlay) + { + char* entName = gent->targetname; + char* location; + + // Select Entity Name + //-------------------- + if (!entName || !entName[0]) + { + entName = gent->NPC_targetname; + } + if (!entName || !entName[0]) + { + entName = gent->NPC_type; + } + if (!entName || !entName[0]) + { + entName = gent->classname; + } + if (!entName || !entName[0]) + { + entName = "UNKNOWN"; + } + + // Select Play Location + //---------------------- + if (bodyPlay && torsPlay) + { + location = "BOTH "; + } + else if (bodyPlay) + { + location = "LEGS "; + } + else + { + location = "TORSO"; + } + + // Print It! + //----------- + Com_Printf("[%10d] ent[%3d-%18s] %s anim[%3d] - %s\n", + actualTime, + gent->s.number, + entName, + location, + anim, + animTable[anim].name ); + } + } +#endif + + +// PLAY ON THE TORSO +//======================== + if (torsPlay) + { + *torsoAnim = anim; + float oldAnimCurrent = animCurrent; + if (animCurrent!=bodyCurrent && torsOnAnimNow && !animRestart && !torsMatchBodyFrame) + { + animCurrent = torsCurrent; + } + + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + if (gent->motionBone!=-1) + { + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->motionBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + } + + animCurrent = oldAnimCurrent; + + // If This Animation Is To Be Locked And Held, Calculate The Duration And Set The Timer + //-------------------------------------------------------------------------------------- + if (animHold || animHoldless) + { + PM_SetTorsoAnimTimer(gent, torsoAnimTimer, animHoldMSec); + } + } + +// PLAY ON THE WHOLE BODY +//======================== + if (bodyPlay) + { + *legsAnim = anim; + + if (bodyOnAnimNow && !animRestart && !bodyMatchTorsFrame) + { + animCurrent = bodyCurrent; + } + + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, + animStart, + animEnd, + (bodyOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + // If This Animation Is To Be Locked And Held, Calculate The Duration And Set The Timer + //-------------------------------------------------------------------------------------- + if (animHold || animHoldless) + { + PM_SetLegsAnimTimer(gent, legsAnimTimer, animHoldMSec); + } + } + + + + + +// PRINT SOME DEBUG TEXT OF EXISTING VALUES +//========================================== + if (false) + { + gi.Printf("PLAYANIM: (%3d) Speed(%4.2f) ", anim, animSpeed); + if (bodyAnimating) + { + gi.Printf("BODY: (%4.2f) (%4.2f) ", bodyCurrent, bodySpeed); + } + else + { + gi.Printf(" "); + } + if (torsAnimating) + { + gi.Printf("TORS: (%4.2f) (%4.2f)\n", torsCurrent, torsSpeed); + } + else + { + gi.Printf("\n"); + } + } +} + + + +void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime) +{ // FIXME : once torsoAnim and legsAnim are in the same structure for NPC and Players + // rename PM_SetAnimFinal to PM_SetAnim and have both NPC and Players call PM_SetAnim + + if ( pm->ps->pm_type >= PM_DEAD ) + {//FIXME: sometimes we'll want to set anims when your dead... twitches, impacts, etc. + return; + } + + if ( pm->gent == NULL ) + { + return; + } + + if ( !pm->gent || pm->gent->health > 0 ) + {//don't lock anims if the guy is dead + if ( pm->ps->torsoAnimTimer + && PM_LockedAnim( pm->ps->torsoAnim ) + && !PM_LockedAnim( anim ) ) + {//nothing can override these special anims + setAnimParts &= ~SETANIM_TORSO; + } + + if ( pm->ps->legsAnimTimer + && PM_LockedAnim( pm->ps->legsAnim ) + && !PM_LockedAnim( anim ) ) + {//nothing can override these special anims + setAnimParts &= ~SETANIM_LEGS; + } + } + + if ( !setAnimParts ) + { + return; + } + + if (setAnimFlags&SETANIM_FLAG_OVERRIDE) + { +// pm->ps->animationTimer = 0; + + if (setAnimParts & SETANIM_TORSO) + { + if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->torsoAnim != anim ) + { + PM_SetTorsoAnimTimer( pm->gent, &pm->ps->torsoAnimTimer, 0 ); + } + } + if (setAnimParts & SETANIM_LEGS) + { + if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->legsAnim != anim ) + { + PM_SetLegsAnimTimer( pm->gent, &pm->ps->legsAnimTimer, 0 ); + } + } + } + + PM_SetAnimFinal(&pm->ps->torsoAnim,&pm->ps->legsAnim,setAnimParts,anim,setAnimFlags,&pm->ps->torsoAnimTimer,&pm->ps->legsAnimTimer,&g_entities[pm->ps->clientNum],blendTime);//was pm->gent +} + +bool TorsoAgainstWindTest( gentity_t* ent ) +{ + if (ent&&//valid ent + ent->client&&//a client + (ent->client->ps.weapon!=WP_SABER||ent->client->ps.saberMove==LS_READY)&&//either not holding a saber or the saber is in the ready pose + (ent->s.numbercurrentOrigin) && + gi.WE_IsOutside(ent->currentOrigin) ) + { + if (Q_stricmp(level.mapname, "t2_wedge")!=0) + { + vec3_t fwd; + vec3_t windDir; + if (gi.WE_GetWindVector(windDir, ent->currentOrigin)) + { + VectorScale(windDir, -1.0f, windDir); + AngleVectors(pm->gent->currentAngles, fwd, 0, 0); + if (DotProduct(fwd, windDir)>0.65f) + { + if (ent->client && ent->client->ps.torsoAnim!=BOTH_WIND) + { + NPC_SetAnim(ent, SETANIM_TORSO, BOTH_WIND, SETANIM_FLAG_NORMAL, 400); + } + return true; + } + } + } + } + return false; +} + +/* +------------------------- +PM_TorsoAnimLightsaber +------------------------- +*/ + + +// Note that this function is intended to set the animation for the player, but +// only does idle-ish anims. Anything that has a timer associated, such as attacks and blocks, +// are set by PM_WeaponLightsaber() + +extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt ); +extern qboolean PM_LandingAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +qboolean PM_InCartwheel( int anim ); +void PM_TorsoAnimLightsaber() +{ + // ********************************************************* + // WEAPON_READY + // ********************************************************* + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + {//holding an enemy aloft with force-grip + return; + } + + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + {//lightning + return; + } + + if ( pm->ps->forcePowersActive&(1<ps->saber[0].blade[0].active + && pm->ps->saber[0].blade[0].length < 3 + && !(pm->ps->saberEventFlags&SEF_HITWALL) + && pm->ps->weaponstate == WEAPON_RAISING ) + { + if (!G_IsRidingVehicle(pm->gent)) + { + PM_SetSaberMove(LS_DRAW); + } + return; + } + else if ( !pm->ps->SaberActive() && pm->ps->SaberLength() ) + { + if (!G_IsRidingVehicle(pm->gent)) + { + PM_SetSaberMove(LS_PUTAWAY); + } + return; + } + + if (pm->ps->weaponTime > 0) + { // weapon is already busy. + if ( pm->ps->torsoAnim == BOTH_TOSS1 + || pm->ps->torsoAnim == BOTH_TOSS2 ) + {//in toss + if ( !pm->ps->torsoAnimTimer ) + {//weird, get out of it, I guess + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + } + return; + } + + if ( pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + {//ready + if ( pm->ps->weapon == WP_SABER && (pm->ps->SaberLength()) ) + {//saber is on + // Select the proper idle Lightsaber attack move from the chart. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( (PM_RunningAnim( pm->ps->legsAnim ) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) + && pm->ps->saberBlockingTime < cg.time ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + /* + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + {//jumping, landing cartwheel, flipping + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetSaberMove( LS_READY ); + } + */ + } + else if (TorsoAgainstWindTest(pm->gent)) + { + } + else if( pm->ps->legsAnim == BOTH_RUN1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN2 )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN_DUAL ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK_DUAL ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + { + //??? Why nothing? What if you were running??? + //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_JUMP1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else + {//Used to default to both_stand1 which is an arms-down anim +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // Select the next proper pose for the lightsaber assuming that there are no attacks. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + } + + // ********************************************************* + // WEAPON_IDLE + // ********************************************************* + + else if ( pm->ps->weaponstate == WEAPON_IDLE ) + { + if (TorsoAgainstWindTest(pm->gent)) + { + } + else if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else + { +// This is now set in SetSaberMove. + // Idle for Lightsaber + if ( pm->gent && pm->gent->client ) + { +// pm->gent->client->saberTrail.inAction = qfalse; + } + + qboolean saberInAir = qtrue; + if ( pm->ps->saberInFlight ) + {//guiding saber + if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + {//we're stuck in a broken parry + saberInAir = qfalse; + } + if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + {// + if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + {//fell to the ground and we're not trying to pull it back + saberInAir = qfalse; + } + } + } + if ( pm->ps->saberInFlight + && saberInAir + && (!pm->ps->dualSabers || !pm->ps->saber[1].Active())) + { + if ( !PM_ForceAnim( pm->ps->torsoAnim ) + || pm->ps->torsoAnimTimer < 300 ) + {//don't interrupt a force power anim + if ( pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer ) + { + PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } + } + else + {//saber is on + // Idle for Lightsaber + if ( pm->gent && pm->gent->client ) + { + if ( !G_InCinematicSaberAnim( pm->gent ) ) + { + pm->gent->client->ps.SaberDeactivateTrail( 0 ); + } + } + // Idle for idle/ready Lightsaber +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONIDLE1 + // Select the proper idle Lightsaber attack move from the chart. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( (PM_RunningAnim( pm->ps->legsAnim ) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) + && pm->ps->saberBlockingTime < cg.time ) + {//running w/1-handed weapon uses full-body anim + int setFlags = SETANIM_FLAG_NORMAL; + if ( PM_LandingAnim( pm->ps->torsoAnim ) ) + { + setFlags = SETANIM_FLAG_OVERRIDE; + } + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,setFlags); + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + } + } + } +} + + + + +/* +------------------------- +PM_TorsoAnimation +------------------------- +*/ + +void PM_TorsoAnimation( void ) +{//FIXME: Write a much smarter and more appropriate anim picking routine logic... +// int oldAnim; + if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps )) + {//in knockdown + return; + } + + if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + { + return; + } + + if ( (pm->ps->eFlags&EF_FORCE_DRAINED) ) + {//being drained + //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + return; + } + if ( (pm->ps->forcePowersActive&(1<ps->forceDrainEntityNum < ENTITYNUM_WORLD ) + {//draining + //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGER1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + return; + } + + if( pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags & SCF_FORCED_MARCH) ) + { + return; + } + + if(pm->gent != NULL && pm->gent->client) + { + pm->gent->client->renderInfo.torsoFpsMod = 1.0f; + } + + if ( pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON ) + { + if ( pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use )//ugly way to tell, but... + {//full body + PM_SetAnim(pm,SETANIM_BOTH,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else + {//torso + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + return; + } +/* else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->ps->clientNum < MAX_CLIENTS && (m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].numHands == 2 || g_speederControlScheme->value == 2) ) + {//can't look around + PM_SetAnim(pm,SETANIM_TORSO,m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].riderAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + return; + }*/ + + if ( pm->ps->taunting > level.time ) + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_ALORA_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else if ( pm->ps->weapon == WP_SABER && pm->ps->saberAnimLevel == SS_DUAL && PM_HasAnimation( pm->gent, BOTH_DUAL_TAUNT ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_DUAL_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else if ( pm->ps->weapon == WP_SABER + && pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->saber[0].type == SABER_STAFF ) + {//turn on the blades + if ( PM_HasAnimation( pm->gent, BOTH_STAFF_TAUNT ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_STAFF_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + /* + else + { + if ( !pm->ps->saber[0].blade[0].active ) + {//first blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 0, qtrue ); + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is also off, extend time of this taunt so we have enough time to turn them both on + pm->ps->taunting = level.time + 3000; + } + } + else if ( (pm->ps->taunting - level.time) < 1500 ) + {//only 1500ms left in taunt + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 1, qtrue ); + } + } + //pose + PM_SetAnim(pm,SETANIM_BOTH,BOTH_SABERSTAFF_STANCE,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + pm->ps->torsoAnimTimer = pm->ps->legsAnimTimer = (pm->ps->taunting - level.time); + } + */ + } + else if ( PM_HasAnimation( pm->gent, BOTH_GESTURE1 ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_GESTURE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + pm->gent->client->ps.SaberActivateTrail( 100 ); + //FIXME: will this reset? + //FIXME: force-control (yellow glow) effect on hand and saber? + } + else + { + //PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL); + } + return; + } + + if (pm->ps->weapon == WP_SABER ) // WP_LIGHTSABER + { + qboolean saberInAir = qfalse; + if ( pm->ps->SaberLength() && !pm->ps->saberInFlight ) + { + PM_TorsoAnimLightsaber(); + } + else + { + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + {//holding an enemy aloft with force-grip + return; + } + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + {//lightning + return; + } + if ( pm->ps->forcePowersActive&(1<ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + {//we're stuck in a broken parry + PM_TorsoAnimLightsaber(); + } + else + { + if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + {// + if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + {//fell to the ground and we're not trying to pull it back + saberInAir = qfalse; + } + } + + if ( pm->ps->saberInFlight + && saberInAir + && (!pm->ps->dualSabers //not using 2 sabers + || !pm->ps->saber[1].Active() //left one off + || pm->ps->torsoAnim == BOTH_SABERDUAL_STANCE//not attacking + || pm->ps->torsoAnim == BOTH_SABERPULL//not attacking + || pm->ps->torsoAnim == BOTH_STAND1//not attacking + || PM_RunningAnim( pm->ps->torsoAnim ) //not attacking + || PM_WalkingAnim( pm->ps->torsoAnim ) //not attacking + || PM_JumpingAnim( pm->ps->torsoAnim )//not attacking + || PM_SwimmingAnim( pm->ps->torsoAnim ) )//not attacking + ) + { + if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 ) + {//don't interrupt a force power anim + if ( pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer ) + { + PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } + } + else + { + if ( PM_InSlopeAnim( pm->ps->legsAnim ) ) + {//HMM... this probably breaks the saber putaway and select anims + if ( pm->ps->SaberLength() > 0 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + } + } + } + } + + if (pm->ps->weaponTime<= 0 && (pm->ps->saberMove==LS_READY || pm->ps->SaberLength()==0) && !saberInAir) + { + TorsoAgainstWindTest(pm->gent); + } + return; + } + + if ( PM_ForceAnim( pm->ps->torsoAnim ) + && pm->ps->torsoAnimTimer > 0 ) + {//in a force anim, don't do a stand anim + return; + } + + + qboolean weaponBusy = qfalse; + + if ( pm->ps->weapon == WP_NONE ) + { + weaponBusy = qfalse; + } + else if ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->lastShotTime > level.time - 3000 ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->weaponTime > 0 ) + { + weaponBusy = qtrue; + } + else if ( pm->gent && pm->gent->client->fireDelay > 0 ) + { + weaponBusy = qtrue; + } + else if ( TorsoAgainstWindTest(pm->gent) ) + { + return; + } + else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.zoomTime > cg.time - 5000 ) + {//if we used binoculars recently, aim weapon + weaponBusy = qtrue; + pm->ps->weaponstate = WEAPON_IDLE; + } + else if ( pm->ps->pm_flags & PMF_DUCKED ) + {//ducking is considered on alert... plus looks stupid to have arms hanging down when crouched + weaponBusy = qtrue; + } + + if ( pm->ps->weapon == WP_NONE || + pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + { + if ( pm->ps->weapon == WP_SABER && pm->ps->SaberLength() ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + } + else if( pm->ps->legsAnim == BOTH_RUN1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN2 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN4 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN_STAFF && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN_DUAL && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK2 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK_STAFF && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK_DUAL&& !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + { + //??? Why nothing? What if you were running??? + //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_JUMP1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL, 100); // Only blend over 100ms + } + else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIMFORWARD && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + } + else if ( pm->ps->weapon == WP_NONE ) + { + int legsAnim = pm->ps->legsAnim; + /* + if ( PM_RollingAnim( legsAnim ) || + PM_FlippingAnim( legsAnim ) || + PM_JumpingAnim( legsAnim ) || + PM_PainAnim( legsAnim ) || + PM_SwimmingAnim( legsAnim ) ) + */ + { + PM_SetAnim(pm, SETANIM_TORSO, legsAnim, SETANIM_FLAG_NORMAL ); + } + } + else + {//Used to default to both_stand1 which is an arms-down anim + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else if ( pm->gent != NULL + && (pm->gent->s.numbergent)) + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT ) + {//PLayer- temp hack for weapon frame + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->ps->weapon == WP_MELEE ) + {//hehe + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + {//use legs anim + //FIXME: or just use whatever's currently playing? + //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); + } + else + { + switch(pm->ps->weapon) + { + // ******************************************************** + case WP_SABER: // WP_LIGHTSABER + // Ready pose for Lightsaber +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // Select the next proper pose for the lightsaber assuming that there are no attacks. + if (pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + break; + // ******************************************************** + + case WP_BRYAR_PISTOL: + //FIXME: if recently fired, hold the ready! + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + break; + case WP_BLASTER_PISTOL: + if ( pm->gent + && pm->gent->weaponModel[1] > 0 ) + {//dual pistols + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + } + else + {//single pistols + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + } + break; + case WP_NONE: + //NOTE: should never get here + break; + case WP_MELEE: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + break; + case WP_TUSKEN_STAFF: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND3, SETANIM_FLAG_NORMAL); + } + break; + + case WP_NOGHRI_STICK: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); + break; + + case WP_BLASTER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); + break; + case WP_DISRUPTOR: + case WP_TUSKEN_RIFLE: + if ( (pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running sniper weapon uses normal ready + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + } + else + { + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );//TORSO_WEAPONREADY4//SETANIM_FLAG_RESTART| + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + } + break; + case WP_BOT_LASER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + break; + case WP_THERMAL: + if ( pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT + && (PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim )) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && (pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT) ) + {//player pulling back to throw + if ( PM_StandingAnim( pm->ps->legsAnim ) ) + { + PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else if ( pm->ps->legsAnim == BOTH_THERMAL_READY ) + {//sigh... hold it so pm_footsteps doesn't override + if ( pm->ps->legsAnimTimer < 100 ) + { + pm->ps->legsAnimTimer = 100; + } + } + PM_SetAnim( pm, SETANIM_TORSO, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY10, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + } + break; + case WP_REPEATER: + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + {// + if ( pm->gent->alt_fire ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY1,SETANIM_FLAG_NORMAL); + } + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + default: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + break; + } + } + } + } + else if ( pm->ps->weaponstate == WEAPON_IDLE ) + { + if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIMFORWARD ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + } + else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + {//use legs anim + //FIXME: or just use whatever's currently playing? + //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); + } + else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( !weaponBusy + && pm->ps->weapon != WP_BOWCASTER + && pm->ps->weapon != WP_REPEATER + && pm->ps->weapon != WP_FLECHETTE + && pm->ps->weapon != WP_ROCKET_LAUNCHER + && pm->ps->weapon != WP_CONCUSSION + && ( PM_RunningAnim( pm->ps->legsAnim ) + || (PM_WalkingAnim( pm->ps->legsAnim ) && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) ) + {//running w/1-handed or light 2-handed weapon uses full-body anim if you're not using the weapon right now + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + switch ( pm->ps->weapon ) + { + // ******************************************************** + case WP_SABER: // WP_LIGHTSABER + // Shouldn't get here, should go to TorsoAnimLightsaber + break; + // ******************************************************** + + case WP_BRYAR_PISTOL: + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + } + break; + case WP_BLASTER_PISTOL: + if ( pm->gent + && pm->gent->weaponModel[1] > 0 ) + {//dual pistols + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else + {//single pistols + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + } + } + break; + + case WP_NONE: + //NOTE: should never get here + break; + + case WP_MELEE: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + break; + + case WP_TUSKEN_STAFF: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND3, SETANIM_FLAG_NORMAL); + } + break; + + case WP_NOGHRI_STICK: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_BLASTER: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_DISRUPTOR: + case WP_TUSKEN_RIFLE: + if ( (pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running sniper weapon uses normal ready + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + } + else + { + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + } + break; + + case WP_BOT_LASER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + break; + + case WP_THERMAL: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE10, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + + case WP_REPEATER: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + + default: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + } + } + } + } +} + +//========================================================================= +// Anim checking utils +//========================================================================= + +int PM_GetTurnAnim( gentity_t *gent, int anim ) +{ + if ( !gent ) + { + return -1; + } + + switch( anim ) + { + case BOTH_STAND1: //# Standing idle: no weapon: hands down + case BOTH_STAND1IDLE1: //# Random standing idle + case BOTH_STAND2: //# Standing idle with a weapon + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle + case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle + case BOTH_STAND4: //# two handed: gun down: relaxed stand + case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle + case BOTH_STAND6: //# one handed: gun at side: relaxed stand + case BOTH_STAND2TO4: //# Transition from stand2 to stand4 + case BOTH_STAND4TO2: //# Transition from stand4 to stand2 + case BOTH_GESTURE1: //# Generic gesture: non-specific + case BOTH_GESTURE2: //# Generic gesture: non-specific + case BOTH_TALK1: //# Generic talk anim + case BOTH_TALK2: //# Generic talk anim + if ( PM_HasAnimation( gent, LEGS_TURN1 ) ) + { + return LEGS_TURN1; + } + else + { + return -1; + } + break; + case BOTH_ATTACK1: //# Attack with generic 1-handed weapon + case BOTH_ATTACK2: //# Attack with generic 2-handed weapon + case BOTH_ATTACK3: //# Attack with heavy 2-handed weapon + case BOTH_ATTACK4: //# Attack with ??? + case BOTH_MELEE1: //# First melee attack + case BOTH_MELEE2: //# Second melee attack + case BOTH_GUARD_LOOKAROUND1: //# Cradling weapon and looking around + case BOTH_GUARD_IDLE1: //# Cradling weapon and standing + if ( PM_HasAnimation( gent, LEGS_TURN2 ) ) + { + return LEGS_TURN2; + } + else + { + return -1; + } + break; + default: + return -1; + break; + } +} + +int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) +{ + if ( !gent ) + { + return -1; + } + + switch( anim ) + { + case BOTH_STAND1: //# Standing idle: no weapon: hands down + case BOTH_STAND1IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND1 ) ) + { + return BOTH_TURNSTAND1; + } + else + { + return -1; + } + break; + case BOTH_STAND2: //# Standing idle with a weapon + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND2 ) ) + { + return BOTH_TURNSTAND2; + } + else + { + return -1; + } + break; + case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND3 ) ) + { + return BOTH_TURNSTAND3; + } + else + { + return -1; + } + break; + case BOTH_STAND4: //# two handed: gun down: relaxed stand + if ( PM_HasAnimation( gent, BOTH_TURNSTAND4 ) ) + { + return BOTH_TURNSTAND4; + } + else + { + return -1; + } + break; + case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND5 ) ) + { + return BOTH_TURNSTAND5; + } + else + { + return -1; + } + break; + case BOTH_CROUCH1: //# Transition from standing to crouch + case BOTH_CROUCH1IDLE: //# Crouching idle + /* + case BOTH_UNCROUCH1: //# Transition from crouch to standing + case BOTH_CROUCH2TOSTAND1: //# going from crouch2 to stand1 + case BOTH_CROUCH3: //# Desann crouching down to Kyle (cin 9) + case BOTH_UNCROUCH3: //# Desann uncrouching down to Kyle (cin 9) + case BOTH_CROUCH4: //# Slower version of crouch1 for cinematics + case BOTH_UNCROUCH4: //# Slower version of uncrouch1 for cinematics + */ + if ( PM_HasAnimation( gent, BOTH_TURNCROUCH1 ) ) + { + return BOTH_TURNCROUCH1; + } + else + { + return -1; + } + break; + default: + return -1; + break; + } +} + +qboolean PM_InOnGroundAnim ( playerState_t *ps ) +{ + switch( ps->legsAnim ) + { + case BOTH_DEAD1: + case BOTH_DEAD2: + case BOTH_DEAD3: + case BOTH_DEAD4: + case BOTH_DEAD5: + case BOTH_DEADFORWARD1: + case BOTH_DEADBACKWARD1: + case BOTH_DEADFORWARD2: + case BOTH_DEADBACKWARD2: + case BOTH_LYINGDEATH1: + case BOTH_LYINGDEAD1: + case BOTH_SLEEP1: //# laying on back-rknee up-rhand on torso + return qtrue; + break; + case BOTH_KNOCKDOWN1: //# + case BOTH_KNOCKDOWN2: //# + case BOTH_KNOCKDOWN3: //# + case BOTH_KNOCKDOWN4: //# + case BOTH_KNOCKDOWN5: //# + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_RELEASED: + if ( ps->legsAnimTimer < 500 ) + {//pretty much horizontal by this point + return qtrue; + } + break; + case BOTH_PLAYER_PA_3_FLY: + if ( ps->legsAnimTimer < 300 ) + {//pretty much horizontal by this point + return qtrue; + } + /* + else if ( ps->clientNum < MAX_CLIENTS + && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) + { + return qtrue; + } + */ + break; + case BOTH_GETUP1: + case BOTH_GETUP2: + case BOTH_GETUP3: + case BOTH_GETUP4: + case BOTH_GETUP5: + case BOTH_GETUP_CROUCH_F1: + case BOTH_GETUP_CROUCH_B1: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + if ( ps->legsAnimTimer > PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim )-400 ) + {//still pretty much horizontal at this point + return qtrue; + } + break; + } + + return qfalse; +} + +qboolean PM_InSpecialDeathAnim( int anim ) +{ + switch( pm->ps->legsAnim ) + { + case BOTH_DEATH_ROLL: //# Death anim from a roll + case BOTH_DEATH_FLIP: //# Death anim from a flip + case BOTH_DEATH_SPIN_90_R: //# Death anim when facing 90 degrees right + case BOTH_DEATH_SPIN_90_L: //# Death anim when facing 90 degrees left + case BOTH_DEATH_SPIN_180: //# Death anim when facing backwards + case BOTH_DEATH_LYING_UP: //# Death anim when lying on back + case BOTH_DEATH_LYING_DN: //# Death anim when lying on front + case BOTH_DEATH_FALLING_DN: //# Death anim when falling on face + case BOTH_DEATH_FALLING_UP: //# Death anim when falling on back + case BOTH_DEATH_CROUCHED: //# Death anim when crouched + return qtrue; + break; + default: + return qfalse; + break; + } +} + +qboolean PM_InDeathAnim ( void ) +{//Purposely does not cover stumbledeath and falldeath... + switch( pm->ps->legsAnim ) + { + case BOTH_DEATH1: //# First Death anim + case BOTH_DEATH2: //# Second Death anim + case BOTH_DEATH3: //# Third Death anim + case BOTH_DEATH4: //# Fourth Death anim + case BOTH_DEATH5: //# Fifth Death anim + case BOTH_DEATH6: //# Sixth Death anim + case BOTH_DEATH7: //# Seventh Death anim + case BOTH_DEATH8: //# + case BOTH_DEATH9: //# + case BOTH_DEATH10: //# + case BOTH_DEATH11: //# + case BOTH_DEATH12: //# + case BOTH_DEATH13: //# + case BOTH_DEATH14: //# + case BOTH_DEATH14_UNGRIP: //# Desann's end death (cin #35) + case BOTH_DEATH14_SITUP: //# Tavion sitting up after having been thrown (cin #23) + case BOTH_DEATH15: //# + case BOTH_DEATH16: //# + case BOTH_DEATH17: //# + case BOTH_DEATH18: //# + case BOTH_DEATH19: //# + case BOTH_DEATH20: //# + case BOTH_DEATH21: //# + case BOTH_DEATH22: //# + case BOTH_DEATH23: //# + case BOTH_DEATH24: //# + case BOTH_DEATH25: //# + + case BOTH_DEATHFORWARD1: //# First Death in which they get thrown forward + case BOTH_DEATHFORWARD2: //# Second Death in which they get thrown forward + case BOTH_DEATHFORWARD3: //# Tavion's falling in cin# 23 + case BOTH_DEATHBACKWARD1: //# First Death in which they get thrown backward + case BOTH_DEATHBACKWARD2: //# Second Death in which they get thrown backward + + case BOTH_DEATH1IDLE: //# Idle while close to death + case BOTH_LYINGDEATH1: //# Death to play when killed lying down + case BOTH_STUMBLEDEATH1: //# Stumble forward and fall face first death + case BOTH_FALLDEATH1: //# Fall forward off a high cliff and splat death - start + case BOTH_FALLDEATH1INAIR: //# Fall forward off a high cliff and splat death - loop + case BOTH_FALLDEATH1LAND: //# Fall forward off a high cliff and splat death - hit bottom + //# #sep case BOTH_ DEAD POSES # Should be last frame of corresponding previous anims + case BOTH_DEAD1: //# First Death finished pose + case BOTH_DEAD2: //# Second Death finished pose + case BOTH_DEAD3: //# Third Death finished pose + case BOTH_DEAD4: //# Fourth Death finished pose + case BOTH_DEAD5: //# Fifth Death finished pose + case BOTH_DEAD6: //# Sixth Death finished pose + case BOTH_DEAD7: //# Seventh Death finished pose + case BOTH_DEAD8: //# + case BOTH_DEAD9: //# + case BOTH_DEAD10: //# + case BOTH_DEAD11: //# + case BOTH_DEAD12: //# + case BOTH_DEAD13: //# + case BOTH_DEAD14: //# + case BOTH_DEAD15: //# + case BOTH_DEAD16: //# + case BOTH_DEAD17: //# + case BOTH_DEAD18: //# + case BOTH_DEAD19: //# + case BOTH_DEAD20: //# + case BOTH_DEAD21: //# + case BOTH_DEAD22: //# + case BOTH_DEAD23: //# + case BOTH_DEAD24: //# + case BOTH_DEAD25: //# + case BOTH_DEADFORWARD1: //# First thrown forward death finished pose + case BOTH_DEADFORWARD2: //# Second thrown forward death finished pose + case BOTH_DEADBACKWARD1: //# First thrown backward death finished pose + case BOTH_DEADBACKWARD2: //# Second thrown backward death finished pose + case BOTH_LYINGDEAD1: //# Killed lying down death finished pose + case BOTH_STUMBLEDEAD1: //# Stumble forward death finished pose + case BOTH_FALLDEAD1LAND: //# Fall forward and splat death finished pose + //# #sep case BOTH_ DEAD TWITCH/FLOP # React to being shot from death poses + case BOTH_DEADFLOP1: //# React to being shot from First Death finished pose + case BOTH_DEADFLOP2: //# React to being shot from Second Death finished pose + case BOTH_DISMEMBER_HEAD1: //# + case BOTH_DISMEMBER_TORSO1: //# + case BOTH_DISMEMBER_LLEG: //# + case BOTH_DISMEMBER_RLEG: //# + case BOTH_DISMEMBER_RARM: //# + case BOTH_DISMEMBER_LARM: //# + return qtrue; + break; + default: + return PM_InSpecialDeathAnim( pm->ps->legsAnim ); + break; + } +} + +qboolean PM_InCartwheel( int anim ) +{ + switch ( anim ) + { + case BOTH_ARIAL_LEFT: + case BOTH_ARIAL_RIGHT: + case BOTH_ARIAL_F1: + case BOTH_CARTWHEEL_LEFT: + case BOTH_CARTWHEEL_RIGHT: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_InButterfly( int anim ) +{ + switch ( anim ) + { + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_StandingAnim( int anim ) +{//NOTE: does not check idles or special (cinematic) stands + switch ( anim ) + { + case BOTH_STAND1: + case BOTH_STAND2: + case BOTH_STAND3: + case BOTH_STAND4: + case BOTH_ATTACK3: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_InAirKickingAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + return qtrue; + } + return qfalse; +} + +qboolean PM_KickingAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + //NOT a kick, but acts like one: + case BOTH_A7_HILT: + //NOT kicks, but do kick traces anyway + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + return qtrue; + break; + default: + return PM_InAirKickingAnim( anim ); + break; + } + //return qfalse; +} + +qboolean PM_StabDownAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + return qtrue; + } + return qfalse; +} + +qboolean PM_GoingToAttackDown( playerState_t *ps ) +{ + if ( PM_StabDownAnim( ps->torsoAnim )//stabbing downward + || ps->saberMove == LS_A_LUNGE//lunge + || ps->saberMove == LS_A_JUMP_T__B_//death from above + || ps->saberMove == LS_A_T2B//attacking top to bottom + || ps->saberMove == LS_S_T2B//starting at attack downward + || (PM_SaberInTransition( ps->saberMove ) && saberMoveData[ps->saberMove].endQuad == Q_T) )//transitioning to a top to bottom attack + { + return qtrue; + } + return qfalse; +} + +qboolean PM_ForceUsingSaberAnim( int anim ) +{//saber/acrobatic anims that should prevent you from recharging force power while you're in them... + switch ( anim ) + { + case BOTH_JUMPFLIPSLASHDOWN1: + case BOTH_JUMPFLIPSTABDOWN: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_FORCELONGLEAP_START: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_FORCEWALLRUNFLIP_START: + case BOTH_FORCEWALLRUNFLIP_END: + case BOTH_FORCEWALLRUNFLIP_ALT: + case BOTH_FORCEWALLREBOUND_FORWARD: + case BOTH_FORCEWALLREBOUND_LEFT: + case BOTH_FORCEWALLREBOUND_BACK: + case BOTH_FORCEWALLREBOUND_RIGHT: + case BOTH_FLIP_ATTACK7: + case BOTH_FLIP_HOLD7: + case BOTH_FLIP_LAND: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_ARIAL_LEFT: + case BOTH_ARIAL_RIGHT: + case BOTH_CARTWHEEL_LEFT: + case BOTH_CARTWHEEL_RIGHT: + case BOTH_FLIP_LEFT: + case BOTH_FLIP_BACK1: + case BOTH_FLIP_BACK2: + case BOTH_FLIP_BACK3: + case BOTH_ALORA_FLIP_B: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_WALL_RUN_RIGHT: + case BOTH_WALL_RUN_RIGHT_FLIP: + case BOTH_WALL_RUN_RIGHT_STOP: + case BOTH_WALL_RUN_LEFT: + case BOTH_WALL_RUN_LEFT_FLIP: + case BOTH_WALL_RUN_LEFT_STOP: + case BOTH_WALL_FLIP_RIGHT: + case BOTH_WALL_FLIP_LEFT: + case BOTH_FORCEJUMP1: + case BOTH_FORCEINAIR1: + case BOTH_FORCELAND1: + case BOTH_FORCEJUMPBACK1: + case BOTH_FORCEINAIRBACK1: + case BOTH_FORCELANDBACK1: + case BOTH_FORCEJUMPLEFT1: + case BOTH_FORCEINAIRLEFT1: + case BOTH_FORCELANDLEFT1: + case BOTH_FORCEJUMPRIGHT1: + case BOTH_FORCEINAIRRIGHT1: + case BOTH_FORCELANDRIGHT1: + case BOTH_FLIP_F: + case BOTH_FLIP_B: + case BOTH_FLIP_L: + case BOTH_FLIP_R: + case BOTH_ALORA_FLIP_1: + case BOTH_ALORA_FLIP_2: + case BOTH_ALORA_FLIP_3: + case BOTH_DODGE_FL: + case BOTH_DODGE_FR: + case BOTH_DODGE_BL: + case BOTH_DODGE_BR: + case BOTH_DODGE_L: + case BOTH_DODGE_R: + case BOTH_DODGE_HOLD_FL: + case BOTH_DODGE_HOLD_FR: + case BOTH_DODGE_HOLD_BL: + case BOTH_DODGE_HOLD_BR: + case BOTH_DODGE_HOLD_L: + case BOTH_DODGE_HOLD_R: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_BROLL_L: + case BOTH_GETUP_BROLL_R: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + case BOTH_GETUP_FROLL_L: + case BOTH_GETUP_FROLL_R: + case BOTH_WALL_FLIP_BACK1: + case BOTH_WALL_FLIP_BACK2: + case BOTH_SPIN1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_DEFLECTSLASH__R__L_FIN: + case BOTH_ARIAL_F1: + return qtrue; + } + return qfalse; +} + +qboolean G_HasKnockdownAnims( gentity_t *ent ) +{ + if ( PM_HasAnimation( ent, BOTH_KNOCKDOWN1 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN2 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN3 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN4 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN5 ) ) + { + return qtrue; + } + return qfalse; +} + +qboolean PM_InAttackRoll( int anim ) +{ + switch ( anim ) + { + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + return qtrue; + } + return qfalse; +} + +qboolean PM_LockedAnim( int anim ) +{//anims that can *NEVER* be overridden, regardless + switch ( anim ) + { + case BOTH_KYLE_PA_1: + case BOTH_KYLE_PA_2: + case BOTH_KYLE_PA_3: + case BOTH_PLAYER_PA_1: + case BOTH_PLAYER_PA_2: + case BOTH_PLAYER_PA_3: + case BOTH_PLAYER_PA_3_FLY: + case BOTH_TAVION_SCEPTERGROUND: + case BOTH_TAVION_SWORDPOWER: + case BOTH_SCEPTER_START: + case BOTH_SCEPTER_HOLD: + case BOTH_SCEPTER_STOP: + //grabbed by wampa + case BOTH_GRABBED: //# + case BOTH_RELEASED: //# when Wampa drops player, transitions into fall on back + case BOTH_HANG_IDLE: //# + case BOTH_HANG_ATTACK: //# + case BOTH_HANG_PAIN: //# + return qtrue; + } + return qfalse; +} + +qboolean PM_SuperBreakLoseAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_LK_S_DL_S_SB_1_L: //super break I lost + case BOTH_LK_S_DL_T_SB_1_L: //super break I lost + case BOTH_LK_S_ST_S_SB_1_L: //super break I lost + case BOTH_LK_S_ST_T_SB_1_L: //super break I lost + case BOTH_LK_S_S_S_SB_1_L: //super break I lost + case BOTH_LK_S_S_T_SB_1_L: //super break I lost + case BOTH_LK_DL_DL_S_SB_1_L: //super break I lost + case BOTH_LK_DL_DL_T_SB_1_L: //super break I lost + case BOTH_LK_DL_ST_S_SB_1_L: //super break I lost + case BOTH_LK_DL_ST_T_SB_1_L: //super break I lost + case BOTH_LK_DL_S_S_SB_1_L: //super break I lost + case BOTH_LK_DL_S_T_SB_1_L: //super break I lost + case BOTH_LK_ST_DL_S_SB_1_L: //super break I lost + case BOTH_LK_ST_DL_T_SB_1_L: //super break I lost + case BOTH_LK_ST_ST_S_SB_1_L: //super break I lost + case BOTH_LK_ST_ST_T_SB_1_L: //super break I lost + case BOTH_LK_ST_S_S_SB_1_L: //super break I lost + case BOTH_LK_ST_S_T_SB_1_L: //super break I lost + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_SuperBreakWinAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_LK_S_DL_S_SB_1_W: //super break I won + case BOTH_LK_S_DL_T_SB_1_W: //super break I won + case BOTH_LK_S_ST_S_SB_1_W: //super break I won + case BOTH_LK_S_ST_T_SB_1_W: //super break I won + case BOTH_LK_S_S_S_SB_1_W: //super break I won + case BOTH_LK_S_S_T_SB_1_W: //super break I won + case BOTH_LK_DL_DL_S_SB_1_W: //super break I won + case BOTH_LK_DL_DL_T_SB_1_W: //super break I won + case BOTH_LK_DL_ST_S_SB_1_W: //super break I won + case BOTH_LK_DL_ST_T_SB_1_W: //super break I won + case BOTH_LK_DL_S_S_SB_1_W: //super break I won + case BOTH_LK_DL_S_T_SB_1_W: //super break I won + case BOTH_LK_ST_DL_S_SB_1_W: //super break I won + case BOTH_LK_ST_DL_T_SB_1_W: //super break I won + case BOTH_LK_ST_ST_S_SB_1_W: //super break I won + case BOTH_LK_ST_ST_T_SB_1_W: //super break I won + case BOTH_LK_ST_S_S_SB_1_W: //super break I won + case BOTH_LK_ST_S_T_SB_1_W: //super break I won + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_SaberLockBreakAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_BF1BREAK: + case BOTH_BF2BREAK: + case BOTH_CWCIRCLEBREAK: + case BOTH_CCWCIRCLEBREAK: + case BOTH_LK_S_DL_S_B_1_L: //normal break I lost + case BOTH_LK_S_DL_S_B_1_W: //normal break I won + case BOTH_LK_S_DL_T_B_1_L: //normal break I lost + case BOTH_LK_S_DL_T_B_1_W: //normal break I won + case BOTH_LK_S_ST_S_B_1_L: //normal break I lost + case BOTH_LK_S_ST_S_B_1_W: //normal break I won + case BOTH_LK_S_ST_T_B_1_L: //normal break I lost + case BOTH_LK_S_ST_T_B_1_W: //normal break I won + case BOTH_LK_S_S_S_B_1_L: //normal break I lost + case BOTH_LK_S_S_S_B_1_W: //normal break I won + case BOTH_LK_S_S_T_B_1_L: //normal break I lost + case BOTH_LK_S_S_T_B_1_W: //normal break I won + case BOTH_LK_DL_DL_S_B_1_L: //normal break I lost + case BOTH_LK_DL_DL_S_B_1_W: //normal break I won + case BOTH_LK_DL_DL_T_B_1_L: //normal break I lost + case BOTH_LK_DL_DL_T_B_1_W: //normal break I won + case BOTH_LK_DL_ST_S_B_1_L: //normal break I lost + case BOTH_LK_DL_ST_S_B_1_W: //normal break I won + case BOTH_LK_DL_ST_T_B_1_L: //normal break I lost + case BOTH_LK_DL_ST_T_B_1_W: //normal break I won + case BOTH_LK_DL_S_S_B_1_L: //normal break I lost + case BOTH_LK_DL_S_S_B_1_W: //normal break I won + case BOTH_LK_DL_S_T_B_1_L: //normal break I lost + case BOTH_LK_DL_S_T_B_1_W: //normal break I won + case BOTH_LK_ST_DL_S_B_1_L: //normal break I lost + case BOTH_LK_ST_DL_S_B_1_W: //normal break I won + case BOTH_LK_ST_DL_T_B_1_L: //normal break I lost + case BOTH_LK_ST_DL_T_B_1_W: //normal break I won + case BOTH_LK_ST_ST_S_B_1_L: //normal break I lost + case BOTH_LK_ST_ST_S_B_1_W: //normal break I won + case BOTH_LK_ST_ST_T_B_1_L: //normal break I lost + case BOTH_LK_ST_ST_T_B_1_W: //normal break I won + case BOTH_LK_ST_S_S_B_1_L: //normal break I lost + case BOTH_LK_ST_S_S_B_1_W: //normal break I won + case BOTH_LK_ST_S_T_B_1_L: //normal break I lost + case BOTH_LK_ST_S_T_B_1_W: //normal break I won + return (PM_SuperBreakLoseAnim(anim)||PM_SuperBreakWinAnim(anim)); + break; + } + return qfalse; +} + +qboolean PM_GetupAnimNoMove( int legsAnim ) +{ + switch( legsAnim ) + { + case BOTH_GETUP1: + case BOTH_GETUP2: + case BOTH_GETUP3: + case BOTH_GETUP4: + case BOTH_GETUP5: + case BOTH_GETUP_CROUCH_F1: + case BOTH_GETUP_CROUCH_B1: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + return qtrue; + } + return qfalse; +} + +qboolean PM_KnockDownAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_KNOCKDOWN1: + case BOTH_KNOCKDOWN2: + case BOTH_KNOCKDOWN3: + case BOTH_KNOCKDOWN4: + case BOTH_KNOCKDOWN5: + /* + //special anims: + case BOTH_RELEASED: + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_PLAYER_PA_3_FLY: + */ + return qtrue; + break; + /* + default: + return PM_InGetUp( ps ); + break; + */ + } + return qfalse; +} + +qboolean PM_KnockDownAnimExtended( int anim ) +{ + switch ( anim ) + { + case BOTH_KNOCKDOWN1: + case BOTH_KNOCKDOWN2: + case BOTH_KNOCKDOWN3: + case BOTH_KNOCKDOWN4: + case BOTH_KNOCKDOWN5: + //special anims: + case BOTH_RELEASED: + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_PLAYER_PA_3_FLY: + return qtrue; + break; + /* + default: + return PM_InGetUp( ps ); + break; + */ + } + return qfalse; +} + +qboolean PM_SaberInKata( saberMoveName_t saberMove ) +{ + switch ( saberMove ) + { + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + return qtrue; + default: + break; + } + return qfalse; +} + +qboolean PM_CanRollFromSoulCal( playerState_t *ps ) +{ + if ( ps->legsAnim == BOTH_A7_SOULCAL + && ps->legsAnimTimer < 700 + && ps->legsAnimTimer > 250 ) + { + return qtrue; + } + return qfalse; +} + +qboolean BG_FullBodyTauntAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_GESTURE1: + case BOTH_DUAL_TAUNT: + case BOTH_STAFF_TAUNT: + case BOTH_BOW: + case BOTH_MEDITATE: + case BOTH_SHOWOFF_FAST: + case BOTH_SHOWOFF_MEDIUM: + case BOTH_SHOWOFF_STRONG: + case BOTH_SHOWOFF_DUAL: + case BOTH_SHOWOFF_STAFF: + case BOTH_VICTORY_FAST: + case BOTH_VICTORY_MEDIUM: + case BOTH_VICTORY_STRONG: + case BOTH_VICTORY_DUAL: + case BOTH_VICTORY_STAFF: + return qtrue; + break; + } + return qfalse; +} diff --git a/code/game/bg_panimate.cpp.REMOTE.8780.cpp b/code/game/bg_panimate.cpp.REMOTE.8780.cpp new file mode 100644 index 0000000000..48b23f04f6 --- /dev/null +++ b/code/game/bg_panimate.cpp.REMOTE.8780.cpp @@ -0,0 +1,7258 @@ +/* +=========================================================================== +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +#include "common_headers.h" + + +// define GAME_INCLUDE so that g_public.h does not define the +// short, server-visible gclient_t and gentity_t structures, +// because we define the full size ones in this file +#define GAME_INCLUDE + +#include "../qcommon/q_shared.h" +#include "g_shared.h" +#include "bg_local.h" +#include "../cgame/cg_local.h" +#include "anims.h" +#include "Q3_Interface.h" +#include "g_local.h" +#include "wp_saber.h" +#include "g_vehicles.h" + +extern pmove_t *pm; +extern pml_t pml; +extern cvar_t *g_ICARUSDebug; +extern cvar_t *g_timescale; +extern cvar_t *g_synchSplitAnims; +extern cvar_t *g_AnimWarning; +extern cvar_t *g_noFootSlide; +extern cvar_t *g_noFootSlideRunScale; +extern cvar_t *g_noFootSlideWalkScale; +extern cvar_t *g_saberAnimSpeed; +extern cvar_t *g_saberAutoAim; +extern cvar_t *g_speederControlScheme; +extern cvar_t *g_saberNewControlScheme; + +extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); +extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); +extern qboolean ValidAnimFileIndex ( int index ); +extern qboolean PM_ControlledByPlayer( void ); +extern qboolean PM_DroidMelee( int npc_class ); +extern qboolean PM_PainAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +extern qboolean PM_FlippingAnim( int anim ); +extern qboolean PM_RollingAnim( int anim ); +extern qboolean PM_SwimmingAnim( int anim ); +extern qboolean PM_InKnockDown( playerState_t *ps ); +extern qboolean PM_InRoll( playerState_t *ps ); +extern qboolean PM_DodgeAnim( int anim ); +extern qboolean PM_InSlopeAnim( int anim ); +extern qboolean PM_ForceAnim( int anim ); +extern qboolean PM_InKnockDownOnGround( playerState_t *ps ); +extern qboolean PM_InSpecialJump( int anim ); +extern qboolean PM_RunningAnim( int anim ); +extern qboolean PM_WalkingAnim( int anim ); +extern qboolean PM_SwimmingAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +extern qboolean PM_SaberStanceAnim( int anim ); +extern qboolean PM_SaberDrawPutawayAnim( int anim ); +extern void PM_SetJumped( float height, qboolean force ); +extern qboolean PM_InGetUpNoRoll( playerState_t *ps ); +extern qboolean PM_CrouchAnim( int anim ); +extern qboolean G_TryingKataAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingCartwheel( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingSpecial( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingJumpAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingJumpForwardAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingLungeAttack( gentity_t *self, usercmd_t *cmd ); +extern qboolean G_TryingPullAttack( gentity_t *self, usercmd_t *cmd, qboolean amPulling ); +extern qboolean G_InCinematicSaberAnim( gentity_t *self ); +extern qboolean G_ControlledByPlayer( gentity_t *self ); + +extern int g_crosshairEntNum; + +int PM_AnimLength( int index, animNumber_t anim ); +qboolean PM_LockedAnim( int anim ); +qboolean PM_StandingAnim( int anim ); +qboolean PM_InOnGroundAnim ( playerState_t *ps ); +qboolean PM_SuperBreakWinAnim( int anim ); +qboolean PM_SuperBreakLoseAnim( int anim ); +qboolean PM_LockedAnim( int anim ); +saberMoveName_t PM_SaberFlipOverAttackMove( void ); +qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ); +saberMoveName_t PM_SaberJumpForwardAttackMove( void ); +qboolean PM_CheckJumpForwardAttackMove( void ); +saberMoveName_t PM_SaberBackflipAttackMove( void ); +qboolean PM_CheckBackflipAttackMove( void ); +saberMoveName_t PM_SaberDualJumpAttackMove( void ); +qboolean PM_CheckDualJumpAttackMove( void ); +saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ); +qboolean PM_CheckLungeAttackMove( void ); +// Okay, here lies the much-dreaded Pat-created FSM movement chart... Heretic II strikes again! +// Why am I inflicting this on you? Well, it's better than hardcoded states. +// Ideally this will be replaced with an external file or more sophisticated move-picker +// once the game gets out of prototype stage. + +// Silly, but I'm replacing these macros so they are shorter! +#define AFLAG_IDLE (SETANIM_FLAG_NORMAL) +#define AFLAG_ACTIVE (SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS) +#define AFLAG_WAIT (SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS) +#define AFLAG_FINISH (SETANIM_FLAG_HOLD) + +//FIXME: add the alternate anims for each style? +saberMoveData_t saberMoveData[LS_MOVE_MAX] = {// NB:randomized + // name anim(do all styles?)startQ endQ setanimflag blend, blocking chain_idle chain_attack trailLen + {"None", BOTH_STAND1, Q_R, Q_R, AFLAG_IDLE, 350, BLK_NO, LS_NONE, LS_NONE, 0 }, // LS_NONE = 0, + + // General movements with saber + {"Ready", BOTH_STAND2, Q_R, Q_R, AFLAG_IDLE, 350, BLK_WIDE, LS_READY, LS_S_R2L, 0 }, // LS_READY, + {"Draw", BOTH_STAND1TO2, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_DRAW, + {"Putaway", BOTH_STAND2TO1, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_PUTAWAY, + + // Attacks + //UL2LR + {"TL2BR Att", BOTH_A1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TL2BR, LS_R_TL2BR, 200 }, // LS_A_TL2BR + //SLASH LEFT + {"L2R Att", BOTH_A1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_L2R, LS_R_L2R, 200 }, // LS_A_L2R + //LL2UR + {"BL2TR Att", BOTH_A1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_TIGHT, LS_R_BL2TR, LS_R_BL2TR, 200 }, // LS_A_BL2TR + //LR2UL + {"BR2TL Att", BOTH_A1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_BR2TL, LS_R_BR2TL, 200 }, // LS_A_BR2TL + //SLASH RIGHT + {"R2L Att", BOTH_A1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_R2L, LS_R_R2L, 200 },// LS_A_R2L + //UR2LL + {"TR2BL Att", BOTH_A1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TR2BL, LS_R_TR2BL, 200 }, // LS_A_TR2BL + //SLASH DOWN + {"T2B Att", BOTH_A1_T__B_, Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_T2B, LS_R_T2B, 200 }, // LS_A_T2B + //special attacks + {"Back Stab", BOTH_A2_STABBACK1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACKSTAB + {"Back Att", BOTH_ATTACK_BACK, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK + {"CR Back Att", BOTH_CROUCHATTACKBACK1,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK_CR + {"RollStab", BOTH_ROLL_STAB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_ROLL_STAB + {"Lunge Att", BOTH_LUNGE2_B__T_, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_LUNGE + {"Jump Att", BOTH_FORCELEAP2_T__B_,Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_JUMP_T__B_ + {"Flip Stab", BOTH_JUMPFLIPSTABDOWN,Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_FLIP_STAB + {"Flip Slash", BOTH_JUMPFLIPSLASHDOWN1,Q_L,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R_T_, 200 }, // LS_A_FLIP_SLASH + {"DualJump Atk",BOTH_JUMPATTACK6, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_BL_TR, 200 }, // LS_JUMPATTACK_DUAL + + {"DualJumpAtkL_A",BOTH_ARIAL_LEFT, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TL2BR, 200 }, // LS_JUMPATTACK_ARIAL_LEFT + {"DualJumpAtkR_A",BOTH_ARIAL_RIGHT, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TR2BL, 200 }, // LS_JUMPATTACK_ARIAL_RIGHT + + {"DualJumpAtkL_A",BOTH_CARTWHEEL_LEFT, Q_R,Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TL_BR, 200 }, // LS_JUMPATTACK_CART_LEFT + {"DualJumpAtkR_A",BOTH_CARTWHEEL_RIGHT, Q_R,Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TR_BL, 200 }, // LS_JUMPATTACK_CART_RIGHT + + {"DualJumpAtkLStaff", BOTH_BUTTERFLY_FL1,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_JUMPATTACK_STAFF_LEFT + {"DualJumpAtkRStaff", BOTH_BUTTERFLY_FR1,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_JUMPATTACK_STAFF_RIGHT + + {"ButterflyLeft", BOTH_BUTTERFLY_LEFT,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_BUTTERFLY_LEFT + {"ButterflyRight", BOTH_BUTTERFLY_RIGHT,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_BUTTERFLY_RIGHT + + {"BkFlip Atk", BOTH_JUMPATTACK7, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_BACKFLIP_ATK + {"DualSpinAtk", BOTH_SPINATTACK6, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_DUAL + {"StfSpinAtk", BOTH_SPINATTACK7, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK + {"LngLeapAtk", BOTH_FORCELONGLEAP_ATTACK,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_LEAP_ATTACK + {"SwoopAtkR", BOTH_VS_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_RIGHT + {"SwoopAtkL", BOTH_VS_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_LEFT + {"TauntaunAtkR",BOTH_VT_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_RIGHT + {"TauntaunAtkL",BOTH_VT_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_LEFT + {"StfKickFwd", BOTH_A7_KICK_F, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F + {"StfKickBack", BOTH_A7_KICK_B, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B + {"StfKickRight",BOTH_A7_KICK_R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R + {"StfKickLeft", BOTH_A7_KICK_L, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L + {"StfKickSpin", BOTH_A7_KICK_S, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_S + {"StfKickBkFwd",BOTH_A7_KICK_BF, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_BF + {"StfKickSplit",BOTH_A7_KICK_RL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_RL + {"StfKickFwdAir",BOTH_A7_KICK_F_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F_AIR + {"StfKickBackAir",BOTH_A7_KICK_B_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B_AIR + {"StfKickRightAir",BOTH_A7_KICK_R_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R_AIR + {"StfKickLeftAir",BOTH_A7_KICK_L_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L_AIR + {"StabDown", BOTH_STABDOWN, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN + {"StabDownStf", BOTH_STABDOWN_STAFF,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_STAFF + {"StabDownDual",BOTH_STABDOWN_DUAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_DUAL + {"dualspinprot",BOTH_A6_SABERPROTECT,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_DUAL_SPIN_PROTECT + {"StfSoulCal", BOTH_A7_SOULCAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_STAFF_SOULCAL + {"specialfast", BOTH_A1_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A1_SPECIAL + {"specialmed", BOTH_A2_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A2_SPECIAL + {"specialstr", BOTH_A3_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A3_SPECIAL + {"upsidedwnatk",BOTH_FLIP_ATTACK7, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_UPSIDE_DOWN_ATTACK + {"pullatkstab", BOTH_PULL_IMPALE_STAB,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_STAB + {"pullatkswing",BOTH_PULL_IMPALE_SWING,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_SWING + {"AloraSpinAtk",BOTH_ALORA_SPIN_SLASH,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_ALORA + {"Dual FB Atk", BOTH_A6_FB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_FB + {"Dual LR Atk", BOTH_A6_LR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_LR + {"StfHiltBash", BOTH_A7_HILT, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_HILT_BASH + + //starts + {"TL2BR St", BOTH_S1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TL2BR, LS_A_TL2BR, 200 }, // LS_S_TL2BR + {"L2R St", BOTH_S1_S1__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_L2R, LS_A_L2R, 200 }, // LS_S_L2R + {"BL2TR St", BOTH_S1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BL2TR, LS_A_BL2TR, 200 }, // LS_S_BL2TR + {"BR2TL St", BOTH_S1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BR2TL, LS_A_BR2TL, 200 }, // LS_S_BR2TL + {"R2L St", BOTH_S1_S1__R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_R2L, LS_A_R2L, 200 }, // LS_S_R2L + {"TR2BL St", BOTH_S1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TR2BL, LS_A_TR2BL, 200 }, // LS_S_TR2BL + {"T2B St", BOTH_S1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_T2B, LS_A_T2B, 200 }, // LS_S_T2B + + //returns + {"TL2BR Ret", BOTH_R1_BR_S1, Q_BR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TL2BR + {"L2R Ret", BOTH_R1__R_S1, Q_R, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_L2R + {"BL2TR Ret", BOTH_R1_TR_S1, Q_TR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BL2TR + {"BR2TL Ret", BOTH_R1_TL_S1, Q_TL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BR2TL + {"R2L Ret", BOTH_R1__L_S1, Q_L, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_R2L + {"TR2BL Ret", BOTH_R1_BL_S1, Q_BL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TR2BL + {"T2B Ret", BOTH_R1_B__S1, Q_B, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_T2B + + //Transitions + {"BR2R Trans", BOTH_T1_BR__R, Q_BR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc bottom right to right + {"BR2TR Trans", BOTH_T1_BR_TR, Q_BR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc bottom right to top right (use: BOTH_T1_TR_BR) + {"BR2T Trans", BOTH_T1_BR_T_, Q_BR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom right to top (use: BOTH_T1_T__BR) + {"BR2TL Trans", BOTH_T1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast weak spin bottom right to top left + {"BR2L Trans", BOTH_T1_BR__L, Q_BR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin bottom right to left + {"BR2BL Trans", BOTH_T1_BR_BL, Q_BR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin bottom right to bottom left + {"R2BR Trans", BOTH_T1__R_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc right to bottom right (use: BOTH_T1_BR__R) + {"R2TR Trans", BOTH_T1__R_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc right to top right + {"R2T Trans", BOTH_T1__R_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast ar right to top (use: BOTH_T1_T___R) + {"R2TL Trans", BOTH_T1__R_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc right to top left + {"R2L Trans", BOTH_T1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin right to left + {"R2BL Trans", BOTH_T1__R_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin right to bottom left + {"TR2BR Trans", BOTH_T1_TR_BR, Q_TR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top right to bottom right + {"TR2R Trans", BOTH_T1_TR__R, Q_TR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top right to right (use: BOTH_T1__R_TR) + {"TR2T Trans", BOTH_T1_TR_T_, Q_TR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top right to top (use: BOTH_T1_T__TR) + {"TR2TL Trans", BOTH_T1_TR_TL, Q_TR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top right to top left + {"TR2L Trans", BOTH_T1_TR__L, Q_TR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top right to left + {"TR2BL Trans", BOTH_T1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin top right to bottom left + {"T2BR Trans", BOTH_T1_T__BR, Q_T, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top to bottom right + {"T2R Trans", BOTH_T1_T___R, Q_T, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top to right + {"T2TR Trans", BOTH_T1_T__TR, Q_T, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top to top right + {"T2TL Trans", BOTH_T1_T__TL, Q_T, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top to top left + {"T2L Trans", BOTH_T1_T___L, Q_T, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top to left + {"T2BL Trans", BOTH_T1_T__BL, Q_T, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top to bottom left + {"TL2BR Trans", BOTH_T1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin top left to bottom right + {"TL2R Trans", BOTH_T1_TL__R, Q_TL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top left to right (use: BOTH_T1__R_TL) + {"TL2TR Trans", BOTH_T1_TL_TR, Q_TL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top left to top right (use: BOTH_T1_TR_TL) + {"TL2T Trans", BOTH_T1_TL_T_, Q_TL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top left to top (use: BOTH_T1_T__TL) + {"TL2L Trans", BOTH_T1_TL__L, Q_TL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top left to left (use: BOTH_T1__L_TL) + {"TL2BL Trans", BOTH_T1_TL_BL, Q_TL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top left to bottom left + {"L2BR Trans", BOTH_T1__L_BR, Q_L, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin left to bottom right + {"L2R Trans", BOTH_T1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin left to right + {"L2TR Trans", BOTH_T1__L_TR, Q_L, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc left to top right (use: BOTH_T1_TR__L) + {"L2T Trans", BOTH_T1__L_T_, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc left to top (use: BOTH_T1_T___L) + {"L2TL Trans", BOTH_T1__L_TL, Q_L, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc left to top left + {"L2BL Trans", BOTH_T1__L_BL, Q_L, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc left to bottom left (use: BOTH_T1_BL__L) + {"BL2BR Trans", BOTH_T1_BL_BR, Q_BL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin bottom left to bottom right + {"BL2R Trans", BOTH_T1_BL__R, Q_BL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin bottom left to right + {"BL2TR Trans", BOTH_T1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast weak spin bottom left to top right + {"BL2T Trans", BOTH_T1_BL_T_, Q_BL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom left to top (use: BOTH_T1_T__BL) + {"BL2TL Trans", BOTH_T1_BL_TL, Q_BL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc bottom left to top left (use: BOTH_T1_TL_BL) + {"BL2L Trans", BOTH_T1_BL__L, Q_BL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc bottom left to left + + //Bounces + {"Bounce BR", BOTH_B1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + {"Bounce R", BOTH_B1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + {"Bounce TR", BOTH_B1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + {"Bounce T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + {"Bounce TL", BOTH_B1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + {"Bounce L", BOTH_B1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + {"Bounce BL", BOTH_B1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + + //Deflected attacks (like bounces, but slide off enemy saber, not straight back) + {"Deflect BR", BOTH_D1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 }, + {"Deflect R", BOTH_D1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 }, + {"Deflect TR", BOTH_D1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 }, + {"Deflect T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + {"Deflect TL", BOTH_D1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 }, + {"Deflect L", BOTH_D1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 }, + {"Deflect BL", BOTH_D1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 }, + {"Deflect B", BOTH_D1_B____, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 }, + + //Reflected attacks + {"Reflected BR",BOTH_V1_BR_S1, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BR + {"Reflected R", BOTH_V1__R_S1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__R + {"Reflected TR",BOTH_V1_TR_S1, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TR + {"Reflected T", BOTH_V1_T__S1, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_T_ + {"Reflected TL",BOTH_V1_TL_S1, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TL + {"Reflected L", BOTH_V1__L_S1, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__L + {"Reflected BL",BOTH_V1_BL_S1, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BL + {"Reflected B", BOTH_V1_B__S1, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_B_ + + // Broken parries + {"BParry Top", BOTH_H1_S1_T_, Q_T, Q_B, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UP, + {"BParry UR", BOTH_H1_S1_TR, Q_TR, Q_BL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UR, + {"BParry UL", BOTH_H1_S1_TL, Q_TL, Q_BR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UL, + {"BParry LR", BOTH_H1_S1_BL, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR, + {"BParry Bot", BOTH_H1_S1_B_, Q_B, Q_T, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + {"BParry LL", BOTH_H1_S1_BR, Q_BR, Q_TL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL + + // Knockaways + {"Knock Top", BOTH_K1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_T__BR, 150 }, // LS_PARRY_UP, + {"Knock UR", BOTH_K1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_TR__R, 150 }, // LS_PARRY_UR, + {"Knock UL", BOTH_K1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_T1_TL__L, 150 }, // LS_PARRY_UL, + {"Knock LR", BOTH_K1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_T1_BL_TL, 150 }, // LS_PARRY_LR, + {"Knock LL", BOTH_K1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_T1_BR_TR, 150 }, // LS_PARRY_LL + + // Parry + {"Parry Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 150 }, // LS_PARRY_UP, + {"Parry UR", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 150 }, // LS_PARRY_UR, + {"Parry UL", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 150 }, // LS_PARRY_UL, + {"Parry LR", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 150 }, // LS_PARRY_LR, + {"Parry LL", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 150 }, // LS_PARRY_LL + + // Reflecting a missile + {"Reflect Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 300 }, // LS_PARRY_UP, + {"Reflect UR", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 300 }, // LS_PARRY_UR, + {"Reflect UL", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 300 }, // LS_PARRY_UL, + {"Reflect LR", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 300 }, // LS_PARRY_LR + {"Reflect LL", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 300 }, // LS_PARRY_LL, +}; + + +saberMoveName_t transitionMove[Q_NUM_QUADS][Q_NUM_QUADS] = +{ + { + LS_NONE, //Can't transition to same pos! + LS_T1_BR__R,//40 + LS_T1_BR_TR, + LS_T1_BR_T_, + LS_T1_BR_TL, + LS_T1_BR__L, + LS_T1_BR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1__R_BR,//46 + LS_NONE, //Can't transition to same pos! + LS_T1__R_TR, + LS_T1__R_T_, + LS_T1__R_TL, + LS_T1__R__L, + LS_T1__R_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_TR_BR,//52 + LS_T1_TR__R, + LS_NONE, //Can't transition to same pos! + LS_T1_TR_T_, + LS_T1_TR_TL, + LS_T1_TR__L, + LS_T1_TR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_T__BR,//58 + LS_T1_T___R, + LS_T1_T__TR, + LS_NONE, //Can't transition to same pos! + LS_T1_T__TL, + LS_T1_T___L, + LS_T1_T__BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_TL_BR,//64 + LS_T1_TL__R, + LS_T1_TL_TR, + LS_T1_TL_T_, + LS_NONE, //Can't transition to same pos! + LS_T1_TL__L, + LS_T1_TL_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1__L_BR,//70 + LS_T1__L__R, + LS_T1__L_TR, + LS_T1__L_T_, + LS_T1__L_TL, + LS_NONE, //Can't transition to same pos! + LS_T1__L_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_BL_BR,//76 + LS_T1_BL__R, + LS_T1_BL_TR, + LS_T1_BL_T_, + LS_T1_BL_TL, + LS_T1_BL__L, + LS_NONE, //Can't transition to same pos! + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + }, + { + LS_T1_BL_BR,//NOTE: there are no transitions from bottom, so re-use the bottom right transitions + LS_T1_BR__R, + LS_T1_BR_TR, + LS_T1_BR_T_, + LS_T1_BR_TL, + LS_T1_BR__L, + LS_T1_BR_BL, + LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any + } +}; + +void PM_VelocityForSaberMove( playerState_t *ps, vec3_t throwDir ) +{ + vec3_t vForward = { 0.0f }, vRight = { 0.0f }, vUp = { 0.0f }, startQ = { 0.0f }, endQ = { 0.0f }; + + AngleVectors( ps->viewangles, vForward, vRight, vUp ); + + switch ( saberMoveData[ps->saberMove].startQuad ) + { + case Q_BR: + VectorScale( vRight, 1, startQ ); + VectorMA( startQ, -1, vUp, startQ ); + break; + case Q_R: + VectorScale( vRight, 2, startQ ); + break; + case Q_TR: + VectorScale( vRight, 1, startQ ); + VectorMA( startQ, 1, vUp, startQ ); + break; + case Q_T: + VectorScale( vUp, 2, startQ ); + break; + case Q_TL: + VectorScale( vRight, -1, startQ ); + VectorMA( startQ, 1, vUp, startQ ); + break; + case Q_L: + VectorScale( vRight, -2, startQ ); + break; + case Q_BL: + VectorScale( vRight, -1, startQ ); + VectorMA( startQ, -1, vUp, startQ ); + break; + case Q_B: + VectorScale( vUp, -2, startQ ); + break; + } + switch ( saberMoveData[ps->saberMove].endQuad ) + { + case Q_BR: + VectorScale( vRight, 1, endQ ); + VectorMA( endQ, -1, vUp, endQ ); + break; + case Q_R: + VectorScale( vRight, 2, endQ ); + break; + case Q_TR: + VectorScale( vRight, 1, endQ ); + VectorMA( endQ, 1, vUp, endQ ); + break; + case Q_T: + VectorScale( vUp, 2, endQ ); + break; + case Q_TL: + VectorScale( vRight, -1, endQ ); + VectorMA( endQ, 1, vUp, endQ ); + break; + case Q_L: + VectorScale( vRight, -2, endQ ); + break; + case Q_BL: + VectorScale( vRight, -1, endQ ); + VectorMA( endQ, -1, vUp, endQ ); + break; + case Q_B: + VectorScale( vUp, -2, endQ ); + break; + } + VectorMA( endQ, 2, vForward, endQ ); + VectorScale( throwDir, 125, throwDir );//FIXME: pass in the throw strength? + VectorSubtract( endQ, startQ, throwDir ); +} + +qboolean PM_VelocityForBlockedMove( playerState_t *ps, vec3_t throwDir ) +{ + vec3_t vForward, vRight, vUp; + AngleVectors( ps->viewangles, vForward, vRight, vUp ); + switch ( ps->saberBlocked ) + { + case BLOCKED_UPPER_RIGHT: + VectorScale( vRight, 1, throwDir ); + VectorMA( throwDir, 1, vUp, throwDir ); + break; + case BLOCKED_UPPER_LEFT: + VectorScale( vRight, -1, throwDir ); + VectorMA( throwDir, 1, vUp, throwDir ); + break; + case BLOCKED_LOWER_RIGHT: + VectorScale( vRight, 1, throwDir ); + VectorMA( throwDir, -1, vUp, throwDir ); + break; + case BLOCKED_LOWER_LEFT: + VectorScale( vRight, -1, throwDir ); + VectorMA( throwDir, -1, vUp, throwDir ); + break; + case BLOCKED_TOP: + VectorScale( vUp, 2, throwDir ); + break; + default: + return qfalse; + break; + } + VectorMA( throwDir, 2, vForward, throwDir ); + VectorScale( throwDir, 250, throwDir );//FIXME: pass in the throw strength? + return qtrue; +} + +int PM_AnimLevelForSaberAnim( int anim ) +{ + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ ) + { + return FORCE_LEVEL_1; + } + if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ ) + { + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ ) + { + return FORCE_LEVEL_3; + } + if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ ) + {//desann + return FORCE_LEVEL_4; + } + if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ ) + {//tavion + return FORCE_LEVEL_5; + } + if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ ) + {//dual + return SS_DUAL; + } + if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ ) + {//staff + return SS_STAFF; + } + return FORCE_LEVEL_0; +} + +int PM_PowerLevelForSaberAnim( playerState_t *ps, int saberNum ) +{ + int anim = ps->torsoAnim; + int animTimeElapsed = PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim ) - ps->torsoAnimTimer; + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ ) + { + //FIXME: these two need their own style + if ( ps->saber[0].type == SABER_LANCE ) + { + return FORCE_LEVEL_4; + } + else if ( ps->saber[0].type == SABER_TRIDENT ) + { + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_1; + } + if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ ) + { + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ ) + { + return FORCE_LEVEL_3; + } + if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ ) + {//desann + return FORCE_LEVEL_4; + } + if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ ) + {//tavion + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ ) + {//dual + return FORCE_LEVEL_2; + } + if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ ) + {//staff + return FORCE_LEVEL_2; + } + if ( ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR ) + || ( anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR ) + || ( anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR ) ) + {//parries + switch ( ps->saberAnimLevel ) + { + case SS_STRONG: + case SS_DESANN: + return FORCE_LEVEL_3; + break; + case SS_TAVION: + case SS_STAFF: + case SS_DUAL: + case SS_MEDIUM: + return FORCE_LEVEL_2; + break; + case SS_FAST: + return FORCE_LEVEL_1; + break; + default: + return FORCE_LEVEL_0; + break; + } + } + if ( ( anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR ) + || ( anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR ) + || ( anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR ) ) + {//knockaways + return FORCE_LEVEL_3; + } + if ( ( anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1 ) + || ( anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6 ) + || ( anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7 ) ) + {//knocked-away attacks + return FORCE_LEVEL_1; + } + if ( ( anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR ) + || ( anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR ) + || ( anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR ) ) + {//broken parries + return FORCE_LEVEL_0; + } + switch ( anim ) + { + case BOTH_A2_STABBACK1: + if ( ps->torsoAnimTimer < 450 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 400 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ATTACK_BACK: + if ( ps->torsoAnimTimer < 500 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_CROUCHATTACKBACK1: + if ( ps->torsoAnimTimer < 800 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_K1_S1_T_: //# knockaway saber top + case BOTH_K1_S1_TR: //# knockaway saber top right + case BOTH_K1_S1_TL: //# knockaway saber top left + case BOTH_K1_S1_BL: //# knockaway saber bottom left + case BOTH_K1_S1_B_: //# knockaway saber bottom + case BOTH_K1_S1_BR: //# knockaway saber bottom right + //FIXME: break up? + return FORCE_LEVEL_3; + break; + case BOTH_LUNGE2_B__T_: + if ( ps->torsoAnimTimer < 400 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 150 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELEAP2_T__B_: + if ( ps->torsoAnimTimer < 400 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 550 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + return FORCE_LEVEL_3;//??? + break; + case BOTH_JUMPFLIPSLASHDOWN1: + if ( ps->torsoAnimTimer <= 900 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 550 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPFLIPSTABDOWN: + if ( ps->torsoAnimTimer <= 1200 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed <= 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_JUMPATTACK6: + /* + if (pm->ps) + { + if ( ( pm->ps->legsAnimTimer >= 1450 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) + ||(pm->ps->legsAnimTimer >= 400 + && PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) ) + {//pretty much sideways + return FORCE_LEVEL_3; + } + } + */ + if ( ( ps->torsoAnimTimer >= 1450 + && animTimeElapsed >= 400 ) + ||(ps->torsoAnimTimer >= 400 + && animTimeElapsed >= 1100 ) ) + {//pretty much sideways + return FORCE_LEVEL_3; + } + return FORCE_LEVEL_0; + break; + case BOTH_JUMPATTACK7: + if ( ps->torsoAnimTimer <= 1200 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK6: + if ( animTimeElapsed <= 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_SPINATTACK7: + if ( ps->torsoAnimTimer <= 500 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 500 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FORCELONGLEAP_ATTACK: + if ( animTimeElapsed <= 200 ) + {//1st four frames of anim + return FORCE_LEVEL_3; + } + break; + /* + case BOTH_A7_KICK_F://these kicks attack, too + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + //FIXME: break up + return FORCE_LEVEL_3; + break; + */ + case BOTH_STABDOWN: + if ( ps->torsoAnimTimer <= 900 ) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_STAFF: + if ( ps->torsoAnimTimer <= 850 ) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_STABDOWN_DUAL: + if ( ps->torsoAnimTimer <= 900 ) + {//end of anim + return FORCE_LEVEL_3; + } + break; + case BOTH_A6_SABERPROTECT: + if ( ps->torsoAnimTimer < 650 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_SOULCAL: + if ( ps->torsoAnimTimer < 650 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A1_SPECIAL: + if ( ps->torsoAnimTimer < 600 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A2_SPECIAL: + if ( ps->torsoAnimTimer < 300 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A3_SPECIAL: + if ( ps->torsoAnimTimer < 700 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 200 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_FLIP_ATTACK7: + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_STAB: + if ( ps->torsoAnimTimer < 1000 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_PULL_IMPALE_SWING: + if ( ps->torsoAnimTimer < 500 )//750 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 650 )//600 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_ALORA_SPIN_SLASH: + if ( ps->torsoAnimTimer < 900 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_FB: + if ( ps->torsoAnimTimer < 250 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A6_LR: + if ( ps->torsoAnimTimer < 250 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_3; + break; + case BOTH_A7_HILT: + return FORCE_LEVEL_0; + break; +//===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_LK_S_DL_T_SB_1_W: + if ( ps->torsoAnimTimer < 700 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_S_SB_1_W: + if ( ps->torsoAnimTimer < 300 ) + {//end of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + if ( ps->torsoAnimTimer < 700 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 400 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + if ( ps->torsoAnimTimer < 150 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 400 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_T_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + if ( animTimeElapsed < 1000 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_ST_T_SB_1_W: + if ( ps->torsoAnimTimer < 950 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 650 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_S_SB_1_W: + if ( saberNum != 0 ) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if ( ps->torsoAnimTimer < 900 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 450 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_DL_S_T_SB_1_W: + if ( saberNum != 0 ) + {//only right hand saber does damage in this suberbreak + return FORCE_LEVEL_0; + } + if ( ps->torsoAnimTimer < 250 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 150 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_S_SB_1_W: + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_DL_T_SB_1_W: + //special suberbreak - doesn't kill, just kicks them backwards + return FORCE_LEVEL_0; + break; + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + if ( ps->torsoAnimTimer < 800 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 350 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + return FORCE_LEVEL_5; + break; + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + return FORCE_LEVEL_5; + break; +//===SABERLOCK SUPERBREAKS START=========================================================================== + case BOTH_HANG_ATTACK: + //FIME: break up + if ( ps->torsoAnimTimer < 1000 ) + {//end of anim + return FORCE_LEVEL_0; + } + else if ( animTimeElapsed < 250 ) + {//beginning of anim + return FORCE_LEVEL_0; + } + else + {//sweet spot + return FORCE_LEVEL_5; + } + break; + case BOTH_ROLL_STAB: + if ( animTimeElapsed > 400 ) + {//end of anim + return FORCE_LEVEL_0; + } + else + { + return FORCE_LEVEL_3; + } + break; + } + return FORCE_LEVEL_0; +} + +qboolean PM_InAnimForSaberMove( int anim, int saberMove ) +{ + switch ( anim ) + {//special case anims + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qtrue; + } + if ( PM_SaberDrawPutawayAnim( anim ) ) + { + if ( saberMove == LS_DRAW || saberMove == LS_PUTAWAY ) + { + return qtrue; + } + return qfalse; + } + else if ( PM_SaberStanceAnim( anim ) ) + { + if ( saberMove == LS_READY ) + { + return qtrue; + } + return qfalse; + } + int animLevel = PM_AnimLevelForSaberAnim( anim ); + if ( animLevel <= 0 ) + {//NOTE: this will always return false for the ready poses and putaway/draw... + return qfalse; + } + //drop the anim to the first level and start the checks there + anim -= (animLevel-FORCE_LEVEL_1)*SABER_ANIM_GROUP_SIZE; + //check level 1 + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 2 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 3 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 4 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + //check level 5 + anim += SABER_ANIM_GROUP_SIZE; + if ( anim == saberMoveData[saberMove].animToUse ) + { + return qtrue; + } + if ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_H1_S1_BR ) + {//parries, knockaways and broken parries + return (anim==saberMoveData[saberMove].animToUse); + } + return qfalse; +} + +qboolean PM_SaberInIdle( int move ) +{ + switch ( move ) + { + case LS_NONE: + case LS_READY: + case LS_DRAW: + case LS_PUTAWAY: + return qtrue; + break; + } + return qfalse; +} +qboolean PM_SaberInSpecialAttack( int anim ) +{ + switch ( anim ) + { + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberInAttackPure( int move ) +{ + if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInAttack( int move ) +{ + if ( move >= LS_A_TL2BR && move <= LS_A_T2B ) + { + return qtrue; + } + switch ( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_ARIAL_LEFT: + case LS_JUMPATTACK_ARIAL_RIGHT: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qtrue; + break; + } + return qfalse; +} +qboolean PM_SaberInTransition( int move ) +{ + if ( move >= LS_T1_BR__R && move <= LS_T1_BL__L ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInStart( int move ) +{ + if ( move >= LS_S_TL2BR && move <= LS_S_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInReturn( int move ) +{ + if ( move >= LS_R_TL2BR && move <= LS_R_T2B ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInTransitionAny( int move ) +{ + if ( PM_SaberInStart( move ) ) + { + return qtrue; + } + else if ( PM_SaberInTransition( move ) ) + { + return qtrue; + } + else if ( PM_SaberInReturn( move ) ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInBounce( int move ) +{ + if ( move >= LS_B1_BR && move <= LS_B1_BL ) + { + return qtrue; + } + if ( move >= LS_D1_BR && move <= LS_D1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInBrokenParry( int move ) +{ + if ( move >= LS_V1_BR && move <= LS_V1_B_ ) + { + return qtrue; + } + if ( move >= LS_H1_T_ && move <= LS_H1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInDeflect( int move ) +{ + if ( move >= LS_D1_BR && move <= LS_D1_B_ ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInParry( int move ) +{ + if ( move >= LS_PARRY_UP && move <= LS_PARRY_LL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInKnockaway( int move ) +{ + if ( move >= LS_K1_T_ && move <= LS_K1_BL ) + { + return qtrue; + } + return qfalse; +} +qboolean PM_SaberInReflect( int move ) +{ + if ( move >= LS_REFLECT_UP && move <= LS_REFLECT_LL ) + { + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberInSpecial( int move ) +{ + switch( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_ARIAL_LEFT: + case LS_JUMPATTACK_ARIAL_RIGHT: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qtrue; + } + return qfalse; +} + +qboolean PM_KickMove( int move ) +{ + switch( move ) + { + case LS_KICK_F: + case LS_KICK_B: + case LS_KICK_R: + case LS_KICK_L: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_HILT_BASH: + case LS_KICK_F_AIR: + case LS_KICK_B_AIR: + case LS_KICK_R_AIR: + case LS_KICK_L_AIR: + return qtrue; + } + return qfalse; +} + +qboolean PM_SaberCanInterruptMove( int move, int anim ) +{ + if ( PM_InAnimForSaberMove( anim, move ) ) + { + switch( move ) + { + case LS_A_BACK: + case LS_A_BACK_CR: + case LS_A_BACKSTAB: + case LS_ROLL_STAB: + case LS_A_LUNGE: + case LS_A_JUMP_T__B_: + case LS_A_FLIP_STAB: + case LS_A_FLIP_SLASH: + case LS_JUMPATTACK_DUAL: + case LS_JUMPATTACK_CART_LEFT: + case LS_JUMPATTACK_CART_RIGHT: + case LS_JUMPATTACK_STAFF_LEFT: + case LS_JUMPATTACK_STAFF_RIGHT: + case LS_BUTTERFLY_LEFT: + case LS_BUTTERFLY_RIGHT: + case LS_A_BACKFLIP_ATK: + case LS_SPINATTACK_DUAL: + case LS_SPINATTACK: + case LS_LEAP_ATTACK: + case LS_SWOOP_ATTACK_RIGHT: + case LS_SWOOP_ATTACK_LEFT: + case LS_TAUNTAUN_ATTACK_RIGHT: + case LS_TAUNTAUN_ATTACK_LEFT: + case LS_KICK_S: + case LS_KICK_BF: + case LS_KICK_RL: + case LS_STABDOWN: + case LS_STABDOWN_STAFF: + case LS_STABDOWN_DUAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_UPSIDE_DOWN_ATTACK: + case LS_PULL_ATTACK_STAB: + case LS_PULL_ATTACK_SWING: + case LS_SPINATTACK_ALORA: + case LS_DUAL_FB: + case LS_DUAL_LR: + case LS_HILT_BASH: + return qfalse; + } + + if ( PM_SaberInAttackPure( move ) ) + { + return qfalse; + } + if ( PM_SaberInStart( move ) ) + { + return qfalse; + } + if ( PM_SaberInTransition( move ) ) + { + return qfalse; + } + if ( PM_SaberInBounce( move ) ) + { + return qfalse; + } + if ( PM_SaberInBrokenParry( move ) ) + { + return qfalse; + } + if ( PM_SaberInDeflect( move ) ) + { + return qfalse; + } + if ( PM_SaberInParry( move ) ) + { + return qfalse; + } + if ( PM_SaberInKnockaway( move ) ) + { + return qfalse; + } + if ( PM_SaberInReflect( move ) ) + { + return qfalse; + } + } + switch ( anim ) + { + case BOTH_A2_STABBACK1: + case BOTH_ATTACK_BACK: + case BOTH_CROUCHATTACKBACK1: + case BOTH_ROLL_STAB: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_LUNGE2_B__T_: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPFLIPSLASHDOWN1://# + case BOTH_JUMPFLIPSTABDOWN://# + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_SPINATTACK6: + case BOTH_SPINATTACK7: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_VS_ATR_S: + case BOTH_VS_ATL_S: + case BOTH_VT_ATR_S: + case BOTH_VT_ATL_S: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_FLIP_ATTACK7: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_ALORA_SPIN_SLASH: + case BOTH_A6_FB: + case BOTH_A6_LR: + case BOTH_A7_HILT: + case BOTH_LK_S_DL_S_SB_1_W: + case BOTH_LK_S_DL_T_SB_1_W: + case BOTH_LK_S_ST_S_SB_1_W: + case BOTH_LK_S_ST_T_SB_1_W: + case BOTH_LK_S_S_S_SB_1_W: + case BOTH_LK_S_S_T_SB_1_W: + case BOTH_LK_DL_DL_S_SB_1_W: + case BOTH_LK_DL_DL_T_SB_1_W: + case BOTH_LK_DL_ST_S_SB_1_W: + case BOTH_LK_DL_ST_T_SB_1_W: + case BOTH_LK_DL_S_S_SB_1_W: + case BOTH_LK_DL_S_T_SB_1_W: + case BOTH_LK_ST_DL_S_SB_1_W: + case BOTH_LK_ST_DL_T_SB_1_W: + case BOTH_LK_ST_ST_S_SB_1_W: + case BOTH_LK_ST_ST_T_SB_1_W: + case BOTH_LK_ST_S_S_SB_1_W: + case BOTH_LK_ST_S_T_SB_1_W: + case BOTH_HANG_ATTACK: + return qfalse; + } + return qtrue; +} + +saberMoveName_t PM_BrokenParryForAttack( int move ) +{ + //Our attack was knocked away by a knockaway parry + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( saberMoveData[move].startQuad ) + { + case Q_B: + return LS_V1_B_; + break; + case Q_BR: + return LS_V1_BR; + break; + case Q_R: + return LS_V1__R; + break; + case Q_TR: + return LS_V1_TR; + break; + case Q_T: + return LS_V1_T_; + break; + case Q_TL: + return LS_V1_TL; + break; + case Q_L: + return LS_V1__L; + break; + case Q_BL: + return LS_V1_BL; + break; + } + return LS_NONE; +} + +saberMoveName_t PM_BrokenParryForParry( int move ) +{ + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( move ) + { + case LS_PARRY_UP: + //Hmm... since we don't know what dir the hit came from, randomly pick knock down or knock back + if ( Q_irand( 0, 1 ) ) + { + return LS_H1_B_; + } + else + { + return LS_H1_T_; + } + break; + case LS_PARRY_UR: + return LS_H1_TR; + break; + case LS_PARRY_UL: + return LS_H1_TL; + break; + case LS_PARRY_LR: + return LS_H1_BR; + break; + case LS_PARRY_LL: + return LS_H1_BL; + break; + case LS_READY: + return LS_H1_B_;//??? + break; + } + return LS_NONE; +} + +saberMoveName_t PM_KnockawayForParry( int move ) +{ + //FIXME: need actual anims for this + //FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center + switch ( move ) + { + case BLOCKED_TOP://LS_PARRY_UP: + return LS_K1_T_;//push up + break; + case BLOCKED_UPPER_RIGHT://LS_PARRY_UR: + default://case LS_READY: + return LS_K1_TR;//push up, slightly to right + break; + case BLOCKED_UPPER_LEFT://LS_PARRY_UL: + return LS_K1_TL;//push up and to left + break; + case BLOCKED_LOWER_RIGHT://LS_PARRY_LR: + return LS_K1_BR;//push down and to left + break; + case BLOCKED_LOWER_LEFT://LS_PARRY_LL: + return LS_K1_BL;//push down and to right + break; + } + //return LS_NONE; +} + +saberMoveName_t PM_SaberBounceForAttack( int move ) +{ + switch ( saberMoveData[move].startQuad ) + { + case Q_B: + case Q_BR: + return LS_B1_BR; + break; + case Q_R: + return LS_B1__R; + break; + case Q_TR: + return LS_B1_TR; + break; + case Q_T: + return LS_B1_T_; + break; + case Q_TL: + return LS_B1_TL; + break; + case Q_L: + return LS_B1__L; + break; + case Q_BL: + return LS_B1_BL; + break; + } + return LS_NONE; +} + +saberMoveName_t PM_AttackMoveForQuad( int quad ) +{ + switch ( quad ) + { + case Q_B: + case Q_BR: + return LS_A_BR2TL; + break; + case Q_R: + return LS_A_R2L; + break; + case Q_TR: + return LS_A_TR2BL; + break; + case Q_T: + return LS_A_T2B; + break; + case Q_TL: + return LS_A_TL2BR; + break; + case Q_L: + return LS_A_L2R; + break; + case Q_BL: + return LS_A_BL2TR; + break; + } + return LS_NONE; +} + +int saberMoveTransitionAngle[Q_NUM_QUADS][Q_NUM_QUADS] = +{ + { + 0,//Q_BR,Q_BR, + 45,//Q_BR,Q_R, + 90,//Q_BR,Q_TR, + 135,//Q_BR,Q_T, + 180,//Q_BR,Q_TL, + 215,//Q_BR,Q_L, + 270,//Q_BR,Q_BL, + 45,//Q_BR,Q_B, + }, + { + 45,//Q_R,Q_BR, + 0,//Q_R,Q_R, + 45,//Q_R,Q_TR, + 90,//Q_R,Q_T, + 135,//Q_R,Q_TL, + 180,//Q_R,Q_L, + 215,//Q_R,Q_BL, + 90,//Q_R,Q_B, + }, + { + 90,//Q_TR,Q_BR, + 45,//Q_TR,Q_R, + 0,//Q_TR,Q_TR, + 45,//Q_TR,Q_T, + 90,//Q_TR,Q_TL, + 135,//Q_TR,Q_L, + 180,//Q_TR,Q_BL, + 135,//Q_TR,Q_B, + }, + { + 135,//Q_T,Q_BR, + 90,//Q_T,Q_R, + 45,//Q_T,Q_TR, + 0,//Q_T,Q_T, + 45,//Q_T,Q_TL, + 90,//Q_T,Q_L, + 135,//Q_T,Q_BL, + 180,//Q_T,Q_B, + }, + { + 180,//Q_TL,Q_BR, + 135,//Q_TL,Q_R, + 90,//Q_TL,Q_TR, + 45,//Q_TL,Q_T, + 0,//Q_TL,Q_TL, + 45,//Q_TL,Q_L, + 90,//Q_TL,Q_BL, + 135,//Q_TL,Q_B, + }, + { + 215,//Q_L,Q_BR, + 180,//Q_L,Q_R, + 135,//Q_L,Q_TR, + 90,//Q_L,Q_T, + 45,//Q_L,Q_TL, + 0,//Q_L,Q_L, + 45,//Q_L,Q_BL, + 90,//Q_L,Q_B, + }, + { + 270,//Q_BL,Q_BR, + 215,//Q_BL,Q_R, + 180,//Q_BL,Q_TR, + 135,//Q_BL,Q_T, + 90,//Q_BL,Q_TL, + 45,//Q_BL,Q_L, + 0,//Q_BL,Q_BL, + 45,//Q_BL,Q_B, + }, + { + 45,//Q_B,Q_BR, + 90,//Q_B,Q_R, + 135,//Q_B,Q_TR, + 180,//Q_B,Q_T, + 135,//Q_B,Q_TL, + 90,//Q_B,Q_L, + 45,//Q_B,Q_BL, + 0//Q_B,Q_B, + } +}; + +int PM_SaberAttackChainAngle( int move1, int move2 ) +{ + if ( move1 == -1 || move2 == -1 ) + { + return -1; + } + return saberMoveTransitionAngle[saberMoveData[move1].endQuad][saberMoveData[move2].startQuad]; +} + +qboolean PM_SaberKataDone( int curmove = LS_NONE, int newmove = LS_NONE ) +{ + if ( pm->ps->forceRageRecoveryTime > level.time ) + {//rage recovery, only 1 swing at a time (tired) + if ( pm->ps->saberAttackChainCount > 0 ) + {//swung once + return qtrue; + } + else + {//allow one attack + return qfalse; + } + } + else if ( (pm->ps->forcePowersActive&(1<ps->saber[0].maxChain == -1 ) + { + return qfalse; + } + else if ( pm->ps->saber[0].maxChain != 0 ) + { + if ( pm->ps->saberAttackChainCount >= pm->ps->saber[0].maxChain ) + { + return qtrue; + } + else + { + return qfalse; + } + } + + if ( pm->ps->saberAnimLevel == SS_DESANN || pm->ps->saberAnimLevel == SS_TAVION ) + {//desann and tavion can link up as many attacks as they want + return qfalse; + } + //FIXME: instead of random, apply some sort of logical conditions to whether or + // not you can chain? Like if you were completely missed, you can't chain as much, or...? + // And/Or based on FP_SABER_OFFENSE level? So number of attacks you can chain + // increases with your FP_SABER_OFFENSE skill? + if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + //TEMP: for now, let staff attacks infinitely chain + return qfalse; + /* + if ( pm->ps->saberAttackChainCount > Q_irand( 3, 4 ) ) + { + return qtrue; + } + else if ( pm->ps->saberAttackChainCount > 0 ) + { + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 3 ) + { + return qtrue; + } + } + } + */ + } + else if ( pm->ps->saberAnimLevel == SS_DUAL ) + { + //TEMP: for now, let staff attacks infinitely chain + return qfalse; + } + else if ( pm->ps->saberAnimLevel == FORCE_LEVEL_3 ) + { + if ( curmove == LS_NONE || newmove == LS_NONE ) + { + if ( pm->ps->saberAnimLevel >= FORCE_LEVEL_3 && pm->ps->saberAttackChainCount > Q_irand( 0, 1 ) ) + { + return qtrue; + } + } + else if ( pm->ps->saberAttackChainCount > Q_irand( 2, 3 ) ) + { + return qtrue; + } + else if ( pm->ps->saberAttackChainCount > 0 ) + { + int chainAngle = PM_SaberAttackChainAngle( curmove, newmove ); + if ( chainAngle < 135 || chainAngle > 215 ) + {//if trying to chain to a move that doesn't continue the momentum + return qtrue; + } + else if ( chainAngle == 180 ) + {//continues the momentum perfectly, allow it to chain 66% of the time + if ( pm->ps->saberAttackChainCount > 1 ) + { + return qtrue; + } + } + else + {//would continue the movement somewhat, 50% chance of continuing + if ( pm->ps->saberAttackChainCount > 2 ) + { + return qtrue; + } + } + } + } + else + {//FIXME: have chainAngle influence fast and medium chains as well? + if ( (pm->ps->saberAnimLevel == FORCE_LEVEL_2 || pm->ps->saberAnimLevel == SS_DUAL) + && pm->ps->saberAttackChainCount > Q_irand( 2, 5 ) ) + { + return qtrue; + } + } + return qfalse; +} + +qboolean PM_CheckEnemyInBack( float backCheckDist ) +{ + if ( !pm->gent || !pm->gent->client ) + { + return qfalse; + } + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) + && !g_saberAutoAim->integer && pm->cmd.forwardmove >= 0 ) + {//don't auto-backstab + return qfalse; + } + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + {//only when on ground + return qfalse; + } + trace_t trace; + vec3_t end, fwd, fwdAngles = {0,pm->ps->viewangles[YAW],0}; + + AngleVectors( fwdAngles, fwd, NULL, NULL ); + VectorMA( pm->ps->origin, -backCheckDist, fwd, end ); + + pm->trace( &trace, pm->ps->origin, vec3_origin, vec3_origin, end, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 ); + if ( trace.fraction < 1.0f && trace.entityNum < ENTITYNUM_WORLD ) + { + gentity_t *traceEnt = &g_entities[trace.entityNum]; + if ( traceEnt + && traceEnt->health > 0 + && traceEnt->client + && traceEnt->client->playerTeam == pm->gent->client->enemyTeam + && traceEnt->client->ps.groundEntityNum != ENTITYNUM_NONE ) + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + {//player + if ( pm->gent ) + {//set player enemy to traceEnt so he auto-aims at him + pm->gent->enemy = traceEnt; + } + } + return qtrue; + } + } + return qfalse; +} + +saberMoveName_t PM_PickBackStab( void ) +{ + if ( !pm->gent || !pm->gent->client ) + { + return LS_READY; + } + if ( pm->ps->dualSabers + && pm->ps->saber[1].Active() ) + { + if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + if ( pm->gent->client->ps.saberAnimLevel == SS_TAVION ) + { + return LS_A_BACKSTAB; + } + else if ( pm->gent->client->ps.saberAnimLevel == SS_DESANN ) + { + if ( pm->ps->saberMove == LS_READY || !Q_irand( 0, 3 ) ) + { + return LS_A_BACKSTAB; + } + else if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + else if ( pm->ps->saberAnimLevel == FORCE_LEVEL_2 + || pm->ps->saberAnimLevel == SS_DUAL ) + {//using medium attacks or dual sabers + if ( pm->ps->pm_flags & PMF_DUCKED ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + else + { + return LS_A_BACKSTAB; + } +} + +saberMoveName_t PM_CheckStabDown( void ) +{ + if ( !pm->gent || !pm->gent->enemy || !pm->gent->enemy->client ) + { + return LS_NONE; + } + if ( (pm->ps->saber[0].saberFlags&SFL_NO_STABDOWN) ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_STABDOWN) ) + { + return LS_NONE; + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingKataAttack( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//want to try a special + return LS_NONE; + } + } + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + {//player + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + {//sorry must be on ground (or have just jumped) + if ( level.time-pm->ps->lastOnGround <= 50 && (pm->ps->pm_flags&PMF_JUMPING) ) + {//just jumped, it's okay + } + else + { + return LS_NONE; + } + } + /* + if ( pm->cmd.upmove > 0 ) + {//trying to jump + } + else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE //in air + && level.time-pm->ps->lastOnGround <= 250 //just left ground + && (pm->ps->pm_flags&PMF_JUMPING) )//jumped + {//just jumped + } + else + { + return LS_NONE; + } + */ + pm->ps->velocity[2] = 0; + pm->cmd.upmove = 0; + } + else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + {//NPC + if ( pm->ps->groundEntityNum == ENTITYNUM_NONE )//in air + {//sorry must be on ground (or have just jumped) + if ( level.time-pm->ps->lastOnGround <= 250 && (pm->ps->pm_flags&PMF_JUMPING) ) + {//just jumped, it's okay + } + else + { + return LS_NONE; + } + } + if ( !pm->gent->NPC ) + {//wtf??? + return LS_NONE; + } + if ( Q_irand( 0, RANK_CAPTAIN ) > pm->gent->NPC->rank ) + {//lower ranks do this less often + return LS_NONE; + } + } + vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, NULL, NULL ); + VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + float enemyZDiff = enemyDir[2]; + enemyDir[2] = 0; + float enemyHDist = VectorNormalize( enemyDir )-(pm->gent->maxs[0]+pm->gent->enemy->maxs[0]); + float dot = DotProduct( enemyDir, faceFwd ); + + if ( //(pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + dot > 0.65f + //&& enemyHDist >= 32 //was 48 + && enemyHDist <= 164//was 112 + && PM_InKnockDownOnGround( &pm->gent->enemy->client->ps )//still on ground + && !PM_InGetUpNoRoll( &pm->gent->enemy->client->ps )//not getting up yet + && enemyZDiff <= 20 ) + {//guy is on the ground below me, do a top-down attack + if ( pm->gent->enemy->s.number >= MAX_CLIENTS + || !G_ControlledByPlayer( pm->gent->enemy ) ) + {//don't get up while I'm doing this + //stop them from trying to get up for at least another 3 seconds + TIMER_Set( pm->gent->enemy, "noGetUpStraight", 3000 ); + } + //pick the right anim + if ( pm->ps->saberAnimLevel == SS_DUAL + || (pm->ps->dualSabers&&pm->ps->saber[1].Active()) ) + { + return LS_STABDOWN_DUAL; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + return LS_STABDOWN_STAFF; + } + else + { + return LS_STABDOWN; + } + } + return LS_NONE; +} + +extern saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ); +saberMoveName_t PM_SaberFlipOverAttackMove( void ); +saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ) +{ + saberMoveName_t autoMove = LS_INVALID; + + if( !pm->gent->enemy ) + { + return LS_NONE; + } + + vec3_t enemy_org, enemyDir, faceFwd, faceRight, faceUp, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, faceRight, faceUp ); + //FIXME: predict enemy position? + if ( pm->gent->enemy->client ) + { + //VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); + //HMM... using this will adjust for bbox size, so let's do that... + vec3_t size; + VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); + VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + + VectorSubtract( pm->gent->enemy->client->renderInfo.eyePoint, pm->ps->origin, enemyDir ); + } + else + { + if ( pm->gent->enemy->bmodel && VectorCompare( vec3_origin, pm->gent->enemy->currentOrigin ) ) + {//a brush model without an origin brush + vec3_t size; + VectorSubtract( pm->gent->enemy->absmax, pm->gent->enemy->absmin, size ); + VectorMA( pm->gent->enemy->absmin, 0.5, size, enemy_org ); + } + else + { + VectorCopy( pm->gent->enemy->currentOrigin, enemy_org ); + } + VectorSubtract( enemy_org, pm->ps->origin, enemyDir ); + } + float enemyZDiff = enemyDir[2]; + float enemyDist = VectorNormalize( enemyDir ); + float dot = DotProduct( enemyDir, faceFwd ); + if ( dot > 0 ) + {//enemy is in front + if ( allowStabDown ) + {//okay to try this + saberMoveName_t stabDownMove = PM_CheckStabDown(); + if ( stabDownMove != LS_NONE ) + { + return stabDownMove; + } + } + if ( (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + && dot > 0.65f + && enemyDist <= 64 && pm->gent->enemy->client + && (enemyZDiff <= 20 || PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) || PM_CrouchAnim( pm->gent->enemy->client->ps.legsAnim ) ) ) + {//swing down at them + return LS_A_T2B; + } + if ( allowFB ) + {//directly in front anim allowed + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( enemyDist > 200 || pm->gent->enemy->health <= 0 ) + {//hmm, look in back for an enemy + if ( pm->ps->clientNum && !PM_ControlledByPlayer() ) + {//player should never do this automatically + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 100 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + } + //this is the default only if they're *right* in front... + if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + {//NPC or player not in 1st person + if ( PM_CheckFlipOverAttackMove( qtrue ) ) + {//enemy must be close and in front + return PM_SaberFlipOverAttackMove(); + } + } + if ( PM_CheckLungeAttackMove() ) + {//NPC + autoMove = PM_SaberLungeAttackMove( qtrue ); + } + else + { + autoMove = LS_A_T2B; + } + } + else + {//pick a random one + if ( Q_irand( 0, 1 ) ) + { + autoMove = LS_A_TR2BL; + } + else + { + autoMove = LS_A_TL2BR; + } + } + float dotR = DotProduct( enemyDir, faceRight ); + if ( dotR > 0.35 ) + {//enemy is to far right + autoMove = LS_A_L2R; + } + else if ( dotR < -0.35 ) + {//far left + autoMove = LS_A_R2L; + } + else if ( dotR > 0.15 ) + {//enemy is to near right + autoMove = LS_A_TR2BL; + } + else if ( dotR < -0.15 ) + {//near left + autoMove = LS_A_TL2BR; + } + if ( DotProduct( enemyDir, faceUp ) > 0.5 ) + {//enemy is above me + if ( autoMove == LS_A_TR2BL ) + { + autoMove = LS_A_BL2TR; + } + else if ( autoMove == LS_A_TL2BR ) + { + autoMove = LS_A_BR2TL; + } + } + } + else if ( allowFB ) + {//back attack allowed + //if ( !PM_InKnockDown( pm->ps ) ) + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( !pm->gent->enemy->client || pm->gent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE ) + {//enemy not a client or is a client and on ground + if ( dot < -0.75f + && enemyDist < 128 + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,2))) ) + {//fast back-stab + if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + {//can't do it while ducked? + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + {//only fencers and above can do this + autoMove = LS_A_BACKSTAB; + } + } + } + else if ( pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF ) + {//higher level back spin-attacks + if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) ) + { + if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + { + autoMove = LS_A_BACK_CR; + } + else + { + autoMove = LS_A_BACK; + } + } + } + } + } + } + } + return autoMove; +} + +qboolean PM_InSecondaryStyle( void ) +{ + if ( pm->ps->saber[0].numBlades > 1 + && pm->ps->saber[0].singleBladeStyle + && (pm->ps->saber[0].stylesForbidden&(1<ps->saber[0].singleBladeStyle)) + && pm->ps->saberAnimLevel == pm->ps->saber[0].singleBladeStyle ) + { + return qtrue; + } + + if ( pm->ps->dualSabers + && !pm->ps->saber[1].Active() )//pm->ps->saberAnimLevel != SS_DUAL ) + { + return qtrue; + } + return qfalse; +} + +saberMoveName_t PM_SaberLungeAttackMove( qboolean fallbackToNormalLunge ) +{ + G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER_FB ); + + //see if we have an overridden (or cancelled) lunge move + if ( pm->ps->saber[0].lungeAtkMove != LS_INVALID ) + { + if ( pm->ps->saber[0].lungeAtkMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].lungeAtkMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove != LS_INVALID ) + { + if ( pm->ps->saber[1].lungeAtkMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].lungeAtkMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand( 0, 3 ) ) + {//alora NPC + return LS_SPINATTACK_ALORA; + } + else + { + if ( pm->ps->dualSabers ) + { + return LS_SPINATTACK_DUAL; + } + switch ( pm->ps->saberAnimLevel ) + { + case SS_DUAL: + return LS_SPINATTACK_DUAL; + break; + case SS_STAFF: + return LS_SPINATTACK; + break; + default://normal lunge + if ( fallbackToNormalLunge ) + { + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + //do the lunge + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 150, pm->ps->velocity ); + pm->ps->velocity[2] = 50; + PM_AddEvent( EV_JUMP ); + + return LS_A_LUNGE; + } + break; + } + } + return LS_NONE; +} + +qboolean PM_CheckLungeAttackMove( void ) +{ + //check to see if it's cancelled? + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE + || pm->ps->saber[1].lungeAtkMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].lungeAtkMove == LS_NONE ) + { + if ( pm->ps->saber[0].lungeAtkMove == LS_NONE + || pm->ps->saber[0].lungeAtkMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + if ( pm->ps->saberAnimLevel == SS_FAST//fast + || pm->ps->saberAnimLevel == SS_DUAL//dual + || pm->ps->saberAnimLevel == SS_STAFF //staff + || pm->ps->saberAnimLevel == SS_DESANN + || pm->ps->dualSabers ) + {//alt+back+attack using fast, dual or staff attacks + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove < 0 || (pm->ps->pm_flags&PMF_DUCKED) ) + {//ducking + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE + || pm->ps->legsAnim == BOTH_SABERDUAL_STANCE + || (level.time-pm->ps->lastStationary) <= 500 ) + {//standing or just stopped standing + if ( pm->gent + && pm->gent->NPC //NPC + && pm->gent->NPC->rank >= RANK_LT_JG //high rank + && ( pm->gent->NPC->rank == RANK_LT_JG || Q_irand( -3, pm->gent->NPC->rank ) >= RANK_LT_JG )//Q_irand( 0, pm->gent->NPC->rank ) >= RANK_LT_JG ) + && !Q_irand( 0, 3-g_spskill->integer ) ) + {//only fencer and higher can do this + if ( pm->ps->saberAnimLevel == SS_DESANN ) + { + if ( !Q_irand( 0, 4 ) ) + { + return qtrue; + } + } + else + { + return qtrue; + } + } + } + } + } + else + {//player + if ( G_TryingLungeAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + { + return qtrue; + } + } + } + + return qfalse; +} + +saberMoveName_t PM_SaberJumpForwardAttackMove( void ) +{ + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + } + if ( pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF ) + { + pm->cmd.upmove = 0;//no jump just yet + + if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + if ( Q_irand(0, 1) ) + { + return LS_JUMPATTACK_STAFF_LEFT; + } + else + { + return LS_JUMPATTACK_STAFF_RIGHT; + } + } + + return LS_JUMPATTACK_DUAL; + } + else + { + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 200, pm->ps->velocity ); + pm->ps->velocity[2] = 180; + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + + //FIXME: NPCs yell? + PM_AddEvent( EV_JUMP ); + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + pm->cmd.upmove = 0; + + return LS_A_JUMP_T__B_; + } +} + +qboolean PM_CheckJumpForwardAttackMove( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( pm->cmd.forwardmove > 0 //going forward + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped (if not player) + ) + { + if ( pm->ps->saberAnimLevel == SS_DUAL + || pm->ps->saberAnimLevel == SS_STAFF ) + {//dual and staff + if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim + && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + && pm->ps->weaponTime <= 0//not busy + && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + { + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//jumping NPC + { + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + { + return qtrue; + } + } + } + else + {//PLAYER + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power to attack + { + return qtrue; + } + } + } + } + //check strong + else if ( pm->ps->saberAnimLevel == SS_STRONG //strong style + || pm->ps->saberAnimLevel == SS_DESANN )//desann + { + if ( //&& !PM_InKnockDown( pm->ps ) + !pm->ps->dualSabers + //&& (pm->ps->legsAnim == BOTH_STAND2||pm->ps->legsAnim == BOTH_SABERFAST_STANCE||pm->ps->legsAnim == BOTH_SABERSLOW_STANCE||level.time-pm->ps->lastStationary<=500)//standing or just started moving + ) + {//strong attack: jump-hack + /* + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || level.time-pm->ps->lastStationary <= 250 )//standing or just started moving + */ + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING) )//NPC jumping + { + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + {//only acrobat or boss and higher can do this + if ( pm->ps->legsAnim == BOTH_STAND2 + || pm->ps->legsAnim == BOTH_SABERFAST_STANCE + || pm->ps->legsAnim == BOTH_SABERSLOW_STANCE + || level.time-pm->ps->lastStationary <= 250 ) + {//standing or just started moving + if ( pm->gent->client + && pm->gent->client->NPC_class == CLASS_DESANN ) + { + if ( !Q_irand( 0, 1 ) ) + { + return qtrue; + } + } + else + { + return qtrue; + } + } + } + } + } + else + {//player + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) ) + { + return qtrue; + } + } + } + } + } + return qfalse; +} + +saberMoveName_t PM_SaberFlipOverAttackMove( void ) +{ + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkFwdMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkFwdMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + return LS_NONE; + } + } + //FIXME: check above for room enough to jump! + //FIXME: while in this jump, keep velocity[2] at a minimum until the end of the anim + vec3_t fwdAngles, jumpFwd; + + VectorCopy( pm->ps->viewangles, fwdAngles ); + fwdAngles[PITCH] = fwdAngles[ROLL] = 0; + AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); + VectorScale( jumpFwd, 150, pm->ps->velocity ); + pm->ps->velocity[2] = 250; + //250 is normalized for a standing enemy at your z level, about 64 tall... adjust for actual maxs[2]-mins[2] of enemy and for zdiff in origins + if ( pm->gent && pm->gent->enemy ) + { //go higher for taller enemies + pm->ps->velocity[2] *= (pm->gent->enemy->maxs[2]-pm->gent->enemy->mins[2])/64.0f; + //go higher for enemies higher than you, lower for those lower than you + float zDiff = pm->gent->enemy->currentOrigin[2] - pm->ps->origin[2]; + pm->ps->velocity[2] += (zDiff)*1.5f; + //clamp to decent-looking values + //FIXME: still jump too low sometimes + if ( zDiff <= 0 && pm->ps->velocity[2] < 200 ) + {//if we're on same level, don't let me jump so low, I clip into the ground + pm->ps->velocity[2] = 200; + } + else if ( pm->ps->velocity[2] < 50 ) + { + pm->ps->velocity[2] = 50; + } + else if ( pm->ps->velocity[2] > 400 ) + { + pm->ps->velocity[2] = 400; + } + } + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + + //FIXME: NPCs yell? + PM_AddEvent( EV_JUMP ); + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + pm->cmd.upmove = 0; + //FIXME: don't allow this to land on other people + + pm->gent->angle = pm->ps->viewangles[YAW];//so we know what yaw we started this at + + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_FB ); + + if ( Q_irand( 0, 1 ) ) + { + return LS_A_FLIP_STAB; + } + else + { + return LS_A_FLIP_SLASH; + } +} + +qboolean PM_CheckFlipOverAttackMove( qboolean checkEnemy ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[1].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkFwdMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkFwdMove == LS_NONE + || pm->ps->saber[0].jumpAtkFwdMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( (pm->ps->saberAnimLevel == SS_MEDIUM //medium + || pm->ps->saberAnimLevel == SS_TAVION )//tavion + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + ) + { + qboolean tryMove = qfalse; + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->cmd.upmove > 0//want to jump + || (pm->ps->pm_flags&PMF_JUMPING) )//jumping + {//flip over-forward down-attack + if ( (pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) + && !Q_irand(0, 2) ) )//NPC who can do this, 33% chance + {//only player or acrobat or boss and higher can do this + tryMove = qtrue; + } + } + } + else + {//player + if ( G_TryingJumpForwardAttack( pm->gent, &pm->cmd ) + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) )//have enough power + { + if ( !pm->cmd.rightmove ) + { + if ( pm->ps->legsAnim == BOTH_JUMP1 + || pm->ps->legsAnim == BOTH_FORCEJUMP1 + || pm->ps->legsAnim == BOTH_INAIR1 + || pm->ps->legsAnim == BOTH_FORCEINAIR1 ) + {//in a non-flip forward jump + tryMove = qtrue; + } + } + } + } + + if ( tryMove ) + { + if ( !checkEnemy ) + {//based just on command input + return qtrue; + } + else + {//based on presence of enemy + if ( pm->gent->enemy )//have an enemy + { + vec3_t fwdAngles = {0,pm->ps->viewangles[YAW],0}; + if ( pm->gent->enemy->health > 0 + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->gent->enemy->maxs[2] > 12 + && (!pm->gent->enemy->client || !PM_InKnockDownOnGround( &pm->gent->enemy->client->ps ) ) + && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 10000 + && InFront( pm->gent->enemy->currentOrigin, pm->gent->currentOrigin, fwdAngles, 0.3f ) ) + {//enemy must be alive, not low to ground, close and in front + return qtrue; + } + } + return qfalse; + } + } + } + return qfalse; +} + +saberMoveName_t PM_SaberBackflipAttackMove( void ) +{ + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].jumpAtkBackMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkBackMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].jumpAtkBackMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove != LS_INVALID ) + { + if ( pm->ps->saber[1].jumpAtkBackMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].jumpAtkBackMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + { + return LS_NONE; + } + } + pm->cmd.upmove = 0;//no jump just yet + return LS_A_BACKFLIP_ATK; +} + +qboolean PM_CheckBackflipAttackMove( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return qfalse; + } + + //check to see if it's cancelled? + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE ) + { + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE + || pm->ps->saber[1].jumpAtkBackMove == LS_INVALID ) + { + return qfalse; + } + } + else + { + return qfalse; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].jumpAtkBackMove == LS_NONE ) + { + if ( pm->ps->saber[0].jumpAtkBackMove == LS_NONE + || pm->ps->saber[0].jumpAtkBackMove == LS_INVALID ) + { + return qfalse; + } + } + } + //do normal checks + + if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //can force jump + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->gent && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + //&& (pm->ps->legsAnim == BOTH_SABERSTAFF_STANCE || level.time-pm->ps->lastStationary<=250)//standing or just started moving + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) )//on ground or just jumped (if not player) + { + if ( pm->cmd.forwardmove < 0 //moving backwards + && pm->ps->saberAnimLevel == SS_STAFF //using staff + && (pm->cmd.upmove > 0 || (pm->ps->pm_flags&PMF_JUMPING)) )//jumping + {//jumping backwards and using staff + if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim + && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + && pm->ps->weaponTime <= 0//not busy + && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + {//not already attacking + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + {//NPC + if ( pm->gent + && pm->gent->NPC + && (pm->gent->NPC->rank==RANK_CREWMAN||pm->gent->NPC->rank>=RANK_LT) ) + {//acrobat or boss and higher can do this + return qtrue; + } + } + else + {//player + return qtrue; + } + } + } + } + return qfalse; +} + +saberMoveName_t PM_CheckDualSpinProtect( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[0].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].kataMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[1].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].kataMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].kataMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->ps->saberMove == LS_READY//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? + //&& pm->ps->viewangles[0] > 30 //looking down + && pm->ps->saberAnimLevel == SS_DUAL//using dual saber style + && pm->ps->saber[0].Active() && pm->ps->saber[1].Active()//both sabers on + //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 + //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing + && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack + ) + {//FIXME: some NPC logic to do this? + /* + if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching + && g_crosshairEntNum >= ENTITYNUM_WORLD ) + */ + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_PUSH, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + } + return LS_DUAL_SPIN_PROTECT; + } + } + return LS_NONE; +} + +saberMoveName_t PM_CheckStaffKata( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + //see if we have an overridden (or cancelled) kata move + if ( pm->ps->saber[0].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[0].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[0].kataMove; + } + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove != LS_INVALID ) + { + if ( pm->ps->saber[1].kataMove != LS_NONE ) + { + return (saberMoveName_t)pm->ps->saber[1].kataMove; + } + } + } + //no overrides, cancelled? + if ( pm->ps->saber[0].kataMove == LS_NONE ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers ) + { + if ( pm->ps->saber[1].kataMove == LS_NONE ) + { + return LS_NONE; + } + } + //do normal checks + if ( pm->ps->saberMove == LS_READY//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY...? + //&& pm->ps->viewangles[0] > 30 //looking down + && pm->ps->saberAnimLevel == SS_STAFF//using dual saber style + && pm->ps->saber[0].Active()//saber on + //&& pm->ps->forcePowerLevel[FP_PUSH]>=FORCE_LEVEL_3//force push 3 + //&& ((pm->ps->forcePowersActive&(1<ps->forcePowerDebounce[FP_PUSH]>level.time)//force-pushing + && G_TryingKataAttack( pm->gent, &pm->cmd )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER//DUAL_SPIN_PROTECT_POWER//force push 3 + && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack + ) + {//FIXME: some NPC logic to do this? + /* + if ( (pm->ps->pm_flags&PMF_DUCKED||pm->cmd.upmove<0)//crouching + && g_crosshairEntNum >= ENTITYNUM_WORLD ) + */ + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER, qtrue );//drain the required force power + } + return LS_STAFF_SOULCAL; + } + } + return LS_NONE; +} + +extern qboolean WP_ForceThrowable( gentity_t *ent, gentity_t *forwardEnt, gentity_t *self, qboolean pull, float cone, float radius, vec3_t forward ); +saberMoveName_t PM_CheckPullAttack( void ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + return LS_NONE; + } + + if ( (pm->ps->saber[0].saberFlags&SFL_NO_PULL_ATTACK) ) + { + return LS_NONE; + } + if ( pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_PULL_ATTACK) ) + { + return LS_NONE; + } + + if ( (pm->ps->saberMove == LS_READY||PM_SaberInReturn(pm->ps->saberMove)||PM_SaberInReflect(pm->ps->saberMove))//ready + //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY + && pm->ps->saberAnimLevel >= SS_FAST//single saber styles - FIXME: Tavion? + && pm->ps->saberAnimLevel <= SS_STRONG//single saber styles - FIXME: Tavion? + && G_TryingPullAttack( pm->gent, &pm->cmd, qfalse )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS)//holding focus + //&& pm->cmd.forwardmove<0//pulling back + && (pm->cmd.buttons&BUTTON_ATTACK)//attacking + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB//have enough power + ) + {//FIXME: some NPC logic to do this? + qboolean doMove = g_saberNewControlScheme->integer?qtrue:qfalse;//in new control scheme, can always do this, even if there's no-one to do it to + if ( g_saberNewControlScheme->integer + || g_crosshairEntNum < ENTITYNUM_WORLD )//in old control scheme, there has to be someone there + { + saberMoveName_t pullAttackMove = LS_NONE; + if ( pm->ps->saberAnimLevel == SS_FAST ) + { + pullAttackMove = LS_PULL_ATTACK_STAB; + } + else + { + pullAttackMove = LS_PULL_ATTACK_SWING; + } + + if ( g_crosshairEntNum < ENTITYNUM_WORLD + && pm->gent && pm->gent->client ) + { + gentity_t *targEnt = &g_entities[g_crosshairEntNum]; + if ( targEnt->client + && targEnt->health > 0 + //FIXME: check other things like in knockdown, saberlock, uninterruptable anims, etc. + && !PM_InOnGroundAnim( &targEnt->client->ps ) + && !PM_LockedAnim( targEnt->client->ps.legsAnim ) + && !PM_SuperBreakLoseAnim( targEnt->client->ps.legsAnim ) + && !PM_SuperBreakWinAnim( targEnt->client->ps.legsAnim ) + && targEnt->client->ps.saberLockTime <= 0 + && WP_ForceThrowable( targEnt, targEnt, pm->gent, qtrue, 1.0f, 0.0f, NULL ) ) + { + if ( !g_saberNewControlScheme->integer ) + {//in old control scheme, make sure they're close or far enough away for the move we'll be doing + float targDist = Distance( targEnt->currentOrigin, pm->ps->origin ); + if ( pullAttackMove == LS_PULL_ATTACK_STAB ) + {//must be closer than 512 + if ( targDist > 384.0f ) + { + return LS_NONE; + } + } + else//if ( pullAttackMove == LS_PULL_ATTACK_SWING ) + {//must be farther than 256 + if ( targDist > 512.0f ) + { + return LS_NONE; + } + if ( targDist < 192.0f ) + { + return LS_NONE; + } + } + } + + vec3_t targAngles = {0,targEnt->client->ps.viewangles[YAW],0}; + if ( InFront( pm->ps->origin, targEnt->currentOrigin, targAngles ) ) + { + NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_F, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + } + else + { + NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_B, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); + } + //hold the anim until I'm with done pull anim + targEnt->client->ps.legsAnimTimer = targEnt->client->ps.torsoAnimTimer = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, (animNumber_t)saberMoveData[pullAttackMove].animToUse ); + //set pullAttackTime + pm->gent->client->ps.pullAttackTime = targEnt->client->ps.pullAttackTime = level.time+targEnt->client->ps.legsAnimTimer; + //make us know about each other + pm->gent->client->ps.pullAttackEntNum = g_crosshairEntNum; + targEnt->client->ps.pullAttackEntNum = pm->ps->clientNum; + //do effect and sound on me + pm->ps->powerups[PW_FORCE_PUSH] = level.time + 1000; + if ( pm->gent ) + { + G_Sound( pm->gent, G_SoundIndex( "sound/weapons/force/pull.wav" ) ); + } + doMove = qtrue; + } + } + if ( doMove ) + { + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_PULL, SABER_ALT_ATTACK_POWER_FB ); + } + return pullAttackMove; + } + } + } + return LS_NONE; +} + +saberMoveName_t PM_CheckPlayerAttackFromParry( int curmove ) +{ + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + { + if ( curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL ) + {//in a parry + switch ( saberMoveData[curmove].endQuad ) + { + case Q_T: + return LS_A_T2B; + break; + case Q_TR: + return LS_A_TR2BL; + break; + case Q_TL: + return LS_A_TL2BR; + break; + case Q_BR: + return LS_A_BR2TL; + break; + case Q_BL: + return LS_A_BL2TR; + break; + //shouldn't be a parry that ends at L, R or B + } + } + } + return LS_NONE; +} + + +saberMoveName_t PM_SaberAttackForMovement( int forwardmove, int rightmove, int curmove ) +{ + qboolean noSpecials = qfalse; + + if ( pm->ps->clientNum < MAX_CLIENTS + && PM_InSecondaryStyle() ) + { + noSpecials = qtrue; + } + + saberMoveName_t overrideJumpRightAttackMove = LS_INVALID; + if ( pm->ps->saber[0].jumpAtkRightMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkRightMove != LS_NONE ) + {//actually overriding + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkRightMove; + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove > LS_NONE ) + {//would be cancelling it, but check the second saber, too + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; + } + else + {//nope, just cancel it + overrideJumpRightAttackMove = LS_NONE; + } + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkRightMove != LS_INVALID ) + {//first saber not overridden, check second + overrideJumpRightAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkRightMove; + } + + saberMoveName_t overrideJumpLeftAttackMove = LS_INVALID; + if ( pm->ps->saber[0].jumpAtkLeftMove != LS_INVALID ) + { + if ( pm->ps->saber[0].jumpAtkLeftMove != LS_NONE ) + {//actually overriding + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkLeftMove; + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove > LS_NONE ) + {//would be cancelling it, but check the second saber, too + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; + } + else + {//nope, just cancel it + overrideJumpLeftAttackMove = LS_NONE; + } + } + else if ( pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkLeftMove != LS_INVALID ) + {//first saber not overridden, check second + overrideJumpLeftAttackMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkLeftMove; + } + if ( rightmove > 0 ) + {//moving right + if ( !noSpecials + && overrideJumpRightAttackMove != LS_NONE + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC + ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + {//cartwheel right + vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + } + pm->cmd.upmove = 0; + if ( overrideJumpRightAttackMove != LS_INVALID ) + {//overridden with another move + return overrideJumpRightAttackMove; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + return LS_BUTTERFLY_RIGHT; + } + else + { + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + {//okay to do cartwheels with this saber + /* + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//still on ground + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_CART_RIGHT; + } + else + */ + {//in air + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + PM_SetJumped( JUMP_VELOCITY, qtrue ); + return LS_JUMPATTACK_ARIAL_RIGHT; + } + } + } + } + else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_RIGHT + && pm->ps->legsAnim != BOTH_ARIAL_RIGHT ) + {//not in a cartwheel/arial + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( forwardmove > 0 ) + {//forward right = TL2BR slash + return LS_A_TL2BR; + } + else if ( forwardmove < 0 ) + {//backward right = BL2TR uppercut + return LS_A_BL2TR; + } + else + {//just right is a left slice + return LS_A_L2R; + } + } + } + else if ( rightmove < 0 ) + {//moving left + if ( !noSpecials + && overrideJumpLeftAttackMove != LS_NONE + && (pm->ps->groundEntityNum != ENTITYNUM_NONE||level.time-pm->ps->lastOnGround<=250) //on ground or just jumped + && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have force jump 1 at least + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR//have enough power + && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0)//jumping NPC + ||((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/)) )//focus-holding player + {//cartwheel left + vec3_t right, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + if ( pm->gent ) + { + G_DrainPowerForSpecialMove( pm->gent, FP_LEVITATION, SABER_ALT_ATTACK_POWER_LR ); + } + pm->cmd.upmove = 0; + if ( overrideJumpRightAttackMove != LS_INVALID ) + {//overridden with another move + return overrideJumpRightAttackMove; + } + else if ( pm->ps->saberAnimLevel == SS_STAFF ) + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + return LS_BUTTERFLY_LEFT; + } + else + { + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + {//okay to do cartwheels with this saber + /* + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//still on ground + VectorClear( pm->ps->velocity ); + return LS_JUMPATTACK_ARIAL_LEFT; + } + else + */ + { + AngleVectors( fwdAngles, NULL, right, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + PM_SetJumped( JUMP_VELOCITY, qtrue ); + return LS_JUMPATTACK_CART_LEFT; + } + } + } + } + else if ( pm->ps->legsAnim != BOTH_CARTWHEEL_LEFT + && pm->ps->legsAnim != BOTH_ARIAL_LEFT ) + {//not in a left cartwheel/arial + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( forwardmove > 0 ) + {//forward left = TR2BL slash + return LS_A_TR2BL; + } + else if ( forwardmove < 0 ) + {//backward left = BR2TL uppercut + return LS_A_BR2TL; + } + else + {//just left is a right slice + return LS_A_R2L; + } + } + } + else + {//not moving left or right + if ( forwardmove > 0 ) + {//forward= T2B slash + saberMoveName_t stabDownMove = noSpecials?LS_NONE:PM_CheckStabDown(); + if ( stabDownMove != LS_NONE ) + { + return stabDownMove; + } + if ( ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zoomed in + {//player in thirdperson, not zoomed in + //flip-over attack logic + if ( !noSpecials && PM_CheckFlipOverAttackMove( qfalse ) ) + {//flip over-forward down-attack + return PM_SaberFlipOverAttackMove(); + } + //lunge attack logic + else if ( PM_CheckLungeAttackMove() ) + { + return PM_SaberLungeAttackMove( qtrue ); + } + //jump forward attack logic + else if ( !noSpecials && PM_CheckJumpForwardAttackMove() ) + { + return PM_SaberJumpForwardAttackMove(); + } + } + + //player NPC with enemy: autoMove logic + if ( pm->gent + && pm->gent->enemy + && pm->gent->enemy->client ) + {//I have an active enemy + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + {//a player who is running at an enemy + //if the enemy is not a jedi, don't use top-down, pick a diagonal or side attack + if ( pm->gent->enemy->s.weapon != WP_SABER + && pm->gent->enemy->client->NPC_class != CLASS_REMOTE//too small to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_SEEKER//too small to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_GONK//too short to do auto-aiming accurately + && pm->gent->enemy->client->NPC_class != CLASS_HOWLER//too short to do auto-aiming accurately + && g_saberAutoAim->integer ) + { + saberMoveName_t autoMove = PM_AttackForEnemyPos( qfalse, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer()) ); + if ( autoMove != LS_INVALID ) + { + return autoMove; + } + } + } + + if ( pm->ps->clientNum>=MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + {//NPC + if ( PM_CheckFlipOverAttackMove( qtrue ) ) + { + return PM_SaberFlipOverAttackMove(); + } + } + } + + //Regular NPCs + if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) //NPC ONLY + {//NPC or player in third person, not zoomed in + //fwd jump attack logic + if ( PM_CheckJumpForwardAttackMove() ) + { + return PM_SaberJumpForwardAttackMove(); + } + //lunge attack logic + else if ( PM_CheckLungeAttackMove() ) + { + return PM_SaberLungeAttackMove( qtrue ); + } + } + + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + return LS_A_T2B; + } + else if ( forwardmove < 0 ) + {//backward= T2B slash//B2T uppercut? + if ( g_saberNewControlScheme->integer ) + { + saberMoveName_t pullAtk = PM_CheckPullAttack(); + if ( pullAtk != LS_NONE ) + { + return pullAtk; + } + } + + if ( g_saberNewControlScheme->integer + && (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //PLAYER ONLY + && (pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus, trying special backwards attacks + {//player lunge attack logic + if ( ( pm->ps->dualSabers //or dual + || pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->SaberStaff() )//or staff + && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB*/ )//have enough force power to pull it off + {//alt+back+attack using fast, dual or staff attacks + PM_SaberLungeAttackMove( qfalse ); + } + } + else if ( (pm->ps->clientNum&&!PM_ControlledByPlayer()) //NPC + || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode) )//player in third person, not zooomed + {//NPC or player in third person, not zoomed + if ( PM_CheckBackflipAttackMove() ) + { + return PM_SaberBackflipAttackMove();//backflip attack + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //if ( !PM_InKnockDown( pm->ps ) ) + //check backstabs + if ( !(pm->ps->saber[0].saberFlags&SFL_NO_BACK_ATTACK) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_BACK_ATTACK)) ) + {//okay to do backstabs with this saber + if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + {//only when on ground + if ( pm->gent && pm->gent->enemy ) + {//FIXME: or just trace for a valid enemy standing behind me? And no enemy in front? + vec3_t enemyDir, faceFwd, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + AngleVectors( facingAngles, faceFwd, NULL, NULL ); + VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + float dot = DotProduct( enemyDir, faceFwd ); + if ( dot < 0 ) + {//enemy is behind me + if ( dot < -0.75f + && DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ) < 16384//128 squared + && (pm->ps->saberAnimLevel == SS_FAST || pm->ps->saberAnimLevel == SS_STAFF || (pm->gent->client &&(pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA)&&Q_irand(0,1))) ) + {//fast attacks and Tavion + if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + {//can't do it while ducked? + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG) ) + {//only fencers and above can do this + return LS_A_BACKSTAB; + } + } + } + else if ( pm->ps->saberAnimLevel != SS_FAST + && pm->ps->saberAnimLevel != SS_STAFF ) + {//medium and higher attacks + if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 ) + { + return LS_A_BACK_CR; + } + else + { + return LS_A_BACK; + } + } + } + else + {//enemy in front + float enemyDistSq = DistanceSquared( pm->gent->currentOrigin, pm->gent->enemy->currentOrigin ); + if ( ((pm->ps->saberAnimLevel == FORCE_LEVEL_1 || + pm->ps->saberAnimLevel == SS_STAFF || + pm->gent->client->NPC_class == CLASS_TAVION || + pm->gent->client->NPC_class == CLASS_ALORA || + (pm->gent->client->NPC_class == CLASS_DESANN && !Q_irand(0,3))) && + enemyDistSq > 16384) || + pm->gent->enemy->health <= 0 )//128 squared + {//my enemy is pretty far in front of me and I'm using fast attacks + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || + ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 128 ) ) + { + return PM_PickBackStab(); + } + } + } + else if ( ((pm->ps->saberAnimLevel >= FORCE_LEVEL_2 || pm->gent->client->NPC_class == CLASS_DESANN) && enemyDistSq > 40000) || pm->gent->enemy->health <= 0 )//200 squared + {//enemy is very faw away and I'm using medium/strong attacks + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || + ( pm->gent && pm->gent->client && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, pm->gent->NPC->rank ) > RANK_ENSIGN ) ) + {//only fencers and higher can do this, higher rank does it more + if ( PM_CheckEnemyInBack( 164 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + else + {//no current enemy + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->gent && pm->gent->client ) + {//only player + if ( PM_CheckEnemyInBack( 128 ) ) + { + return PM_PickBackStab(); + } + } + } + } + } + } + + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + //else just swing down + return LS_A_T2B; + } + else + {//not moving in any direction + if ( PM_SaberInBounce( curmove ) ) + {//bounces should go to their default attack if you don't specify a direction but are attacking + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial(pm->gent,&pm->cmd) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + saberMoveName_t newmove; + if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + } + else + {//player uses chain-attack + newmove = saberMoveData[curmove].chain_attack; + } + if ( PM_SaberKataDone( curmove, newmove ) ) + { + return saberMoveData[curmove].chain_idle; + } + else + { + return newmove; + } + } + else if ( PM_SaberInKnockaway( curmove ) ) + {//bounces should go to their default attack if you don't specify a direction but are attacking + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + saberMoveName_t newmove; + if ( pm->ps->clientNum && !PM_ControlledByPlayer() && Q_irand( 0, 3 ) ) + {//use NPC random + newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + } + else + { + if ( pm->ps->saberAnimLevel == SS_FAST || + pm->ps->saberAnimLevel == SS_TAVION ) + {//player is in fast attacks, so come right back down from the same spot + newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad ); + } + else + {//use a transition to wrap to another attack from a different dir + newmove = saberMoveData[curmove].chain_attack; + } + } + if ( PM_SaberKataDone( curmove, newmove ) ) + { + return saberMoveData[curmove].chain_idle; + } + else + { + return newmove; + } + } + else if ( curmove == LS_READY + || curmove == LS_A_FLIP_STAB + || curmove == LS_A_FLIP_SLASH + || ( curmove >= LS_PARRY_UP + && curmove <= LS_REFLECT_LL ) ) + {//Not moving at all, not too busy to attack + //push + lookdown + attack + dual sabers = LS_DUAL_SPIN_PROTECT + if ( g_saberNewControlScheme->integer ) + { + if ( PM_CheckDualSpinProtect() ) + { + return LS_DUAL_SPIN_PROTECT; + } + if ( PM_CheckStaffKata() ) + { + return LS_STAFF_SOULCAL; + } + } + if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) //PLAYER ONLY + {//player + if ( G_TryingSpecial( pm->gent, &pm->cmd ) )//(pm->cmd.buttons&BUTTON_FORCE_FOCUS) )//Holding focus + {//if no special worked, do nothing + return LS_NONE; + } + } + //checked all special attacks, if we're in a parry, attack from that move + saberMoveName_t parryAttackMove = PM_CheckPlayerAttackFromParry( curmove ); + if ( parryAttackMove != LS_NONE ) + { + return parryAttackMove; + } + //check regular attacks + if ( pm->ps->clientNum || g_saberAutoAim->integer ) + {//auto-aim + if ( pm->gent && pm->gent->enemy ) + {//based on enemy position, pick a proper attack + saberMoveName_t autoMove = PM_AttackForEnemyPos( qtrue, (qboolean)(pm->ps->clientNum>=MAX_CLIENTS) ); + if ( autoMove != LS_INVALID ) + { + return autoMove; + } + } + else if ( fabs(pm->ps->viewangles[0]) > 30 ) + {//looking far up or far down uses the top to bottom attack, presuming you want a vertical attack + return LS_A_T2B; + } + } + else + {//for now, just pick a random attack + return ((saberMoveName_t)Q_irand( LS_A_TL2BR, LS_A_T2B )); + } + } + } + } + //FIXME: pick a return? + return LS_NONE; +} + +saberMoveName_t PM_SaberAnimTransitionMove( saberMoveName_t curmove, saberMoveName_t newmove ) +{ + //FIXME: take FP_SABER_OFFENSE into account here somehow? + int retmove = newmove; + if ( curmove == LS_READY ) + {//just standing there + switch ( newmove ) + { + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + //transition is the start + retmove = LS_S_TL2BR + (newmove-LS_A_TL2BR); + break; + default: + break; + } + } + else + { + switch ( newmove ) + { + //transitioning to ready pose + case LS_READY: + switch ( curmove ) + { + //transitioning from an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + //transition is the return + retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + break; + default: + break; + } + break; + //transitioning to an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + if ( newmove == curmove ) + {//FIXME: need a spin or something or go to next level, but for now, just play the return + //going into another attack... + //allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3 + //FIXME: don't let strong attacks chain to an attack in the opposite direction ( > 45 degrees?) + if ( PM_SaberKataDone( curmove, newmove ) ) + {//done with this kata, must return to ready before attack again + retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR); + } + else + {//okay to chain to another attack + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + } + } + else if ( saberMoveData[curmove].endQuad == saberMoveData[newmove].startQuad ) + {//new move starts from same quadrant + retmove = newmove; + } + else + { + switch ( curmove ) + { + //transitioning from an attack + case LS_A_TL2BR: + case LS_A_L2R: + case LS_A_BL2TR: + case LS_A_BR2TL: + case LS_A_R2L: + case LS_A_TR2BL: + case LS_A_T2B: + case LS_D1_BR: + case LS_D1__R: + case LS_D1_TR: + case LS_D1_T_: + case LS_D1_TL: + case LS_D1__L: + case LS_D1_BL: + case LS_D1_B_: + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + break; + //transitioning from a return + case LS_R_TL2BR: + case LS_R_L2R: + case LS_R_BL2TR: + case LS_R_BR2TL: + case LS_R_R2L: + case LS_R_TR2BL: + case LS_R_T2B: + //transitioning from a bounce + /* + case LS_BOUNCE_UL2LL: + case LS_BOUNCE_LL2UL: + case LS_BOUNCE_L2LL: + case LS_BOUNCE_L2UL: + case LS_BOUNCE_UR2LR: + case LS_BOUNCE_LR2UR: + case LS_BOUNCE_R2LR: + case LS_BOUNCE_R2UR: + case LS_BOUNCE_TOP: + case LS_OVER_UR2UL: + case LS_OVER_UL2UR: + case LS_BOUNCE_UR: + case LS_BOUNCE_UL: + case LS_BOUNCE_LR: + case LS_BOUNCE_LL: + */ + //transitioning from a parry/reflection/knockaway/broken parry + case LS_PARRY_UP: + case LS_PARRY_UR: + case LS_PARRY_UL: + case LS_PARRY_LR: + case LS_PARRY_LL: + case LS_REFLECT_UP: + case LS_REFLECT_UR: + case LS_REFLECT_UL: + case LS_REFLECT_LR: + case LS_REFLECT_LL: + case LS_K1_T_: + case LS_K1_TR: + case LS_K1_TL: + case LS_K1_BR: + case LS_K1_BL: + case LS_V1_BR: + case LS_V1__R: + case LS_V1_TR: + case LS_V1_T_: + case LS_V1_TL: + case LS_V1__L: + case LS_V1_BL: + case LS_V1_B_: + case LS_H1_T_: + case LS_H1_TR: + case LS_H1_TL: + case LS_H1_BR: + case LS_H1_BL: + retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad]; + break; + //NB: transitioning from transitions is fine + default: + break; + } + } + break; + //transitioning to any other anim is not supported + default: + break; + } + } + + if ( retmove == LS_NONE ) + { + return newmove; + } + + return ((saberMoveName_t)retmove); +} + +/* +------------------------- +PM_LegsAnimForFrame +Returns animNumber for current frame +------------------------- +*/ +int PM_LegsAnimForFrame( gentity_t *ent, int legsFrame ) +{ + //Must be a valid client + if ( ent->client == NULL ) + return -1; + + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return -1; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); + + for ( int animation = 0; animation < BOTH_CIN_1; animation++ ) //first anim after last legs + { + if ( animation >= TORSO_DROPWEAP1 && animation < LEGS_TURN1 ) //first legs only anim + {//not a possible legs anim + continue; + } + + if ( animations[animation].glaIndex != glaIndex ) + { + continue; + } + + if ( animations[animation].firstFrame > legsFrame ) + {//This anim starts after this frame + continue; + } + + if ( animations[animation].firstFrame + animations[animation].numFrames < legsFrame ) + {//This anim ends before this frame + continue; + } + //else, must be in this anim! + return animation; + } + + //Not in ANY torsoAnim? SHOULD NEVER HAPPEN +// assert(0); + return -1; +} + +int PM_ValidateAnimRange( const int startFrame, const int endFrame, const float animSpeed ) +{//given a startframe and endframe, see if that lines up with any known animation + animation_t *animations = level.knownAnimFileSets[0].animations; + + for ( int anim = 0; anim < MAX_ANIMATIONS; anim++ ) + { + if ( animSpeed < 0 ) + {//playing backwards + if ( animations[anim].firstFrame == endFrame ) + { + if ( animations[anim].numFrames + animations[anim].firstFrame == startFrame ) + { + //Com_Printf( "valid reverse anim: %s\n", animTable[anim].name ); + return anim; + } + } + } + else + {//playing forwards + if ( animations[anim].firstFrame == startFrame ) + {//This anim starts on this frame + if ( animations[anim].firstFrame + animations[anim].numFrames == endFrame ) + {//This anim ends on this frame + //Com_Printf( "valid forward anim: %s\n", animTable[anim].name ); + return anim; + } + } + } + //else, must not be this anim! + } + + //Not in ANY anim? SHOULD NEVER HAPPEN + Com_Printf( "invalid anim range %d to %d, speed %4.2f\n", startFrame, endFrame, animSpeed ); + return -1; +} +/* +------------------------- +PM_TorsoAnimForFrame +Returns animNumber for current frame +------------------------- +*/ +int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame ) +{ + //Must be a valid client + if ( ent->client == NULL ) + return -1; + + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return -1; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + int glaIndex = gi.G2API_GetAnimIndex(&(ent->ghoul2[0])); + + for ( int animation = 0; animation < LEGS_TURN1; animation++ ) //first legs only anim + { + if ( animations[animation].glaIndex != glaIndex ) + { + continue; + } + + if ( animations[animation].firstFrame > torsoFrame ) + {//This anim starts after this frame + continue; + } + + if ( animations[animation].firstFrame + animations[animation].numFrames < torsoFrame ) + {//This anim ends before this frame + continue; + } + //else, must be in this anim! + return animation; + } + + //Not in ANY torsoAnim? SHOULD NEVER HAPPEN +// assert(0); + return -1; +} + +qboolean PM_FinishedCurrentLegsAnim( gentity_t *self ) +{ + int junk, curFrame; + float currentFrame, animSpeed; + + if ( !self->client ) + { + return qtrue; + } + + gi.G2API_GetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &animSpeed, NULL ); + curFrame = floor( currentFrame ); + + int legsAnim = self->client->ps.legsAnim; + animation_t *animations = level.knownAnimFileSets[self->client->clientInfo.animFileIndex].animations; + + if ( curFrame >= animations[legsAnim].firstFrame + (animations[legsAnim].numFrames - 2) ) + { + return qtrue; + } + + return qfalse; +} + +/* +------------------------- +PM_HasAnimation +------------------------- +*/ + +qboolean PM_HasAnimation( gentity_t *ent, int animation ) +{ + //Must be a valid client + if ( !ent || ent->client == NULL ) + return qfalse; + + //must be a valid anim number + if ( animation < 0 || animation >= MAX_ANIMATIONS ) + { + return qfalse; + } + //Must have a file index entry + if( ValidAnimFileIndex( ent->client->clientInfo.animFileIndex ) == qfalse ) + return qfalse; + + animation_t *animations = level.knownAnimFileSets[ent->client->clientInfo.animFileIndex].animations; + + //No frames, no anim + if ( animations[animation].numFrames == 0 ) + return qfalse; + + //Has the sequence + return qtrue; +} + +int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim ) +{ + int anim; + int count = 0; + + if ( !self ) + { + return Q_irand(minAnim, maxAnim); + } + + do + { + anim = Q_irand(minAnim, maxAnim); + count++; + } + while ( !PM_HasAnimation( self, anim ) && count < 1000 ); + + return anim; +} + +/* +------------------------- +PM_AnimLength +------------------------- +*/ + +int PM_AnimLength( int index, animNumber_t anim ) +{ + if ( ValidAnimFileIndex( index ) == false ) + return 0; + + return level.knownAnimFileSets[index].animations[anim].numFrames * abs(level.knownAnimFileSets[index].animations[anim].frameLerp); +} + +/* +------------------------- +PM_SetLegsAnimTimer +------------------------- +*/ + +void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time ) +{ + *legsAnimTimer = time; + + if ( *legsAnimTimer < 0 && time != -1 ) + {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional + *legsAnimTimer = 0; + } + + if ( !*legsAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_LOWER ) ) + {//Waiting for legsAnimTimer to complete, and it just got set to zero + if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + {//Not waiting for top + Q3_TaskIDComplete( ent, TID_ANIM_LOWER ); + } + else + {//Waiting for both to finish before complete + Q3_TaskIDClear( &ent->taskID[TID_ANIM_LOWER] );//Bottom is done, regardless + if ( !Q3_TaskIDPending( ent, TID_ANIM_UPPER) ) + {//top is done and we're done + Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + } + } + } +} + +/* +------------------------- +PM_SetTorsoAnimTimer +------------------------- +*/ + +void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time ) +{ + *torsoAnimTimer = time; + + if ( *torsoAnimTimer < 0 && time != -1 ) + {//Cap timer to 0 if was counting down, but let it be -1 if that was intentional + *torsoAnimTimer = 0; + } + + if ( !*torsoAnimTimer && ent && Q3_TaskIDPending( ent, TID_ANIM_UPPER ) ) + {//Waiting for torsoAnimTimer to complete, and it just got set to zero + if ( !Q3_TaskIDPending( ent, TID_ANIM_BOTH) ) + {//Not waiting for bottom + Q3_TaskIDComplete( ent, TID_ANIM_UPPER ); + } + else + {//Waiting for both to finish before complete + Q3_TaskIDClear( &ent->taskID[TID_ANIM_UPPER] );//Top is done, regardless + if ( !Q3_TaskIDPending( ent, TID_ANIM_LOWER) ) + {//lower is done and we're done + Q3_TaskIDComplete( ent, TID_ANIM_BOTH ); + } + } + } +} + +extern qboolean PM_SpinningSaberAnim( int anim ); +extern float saberAnimSpeedMod[NUM_FORCE_POWER_LEVELS]; +void PM_SaberStartTransAnim( int saberAnimLevel, int anim, float *animSpeed, gentity_t *gent ) +{ + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_ROLL_STAB ) + { + if ( g_saberAnimSpeed->value != 1.0f ) + { + *animSpeed *= g_saberAnimSpeed->value; + } + else if ( gent && gent->client && gent->client->ps.weapon == WP_SABER ) + { + if ( gent->client->ps.saber[0].animSpeedScale != 1.0f ) + { + *animSpeed *= gent->client->ps.saber[0].animSpeedScale; + } + if ( gent->client->ps.dualSabers + && gent->client->ps.saber[1].animSpeedScale != 1.0f ) + { + *animSpeed *= gent->client->ps.saber[1].animSpeedScale; + } + } + } + if ( gent + && gent->client + && gent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.dualSabers + && saberAnimLevel == SS_DUAL + && gent->weaponModel[1] ) + {//using a scepter and dual style, slow down anims + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H7_S7_BR ) + { + *animSpeed *= 0.75; + } + } + if ( gent && gent->client && gent->client->ps.forceRageRecoveryTime > level.time ) + {//rage recovery + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR ) + {//animate slower + *animSpeed *= 0.75; + } + } + else if ( gent && gent->NPC && gent->NPC->rank == RANK_CIVILIAN ) + {//grunt reborn + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + {//his fast attacks are slower + if ( !PM_SpinningSaberAnim( anim ) ) + { + *animSpeed *= 0.75; + } + return; + } + } + else if ( gent && gent->client ) + { + if ( gent->client->ps.saber[0].type == SABER_LANCE || gent->client->ps.saber[0].type == SABER_TRIDENT ) + {//FIXME: hack for now - these use the fast anims, but slowed down. Should have own style + if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_R1_TR_S1 ) + {//his fast attacks are slower + if ( !PM_SpinningSaberAnim( anim ) ) + { + *animSpeed *= 0.75; + } + return; + } + } + } + + if ( ( anim >= BOTH_T1_BR__R && + anim <= BOTH_T1_BL_TL ) || + ( anim >= BOTH_T3_BR__R && + anim <= BOTH_T3_BL_TL ) || + ( anim >= BOTH_T5_BR__R && + anim <= BOTH_T5_BL_TL ) ) + { + if ( saberAnimLevel == FORCE_LEVEL_1 || saberAnimLevel == FORCE_LEVEL_5 ) + {//FIXME: should not be necc for FORCE_LEVEL_1's + *animSpeed *= 1.5; + } + else if ( saberAnimLevel == FORCE_LEVEL_3 ) + { + *animSpeed *= 0.75; + } + } +} +/* +void PM_SaberStartTransAnim( int anim, int entNum, int saberOffenseLevel, float *animSpeed ) +{ + //check starts + if ( ( anim >= BOTH_S1_S1_T_ && + anim <= BOTH_S1_S1_TR ) || + ( anim >= BOTH_S1_S1_T_ && + anim <= BOTH_S1_S1_TR ) || + ( anim >= BOTH_S3_S1_T_ && + anim <= BOTH_S3_S1_TR ) ) + { + if ( entNum == 0 ) + { + *animSpeed *= saberAnimSpeedMod[FORCE_LEVEL_3]; + } + else + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; + } + } + //Check transitions + else if ( PM_SpinningSaberAnim( anim ) ) + {//spins stay normal speed + return; + } + else if ( ( anim >= BOTH_T1_BR__R && + anim <= BOTH_T1_BL_TL ) || + ( anim >= BOTH_T2_BR__R && + anim <= BOTH_T2_BL_TL ) || + ( anim >= BOTH_T3_BR__R && + anim <= BOTH_T3_BL_TL ) ) + {//slow down the transitions + if ( entNum == 0 && saberOffenseLevel <= FORCE_LEVEL_2 ) + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]; + } + else + { + *animSpeed *= saberAnimSpeedMod[saberOffenseLevel]/2.0f; + } + } + + return; +} +*/ +extern qboolean player_locked; +extern qboolean MatrixMode; +float PM_GetTimeScaleMod( gentity_t *gent ) +{ + if ( g_timescale->value ) + { + if ( !MatrixMode + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_START + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_ATTACK + && gent->client->ps.legsAnim != BOTH_FORCELONGLEAP_LAND ) + { + if ( gent && gent->s.clientNum == 0 && !player_locked && gent->client->ps.forcePowersActive&(1<value); + } + else if ( gent && gent->client && gent->client->ps.forcePowersActive&(1<value); + } + } + } + return 1.0f; +} + +static inline qboolean PM_IsHumanoid( CGhoul2Info *ghlInfo ) +{ + char *GLAName; + GLAName = gi.G2API_GetGLAName( ghlInfo ); + assert(GLAName); + + if ( !Q_stricmp( "models/players/_humanoid/_humanoid", GLAName ) ) + { + return qtrue; + } + + return qfalse; +} + +/* +------------------------- +PM_SetAnimFinal +------------------------- +*/ +#define G2_DEBUG_TIMING (0) +void PM_SetAnimFinal(int *torsoAnim,int *legsAnim, + int setAnimParts,int anim,int setAnimFlags, + int *torsoAnimTimer,int *legsAnimTimer, + gentity_t *gent,int blendTime) // default blendTime=350 +{ + +// BASIC SETUP AND SAFETY CHECKING +//================================= + + // If It Is A Busted Entity, Don't Do Anything Here. + //--------------------------------------------------- + if (!gent || !gent->client) + { + return; + } + + // Make Sure This Character Has Such An Anim And A Model + //------------------------------------------------------- + if (anim<0 || anim>=MAX_ANIMATIONS || !ValidAnimFileIndex(gent->client->clientInfo.animFileIndex)) + { + #ifndef FINAL_BUILD + if (g_AnimWarning->integer) + { + if (anim<0 || anim>=MAX_ANIMATIONS) + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim Index (%d)!\n", anim); + } + else + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Invalid Anim File Index (%d)!\n", gent->client->clientInfo.animFileIndex); + } + } + #endif + return; + } + + + // Get Global Time Properties + //---------------------------- + float timeScaleMod = PM_GetTimeScaleMod( gent ); + const int actualTime = (cg.time?cg.time:level.time); + const animation_t* animations = level.knownAnimFileSets[gent->client->clientInfo.animFileIndex].animations; + const animation_t& curAnim = animations[anim]; + + // Make Sure This Character Has Such An Anim And A Model + //------------------------------------------------------- + if (animations[anim].numFrames==0) + { + #ifndef FINAL_BUILD + static int LastAnimWarningNum=0; + if (LastAnimWarningNum!=anim) + { + if ((cg_debugAnim.integer==3) || // 3 = do everyone + (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player + (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else + (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + ) + { + gi.Printf(S_COLOR_RED"PM_SetAnimFinal: Anim %s does not exist in this model (%s)!\n", animTable[anim].name, gent->NPC_type ); + } + } + LastAnimWarningNum = anim; + #endif + return; + } + + // If It's Not A Ghoul 2 Model, Just Remember The Anims And Stop, Because Everything Beyond This Is Ghoul2 + //--------------------------------------------------------------------------------------------------------- + if (!gi.G2API_HaveWeGhoul2Models(gent->ghoul2)) + { + if (setAnimParts&SETANIM_TORSO) + { + (*torsoAnim) = anim; + } + if (setAnimParts&SETANIM_LEGS) + { + (*legsAnim) = anim; + } + return; + } + + + // Lower Offensive Skill Slows Down The Saber Start Attack Animations + //-------------------------------------------------------------------- + PM_SaberStartTransAnim( gent->client->ps.saberAnimLevel, anim, &timeScaleMod, gent ); + + + +// SETUP VALUES FOR INCOMMING ANIMATION +//====================================== + const bool animFootMove = (PM_WalkingAnim(anim) || PM_RunningAnim(anim) || anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK); + const bool animHoldless = (setAnimFlags&SETANIM_FLAG_HOLDLESS)!=0; + const bool animHold = (setAnimFlags&SETANIM_FLAG_HOLD)!=0; + const bool animRestart = (setAnimFlags&SETANIM_FLAG_RESTART)!=0; + const bool animOverride = (setAnimFlags&SETANIM_FLAG_OVERRIDE)!=0; + const bool animSync = (g_synchSplitAnims->integer!=0 && !animRestart); + float animCurrent = (-1.0f); + float animSpeed = (50.0f / curAnim.frameLerp * timeScaleMod); // animSpeed is 1.0 if the frameLerp (ms/frame) is 50 (20 fps). + const float animFPS = (fabsf(curAnim.frameLerp)); + const int animDurMSec = (int)(((curAnim.numFrames - 1) * animFPS) / timeScaleMod); + const int animHoldMSec = ((animHoldless && timeScaleMod==1.0f)?((animDurMSec>1)?(animDurMSec-1):(animFPS)):(animDurMSec)); + int animFlags = (curAnim.loopFrames!=-1)?(BONE_ANIM_OVERRIDE_LOOP):(BONE_ANIM_OVERRIDE_FREEZE); + int animStart = (curAnim.firstFrame); + int animEnd = (curAnim.firstFrame)+(animations[anim].numFrames); + + // If We Have A Blend Timer, Add The Blend Flag + //---------------------------------------------- + if (blendTime > 0) + { + animFlags |= BONE_ANIM_BLEND; + } + + // If Animation Is Going Backwards, Swap Last And First Frames + //------------------------------------------------------------- + if (animSpeed<0.0f) + { +// #ifndef FINAL_BUILD + #if 0 + if (g_AnimWarning->integer==1) + { + if (animFlags&BONE_ANIM_OVERRIDE_LOOP) + { + gi.Printf(S_COLOR_YELLOW"PM_SetAnimFinal: WARNING: Anim (%s) looping backwards!\n", animTable[anim].name); + } + } + #endif + + int temp = animEnd; + animEnd = animStart; + animStart = temp; + blendTime = 0; + } + + // If The Animation Is Walking Or Running, Attempt To Scale The Playback Speed To Match + //-------------------------------------------------------------------------------------- + if (g_noFootSlide->integer + && animFootMove + && !(animSpeed<0.0f) + //FIXME: either read speed from animation.cfg or only do this for NPCs + // for whom we've specifically determined the proper numbers! + && gent->client->NPC_class != CLASS_HOWLER + && gent->client->NPC_class != CLASS_WAMPA + && gent->client->NPC_class != CLASS_GONK + && gent->client->NPC_class != CLASS_HOWLER + && gent->client->NPC_class != CLASS_MOUSE + && gent->client->NPC_class != CLASS_PROBE + && gent->client->NPC_class != CLASS_PROTOCOL + && gent->client->NPC_class != CLASS_R2D2 + && gent->client->NPC_class != CLASS_R5D2 + && gent->client->NPC_class != CLASS_SEEKER) + { + bool Walking = !!PM_WalkingAnim(anim); + bool HasDual = (gent->client->ps.saberAnimLevel==SS_DUAL); + bool HasStaff = (gent->client->ps.saberAnimLevel==SS_STAFF); + float moveSpeedOfAnim = 150.0f;//g_noFootSlideRunScale->value; + + if (anim==BOTH_CROUCH1WALK || anim==BOTH_CROUCH1WALKBACK) + { + moveSpeedOfAnim = 75.0f; + } + else + { + if (gent->client->NPC_class == CLASS_HAZARD_TROOPER) + { + moveSpeedOfAnim = 50.0f; + } + else if (gent->client->NPC_class == CLASS_RANCOR) + { + moveSpeedOfAnim = 173.0f; + } + else + { + if (Walking) + { + if (HasDual || HasStaff) + { + moveSpeedOfAnim = 100.0f; + } + else + { + moveSpeedOfAnim = 50.0f;// g_noFootSlideWalkScale->value; + } + } + else + { + if (HasStaff) + { + moveSpeedOfAnim = 250.0f; + } + else + { + moveSpeedOfAnim = 150.0f; + } + } + } + } + + + + + + + animSpeed *= (gent->resultspeed/moveSpeedOfAnim); + if (animSpeed<0.01f) + { + animSpeed = 0.01f; + } + + // Make Sure Not To Play Too Fast An Anim + //---------------------------------------- + float maxPlaybackSpeed = (1.5f * timeScaleMod); + if (animSpeed>maxPlaybackSpeed) + { + animSpeed = maxPlaybackSpeed; + } + } + + +// GET VALUES FOR EXISTING BODY ANIMATION +//========================================== + float bodySpeed = 0.0f; + float bodyCurrent = 0.0f; + int bodyStart = 0; + int bodyEnd = 0; + int bodyFlags = 0; + int bodyAnim = (*legsAnim); + int bodyBone = (gent->rootBone); + bool bodyTimerOn = ((*legsAnimTimer>0) || (*legsAnimTimer)==-1); + bool bodyPlay = ((setAnimParts&SETANIM_LEGS) && (bodyBone!=-1) && (animOverride || !bodyTimerOn)); + bool bodyAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, actualTime, &bodyCurrent, &bodyStart, &bodyEnd, &bodyFlags, &bodySpeed, NULL); + bool bodyOnAnimNow = (bodyAnimating && bodyAnim==anim && bodyStart==animStart && bodyEnd==animEnd); + bool bodyMatchTorsFrame = false; + + +// GET VALUES FOR EXISTING TORSO ANIMATION +//=========================================== + float torsSpeed = 0.0f; + float torsCurrent = 0.0f; + int torsStart = 0; + int torsEnd = 0; + int torsFlags = 0; + int torsAnim = (*torsoAnim); + int torsBone = (gent->lowerLumbarBone); + bool torsTimerOn = ((*torsoAnimTimer)>0 || (*torsoAnimTimer)==-1); + bool torsPlay = (gent->client->NPC_class!=CLASS_RANCOR && (setAnimParts&SETANIM_TORSO) && (torsBone!=-1) && (animOverride || !torsTimerOn)); + bool torsAnimating = !!gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, actualTime, &torsCurrent, &torsStart, &torsEnd, &torsFlags, &torsSpeed, NULL); + bool torsOnAnimNow = (torsAnimating && torsAnim==anim && torsStart==animStart && torsEnd==animEnd); + bool torsMatchBodyFrame = false; + + +// APPLY SYNC TO TORSO +//===================== + if (animSync && torsPlay && !bodyPlay && bodyOnAnimNow && (!torsOnAnimNow || torsCurrent!=bodyCurrent)) + { + torsMatchBodyFrame = true; + animCurrent = bodyCurrent; + } + if (animSync && bodyPlay && !torsPlay && torsOnAnimNow && (!bodyOnAnimNow || bodyCurrent!=torsCurrent)) + { + bodyMatchTorsFrame = true; + animCurrent = torsCurrent; + } + + // If Already Doing These Exact Parameters, Then Don't Play + //---------------------------------------------------------- + if (!animRestart) + { + torsPlay &= !(torsOnAnimNow && torsSpeed==animSpeed && !torsMatchBodyFrame); + bodyPlay &= !(bodyOnAnimNow && bodySpeed==animSpeed && !bodyMatchTorsFrame); + } + +#ifndef FINAL_BUILD + if ((cg_debugAnim.integer==3) || // 3 = do everyone + (cg_debugAnim.integer==1 && gent->s.number==0) || // 1 = only the player + (cg_debugAnim.integer==2 && gent->s.number!=0) || // 2 = only everyone else + (cg_debugAnim.integer==4 && gent->s.number!=cg_debugAnimTarget.integer) // 4 = specific entnum + ) + { + if (bodyPlay || torsPlay) + { + char* entName = gent->targetname; + char* location; + + // Select Entity Name + //-------------------- + if (!entName || !entName[0]) + { + entName = gent->NPC_targetname; + } + if (!entName || !entName[0]) + { + entName = gent->NPC_type; + } + if (!entName || !entName[0]) + { + entName = gent->classname; + } + if (!entName || !entName[0]) + { + entName = "UNKNOWN"; + } + + // Select Play Location + //---------------------- + if (bodyPlay && torsPlay) + { + location = "BOTH "; + } + else if (bodyPlay) + { + location = "LEGS "; + } + else + { + location = "TORSO"; + } + + // Print It! + //----------- + Com_Printf("[%10d] ent[%3d-%18s] %s anim[%3d] - %s\n", + actualTime, + gent->s.number, + entName, + location, + anim, + animTable[anim].name ); + } + } +#endif + + +// PLAY ON THE TORSO +//======================== + if (torsPlay) + { + *torsoAnim = anim; + float oldAnimCurrent = animCurrent; + if (animCurrent!=bodyCurrent && torsOnAnimNow && !animRestart && !torsMatchBodyFrame) + { + animCurrent = torsCurrent; + } + + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], torsBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + if (gent->motionBone!=-1) + { + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->motionBone, + animStart, + animEnd, + (torsOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + } + + animCurrent = oldAnimCurrent; + + // If This Animation Is To Be Locked And Held, Calculate The Duration And Set The Timer + //-------------------------------------------------------------------------------------- + if (animHold || animHoldless) + { + PM_SetTorsoAnimTimer(gent, torsoAnimTimer, animHoldMSec); + } + } + +// PLAY ON THE WHOLE BODY +//======================== + if (bodyPlay) + { + *legsAnim = anim; + + if (bodyOnAnimNow && !animRestart && !bodyMatchTorsFrame) + { + animCurrent = bodyCurrent; + } + + gi.G2API_SetAnimIndex(&gent->ghoul2[gent->playerModel], curAnim.glaIndex); + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], bodyBone, + animStart, + animEnd, + (bodyOnAnimNow && !animRestart)?(animFlags&~BONE_ANIM_BLEND):(animFlags), + animSpeed, + actualTime, + animCurrent, + blendTime); + + // If This Animation Is To Be Locked And Held, Calculate The Duration And Set The Timer + //-------------------------------------------------------------------------------------- + if (animHold || animHoldless) + { + PM_SetLegsAnimTimer(gent, legsAnimTimer, animHoldMSec); + } + } + + + + + +// PRINT SOME DEBUG TEXT OF EXISTING VALUES +//========================================== + if (false) + { + gi.Printf("PLAYANIM: (%3d) Speed(%4.2f) ", anim, animSpeed); + if (bodyAnimating) + { + gi.Printf("BODY: (%4.2f) (%4.2f) ", bodyCurrent, bodySpeed); + } + else + { + gi.Printf(" "); + } + if (torsAnimating) + { + gi.Printf("TORS: (%4.2f) (%4.2f)\n", torsCurrent, torsSpeed); + } + else + { + gi.Printf("\n"); + } + } +} + + + +void PM_SetAnim(pmove_t *pm,int setAnimParts,int anim,int setAnimFlags, int blendTime) +{ // FIXME : once torsoAnim and legsAnim are in the same structure for NPC and Players + // rename PM_SetAnimFinal to PM_SetAnim and have both NPC and Players call PM_SetAnim + + if ( pm->ps->pm_type >= PM_DEAD ) + {//FIXME: sometimes we'll want to set anims when your dead... twitches, impacts, etc. + return; + } + + if ( pm->gent == NULL ) + { + return; + } + + if ( !pm->gent || pm->gent->health > 0 ) + {//don't lock anims if the guy is dead + if ( pm->ps->torsoAnimTimer + && PM_LockedAnim( pm->ps->torsoAnim ) + && !PM_LockedAnim( anim ) ) + {//nothing can override these special anims + setAnimParts &= ~SETANIM_TORSO; + } + + if ( pm->ps->legsAnimTimer + && PM_LockedAnim( pm->ps->legsAnim ) + && !PM_LockedAnim( anim ) ) + {//nothing can override these special anims + setAnimParts &= ~SETANIM_LEGS; + } + } + + if ( !setAnimParts ) + { + return; + } + + if (setAnimFlags&SETANIM_FLAG_OVERRIDE) + { +// pm->ps->animationTimer = 0; + + if (setAnimParts & SETANIM_TORSO) + { + if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->torsoAnim != anim ) + { + PM_SetTorsoAnimTimer( pm->gent, &pm->ps->torsoAnimTimer, 0 ); + } + } + if (setAnimParts & SETANIM_LEGS) + { + if( (setAnimFlags & SETANIM_FLAG_RESTART) || pm->ps->legsAnim != anim ) + { + PM_SetLegsAnimTimer( pm->gent, &pm->ps->legsAnimTimer, 0 ); + } + } + } + + PM_SetAnimFinal(&pm->ps->torsoAnim,&pm->ps->legsAnim,setAnimParts,anim,setAnimFlags,&pm->ps->torsoAnimTimer,&pm->ps->legsAnimTimer,&g_entities[pm->ps->clientNum],blendTime);//was pm->gent +} + +bool TorsoAgainstWindTest( gentity_t* ent ) +{ + if (ent&&//valid ent + ent->client&&//a client + (ent->client->ps.weapon!=WP_SABER||ent->client->ps.saberMove==LS_READY)&&//either not holding a saber or the saber is in the ready pose + (ent->s.numbercurrentOrigin) && + gi.WE_IsOutside(ent->currentOrigin) ) + { + if (Q_stricmp(level.mapname, "t2_wedge")!=0) + { + vec3_t fwd; + vec3_t windDir; + if (gi.WE_GetWindVector(windDir, ent->currentOrigin)) + { + VectorScale(windDir, -1.0f, windDir); + AngleVectors(pm->gent->currentAngles, fwd, 0, 0); + if (DotProduct(fwd, windDir)>0.65f) + { + if (ent->client && ent->client->ps.torsoAnim!=BOTH_WIND) + { + NPC_SetAnim(ent, SETANIM_TORSO, BOTH_WIND, SETANIM_FLAG_NORMAL, 400); + } + return true; + } + } + } + } + return false; +} + +/* +------------------------- +PM_TorsoAnimLightsaber +------------------------- +*/ + + +// Note that this function is intended to set the animation for the player, but +// only does idle-ish anims. Anything that has a timer associated, such as attacks and blocks, +// are set by PM_WeaponLightsaber() + +extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt ); +extern qboolean PM_LandingAnim( int anim ); +extern qboolean PM_JumpingAnim( int anim ); +qboolean PM_InCartwheel( int anim ); +void PM_TorsoAnimLightsaber() +{ + // ********************************************************* + // WEAPON_READY + // ********************************************************* + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + {//holding an enemy aloft with force-grip + return; + } + + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + {//lightning + return; + } + + if ( pm->ps->forcePowersActive&(1<ps->saber[0].blade[0].active + && pm->ps->saber[0].blade[0].length < 3 + && !(pm->ps->saberEventFlags&SEF_HITWALL) + && pm->ps->weaponstate == WEAPON_RAISING ) + { + if (!G_IsRidingVehicle(pm->gent)) + { + PM_SetSaberMove(LS_DRAW); + } + return; + } + else if ( !pm->ps->SaberActive() && pm->ps->SaberLength() ) + { + if (!G_IsRidingVehicle(pm->gent)) + { + PM_SetSaberMove(LS_PUTAWAY); + } + return; + } + + if (pm->ps->weaponTime > 0) + { // weapon is already busy. + if ( pm->ps->torsoAnim == BOTH_TOSS1 + || pm->ps->torsoAnim == BOTH_TOSS2 ) + {//in toss + if ( !pm->ps->torsoAnimTimer ) + {//weird, get out of it, I guess + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + } + return; + } + + if ( pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + {//ready + if ( pm->ps->weapon == WP_SABER && (pm->ps->SaberLength()) ) + {//saber is on + // Select the proper idle Lightsaber attack move from the chart. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( (PM_RunningAnim( pm->ps->legsAnim ) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) + && pm->ps->saberBlockingTime < cg.time ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + /* + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + {//jumping, landing cartwheel, flipping + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetSaberMove( LS_READY ); + } + */ + } + else if (TorsoAgainstWindTest(pm->gent)) + { + } + else if( pm->ps->legsAnim == BOTH_RUN1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN2 )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_RUN_DUAL ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_WALK_DUAL ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + { + //??? Why nothing? What if you were running??? + //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_JUMP1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else + {//Used to default to both_stand1 which is an arms-down anim +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // Select the next proper pose for the lightsaber assuming that there are no attacks. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + } + + // ********************************************************* + // WEAPON_IDLE + // ********************************************************* + + else if ( pm->ps->weaponstate == WEAPON_IDLE ) + { + if (TorsoAgainstWindTest(pm->gent)) + { + } + else if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else + { +// This is now set in SetSaberMove. + // Idle for Lightsaber + if ( pm->gent && pm->gent->client ) + { +// pm->gent->client->saberTrail.inAction = qfalse; + } + + qboolean saberInAir = qtrue; + if ( pm->ps->saberInFlight ) + {//guiding saber + if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + {//we're stuck in a broken parry + saberInAir = qfalse; + } + if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + {// + if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + {//fell to the ground and we're not trying to pull it back + saberInAir = qfalse; + } + } + } + if ( pm->ps->saberInFlight + && saberInAir + && (!pm->ps->dualSabers || !pm->ps->saber[1].Active())) + { + if ( !PM_ForceAnim( pm->ps->torsoAnim ) + || pm->ps->torsoAnimTimer < 300 ) + {//don't interrupt a force power anim + if ( pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer ) + { + PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } + } + else + {//saber is on + // Idle for Lightsaber + if ( pm->gent && pm->gent->client ) + { + if ( !G_InCinematicSaberAnim( pm->gent ) ) + { + pm->gent->client->ps.SaberDeactivateTrail( 0 ); + } + } + // Idle for idle/ready Lightsaber +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONIDLE1 + // Select the proper idle Lightsaber attack move from the chart. + if (pm->ps->saberMove > LS_READY && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + else + { + if ( PM_JumpingAnim( pm->ps->legsAnim ) + || PM_LandingAnim( pm->ps->legsAnim ) + || PM_InCartwheel( pm->ps->legsAnim ) + || PM_FlippingAnim( pm->ps->legsAnim )) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( (PM_RunningAnim( pm->ps->legsAnim ) + || pm->ps->legsAnim == BOTH_WALK_STAFF + || pm->ps->legsAnim == BOTH_WALK_DUAL + || pm->ps->legsAnim == BOTH_WALKBACK_STAFF + || pm->ps->legsAnim == BOTH_WALKBACK_DUAL ) + && pm->ps->saberBlockingTime < cg.time ) + {//running w/1-handed weapon uses full-body anim + int setFlags = SETANIM_FLAG_NORMAL; + if ( PM_LandingAnim( pm->ps->torsoAnim ) ) + { + setFlags = SETANIM_FLAG_OVERRIDE; + } + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,setFlags); + } + else + { + PM_SetSaberMove(LS_READY); + } + } + } + } + } + } + } +} + + + + +/* +------------------------- +PM_TorsoAnimation +------------------------- +*/ + +void PM_TorsoAnimation( void ) +{//FIXME: Write a much smarter and more appropriate anim picking routine logic... +// int oldAnim; + if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps )) + {//in knockdown + return; + } + + if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + { + return; + } + + if ( (pm->ps->eFlags&EF_FORCE_DRAINED) ) + {//being drained + //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + return; + } + if ( (pm->ps->forcePowersActive&(1<ps->forceDrainEntityNum < ENTITYNUM_WORLD ) + {//draining + //PM_SetAnim( pm, SETANIM_TORSO, BOTH_HUGGER1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + return; + } + + if( pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags & SCF_FORCED_MARCH) ) + { + return; + } + + if(pm->gent != NULL && pm->gent->client) + { + pm->gent->client->renderInfo.torsoFpsMod = 1.0f; + } + + if ( pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON ) + { + if ( pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use )//ugly way to tell, but... + {//full body + PM_SetAnim(pm,SETANIM_BOTH,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else + {//torso + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + return; + } +/* else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->ps->clientNum < MAX_CLIENTS && (m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].numHands == 2 || g_speederControlScheme->value == 2) ) + {//can't look around + PM_SetAnim(pm,SETANIM_TORSO,m_pVehicleInfo[((CVehicleNPC *)pm->gent->NPC)->m_iVehicleTypeID].riderAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + return; + }*/ + + if ( pm->ps->taunting > level.time ) + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_ALORA_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else if ( pm->ps->weapon == WP_SABER && pm->ps->saberAnimLevel == SS_DUAL && PM_HasAnimation( pm->gent, BOTH_DUAL_TAUNT ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_DUAL_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + else if ( pm->ps->weapon == WP_SABER + && pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->saber[0].type == SABER_STAFF ) + {//turn on the blades + if ( PM_HasAnimation( pm->gent, BOTH_STAFF_TAUNT ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_STAFF_TAUNT,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + } + /* + else + { + if ( !pm->ps->saber[0].blade[0].active ) + {//first blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 0, qtrue ); + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is also off, extend time of this taunt so we have enough time to turn them both on + pm->ps->taunting = level.time + 3000; + } + } + else if ( (pm->ps->taunting - level.time) < 1500 ) + {//only 1500ms left in taunt + if ( !pm->ps->saber[0].blade[1].active ) + {//second blade is off + //turn it on + pm->ps->SaberBladeActivate( 0, 1, qtrue ); + } + } + //pose + PM_SetAnim(pm,SETANIM_BOTH,BOTH_SABERSTAFF_STANCE,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + pm->ps->torsoAnimTimer = pm->ps->legsAnimTimer = (pm->ps->taunting - level.time); + } + */ + } + else if ( PM_HasAnimation( pm->gent, BOTH_GESTURE1 ) ) + { + PM_SetAnim(pm,SETANIM_BOTH,BOTH_GESTURE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + pm->gent->client->ps.SaberActivateTrail( 100 ); + //FIXME: will this reset? + //FIXME: force-control (yellow glow) effect on hand and saber? + } + else + { + //PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE1,SETANIM_FLAG_NORMAL); + } + return; + } + + if (pm->ps->weapon == WP_SABER ) // WP_LIGHTSABER + { + qboolean saberInAir = qfalse; + if ( pm->ps->SaberLength() && !pm->ps->saberInFlight ) + { + PM_TorsoAnimLightsaber(); + } + else + { + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_GRIP] > FORCE_LEVEL_1 ) + {//holding an enemy aloft with force-grip + return; + } + if ( pm->ps->forcePowersActive&(1<ps->forcePowerLevel[FP_LIGHTNING] > FORCE_LEVEL_1 ) + {//lightning + return; + } + if ( pm->ps->forcePowersActive&(1<ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + {//we're stuck in a broken parry + PM_TorsoAnimLightsaber(); + } + else + { + if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + {// + if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + {//fell to the ground and we're not trying to pull it back + saberInAir = qfalse; + } + } + + if ( pm->ps->saberInFlight + && saberInAir + && (!pm->ps->dualSabers //not using 2 sabers + || !pm->ps->saber[1].Active() //left one off + || pm->ps->torsoAnim == BOTH_SABERDUAL_STANCE//not attacking + || pm->ps->torsoAnim == BOTH_SABERPULL//not attacking + || pm->ps->torsoAnim == BOTH_STAND1//not attacking + || PM_RunningAnim( pm->ps->torsoAnim ) //not attacking + || PM_WalkingAnim( pm->ps->torsoAnim ) //not attacking + || PM_JumpingAnim( pm->ps->torsoAnim )//not attacking + || PM_SwimmingAnim( pm->ps->torsoAnim ) )//not attacking + ) + { + if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 ) + {//don't interrupt a force power anim + if ( pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer ) + { + PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } + } + else + { + if ( PM_InSlopeAnim( pm->ps->legsAnim ) ) + {//HMM... this probably breaks the saber putaway and select anims + if ( pm->ps->SaberLength() > 0 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + } + } + } + } + + if (pm->ps->weaponTime<= 0 && (pm->ps->saberMove==LS_READY || pm->ps->SaberLength()==0) && !saberInAir) + { + TorsoAgainstWindTest(pm->gent); + } + return; + } + + if ( PM_ForceAnim( pm->ps->torsoAnim ) + && pm->ps->torsoAnimTimer > 0 ) + {//in a force anim, don't do a stand anim + return; + } + + + qboolean weaponBusy = qfalse; + + if ( pm->ps->weapon == WP_NONE ) + { + weaponBusy = qfalse; + } + else if ( pm->ps->weaponstate == WEAPON_FIRING || pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->lastShotTime > level.time - 3000 ) + { + weaponBusy = qtrue; + } + else if ( pm->ps->weaponTime > 0 ) + { + weaponBusy = qtrue; + } + else if ( pm->gent && pm->gent->client->fireDelay > 0 ) + { + weaponBusy = qtrue; + } + else if ( TorsoAgainstWindTest(pm->gent) ) + { + return; + } + else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.zoomTime > cg.time - 5000 ) + {//if we used binoculars recently, aim weapon + weaponBusy = qtrue; + pm->ps->weaponstate = WEAPON_IDLE; + } + else if ( pm->ps->pm_flags & PMF_DUCKED ) + {//ducking is considered on alert... plus looks stupid to have arms hanging down when crouched + weaponBusy = qtrue; + } + + if ( pm->ps->weapon == WP_NONE || + pm->ps->weaponstate == WEAPON_READY || + pm->ps->weaponstate == WEAPON_CHARGING || + pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + { + if ( pm->ps->weapon == WP_SABER && pm->ps->SaberLength() ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + } + else if( pm->ps->legsAnim == BOTH_RUN1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN2 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN4 && !weaponBusy )//&& pm->ps->saberAnimLevel != SS_STAFF ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN_STAFF && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_RUN_DUAL && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK2 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK_STAFF && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_WALK_DUAL&& !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_WALK_DUAL,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_CROUCH1IDLE && pm->ps->clientNum != 0 )//player falls through + { + //??? Why nothing? What if you were running??? + //PM_SetAnim(pm,SETANIM_TORSO,BOTH_CROUCH1IDLE,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_JUMP1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_JUMP1,SETANIM_FLAG_NORMAL, 100); // Only blend over 100ms + } + else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIMFORWARD && !weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + } + else if ( pm->ps->weapon == WP_NONE ) + { + int legsAnim = pm->ps->legsAnim; + /* + if ( PM_RollingAnim( legsAnim ) || + PM_FlippingAnim( legsAnim ) || + PM_JumpingAnim( legsAnim ) || + PM_PainAnim( legsAnim ) || + PM_SwimmingAnim( legsAnim ) ) + */ + { + PM_SetAnim(pm, SETANIM_TORSO, legsAnim, SETANIM_FLAG_NORMAL ); + } + } + else + {//Used to default to both_stand1 which is an arms-down anim + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else if ( pm->gent != NULL + && (pm->gent->s.numbergent)) + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT ) + {//PLayer- temp hack for weapon frame + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->ps->weapon == WP_MELEE ) + {//hehe + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + {//use legs anim + //FIXME: or just use whatever's currently playing? + //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); + } + else + { + switch(pm->ps->weapon) + { + // ******************************************************** + case WP_SABER: // WP_LIGHTSABER + // Ready pose for Lightsaber +// PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_NORMAL);//TORSO_WEAPONREADY1 + // Select the next proper pose for the lightsaber assuming that there are no attacks. + if (pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX) + { + PM_SetSaberMove(saberMoveData[pm->ps->saberMove].chain_idle); + } + break; + // ******************************************************** + + case WP_BRYAR_PISTOL: + //FIXME: if recently fired, hold the ready! + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + break; + case WP_BLASTER_PISTOL: + if ( pm->gent + && pm->gent->weaponModel[1] > 0 ) + {//dual pistols + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + } + else + {//single pistols + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + } + break; + case WP_NONE: + //NOTE: should never get here + break; + case WP_MELEE: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + break; + case WP_TUSKEN_STAFF: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND3, SETANIM_FLAG_NORMAL); + } + break; + + case WP_NOGHRI_STICK: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); + break; + + case WP_BLASTER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + //PM_SetAnim(pm,SETANIM_LEGS,BOTH_ATTACK2,SETANIM_FLAG_NORMAL); + break; + case WP_DISRUPTOR: + case WP_TUSKEN_RIFLE: + if ( (pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running sniper weapon uses normal ready + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + } + else + { + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );//TORSO_WEAPONREADY4//SETANIM_FLAG_RESTART| + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + } + break; + case WP_BOT_LASER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + break; + case WP_THERMAL: + if ( pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT + && (PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim )) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && (pm->ps->weaponstate == WEAPON_CHARGING || pm->ps->weaponstate == WEAPON_CHARGING_ALT) ) + {//player pulling back to throw + if ( PM_StandingAnim( pm->ps->legsAnim ) ) + { + PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else if ( pm->ps->legsAnim == BOTH_THERMAL_READY ) + {//sigh... hold it so pm_footsteps doesn't override + if ( pm->ps->legsAnimTimer < 100 ) + { + pm->ps->legsAnimTimer = 100; + } + } + PM_SetAnim( pm, SETANIM_TORSO, BOTH_THERMAL_READY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY10, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + } + break; + case WP_REPEATER: + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + {// + if ( pm->gent->alt_fire ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY1,SETANIM_FLAG_NORMAL); + } + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + default: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + break; + } + } + } + } + else if ( pm->ps->weaponstate == WEAPON_IDLE ) + { + if( pm->ps->legsAnim == BOTH_GUARD_LOOKAROUND1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_LOOKAROUND1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_GUARD_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUARD_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND1IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE1 + || pm->ps->legsAnim == BOTH_STAND2IDLE2 + || pm->ps->legsAnim == BOTH_STAND3IDLE1 + || pm->ps->legsAnim == BOTH_STAND5IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + pm->ps->saberMove = LS_READY; + } + else if( pm->ps->legsAnim == BOTH_STAND2TO4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND2TO4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND4TO2 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4TO2,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_STAND4 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND4,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIM_IDLE1 ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + } + else if( pm->ps->legsAnim == BOTH_SWIMFORWARD ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + } + else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + {//use legs anim + //FIXME: or just use whatever's currently playing? + //PM_SetAnim( pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL ); + } + else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && pm->ps->torsoAnim == BOTH_BUTTON_HOLD ) + {//using something + if ( !pm->ps->useTime ) + {//stopped holding it, release + PM_SetAnim( pm, SETANIM_TORSO, BOTH_BUTTON_RELEASE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + }//else still holding, leave it as it is + } + else + { + if ( !weaponBusy + && pm->ps->weapon != WP_BOWCASTER + && pm->ps->weapon != WP_REPEATER + && pm->ps->weapon != WP_FLECHETTE + && pm->ps->weapon != WP_ROCKET_LAUNCHER + && pm->ps->weapon != WP_CONCUSSION + && ( PM_RunningAnim( pm->ps->legsAnim ) + || (PM_WalkingAnim( pm->ps->legsAnim ) && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) ) + {//running w/1-handed or light 2-handed weapon uses full-body anim if you're not using the weapon right now + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + switch ( pm->ps->weapon ) + { + // ******************************************************** + case WP_SABER: // WP_LIGHTSABER + // Shouldn't get here, should go to TorsoAnimLightsaber + break; + // ******************************************************** + + case WP_BRYAR_PISTOL: + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + } + break; + case WP_BLASTER_PISTOL: + if ( pm->gent + && pm->gent->weaponModel[1] > 0 ) + {//dual pistols + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + else + {//single pistols + if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT || weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY2,SETANIM_FLAG_NORMAL); + } + else if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + } + } + break; + + case WP_NONE: + //NOTE: should never get here + break; + + case WP_MELEE: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + {//ignore + } + else if ( pm->gent && pm->gent->client && !PM_DroidMelee( pm->gent->client->NPC_class ) ) + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND6,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + } + } + break; + + case WP_TUSKEN_STAFF: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND3, SETANIM_FLAG_NORMAL); + } + break; + + case WP_NOGHRI_STICK: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_BLASTER: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + + case WP_DISRUPTOR: + case WP_TUSKEN_RIFLE: + if ( (pm->ps->weaponstate != WEAPON_FIRING + && pm->ps->weaponstate != WEAPON_CHARGING + && pm->ps->weaponstate != WEAPON_CHARGING_ALT) + || PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running sniper weapon uses normal ready + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY3, SETANIM_FLAG_NORMAL ); + } + } + else + { + if ( pm->ps->clientNum ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONREADY4, SETANIM_FLAG_NORMAL ); + } + } + break; + + case WP_BOT_LASER: + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + break; + + case WP_THERMAL: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE10, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + + case WP_REPEATER: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + case WP_TRIP_MINE: + case WP_DET_PACK: + if ( PM_RunningAnim( pm->ps->legsAnim ) + || PM_WalkingAnim( pm->ps->legsAnim ) + || PM_JumpingAnim( pm->ps->legsAnim ) + || PM_SwimmingAnim( pm->ps->legsAnim ) ) + {//running w/1-handed weapon uses full-body anim + PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + } + else + { + if ( weaponBusy ) + { + PM_SetAnim( pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL ); + } + else + { + PM_SetAnim( pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL ); + } + } + break; + + default: + if ( weaponBusy ) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONREADY3,SETANIM_FLAG_NORMAL); + } + else + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + } + break; + } + } + } + } +} + +//========================================================================= +// Anim checking utils +//========================================================================= + +int PM_GetTurnAnim( gentity_t *gent, int anim ) +{ + if ( !gent ) + { + return -1; + } + + switch( anim ) + { + case BOTH_STAND1: //# Standing idle: no weapon: hands down + case BOTH_STAND1IDLE1: //# Random standing idle + case BOTH_STAND2: //# Standing idle with a weapon + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle + case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle + case BOTH_STAND4: //# two handed: gun down: relaxed stand + case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle + case BOTH_STAND6: //# one handed: gun at side: relaxed stand + case BOTH_STAND2TO4: //# Transition from stand2 to stand4 + case BOTH_STAND4TO2: //# Transition from stand4 to stand2 + case BOTH_GESTURE1: //# Generic gesture: non-specific + case BOTH_GESTURE2: //# Generic gesture: non-specific + case BOTH_TALK1: //# Generic talk anim + case BOTH_TALK2: //# Generic talk anim + if ( PM_HasAnimation( gent, LEGS_TURN1 ) ) + { + return LEGS_TURN1; + } + else + { + return -1; + } + break; + case BOTH_ATTACK1: //# Attack with generic 1-handed weapon + case BOTH_ATTACK2: //# Attack with generic 2-handed weapon + case BOTH_ATTACK3: //# Attack with heavy 2-handed weapon + case BOTH_ATTACK4: //# Attack with ??? + case BOTH_MELEE1: //# First melee attack + case BOTH_MELEE2: //# Second melee attack + case BOTH_GUARD_LOOKAROUND1: //# Cradling weapon and looking around + case BOTH_GUARD_IDLE1: //# Cradling weapon and standing + if ( PM_HasAnimation( gent, LEGS_TURN2 ) ) + { + return LEGS_TURN2; + } + else + { + return -1; + } + break; + default: + return -1; + break; + } +} + +int PM_TurnAnimForLegsAnim( gentity_t *gent, int anim ) +{ + if ( !gent ) + { + return -1; + } + + switch( anim ) + { + case BOTH_STAND1: //# Standing idle: no weapon: hands down + case BOTH_STAND1IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND1 ) ) + { + return BOTH_TURNSTAND1; + } + else + { + return -1; + } + break; + case BOTH_STAND2: //# Standing idle with a weapon + case BOTH_SABERFAST_STANCE: + case BOTH_SABERSLOW_STANCE: + case BOTH_STAND2IDLE1: //# Random standing idle + case BOTH_STAND2IDLE2: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND2 ) ) + { + return BOTH_TURNSTAND2; + } + else + { + return -1; + } + break; + case BOTH_STAND3: //# Standing hands behind back: at ease: etc. + case BOTH_STAND3IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND3 ) ) + { + return BOTH_TURNSTAND3; + } + else + { + return -1; + } + break; + case BOTH_STAND4: //# two handed: gun down: relaxed stand + if ( PM_HasAnimation( gent, BOTH_TURNSTAND4 ) ) + { + return BOTH_TURNSTAND4; + } + else + { + return -1; + } + break; + case BOTH_STAND5: //# standing idle, no weapon, hand down, back straight + case BOTH_STAND5IDLE1: //# Random standing idle + if ( PM_HasAnimation( gent, BOTH_TURNSTAND5 ) ) + { + return BOTH_TURNSTAND5; + } + else + { + return -1; + } + break; + case BOTH_CROUCH1: //# Transition from standing to crouch + case BOTH_CROUCH1IDLE: //# Crouching idle + /* + case BOTH_UNCROUCH1: //# Transition from crouch to standing + case BOTH_CROUCH2TOSTAND1: //# going from crouch2 to stand1 + case BOTH_CROUCH3: //# Desann crouching down to Kyle (cin 9) + case BOTH_UNCROUCH3: //# Desann uncrouching down to Kyle (cin 9) + case BOTH_CROUCH4: //# Slower version of crouch1 for cinematics + case BOTH_UNCROUCH4: //# Slower version of uncrouch1 for cinematics + */ + if ( PM_HasAnimation( gent, BOTH_TURNCROUCH1 ) ) + { + return BOTH_TURNCROUCH1; + } + else + { + return -1; + } + break; + default: + return -1; + break; + } +} + +qboolean PM_InOnGroundAnim ( playerState_t *ps ) +{ + switch( ps->legsAnim ) + { + case BOTH_DEAD1: + case BOTH_DEAD2: + case BOTH_DEAD3: + case BOTH_DEAD4: + case BOTH_DEAD5: + case BOTH_DEADFORWARD1: + case BOTH_DEADBACKWARD1: + case BOTH_DEADFORWARD2: + case BOTH_DEADBACKWARD2: + case BOTH_LYINGDEATH1: + case BOTH_LYINGDEAD1: + case BOTH_SLEEP1: //# laying on back-rknee up-rhand on torso + return qtrue; + break; + case BOTH_KNOCKDOWN1: //# + case BOTH_KNOCKDOWN2: //# + case BOTH_KNOCKDOWN3: //# + case BOTH_KNOCKDOWN4: //# + case BOTH_KNOCKDOWN5: //# + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_RELEASED: + if ( ps->legsAnimTimer < 500 ) + {//pretty much horizontal by this point + return qtrue; + } + break; + case BOTH_PLAYER_PA_3_FLY: + if ( ps->legsAnimTimer < 300 ) + {//pretty much horizontal by this point + return qtrue; + } + /* + else if ( ps->clientNum < MAX_CLIENTS + && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) + { + return qtrue; + } + */ + break; + case BOTH_GETUP1: + case BOTH_GETUP2: + case BOTH_GETUP3: + case BOTH_GETUP4: + case BOTH_GETUP5: + case BOTH_GETUP_CROUCH_F1: + case BOTH_GETUP_CROUCH_B1: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + if ( ps->legsAnimTimer > PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim )-400 ) + {//still pretty much horizontal at this point + return qtrue; + } + break; + } + + return qfalse; +} + +qboolean PM_InSpecialDeathAnim( int anim ) +{ + switch( pm->ps->legsAnim ) + { + case BOTH_DEATH_ROLL: //# Death anim from a roll + case BOTH_DEATH_FLIP: //# Death anim from a flip + case BOTH_DEATH_SPIN_90_R: //# Death anim when facing 90 degrees right + case BOTH_DEATH_SPIN_90_L: //# Death anim when facing 90 degrees left + case BOTH_DEATH_SPIN_180: //# Death anim when facing backwards + case BOTH_DEATH_LYING_UP: //# Death anim when lying on back + case BOTH_DEATH_LYING_DN: //# Death anim when lying on front + case BOTH_DEATH_FALLING_DN: //# Death anim when falling on face + case BOTH_DEATH_FALLING_UP: //# Death anim when falling on back + case BOTH_DEATH_CROUCHED: //# Death anim when crouched + return qtrue; + break; + default: + return qfalse; + break; + } +} + +qboolean PM_InDeathAnim ( void ) +{//Purposely does not cover stumbledeath and falldeath... + switch( pm->ps->legsAnim ) + { + case BOTH_DEATH1: //# First Death anim + case BOTH_DEATH2: //# Second Death anim + case BOTH_DEATH3: //# Third Death anim + case BOTH_DEATH4: //# Fourth Death anim + case BOTH_DEATH5: //# Fifth Death anim + case BOTH_DEATH6: //# Sixth Death anim + case BOTH_DEATH7: //# Seventh Death anim + case BOTH_DEATH8: //# + case BOTH_DEATH9: //# + case BOTH_DEATH10: //# + case BOTH_DEATH11: //# + case BOTH_DEATH12: //# + case BOTH_DEATH13: //# + case BOTH_DEATH14: //# + case BOTH_DEATH14_UNGRIP: //# Desann's end death (cin #35) + case BOTH_DEATH14_SITUP: //# Tavion sitting up after having been thrown (cin #23) + case BOTH_DEATH15: //# + case BOTH_DEATH16: //# + case BOTH_DEATH17: //# + case BOTH_DEATH18: //# + case BOTH_DEATH19: //# + case BOTH_DEATH20: //# + case BOTH_DEATH21: //# + case BOTH_DEATH22: //# + case BOTH_DEATH23: //# + case BOTH_DEATH24: //# + case BOTH_DEATH25: //# + + case BOTH_DEATHFORWARD1: //# First Death in which they get thrown forward + case BOTH_DEATHFORWARD2: //# Second Death in which they get thrown forward + case BOTH_DEATHFORWARD3: //# Tavion's falling in cin# 23 + case BOTH_DEATHBACKWARD1: //# First Death in which they get thrown backward + case BOTH_DEATHBACKWARD2: //# Second Death in which they get thrown backward + + case BOTH_DEATH1IDLE: //# Idle while close to death + case BOTH_LYINGDEATH1: //# Death to play when killed lying down + case BOTH_STUMBLEDEATH1: //# Stumble forward and fall face first death + case BOTH_FALLDEATH1: //# Fall forward off a high cliff and splat death - start + case BOTH_FALLDEATH1INAIR: //# Fall forward off a high cliff and splat death - loop + case BOTH_FALLDEATH1LAND: //# Fall forward off a high cliff and splat death - hit bottom + //# #sep case BOTH_ DEAD POSES # Should be last frame of corresponding previous anims + case BOTH_DEAD1: //# First Death finished pose + case BOTH_DEAD2: //# Second Death finished pose + case BOTH_DEAD3: //# Third Death finished pose + case BOTH_DEAD4: //# Fourth Death finished pose + case BOTH_DEAD5: //# Fifth Death finished pose + case BOTH_DEAD6: //# Sixth Death finished pose + case BOTH_DEAD7: //# Seventh Death finished pose + case BOTH_DEAD8: //# + case BOTH_DEAD9: //# + case BOTH_DEAD10: //# + case BOTH_DEAD11: //# + case BOTH_DEAD12: //# + case BOTH_DEAD13: //# + case BOTH_DEAD14: //# + case BOTH_DEAD15: //# + case BOTH_DEAD16: //# + case BOTH_DEAD17: //# + case BOTH_DEAD18: //# + case BOTH_DEAD19: //# + case BOTH_DEAD20: //# + case BOTH_DEAD21: //# + case BOTH_DEAD22: //# + case BOTH_DEAD23: //# + case BOTH_DEAD24: //# + case BOTH_DEAD25: //# + case BOTH_DEADFORWARD1: //# First thrown forward death finished pose + case BOTH_DEADFORWARD2: //# Second thrown forward death finished pose + case BOTH_DEADBACKWARD1: //# First thrown backward death finished pose + case BOTH_DEADBACKWARD2: //# Second thrown backward death finished pose + case BOTH_LYINGDEAD1: //# Killed lying down death finished pose + case BOTH_STUMBLEDEAD1: //# Stumble forward death finished pose + case BOTH_FALLDEAD1LAND: //# Fall forward and splat death finished pose + //# #sep case BOTH_ DEAD TWITCH/FLOP # React to being shot from death poses + case BOTH_DEADFLOP1: //# React to being shot from First Death finished pose + case BOTH_DEADFLOP2: //# React to being shot from Second Death finished pose + case BOTH_DISMEMBER_HEAD1: //# + case BOTH_DISMEMBER_TORSO1: //# + case BOTH_DISMEMBER_LLEG: //# + case BOTH_DISMEMBER_RLEG: //# + case BOTH_DISMEMBER_RARM: //# + case BOTH_DISMEMBER_LARM: //# + return qtrue; + break; + default: + return PM_InSpecialDeathAnim( pm->ps->legsAnim ); + break; + } +} + +qboolean PM_InCartwheel( int anim ) +{ + switch ( anim ) + { + case BOTH_ARIAL_LEFT: + case BOTH_ARIAL_RIGHT: + case BOTH_ARIAL_F1: + case BOTH_CARTWHEEL_LEFT: + case BOTH_CARTWHEEL_RIGHT: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_InButterfly( int anim ) +{ + switch ( anim ) + { + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_StandingAnim( int anim ) +{//NOTE: does not check idles or special (cinematic) stands + switch ( anim ) + { + case BOTH_STAND1: + case BOTH_STAND2: + case BOTH_STAND3: + case BOTH_STAND4: + case BOTH_ATTACK3: + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_InAirKickingAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_B_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + return qtrue; + } + return qfalse; +} + +qboolean PM_KickingAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_A7_KICK_F: + case BOTH_A7_KICK_B: + case BOTH_A7_KICK_R: + case BOTH_A7_KICK_L: + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + //NOT a kick, but acts like one: + case BOTH_A7_HILT: + //NOT kicks, but do kick traces anyway + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + return qtrue; + break; + default: + return PM_InAirKickingAnim( anim ); + break; + } + //return qfalse; +} + +qboolean PM_StabDownAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_STABDOWN: + case BOTH_STABDOWN_STAFF: + case BOTH_STABDOWN_DUAL: + return qtrue; + } + return qfalse; +} + +qboolean PM_GoingToAttackDown( playerState_t *ps ) +{ + if ( PM_StabDownAnim( ps->torsoAnim )//stabbing downward + || ps->saberMove == LS_A_LUNGE//lunge + || ps->saberMove == LS_A_JUMP_T__B_//death from above + || ps->saberMove == LS_A_T2B//attacking top to bottom + || ps->saberMove == LS_S_T2B//starting at attack downward + || (PM_SaberInTransition( ps->saberMove ) && saberMoveData[ps->saberMove].endQuad == Q_T) )//transitioning to a top to bottom attack + { + return qtrue; + } + return qfalse; +} + +qboolean PM_ForceUsingSaberAnim( int anim ) +{//saber/acrobatic anims that should prevent you from recharging force power while you're in them... + switch ( anim ) + { + case BOTH_JUMPFLIPSLASHDOWN1: + case BOTH_JUMPFLIPSTABDOWN: + case BOTH_FORCELEAP2_T__B_: + case BOTH_JUMPATTACK6: + case BOTH_JUMPATTACK7: + case BOTH_FORCELONGLEAP_START: + case BOTH_FORCELONGLEAP_ATTACK: + case BOTH_FORCEWALLRUNFLIP_START: + case BOTH_FORCEWALLRUNFLIP_END: + case BOTH_FORCEWALLRUNFLIP_ALT: + case BOTH_FORCEWALLREBOUND_FORWARD: + case BOTH_FORCEWALLREBOUND_LEFT: + case BOTH_FORCEWALLREBOUND_BACK: + case BOTH_FORCEWALLREBOUND_RIGHT: + case BOTH_FLIP_ATTACK7: + case BOTH_FLIP_HOLD7: + case BOTH_FLIP_LAND: + case BOTH_PULL_IMPALE_STAB: + case BOTH_PULL_IMPALE_SWING: + case BOTH_A6_SABERPROTECT: + case BOTH_A7_SOULCAL: + case BOTH_A1_SPECIAL: + case BOTH_A2_SPECIAL: + case BOTH_A3_SPECIAL: + case BOTH_ARIAL_LEFT: + case BOTH_ARIAL_RIGHT: + case BOTH_CARTWHEEL_LEFT: + case BOTH_CARTWHEEL_RIGHT: + case BOTH_FLIP_LEFT: + case BOTH_FLIP_BACK1: + case BOTH_FLIP_BACK2: + case BOTH_FLIP_BACK3: + case BOTH_ALORA_FLIP_B: + case BOTH_BUTTERFLY_LEFT: + case BOTH_BUTTERFLY_RIGHT: + case BOTH_BUTTERFLY_FL1: + case BOTH_BUTTERFLY_FR1: + case BOTH_WALL_RUN_RIGHT: + case BOTH_WALL_RUN_RIGHT_FLIP: + case BOTH_WALL_RUN_RIGHT_STOP: + case BOTH_WALL_RUN_LEFT: + case BOTH_WALL_RUN_LEFT_FLIP: + case BOTH_WALL_RUN_LEFT_STOP: + case BOTH_WALL_FLIP_RIGHT: + case BOTH_WALL_FLIP_LEFT: + case BOTH_FORCEJUMP1: + case BOTH_FORCEINAIR1: + case BOTH_FORCELAND1: + case BOTH_FORCEJUMPBACK1: + case BOTH_FORCEINAIRBACK1: + case BOTH_FORCELANDBACK1: + case BOTH_FORCEJUMPLEFT1: + case BOTH_FORCEINAIRLEFT1: + case BOTH_FORCELANDLEFT1: + case BOTH_FORCEJUMPRIGHT1: + case BOTH_FORCEINAIRRIGHT1: + case BOTH_FORCELANDRIGHT1: + case BOTH_FLIP_F: + case BOTH_FLIP_B: + case BOTH_FLIP_L: + case BOTH_FLIP_R: + case BOTH_ALORA_FLIP_1: + case BOTH_ALORA_FLIP_2: + case BOTH_ALORA_FLIP_3: + case BOTH_DODGE_FL: + case BOTH_DODGE_FR: + case BOTH_DODGE_BL: + case BOTH_DODGE_BR: + case BOTH_DODGE_L: + case BOTH_DODGE_R: + case BOTH_DODGE_HOLD_FL: + case BOTH_DODGE_HOLD_FR: + case BOTH_DODGE_HOLD_BL: + case BOTH_DODGE_HOLD_BR: + case BOTH_DODGE_HOLD_L: + case BOTH_DODGE_HOLD_R: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_BROLL_L: + case BOTH_GETUP_BROLL_R: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + case BOTH_GETUP_FROLL_L: + case BOTH_GETUP_FROLL_R: + case BOTH_WALL_FLIP_BACK1: + case BOTH_WALL_FLIP_BACK2: + case BOTH_SPIN1: + case BOTH_FJSS_TR_BL: + case BOTH_FJSS_TL_BR: + case BOTH_DEFLECTSLASH__R__L_FIN: + case BOTH_ARIAL_F1: + return qtrue; + } + return qfalse; +} + +qboolean G_HasKnockdownAnims( gentity_t *ent ) +{ + if ( PM_HasAnimation( ent, BOTH_KNOCKDOWN1 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN2 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN3 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN4 ) + && PM_HasAnimation( ent, BOTH_KNOCKDOWN5 ) ) + { + return qtrue; + } + return qfalse; +} + +qboolean PM_InAttackRoll( int anim ) +{ + switch ( anim ) + { + case BOTH_GETUP_BROLL_B: + case BOTH_GETUP_BROLL_F: + case BOTH_GETUP_FROLL_B: + case BOTH_GETUP_FROLL_F: + return qtrue; + } + return qfalse; +} + +qboolean PM_LockedAnim( int anim ) +{//anims that can *NEVER* be overridden, regardless + switch ( anim ) + { + case BOTH_KYLE_PA_1: + case BOTH_KYLE_PA_2: + case BOTH_KYLE_PA_3: + case BOTH_PLAYER_PA_1: + case BOTH_PLAYER_PA_2: + case BOTH_PLAYER_PA_3: + case BOTH_PLAYER_PA_3_FLY: + case BOTH_TAVION_SCEPTERGROUND: + case BOTH_TAVION_SWORDPOWER: + case BOTH_SCEPTER_START: + case BOTH_SCEPTER_HOLD: + case BOTH_SCEPTER_STOP: + //grabbed by wampa + case BOTH_GRABBED: //# + case BOTH_RELEASED: //# when Wampa drops player, transitions into fall on back + case BOTH_HANG_IDLE: //# + case BOTH_HANG_ATTACK: //# + case BOTH_HANG_PAIN: //# + return qtrue; + } + return qfalse; +} + +qboolean PM_SuperBreakLoseAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_LK_S_DL_S_SB_1_L: //super break I lost + case BOTH_LK_S_DL_T_SB_1_L: //super break I lost + case BOTH_LK_S_ST_S_SB_1_L: //super break I lost + case BOTH_LK_S_ST_T_SB_1_L: //super break I lost + case BOTH_LK_S_S_S_SB_1_L: //super break I lost + case BOTH_LK_S_S_T_SB_1_L: //super break I lost + case BOTH_LK_DL_DL_S_SB_1_L: //super break I lost + case BOTH_LK_DL_DL_T_SB_1_L: //super break I lost + case BOTH_LK_DL_ST_S_SB_1_L: //super break I lost + case BOTH_LK_DL_ST_T_SB_1_L: //super break I lost + case BOTH_LK_DL_S_S_SB_1_L: //super break I lost + case BOTH_LK_DL_S_T_SB_1_L: //super break I lost + case BOTH_LK_ST_DL_S_SB_1_L: //super break I lost + case BOTH_LK_ST_DL_T_SB_1_L: //super break I lost + case BOTH_LK_ST_ST_S_SB_1_L: //super break I lost + case BOTH_LK_ST_ST_T_SB_1_L: //super break I lost + case BOTH_LK_ST_S_S_SB_1_L: //super break I lost + case BOTH_LK_ST_S_T_SB_1_L: //super break I lost + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_SuperBreakWinAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_LK_S_DL_S_SB_1_W: //super break I won + case BOTH_LK_S_DL_T_SB_1_W: //super break I won + case BOTH_LK_S_ST_S_SB_1_W: //super break I won + case BOTH_LK_S_ST_T_SB_1_W: //super break I won + case BOTH_LK_S_S_S_SB_1_W: //super break I won + case BOTH_LK_S_S_T_SB_1_W: //super break I won + case BOTH_LK_DL_DL_S_SB_1_W: //super break I won + case BOTH_LK_DL_DL_T_SB_1_W: //super break I won + case BOTH_LK_DL_ST_S_SB_1_W: //super break I won + case BOTH_LK_DL_ST_T_SB_1_W: //super break I won + case BOTH_LK_DL_S_S_SB_1_W: //super break I won + case BOTH_LK_DL_S_T_SB_1_W: //super break I won + case BOTH_LK_ST_DL_S_SB_1_W: //super break I won + case BOTH_LK_ST_DL_T_SB_1_W: //super break I won + case BOTH_LK_ST_ST_S_SB_1_W: //super break I won + case BOTH_LK_ST_ST_T_SB_1_W: //super break I won + case BOTH_LK_ST_S_S_SB_1_W: //super break I won + case BOTH_LK_ST_S_T_SB_1_W: //super break I won + return qtrue; + break; + } + return qfalse; +} + +qboolean PM_SaberLockBreakAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_BF1BREAK: + case BOTH_BF2BREAK: + case BOTH_CWCIRCLEBREAK: + case BOTH_CCWCIRCLEBREAK: + case BOTH_LK_S_DL_S_B_1_L: //normal break I lost + case BOTH_LK_S_DL_S_B_1_W: //normal break I won + case BOTH_LK_S_DL_T_B_1_L: //normal break I lost + case BOTH_LK_S_DL_T_B_1_W: //normal break I won + case BOTH_LK_S_ST_S_B_1_L: //normal break I lost + case BOTH_LK_S_ST_S_B_1_W: //normal break I won + case BOTH_LK_S_ST_T_B_1_L: //normal break I lost + case BOTH_LK_S_ST_T_B_1_W: //normal break I won + case BOTH_LK_S_S_S_B_1_L: //normal break I lost + case BOTH_LK_S_S_S_B_1_W: //normal break I won + case BOTH_LK_S_S_T_B_1_L: //normal break I lost + case BOTH_LK_S_S_T_B_1_W: //normal break I won + case BOTH_LK_DL_DL_S_B_1_L: //normal break I lost + case BOTH_LK_DL_DL_S_B_1_W: //normal break I won + case BOTH_LK_DL_DL_T_B_1_L: //normal break I lost + case BOTH_LK_DL_DL_T_B_1_W: //normal break I won + case BOTH_LK_DL_ST_S_B_1_L: //normal break I lost + case BOTH_LK_DL_ST_S_B_1_W: //normal break I won + case BOTH_LK_DL_ST_T_B_1_L: //normal break I lost + case BOTH_LK_DL_ST_T_B_1_W: //normal break I won + case BOTH_LK_DL_S_S_B_1_L: //normal break I lost + case BOTH_LK_DL_S_S_B_1_W: //normal break I won + case BOTH_LK_DL_S_T_B_1_L: //normal break I lost + case BOTH_LK_DL_S_T_B_1_W: //normal break I won + case BOTH_LK_ST_DL_S_B_1_L: //normal break I lost + case BOTH_LK_ST_DL_S_B_1_W: //normal break I won + case BOTH_LK_ST_DL_T_B_1_L: //normal break I lost + case BOTH_LK_ST_DL_T_B_1_W: //normal break I won + case BOTH_LK_ST_ST_S_B_1_L: //normal break I lost + case BOTH_LK_ST_ST_S_B_1_W: //normal break I won + case BOTH_LK_ST_ST_T_B_1_L: //normal break I lost + case BOTH_LK_ST_ST_T_B_1_W: //normal break I won + case BOTH_LK_ST_S_S_B_1_L: //normal break I lost + case BOTH_LK_ST_S_S_B_1_W: //normal break I won + case BOTH_LK_ST_S_T_B_1_L: //normal break I lost + case BOTH_LK_ST_S_T_B_1_W: //normal break I won + return (PM_SuperBreakLoseAnim(anim)||PM_SuperBreakWinAnim(anim)); + break; + } + return qfalse; +} + +qboolean PM_GetupAnimNoMove( int legsAnim ) +{ + switch( legsAnim ) + { + case BOTH_GETUP1: + case BOTH_GETUP2: + case BOTH_GETUP3: + case BOTH_GETUP4: + case BOTH_GETUP5: + case BOTH_GETUP_CROUCH_F1: + case BOTH_GETUP_CROUCH_B1: + case BOTH_FORCE_GETUP_F1: + case BOTH_FORCE_GETUP_F2: + case BOTH_FORCE_GETUP_B1: + case BOTH_FORCE_GETUP_B2: + case BOTH_FORCE_GETUP_B3: + case BOTH_FORCE_GETUP_B4: + case BOTH_FORCE_GETUP_B5: + case BOTH_FORCE_GETUP_B6: + return qtrue; + } + return qfalse; +} + +qboolean PM_KnockDownAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_KNOCKDOWN1: + case BOTH_KNOCKDOWN2: + case BOTH_KNOCKDOWN3: + case BOTH_KNOCKDOWN4: + case BOTH_KNOCKDOWN5: + /* + //special anims: + case BOTH_RELEASED: + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_PLAYER_PA_3_FLY: + */ + return qtrue; + break; + /* + default: + return PM_InGetUp( ps ); + break; + */ + } + return qfalse; +} + +qboolean PM_KnockDownAnimExtended( int anim ) +{ + switch ( anim ) + { + case BOTH_KNOCKDOWN1: + case BOTH_KNOCKDOWN2: + case BOTH_KNOCKDOWN3: + case BOTH_KNOCKDOWN4: + case BOTH_KNOCKDOWN5: + //special anims: + case BOTH_RELEASED: + case BOTH_LK_DL_ST_T_SB_1_L: + case BOTH_PLAYER_PA_3_FLY: + return qtrue; + break; + /* + default: + return PM_InGetUp( ps ); + break; + */ + } + return qfalse; +} + +qboolean PM_SaberInKata( saberMoveName_t saberMove ) +{ + switch ( saberMove ) + { + case LS_A1_SPECIAL: + case LS_A2_SPECIAL: + case LS_A3_SPECIAL: + case LS_DUAL_SPIN_PROTECT: + case LS_STAFF_SOULCAL: + return qtrue; + default: + break; + } + return qfalse; +} + +qboolean PM_CanRollFromSoulCal( playerState_t *ps ) +{ + if ( ps->legsAnim == BOTH_A7_SOULCAL + && ps->legsAnimTimer < 700 + && ps->legsAnimTimer > 250 ) + { + return qtrue; + } + return qfalse; +} + +qboolean BG_FullBodyTauntAnim( int anim ) +{ + switch ( anim ) + { + case BOTH_GESTURE1: + case BOTH_DUAL_TAUNT: + case BOTH_STAFF_TAUNT: + case BOTH_BOW: + case BOTH_MEDITATE: + case BOTH_SHOWOFF_FAST: + case BOTH_SHOWOFF_MEDIUM: + case BOTH_SHOWOFF_STRONG: + case BOTH_SHOWOFF_DUAL: + case BOTH_SHOWOFF_STAFF: + case BOTH_VICTORY_FAST: + case BOTH_VICTORY_MEDIUM: + case BOTH_VICTORY_STRONG: + case BOTH_VICTORY_DUAL: + case BOTH_VICTORY_STAFF: + return qtrue; + break; + } + return qfalse; +} diff --git a/code/game/bg_pmove.cpp b/code/game/bg_pmove.cpp index f0856a5a6e..e82ce13d13 100644 --- a/code/game/bg_pmove.cpp +++ b/code/game/bg_pmove.cpp @@ -103,7 +103,7 @@ extern void WP_ForcePowerStop( gentity_t *self, forcePowers_t forcePower ); extern qboolean WP_ForcePowerAvailable( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); extern float G_ForceWallJumpStrength( void ); -extern int G_CheckRollSafety( gentity_t *self, int anim, float testDist ); +extern qboolean G_CheckRollSafety( gentity_t *self, int anim, float testDist ); extern saberMoveName_t PM_CheckDualSpinProtect( void ); extern saberMoveName_t PM_CheckPullAttack( void ); extern qboolean JET_Flying( gentity_t *self ); @@ -134,6 +134,8 @@ qboolean PM_InReboundJump( int anim ); qboolean PM_ForceJumpingAnim( int anim ); void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ); +extern qboolean PlayerAffectedByStasis( void ); + extern int parryDebounce[]; extern qboolean cg_usingInFrontOf; extern qboolean player_locked; @@ -146,8 +148,16 @@ extern cvar_t *g_debugMelee; extern cvar_t *g_saberNewControlScheme; extern cvar_t *g_stepSlideFix; extern cvar_t *g_saberAutoBlocking; +extern cvar_t *g_autoRoll; +extern cvar_t *g_saberForceDrainAmount; +extern cvar_t *g_saberLockSuperBreaks; +extern cvar_t *g_saberNewCombat; +extern cvar_t *g_forceNewPowers; +extern cvar_t *g_playerCheatPowers; +extern cvar_t *g_moonJump; +extern cvar_t *g_noIgniteTwirl; -static void PM_SetWaterLevelAtPoint( vec3_t org, int *waterlevel, int *watertype ); +static void PM_SetWaterLevelAtPoint(vec3_t org, int *waterlevel, int *watertype); #define FLY_NONE 0 #define FLY_NORMAL 1 @@ -179,175 +189,178 @@ const float pm_airDecelRate = 1.35f; //Used for air decelleration away from curr int c_pmove = 0; -extern void PM_SetTorsoAnimTimer( gentity_t *ent, int *torsoAnimTimer, int time ); -extern void PM_SetLegsAnimTimer( gentity_t *ent, int *legsAnimTimer, int time ); -extern void PM_TorsoAnimation( void ); -extern int PM_TorsoAnimForFrame( gentity_t *ent, int torsoFrame ); -extern int PM_AnimLength( int index, animNumber_t anim ); -extern qboolean PM_InDeathAnim ( void ); -extern qboolean PM_InOnGroundAnim ( playerState_t *ps ); +extern void PM_SetTorsoAnimTimer(gentity_t *ent, int *torsoAnimTimer, int time); +extern void PM_SetLegsAnimTimer(gentity_t *ent, int *legsAnimTimer, int time); +extern void PM_TorsoAnimation(void); +extern int PM_TorsoAnimForFrame(gentity_t *ent, int torsoFrame); +extern int PM_AnimLength(int index, animNumber_t anim); +extern qboolean PM_InDeathAnim(void); +extern qboolean PM_InOnGroundAnim(playerState_t *ps); extern weaponInfo_t cg_weapons[MAX_WEAPONS]; -extern int PM_PickAnim( gentity_t *self, int minAnim, int maxAnim ); +extern int PM_PickAnim(gentity_t *self, int minAnim, int maxAnim); -extern void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf, trace_t *trace ); +extern void DoImpact(gentity_t *self, gentity_t *other, qboolean damageSelf, trace_t *trace); #define PHASER_RECHARGE_TIME 100 extern saberMoveName_t transitionMove[Q_NUM_QUADS][Q_NUM_QUADS]; -extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent ); -Vehicle_t *PM_RidingVehicle( void ) +extern Vehicle_t *G_IsRidingVehicle(gentity_t *ent); +Vehicle_t *PM_RidingVehicle(void) { - return (G_IsRidingVehicle( pm->gent )); + return (G_IsRidingVehicle(pm->gent)); } -extern qboolean G_ControlledByPlayer( gentity_t *self ); -qboolean PM_ControlledByPlayer( void ) +extern qboolean G_ControlledByPlayer(gentity_t *self); +qboolean PM_ControlledByPlayer(void) { - return G_ControlledByPlayer( pm->gent ); + return G_ControlledByPlayer(pm->gent); } -qboolean BG_UnrestrainedPitchRoll( playerState_t *ps, Vehicle_t *pVeh ) +qboolean BG_UnrestrainedPitchRoll(playerState_t *ps, Vehicle_t *pVeh) { -/* + /* if ( 0 && ps->clientNum < MAX_CLIENTS //real client - && ps->m_iVehicleNum//in a vehicle - && pVeh //valid vehicle data pointer - && pVeh->m_pVehicleInfo//valid vehicle info - && pVeh->m_pVehicleInfo->type == VH_FIGHTER )//fighter - //FIXME: specify per vehicle instead of assuming true for all fighters - //FIXME: map/server setting? + && ps->m_iVehicleNum//in a vehicle + && pVeh //valid vehicle data pointer + && pVeh->m_pVehicleInfo//valid vehicle info + && pVeh->m_pVehicleInfo->type == VH_FIGHTER )//fighter + //FIXME: specify per vehicle instead of assuming true for all fighters + //FIXME: map/server setting? {//can roll and pitch without limitation! - return qtrue; + return qtrue; }*/ return qfalse; } - +qboolean BG_AllowThirdPersonSpecialMove( playerState_t *ps ) +{ + return (qboolean)((cg.renderingThirdPerson || cg_trueguns.integer || ps->weapon == WP_SABER || ps->weapon == WP_MELEE) && !cg.zoomMode); +} /* =============== PM_AddEvent =============== */ -void PM_AddEvent( int newEvent ) +void PM_AddEvent(int newEvent) { - AddEventToPlayerstate( newEvent, 0, pm->ps ); + AddEventToPlayerstate(newEvent, 0, pm->ps); } -qboolean PM_PredictJumpSafe( vec3_t jumpHorizDir, float jumpHorizSpeed, float jumpVertSpeed, int predictTimeLength ) +qboolean PM_PredictJumpSafe(vec3_t jumpHorizDir, float jumpHorizSpeed, float jumpVertSpeed, int predictTimeLength) { return qtrue; } -void PM_GrabWallForJump( int anim ) +void PM_GrabWallForJump(int anim) {//NOTE!!! assumes an appropriate anim is being passed in!!! - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_RESTART|SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); - PM_AddEvent( EV_JUMP );//make sound for grab + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_RESTART | SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0); + PM_AddEvent(EV_JUMP);//make sound for grab pm->ps->pm_flags |= PMF_STUCK_TO_WALL; } -qboolean PM_CheckGrabWall( trace_t *trace ) +qboolean PM_CheckGrabWall(trace_t *trace) { - if ( !pm->gent || !pm->gent->client ) + if (!pm->gent || !pm->gent->client) { return qfalse; } - if ( pm->gent->health <= 0 ) + if (pm->gent->health <= 0) {//must be alive return qfalse; } - if ( pm->gent->client->ps.groundEntityNum != ENTITYNUM_NONE ) + if (pm->gent->client->ps.groundEntityNum != ENTITYNUM_NONE) {//must be in air return qfalse; } - if ( trace->plane.normal[2] != 0 ) + if (trace->plane.normal[2] != 0) {//must be a flat wall return qfalse; } - if ( !trace->plane.normal[0] && !trace->plane.normal[1] ) + if (!trace->plane.normal[0] && !trace->plane.normal[1]) {//invalid normal return qfalse; } - if ( (trace->contents&(CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP)) ) + if ((trace->contents&(CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP))) {//can't jump off of clip brushes return qfalse; } - if ( pm->gent->client->ps.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_1 ) + if (pm->gent->client->ps.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_1) {//must have at least FJ 1 return qfalse; } - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) - && pm->gent->client->ps.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_3 ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + && pm->gent->client->ps.forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_3) {//player must have force jump 3 return qfalse; } - if ( (pm->ps->saber[0].saberFlags&SFL_NO_WALL_GRAB) ) + if ((pm->ps->saber[0].saberFlags&SFL_NO_WALL_GRAB)) { return qfalse; } - if ( pm->ps->dualSabers - && (pm->ps->saber[1].saberFlags&SFL_NO_WALL_GRAB) ) + if (pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_WALL_GRAB)) { return qfalse; } - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) {//player //only if we were in a longjump - if ( pm->ps->legsAnim != BOTH_FORCELONGLEAP_START - && pm->ps->legsAnim != BOTH_FORCELONGLEAP_ATTACK ) + if (pm->ps->legsAnim != BOTH_FORCELONGLEAP_START + && pm->ps->legsAnim != BOTH_FORCELONGLEAP_ATTACK) { return qfalse; } //hit a flat wall during our long jump, see if we should grab it vec3_t moveDir; - VectorCopy( pm->ps->velocity, moveDir ); - VectorNormalize( moveDir ); - if ( DotProduct( moveDir, trace->plane.normal ) > -0.65f ) + VectorCopy(pm->ps->velocity, moveDir); + VectorNormalize(moveDir); + if (DotProduct(moveDir, trace->plane.normal) > -0.65f) {//not enough of a direct impact, just slide off return qfalse; } - if ( fabs(trace->plane.normal[2]) > MAX_WALL_GRAB_SLOPE ) + if (fabs(trace->plane.normal[2]) > MAX_WALL_GRAB_SLOPE) { return qfalse; } //grab it! //FIXME: stop Matrix effect! - VectorClear( pm->ps->velocity ); + VectorClear(pm->ps->velocity); //FIXME: stop slidemove! //NOTE: we know it's forward, so... - PM_GrabWallForJump( BOTH_FORCEWALLREBOUND_FORWARD ); + PM_GrabWallForJump(BOTH_FORCEWALLREBOUND_FORWARD); return qtrue; } else {//NPCs - if ( PM_InReboundJump( pm->ps->legsAnim ) ) + if (PM_InReboundJump(pm->ps->legsAnim)) {//already in a rebound! return qfalse; } - if ( (pm->ps->eFlags&EF_FORCE_GRIPPED) ) + if ((pm->ps->eFlags&EF_FORCE_GRIPPED)) {//being gripped! return qfalse; } /* if ( pm->gent->painDebounceTime > level.time ) {//can't move! - return qfalse; + return qfalse; } if ( (pm->ps->pm_flags&PMF_TIME_KNOCKBACK) ) {//being thrown back! - return qfalse; + return qfalse; } */ - if ( pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) ) + if (pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT)) {//faling to our death! return qfalse; } //FIXME: random chance, based on skill/rank? - if ( pm->ps->legsAnim != BOTH_FORCELONGLEAP_START - && pm->ps->legsAnim != BOTH_FORCELONGLEAP_ATTACK ) + if (pm->ps->legsAnim != BOTH_FORCELONGLEAP_START + && pm->ps->legsAnim != BOTH_FORCELONGLEAP_ATTACK) {//not in a long-jump - if ( !pm->gent->enemy ) + if (!pm->gent->enemy) {//no enemy return qfalse; } @@ -356,10 +369,10 @@ qboolean PM_CheckGrabWall( trace_t *trace ) //if ( pm->gent->enemy->currentOrigin[2] < (pm->ps->origin[2]-128) ) {//enemy is way below us vec3_t enemyDir; - VectorSubtract( pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir ); + VectorSubtract(pm->gent->enemy->currentOrigin, pm->ps->origin, enemyDir); enemyDir[2] = 0; - VectorNormalize( enemyDir ); - if ( DotProduct( enemyDir, trace->plane.normal ) < 0.65f ) + VectorNormalize(enemyDir); + if (DotProduct(enemyDir, trace->plane.normal) < 0.65f) {//jumping off this wall would not launch me in the general direction of my enemy return qfalse; } @@ -371,15 +384,15 @@ qboolean PM_CheckGrabWall( trace_t *trace ) // - including "do not enter" brushes! //hit a flat wall during our long jump, see if we should grab it vec3_t moveDir; - VectorCopy( pm->ps->velocity, moveDir ); - VectorNormalize( moveDir ); - if ( DotProduct( moveDir, trace->plane.normal ) > -0.65f ) + VectorCopy(pm->ps->velocity, moveDir); + VectorNormalize(moveDir); + if (DotProduct(moveDir, trace->plane.normal) > -0.65f) {//not enough of a direct impact, just slide off return qfalse; } //Okay, now see if jumping off this thing would send us into a do not enter brush - if ( !PM_PredictJumpSafe( trace->plane.normal, JUMP_OFF_WALL_SPEED, G_ForceWallJumpStrength(), 1500 ) ) + if (!PM_PredictJumpSafe(trace->plane.normal, JUMP_OFF_WALL_SPEED, G_ForceWallJumpStrength(), 1500)) {//we would hit a do not enter brush, so don't grab the wall return qfalse; } @@ -388,15 +401,15 @@ qboolean PM_CheckGrabWall( trace_t *trace ) //Pick the proper anim int anim = BOTH_FORCEWALLREBOUND_FORWARD; vec3_t facingAngles, wallDir, fwdDir, rtDir; - VectorSubtract( trace->endpos, pm->gent->lastOrigin, wallDir ); + VectorSubtract(trace->endpos, pm->gent->lastOrigin, wallDir); wallDir[2] = 0; - VectorNormalize( wallDir ); - VectorSet( facingAngles, 0, pm->ps->viewangles[YAW], 0 ); - AngleVectors( facingAngles, fwdDir, rtDir, NULL ); - float fDot = DotProduct( fwdDir, wallDir ); - if ( fabs( fDot ) >= 0.5f ) + VectorNormalize(wallDir); + VectorSet(facingAngles, 0, pm->ps->viewangles[YAW], 0); + AngleVectors(facingAngles, fwdDir, rtDir, NULL); + float fDot = DotProduct(fwdDir, wallDir); + if (fabs(fDot) >= 0.5f) {//hit a wall in front/behind - if ( fDot > 0.0f ) + if (fDot > 0.0f) {//in front anim = BOTH_FORCEWALLREBOUND_FORWARD; } @@ -405,7 +418,7 @@ qboolean PM_CheckGrabWall( trace_t *trace ) anim = BOTH_FORCEWALLREBOUND_BACK; } } - else if ( DotProduct( rtDir, wallDir ) > 0 ) + else if (DotProduct(rtDir, wallDir) > 0) {//hit a wall on the right anim = BOTH_FORCEWALLREBOUND_RIGHT; } @@ -413,9 +426,9 @@ qboolean PM_CheckGrabWall( trace_t *trace ) {//hit a wall on the left anim = BOTH_FORCEWALLREBOUND_LEFT; } - VectorClear( pm->ps->velocity ); + VectorClear(pm->ps->velocity); //FIXME: stop slidemove! - PM_GrabWallForJump( anim ); + PM_GrabWallForJump(anim); return qtrue; } //return qfalse; @@ -426,38 +439,38 @@ qboolean PM_ClientImpact( trace_t *trace, qboolean damageSelf ) =============== */ -qboolean PM_ClientImpact( trace_t *trace, qboolean damageSelf ) +qboolean PM_ClientImpact(trace_t *trace, qboolean damageSelf) { gentity_t *traceEnt; int otherEntityNum = trace->entityNum; - if ( !pm->gent ) + if (!pm->gent) { return qfalse; } traceEnt = &g_entities[otherEntityNum]; - if ( otherEntityNum == ENTITYNUM_WORLD - || (traceEnt->bmodel && traceEnt->s.pos.trType == TR_STATIONARY ) ) + if (otherEntityNum == ENTITYNUM_WORLD + || (traceEnt->bmodel && traceEnt->s.pos.trType == TR_STATIONARY)) {//hit world or a non-moving brush - if ( PM_CheckGrabWall( trace ) ) + if (PM_CheckGrabWall(trace)) {//stopped on the wall return qtrue; } } - if( (VectorLength( pm->ps->velocity )*(pm->gent->mass/10)) >= 100 && (pm->gent->client->NPC_class == CLASS_VEHICLE || pm->ps->lastOnGround+100material>=MAT_GLASS&&pm->gent->lastImpact+100<=level.time)) + if ((VectorLength(pm->ps->velocity)*(pm->gent->mass / 10)) >= 100 && (pm->gent->client->NPC_class == CLASS_VEHICLE || pm->ps->lastOnGround + 100material>=MAT_GLASS&&pm->gent->lastImpact+100<=level.time)) { - DoImpact( pm->gent, &g_entities[otherEntityNum], damageSelf, trace ); + DoImpact(pm->gent, &g_entities[otherEntityNum], damageSelf, trace); } - if ( otherEntityNum >= ENTITYNUM_WORLD ) + if (otherEntityNum >= ENTITYNUM_WORLD) { return qfalse; } - if ( !traceEnt || !(traceEnt->contents&pm->tracemask) ) + if (!traceEnt || !(traceEnt->contents&pm->tracemask)) {//it's dead or not in my way anymore return qtrue; } @@ -469,19 +482,19 @@ qboolean PM_ClientImpact( trace_t *trace, qboolean damageSelf ) PM_AddTouchEnt =============== */ -void PM_AddTouchEnt( int entityNum ) { +void PM_AddTouchEnt(int entityNum) { int i; - if ( entityNum == ENTITYNUM_WORLD ) { + if (entityNum == ENTITYNUM_WORLD) { return; } - if ( pm->numtouch == MAXTOUCH ) { + if (pm->numtouch == MAXTOUCH) { return; } // see if it is already added - for ( i = 0 ; i < pm->numtouch ; i++ ) { - if ( pm->touchents[ i ] == entityNum ) { + for (i = 0; i < pm->numtouch; i++) { + if (pm->touchents[i] == entityNum) { return; } } @@ -500,40 +513,41 @@ PM_ClipVelocity Slide off of the impacting surface - This will pull you down onto slopes if heading away from - them and push you up them as you go up them. - Also stops you when you hit walls. +This will pull you down onto slopes if heading away from +them and push you up them as you go up them. +Also stops you when you hit walls. ================== */ -void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) { +void PM_ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; float oldInZ; int i; - if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) ) + if ((pm->ps->pm_flags&PMF_STUCK_TO_WALL)) {//no sliding! - VectorCopy( in, out ); + VectorCopy(in, out); return; } oldInZ = in[2]; - backoff = DotProduct (in, normal); + backoff = DotProduct(in, normal); - if ( backoff < 0 ) { + if (backoff < 0) { backoff *= overbounce; - } else { + } + else { backoff /= overbounce; } - for ( i=0 ; i<3 ; i++ ) + for (i = 0; i<3; i++) { - change = normal[i]*backoff; + change = normal[i] * backoff; /* if ( i == 2 && Flying == FLY_HOVER && change > 0 ) {//don't pull a hovercraft down - change = 0; + change = 0; } else */ @@ -541,12 +555,12 @@ void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) { out[i] = in[i] - change; } } - if ( g_stepSlideFix->integer ) + if (g_stepSlideFix->integer) { - if ( pm->ps->clientNum < MAX_CLIENTS//normal player - && normal[2] < MIN_WALK_NORMAL )//sliding against a steep slope + if (pm->ps->clientNum < MAX_CLIENTS//normal player + && normal[2] < MIN_WALK_NORMAL)//sliding against a steep slope { - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE )//on the ground + if (pm->ps->groundEntityNum != ENTITYNUM_NONE)//on the ground {//if walking on the ground, don't slide up slopes that are too steep to walk on out[2] = oldInZ; } @@ -562,7 +576,7 @@ PM_Friction Handles both ground friction and water friction ================== */ -static void PM_Friction( void ) { +static void PM_Friction(void) { vec3_t vec; float *vel; float speed, newspeed, control; @@ -570,8 +584,8 @@ static void PM_Friction( void ) { vel = pm->ps->velocity; - VectorCopy( vel, vec ); - if ( pml.walking ) { + VectorCopy(vel, vec); + if (pml.walking) { vec[2] = 0; // ignore slope movement } @@ -586,64 +600,64 @@ static void PM_Friction( void ) { drop = 0; // apply ground friction, even if on ladder - if ( pm->gent + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->m_pVehicle - && pm->gent->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL ) + && pm->gent->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL) { friction = pm->gent->m_pVehicle->m_pVehicleInfo->friction; - if ( pm->gent->m_pVehicle && pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 ) + if (pm->gent->m_pVehicle && pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0) {//in a hovering vehicle, have air control - if ( pm->gent->m_pVehicle->m_ulFlags & VEH_FLYING ) + if (pm->gent->m_pVehicle->m_ulFlags & VEH_FLYING) { friction = 0.10f; } } - if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION) ) + if (!(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION)) { control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*friction*pml.frametime; /* if ( Flying == FLY_HOVER ) { - if ( pm->cmd.rightmove ) - {//if turning, increase friction - control *= 2.0f; - } - if ( pm->ps->groundEntityNum < ENTITYNUM_NONE ) - {//on the ground - drop += control*friction*pml.frametime; - } - else if ( pml.groundPlane ) - {//on a slope - drop += control*friction*2.0f*pml.frametime; - } - else - {//in air - drop += control*2.0f*friction*pml.frametime; - } + if ( pm->cmd.rightmove ) + {//if turning, increase friction + control *= 2.0f; + } + if ( pm->ps->groundEntityNum < ENTITYNUM_NONE ) + {//on the ground + drop += control*friction*pml.frametime; + } + else if ( pml.groundPlane ) + {//on a slope + drop += control*friction*2.0f*pml.frametime; + } + else + {//in air + drop += control*2.0f*friction*pml.frametime; + } } */ } } - else if ( Flying != FLY_NORMAL ) + else if (Flying != FLY_NORMAL) { - if ( (pm->watertype & CONTENTS_LADDER) || pm->waterlevel <= 1 ) + if ((pm->watertype & CONTENTS_LADDER) || pm->waterlevel <= 1) { - if ( (pm->watertype & CONTENTS_LADDER) || (pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK)) ) + if ((pm->watertype & CONTENTS_LADDER) || (pml.walking && !(pml.groundTrace.surfaceFlags & SURF_SLICK))) { // if getting knocked back, no friction - if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION) ) + if (!(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION)) { - if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START + if (pm->ps->legsAnim == BOTH_FORCELONGLEAP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK - || pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND ) + || pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND) {//super forward jump - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum != ENTITYNUM_NONE) {//not in air - if ( pm->cmd.forwardmove < 0 ) + if (pm->cmd.forwardmove < 0) {//trying to hold back some friction *= 0.5f;//0.25f; } @@ -652,10 +666,10 @@ static void PM_Friction( void ) { friction *= 0.2f;//0.1f; } pm->cmd.forwardmove = pm->cmd.rightmove = 0; - if ( pml.groundPlane && pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND ) + if (pml.groundPlane && pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND) { //slide effect - G_PlayEffect( "env/slide_dust", pml.groundTrace.endpos, pml.groundTrace.plane.normal ); + G_PlayEffect("env/slide_dust", pml.groundTrace.endpos, pml.groundTrace.plane.normal); //FIXME: slide sound } } @@ -663,10 +677,10 @@ static void PM_Friction( void ) { /* else if ( pm->cmd.buttons & BUTTON_USE ) {//If the use key is pressed. slow the player more quickly - if ( pm->gent->client->NPC_class != CLASS_VEHICLE ) // if not in a vehicle... - {//in a vehicle, use key makes you turbo-boost - friction *= pm_frictionModifier; - } + if ( pm->gent->client->NPC_class != CLASS_VEHICLE ) // if not in a vehicle... + {//in a vehicle, use key makes you turbo-boost + friction *= pm_frictionModifier; + } } */ @@ -676,17 +690,20 @@ static void PM_Friction( void ) { } } } - else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) + else if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->gent && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) && pm->gent->client->moveType == MT_FLYSWIM ) + && (pm->gent->client->NPC_class == CLASS_BOBAFETT + || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER + || pm->gent->client->NPC_class == CLASS_MANDA) + && pm->gent->client->moveType == MT_FLYSWIM) {//player as Boba drop += speed*pm_waterfriction*pml.frametime; } - if ( Flying == FLY_VEHICLE ) + if (Flying == FLY_VEHICLE) { - if ( !(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION) ) + if (!(pm->ps->pm_flags & PMF_TIME_KNOCKBACK) && !(pm->ps->pm_flags & PMF_TIME_NOFRICTION)) { control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*friction*pml.frametime; @@ -694,16 +711,16 @@ static void PM_Friction( void ) { } // apply water friction even if just wading - if ( !waterForceJump ) + if (!waterForceJump) { - if ( pm->waterlevel && !(pm->watertype & CONTENTS_LADDER)) + if (pm->waterlevel && !(pm->watertype & CONTENTS_LADDER)) { drop += speed*pm_waterfriction*pm->waterlevel*pml.frametime; } } // apply flying friction - if ( pm->ps->pm_type == PM_SPECTATOR ) + if (pm->ps->pm_type == PM_SPECTATOR) { drop += speed*pm_flightfriction*pml.frametime; } @@ -729,24 +746,24 @@ Handles user intended acceleration ============== */ -static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) +static void PM_Accelerate(vec3_t wishdir, float wishspeed, float accel) { int i; float addspeed, accelspeed, currentspeed; - currentspeed = DotProduct (pm->ps->velocity, wishdir); + currentspeed = DotProduct(pm->ps->velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) { return; } - accelspeed = ( accel * pml.frametime ) * wishspeed; + accelspeed = (accel * pml.frametime) * wishspeed; if (accelspeed > addspeed) { accelspeed = addspeed; } - for (i=0 ; i<3 ; i++) { + for (i = 0; i<3; i++) { pm->ps->velocity[i] += accelspeed * wishdir[i]; } } @@ -760,28 +777,28 @@ This allows the clients to use axial -127 to 127 values for all directions without getting a sqrt(2) distortion in speed. ============ */ -static float PM_CmdScale( usercmd_t *cmd ) +static float PM_CmdScale(usercmd_t *cmd) { int max; float total; float scale; - max = abs( cmd->forwardmove ); + max = abs(cmd->forwardmove); - if ( abs( cmd->rightmove ) > max ) { - max = abs( cmd->rightmove ); + if (abs(cmd->rightmove) > max) { + max = abs(cmd->rightmove); } - if ( abs( cmd->upmove ) > max ) { - max = abs( cmd->upmove ); + if (abs(cmd->upmove) > max) { + max = abs(cmd->upmove); } - if ( !max ) { + if (!max) { return 0; } - total = sqrt( (float)(( cmd->forwardmove * cmd->forwardmove ) - + ( cmd->rightmove * cmd->rightmove ) - + ( cmd->upmove * cmd->upmove )) ); + total = sqrt((float)((cmd->forwardmove * cmd->forwardmove) + + (cmd->rightmove * cmd->rightmove) + + (cmd->upmove * cmd->upmove))); - scale = (float) pm->ps->speed * max / ( 127.0f * total ); + scale = (float)pm->ps->speed * max / (127.0f * total); return scale; } @@ -795,32 +812,41 @@ Determine the rotation of the legs reletive to the facing dir ================ */ -static void PM_SetMovementDir( void ) { - if ( pm->cmd.forwardmove || pm->cmd.rightmove ) { - if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0 ) { +static void PM_SetMovementDir(void) { + if (pm->cmd.forwardmove || pm->cmd.rightmove) { + if (pm->cmd.rightmove == 0 && pm->cmd.forwardmove > 0) { pm->ps->movementDir = 0; - } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0 ) { + } + else if (pm->cmd.rightmove < 0 && pm->cmd.forwardmove > 0) { pm->ps->movementDir = 1; - } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0 ) { + } + else if (pm->cmd.rightmove < 0 && pm->cmd.forwardmove == 0) { pm->ps->movementDir = 2; - } else if ( pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0 ) { + } + else if (pm->cmd.rightmove < 0 && pm->cmd.forwardmove < 0) { pm->ps->movementDir = 3; - } else if ( pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0 ) { + } + else if (pm->cmd.rightmove == 0 && pm->cmd.forwardmove < 0) { pm->ps->movementDir = 4; - } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0 ) { + } + else if (pm->cmd.rightmove > 0 && pm->cmd.forwardmove < 0) { pm->ps->movementDir = 5; - } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0 ) { + } + else if (pm->cmd.rightmove > 0 && pm->cmd.forwardmove == 0) { pm->ps->movementDir = 6; - } else if ( pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0 ) { + } + else if (pm->cmd.rightmove > 0 && pm->cmd.forwardmove > 0) { pm->ps->movementDir = 7; } - } else { + } + else { // if they aren't actively going directly sideways, // change the animation to the diagonal so they // don't stop too crooked - if ( pm->ps->movementDir == 2 ) { + if (pm->ps->movementDir == 2) { pm->ps->movementDir = 1; - } else if ( pm->ps->movementDir == 6 ) { + } + else if (pm->ps->movementDir == 6) { pm->ps->movementDir = 7; } } @@ -833,9 +859,9 @@ PM_CheckJump ============= */ #define METROID_JUMP 1 -qboolean PM_InReboundJump( int anim ) +qboolean PM_InReboundJump(int anim) { - switch ( anim ) + switch (anim) { case BOTH_FORCEWALLREBOUND_FORWARD: case BOTH_FORCEWALLREBOUND_LEFT: @@ -847,9 +873,9 @@ qboolean PM_InReboundJump( int anim ) return qfalse; } -qboolean PM_InReboundHold( int anim ) +qboolean PM_InReboundHold(int anim) { - switch ( anim ) + switch (anim) { case BOTH_FORCEWALLHOLD_FORWARD: case BOTH_FORCEWALLHOLD_LEFT: @@ -861,9 +887,9 @@ qboolean PM_InReboundHold( int anim ) return qfalse; } -qboolean PM_InReboundRelease( int anim ) +qboolean PM_InReboundRelease(int anim) { - switch ( anim ) + switch (anim) { case BOTH_FORCEWALLRELEASE_FORWARD: case BOTH_FORCEWALLRELEASE_LEFT: @@ -875,9 +901,9 @@ qboolean PM_InReboundRelease( int anim ) return qfalse; } -qboolean PM_InBackFlip( int anim ) +qboolean PM_InBackFlip(int anim) { - switch ( anim ) + switch (anim) { case BOTH_FLIP_BACK1: case BOTH_FLIP_BACK2: @@ -889,9 +915,9 @@ qboolean PM_InBackFlip( int anim ) return qfalse; } -qboolean PM_InSpecialJump( int anim ) +qboolean PM_InSpecialJump(int anim) { - switch ( anim ) + switch (anim) { case BOTH_WALL_RUN_RIGHT: case BOTH_WALL_RUN_RIGHT_STOP: @@ -930,38 +956,38 @@ qboolean PM_InSpecialJump( int anim ) case BOTH_A7_SOULCAL: return qtrue; } - if ( PM_InReboundJump( anim ) ) + if (PM_InReboundJump(anim)) { return qtrue; } - if ( PM_InReboundHold( anim ) ) + if (PM_InReboundHold(anim)) { return qtrue; } - if ( PM_InReboundRelease( anim ) ) + if (PM_InReboundRelease(anim)) { return qtrue; } - if ( PM_InBackFlip( anim ) ) + if (PM_InBackFlip(anim)) { return qtrue; } return qfalse; } -extern void CG_PlayerLockedWeaponSpeech( int jumping ); -qboolean PM_ForceJumpingUp( gentity_t *gent ) +extern void CG_PlayerLockedWeaponSpeech(int jumping); +qboolean PM_ForceJumpingUp(gentity_t *gent) { - if ( !gent || !gent->client ) + if (!gent || !gent->client) { return qfalse; } - if ( gent->NPC ) + if (gent->NPC) {//this is ONLY for the player - if ( player + if (player && player->client - && player->client->ps.viewEntity == gent->s.number ) + && player->client->ps.viewEntity == gent->s.number) {//okay to jump if an NPC controlled by the player } else @@ -970,33 +996,33 @@ qboolean PM_ForceJumpingUp( gentity_t *gent ) } } - if ( !(gent->client->ps.forcePowersActive&(1<client->ps.forceJumpCharge ) + if (!(gent->client->ps.forcePowersActive&(1 << FP_LEVITATION)) && gent->client->ps.forceJumpCharge) {//already jumped and let go return qfalse; } - if ( PM_InSpecialJump( gent->client->ps.legsAnim ) ) + if (PM_InSpecialJump(gent->client->ps.legsAnim)) { return qfalse; } - if ( PM_InKnockDown( &gent->client->ps ) ) + if (PM_InKnockDown(&gent->client->ps)) { return qfalse; } - if ( (gent->s.numbers.numberclient->ps.groundEntityNum == ENTITYNUM_NONE //in air - && ( (gent->client->ps.pm_flags&PMF_JUMPING) && gent->client->ps.velocity[2] > 0 )//jumped & going up or at water surface///*(gent->client->ps.waterHeightLevel==WHL_SHOULDERS&&gent->client->usercmd.upmove>0) ||*/ + if (gent->client->ps.groundEntityNum == ENTITYNUM_NONE //in air + && ((gent->client->ps.pm_flags&PMF_JUMPING) && gent->client->ps.velocity[2] > 0)//jumped & going up or at water surface///*(gent->client->ps.waterHeightLevel==WHL_SHOULDERS&&gent->client->usercmd.upmove>0) ||*/ && gent->client->ps.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 //force-jump capable - && !(gent->client->ps.pm_flags&PMF_TRIGGER_PUSHED) )//not pushed by a trigger + && !(gent->client->ps.pm_flags&PMF_TRIGGER_PUSHED))//not pushed by a trigger { - if( gent->flags & FL_LOCK_PLAYER_WEAPONS ) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + if (gent->flags & FL_LOCK_PLAYER_WEAPONS) // yes this locked weapons check also includes force powers, if we need a separate check later I'll make one { - CG_PlayerLockedWeaponSpeech( qtrue ); + CG_PlayerLockedWeaponSpeech(qtrue); return qfalse; } return qtrue; @@ -1004,25 +1030,25 @@ qboolean PM_ForceJumpingUp( gentity_t *gent ) return qfalse; } -static void PM_JumpForDir( void ) +static void PM_JumpForDir(void) { int anim = BOTH_JUMP1; - if ( pm->cmd.forwardmove > 0 ) + if (pm->cmd.forwardmove > 0) { anim = BOTH_JUMP1; pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } - else if ( pm->cmd.forwardmove < 0 ) + else if (pm->cmd.forwardmove < 0) { anim = BOTH_JUMPBACK1; pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; } - else if ( pm->cmd.rightmove > 0 ) + else if (pm->cmd.rightmove > 0) { anim = BOTH_JUMPRIGHT1; pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } - else if ( pm->cmd.rightmove < 0 ) + else if (pm->cmd.rightmove < 0) { anim = BOTH_JUMPLEFT1; pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; @@ -1032,16 +1058,16 @@ static void PM_JumpForDir( void ) anim = BOTH_JUMP1; pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } - if(!PM_InDeathAnim()) + if (!PM_InDeathAnim()) { - PM_SetAnim(pm,SETANIM_LEGS,anim,SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms } } -qboolean PM_GentCantJump( gentity_t *gent ) +qboolean PM_GentCantJump(gentity_t *gent) {//FIXME: ugh, hacky, set a flag on NPC or something, please... - if ( gent && gent->client && - ( gent->client->NPC_class == CLASS_ATST || + if (gent && gent->client && + (gent->client->NPC_class == CLASS_ATST || gent->client->NPC_class == CLASS_GONK || gent->client->NPC_class == CLASS_MARK1 || gent->client->NPC_class == CLASS_MARK2 || @@ -1052,106 +1078,126 @@ qboolean PM_GentCantJump( gentity_t *gent ) gent->client->NPC_class == CLASS_R5D2 || gent->client->NPC_class == CLASS_SEEKER || gent->client->NPC_class == CLASS_REMOTE || - gent->client->NPC_class == CLASS_SENTRY ) ) + gent->client->NPC_class == CLASS_SENTRY)) { return qtrue; } return qfalse; } -static qboolean PM_CheckJump( void ) +static qboolean PM_CheckJump(void) { //Don't allow jump until all buttons are up - if ( pm->ps->pm_flags & PMF_RESPAWNED ) { + if (pm->ps->pm_flags & PMF_RESPAWNED) { return qfalse; } - if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps ) ) + if (PM_InKnockDown(pm->ps) || PM_InRoll(pm->ps)) {//in knockdown return qfalse; } - if ( PM_GentCantJump( pm->gent ) ) + if (PM_GentCantJump(pm->gent)) { return qfalse; } - if ( PM_KickingAnim( pm->ps->legsAnim ) && !PM_InAirKickingAnim( pm->ps->legsAnim ) ) + if (PM_KickingAnim(pm->ps->legsAnim) && !PM_InAirKickingAnim(pm->ps->legsAnim)) {//can't jump when in a kicking anim return qfalse; } + + if ( pm->ps->weapon == WP_EMPLACED_GUN && !(pm->ps->eFlags & EF_LOCKED_TO_WEAPON)) + { + return qfalse; + } /* if ( pm->cmd.buttons & BUTTON_FORCEJUMP ) { - pm->ps->pm_flags |= PMF_JUMP_HELD; + pm->ps->pm_flags |= PMF_JUMP_HELD; } */ + float jumpMultiplier; + + if (g_moonJump->integer) + { + jumpMultiplier = 5.0; + } + else if (g_forceNewPowers->integer) + { + jumpMultiplier = 1.25; + } + else + { + jumpMultiplier = 1.0; + } + #if METROID_JUMP - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->gent && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) ) + && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER || pm->gent->client->NPC_class == CLASS_MANDA)) {//player playing as boba fett - if ( pm->cmd.upmove > 0 ) + if (pm->cmd.upmove > 0) {//turn on/go up - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && !(pm->ps->pm_flags&PMF_JUMP_HELD) ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE && !(pm->ps->pm_flags&PMF_JUMP_HELD)) {//double-tap - must activate while in air - if ( !JET_Flying( pm->gent ) ) + if (!JET_Flying(pm->gent)) { - JET_FlyStart( pm->gent ); + JET_FlyStart(pm->gent); } } } - else if ( pm->cmd.upmove < 0 ) + else if (pm->cmd.upmove < 0) {//turn it off (or should we just go down)? /* if ( JET_Flying( pm->gent ) ) { - JET_FlyStop( pm->gent ); + JET_FlyStop( pm->gent ); } */ } } - else if ( pm->waterlevel < 3 )//|| (pm->ps->waterHeightLevel==WHL_SHOULDERS&&pm->cmd.upmove>0) ) + else if (pm->waterlevel < 3)//|| (pm->ps->waterHeightLevel==WHL_SHOULDERS&&pm->cmd.upmove>0) ) { - if ( pm->ps->gravity > 0 ) + if (pm->ps->gravity > 0) {//can't do this in zero-G - if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START + if (pm->ps->legsAnim == BOTH_FORCELONGLEAP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK - || pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND ) + || pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND) {//in the middle of a force long-jump /* if ( pm->ps->groundEntityNum == ENTITYNUM_NONE - && pm->cmd.upmove > 0 - && pm->cmd.forwardmove > 0 ) + && pm->cmd.upmove > 0 + && pm->cmd.forwardmove > 0 ) */ - if ( (pm->ps->legsAnim == BOTH_FORCELONGLEAP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK) - && pm->ps->legsAnimTimer > 0 ) + if ((pm->ps->legsAnim == BOTH_FORCELONGLEAP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK) + && pm->ps->legsAnimTimer > 0) {//in the air //FIXME: need an actual set time so it doesn't matter when the attack happens //FIXME: make sure we don't jump further than force jump 3 allows vec3_t jFwdAngs, jFwdVec; - VectorSet( jFwdAngs, 0, pm->ps->viewangles[YAW], 0 ); - AngleVectors( jFwdAngs, jFwdVec, NULL, NULL ); + VectorSet(jFwdAngs, 0, pm->ps->viewangles[YAW], 0); + AngleVectors(jFwdAngs, jFwdVec, NULL, NULL); float oldZVel = pm->ps->velocity[2]; - if ( pm->ps->legsAnimTimer > 150 && oldZVel < 0 ) + if (pm->ps->legsAnimTimer > 150 && oldZVel < 0) { oldZVel = 0; } - VectorScale( jFwdVec, FORCE_LONG_LEAP_SPEED, pm->ps->velocity ); + VectorScale(jFwdVec, FORCE_LONG_LEAP_SPEED, pm->ps->velocity); pm->ps->velocity[2] = oldZVel; pm->ps->pm_flags |= PMF_JUMP_HELD; - pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; - pm->ps->forcePowersActive |= (1<ps->pm_flags |= PMF_JUMPING | PMF_SLOW_MO_FALL; + pm->ps->forcePowersActive |= (1 << FP_LEVITATION); return qtrue; } else {//landing-slide - if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START - || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK ) + if (pm->ps->legsAnim == BOTH_FORCELONGLEAP_START + || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK) {//still in start anim, but it's run out - pm->ps->forcePowersActive |= (1<ps->groundEntityNum == ENTITYNUM_NONE ) + pm->ps->forcePowersActive |= (1 << FP_LEVITATION); + if (pm->ps->groundEntityNum == ENTITYNUM_NONE) {//still in air? //hold it for another 50ms //PM_SetAnim( pm, SETANIM_BOTH, BOTH_FORCELONGLEAP_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); @@ -1161,103 +1207,103 @@ static qboolean PM_CheckJump( void ) {//in land-slide anim //FIXME: force some forward movement? Less if holding back? } - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE//still in air - && pm->ps->origin[2] < pm->ps->jumpZStart )//dropped below original jump start + if (pm->ps->groundEntityNum == ENTITYNUM_NONE//still in air + && pm->ps->origin[2] < pm->ps->jumpZStart)//dropped below original jump start {//slow down pm->ps->velocity[0] *= 0.75f; pm->ps->velocity[1] *= 0.75f; - if ( (pm->ps->velocity[0]+pm->ps->velocity[1])*0.5f<=10.0f ) + if ((pm->ps->velocity[0] + pm->ps->velocity[1])*0.5f <= 10.0f) {//falling straight down - PM_SetAnim( pm, SETANIM_BOTH, BOTH_FORCEINAIR1, SETANIM_FLAG_OVERRIDE ); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_FORCEINAIR1, SETANIM_FLAG_OVERRIDE); } } return qfalse; } } - else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) //player-only for now + else if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //player-only for now && pm->cmd.upmove > 0 //trying to jump && pm->ps->forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_3 //force jump 3 or better && pm->ps->forcePower >= FORCE_LONGJUMP_POWER //this costs 20 force to do - && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1 << FP_SPEED)) //force-speed is on && pm->cmd.forwardmove > 0 //pushing forward && !pm->cmd.rightmove //not strafing && pm->ps->groundEntityNum != ENTITYNUM_NONE//not in mid-air && !(pm->ps->pm_flags&PMF_JUMP_HELD) //&& (float)(level.time-pm->ps->lastStationary) >= (3000.0f*g_timescale->value)//have to have a 3 second running start - relative to force speed slowdown - && (level.time-pm->ps->forcePowerDebounce[FP_SPEED]) <= 250//have to have just started the force speed within the last half second - && pm->gent ) + && (level.time - pm->ps->forcePowerDebounce[FP_SPEED]) <= 250//have to have just started the force speed within the last half second + && pm->gent) {//start a force long-jump! vec3_t jFwdAngs, jFwdVec; //BOTH_FORCELONGLEAP_ATTACK if holding attack, too? - PM_SetAnim( pm, SETANIM_BOTH, BOTH_FORCELONGLEAP_START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - VectorSet( jFwdAngs, 0, pm->ps->viewangles[YAW], 0 ); - AngleVectors( jFwdAngs, jFwdVec, NULL, NULL ); - VectorScale( jFwdVec, FORCE_LONG_LEAP_SPEED, pm->ps->velocity ); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_FORCELONGLEAP_START, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); + VectorSet(jFwdAngs, 0, pm->ps->viewangles[YAW], 0); + AngleVectors(jFwdAngs, jFwdVec, NULL, NULL); + VectorScale(jFwdVec, FORCE_LONG_LEAP_SPEED, pm->ps->velocity); pm->ps->velocity[2] = 320; pml.groundPlane = qfalse; pml.walking = qfalse; pm->ps->groundEntityNum = ENTITYNUM_NONE; pm->ps->jumpZStart = pm->ps->origin[2]; pm->ps->pm_flags |= PMF_JUMP_HELD; - pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + pm->ps->pm_flags |= PMF_JUMPING | PMF_SLOW_MO_FALL; //start force jump - pm->ps->forcePowersActive |= (1<ps->forcePowersActive |= (1 << FP_LEVITATION); pm->cmd.upmove = 0; // keep track of force jump stat - if(pm->ps->clientNum == 0) + if (pm->ps->clientNum == 0) { - if( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { pm->gent->client->sess.missionStats.forceUsed[(int)FP_LEVITATION]++; } } - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); - WP_ForcePowerStop( pm->gent, FP_SPEED ); - WP_ForcePowerDrain( pm->gent, FP_LEVITATION, FORCE_LONGJUMP_POWER );//drain the required force power - G_StartMatrixEffect( pm->gent, 0, pm->ps->legsAnimTimer+500 ); + G_SoundOnEnt(pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav"); + WP_ForcePowerStop(pm->gent, FP_SPEED); + WP_ForcePowerDrain(pm->gent, FP_LEVITATION, FORCE_LONGJUMP_POWER);//drain the required force power + G_StartMatrixEffect(pm->gent, 0, pm->ps->legsAnimTimer + 500); return qtrue; } - else if ( PM_InCartwheel( pm->ps->legsAnim ) - || PM_InButterfly( pm->ps->legsAnim ) ) + else if (PM_InCartwheel(pm->ps->legsAnim) + || PM_InButterfly(pm->ps->legsAnim)) {//can't keep jumping up in cartwheels, ariels and butterflies } //FIXME: still able to pogo-jump... - else if ( PM_ForceJumpingUp( pm->gent ) && (pm->ps->pm_flags&PMF_JUMP_HELD) )//||pm->ps->waterHeightLevel==WHL_SHOULDERS) ) + else if (PM_ForceJumpingUp(pm->gent) && (pm->ps->pm_flags&PMF_JUMP_HELD))//||pm->ps->waterHeightLevel==WHL_SHOULDERS) ) {//force jumping && holding jump /* if ( !pm->ps->forceJumpZStart && (pm->ps->waterHeightLevel==WHL_SHOULDERS&&pm->cmd.upmove>0) ) { - pm->ps->forceJumpZStart = pm->ps->origin[2]; + pm->ps->forceJumpZStart = pm->ps->origin[2]; } */ float curHeight = pm->ps->origin[2] - pm->ps->forceJumpZStart; //check for max force jump level and cap off & cut z vel - if ( ( curHeight<=forceJumpHeight[0] ||//still below minimum jump height - (pm->ps->forcePower&&pm->cmd.upmove>=10) ) &&////still have force power available and still trying to jump up - curHeight < forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] )//still below maximum jump height + if ((curHeight <= forceJumpHeight[0] * jumpMultiplier ||//still below minimum jump height + (pm->ps->forcePower&&pm->cmd.upmove >= 10)) &&////still have force power available and still trying to jump up + curHeight < forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] * jumpMultiplier)//still below maximum jump height {//can still go up //FIXME: after a certain amount of time of held jump, play force jump sound and flip if a dir is being held //FIXME: if hit a wall... should we cut velocity or allow them to slide up it? //FIXME: constantly drain force power at a rate by which the usage for maximum height would use up the full cost of force jump - if ( curHeight > forceJumpHeight[0] ) + if (curHeight > forceJumpHeight[0]) {//passed normal jump height *2? - if ( !(pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1 << FP_LEVITATION)))//haven't started forcejump yet { //start force jump - pm->ps->forcePowersActive |= (1<gent ) + pm->ps->forcePowersActive |= (1 << FP_LEVITATION); + if (pm->gent) { - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + G_SoundOnEnt(pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav"); // keep track of force jump stat - if(pm->ps->clientNum == 0 && pm->gent->client) + if (pm->ps->clientNum == 0 && pm->gent->client) { pm->gent->client->sess.missionStats.forceUsed[(int)FP_LEVITATION]++; } } //play flip //FIXME: do this only when they stop the jump (below) or when they're just about to hit the peak of the jump - if ( PM_InAirKickingAnim( pm->ps->legsAnim ) - && pm->ps->legsAnimTimer ) + if (PM_InAirKickingAnim(pm->ps->legsAnim) + && pm->ps->legsAnimTimer) {//still in kick } else if ((pm->cmd.forwardmove || pm->cmd.rightmove) && //pushing in a dir @@ -1269,100 +1315,100 @@ static qboolean PM_CheckJump( void ) pm->ps->legsAnim != BOTH_ALORA_FLIP_1 && pm->ps->legsAnim != BOTH_ALORA_FLIP_2 && pm->ps->legsAnim != BOTH_ALORA_FLIP_3 - && cg.renderingThirdPerson//third person only + && BG_AllowThirdPersonSpecialMove( pm->ps )//third person only && !cg.zoomMode //not zoomed in && !(pm->ps->saber[0].saberFlags&SFL_NO_FLIPS)//okay to do flips with this saber - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_FLIPS) )//okay to do flips with this saber + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_FLIPS))//okay to do flips with this saber ) {//FIXME: this could end up playing twice if the jump is very long... int anim = BOTH_FORCEINAIR1; int parts = SETANIM_BOTH; - if ( pm->cmd.forwardmove > 0 ) + if (pm->cmd.forwardmove > 0) { /* if ( pm->ps->forcePowerLevel[FP_LEVITATION] < FORCE_LEVEL_2 ) { - anim = BOTH_ARIAL_F1; + anim = BOTH_ARIAL_F1; } else */ - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA && Q_irand( 0, 3 ) ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA && Q_irand(0, 3)) { - anim = Q_irand( BOTH_ALORA_FLIP_1, BOTH_ALORA_FLIP_3 ); + anim = Q_irand(BOTH_ALORA_FLIP_1, BOTH_ALORA_FLIP_3); } else { anim = BOTH_FLIP_F; } } - else if ( pm->cmd.forwardmove < 0 ) + else if (pm->cmd.forwardmove < 0) { anim = BOTH_FLIP_B; } - else if ( pm->cmd.rightmove > 0 ) + else if (pm->cmd.rightmove > 0) { anim = BOTH_FLIP_R; } - else if ( pm->cmd.rightmove < 0 ) + else if (pm->cmd.rightmove < 0) { anim = BOTH_FLIP_L; } - if ( pm->ps->weaponTime ) + if (pm->ps->weaponTime) {//FIXME: really only care if we're in a saber attack anim... parts = SETANIM_LEGS; } - PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, parts, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } - else if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 ) + else if (pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1) {//FIXME: really want to know how far off ground we are, probably... - vec3_t facingFwd, facingRight, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + vec3_t facingFwd, facingRight, facingAngles = { 0, pm->ps->viewangles[YAW], 0 }; int anim = -1; - AngleVectors( facingAngles, facingFwd, facingRight, NULL ); - float dotR = DotProduct( facingRight, pm->ps->velocity ); - float dotF = DotProduct( facingFwd, pm->ps->velocity ); - if ( fabs(dotR) > fabs(dotF) * 1.5 ) + AngleVectors(facingAngles, facingFwd, facingRight, NULL); + float dotR = DotProduct(facingRight, pm->ps->velocity); + float dotF = DotProduct(facingFwd, pm->ps->velocity); + if (fabs(dotR) > fabs(dotF) * 1.5) { - if ( dotR > 150 ) + if (dotR > 150) { anim = BOTH_FORCEJUMPRIGHT1; } - else if ( dotR < -150 ) + else if (dotR < -150) { anim = BOTH_FORCEJUMPLEFT1; } } else { - if ( dotF > 150 ) + if (dotF > 150) { anim = BOTH_FORCEJUMP1; } - else if ( dotF < -150 ) + else if (dotF < -150) { anim = BOTH_FORCEJUMPBACK1; } } - if ( anim != -1 ) + if (anim != -1) { int parts = SETANIM_BOTH; - if ( pm->ps->weaponTime ) + if (pm->ps->weaponTime) {//FIXME: really only care if we're in a saber attack anim... parts = SETANIM_LEGS; } - PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, parts, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } } } else { - if ( !pm->ps->legsAnimTimer ) + if (!pm->ps->legsAnimTimer) {//not in the middle of a legsAnim int anim = pm->ps->legsAnim; int newAnim = -1; - switch ( anim ) + switch (anim) { case BOTH_FORCEJUMP1: newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1; @@ -1377,29 +1423,29 @@ static qboolean PM_CheckJump( void ) newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1; break; } - if ( newAnim != -1 ) + if (newAnim != -1) { int parts = SETANIM_BOTH; - if ( pm->ps->weaponTime ) + if (pm->ps->weaponTime) {//FIXME: really only care if we're in a saber attack anim... parts = SETANIM_LEGS; } - PM_SetAnim( pm, parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, parts, newAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } } } } //need to scale this down, start with height velocity (based on max force jump height) and scale down to regular jump vel - pm->ps->velocity[2] = (forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]]-curHeight)/forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]]*forceJumpStrength[pm->ps->forcePowerLevel[FP_LEVITATION]];//JUMP_VELOCITY; + pm->ps->velocity[2] = (forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] * jumpMultiplier - curHeight) / forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] * jumpMultiplier * forceJumpStrength[pm->ps->forcePowerLevel[FP_LEVITATION]];//JUMP_VELOCITY; pm->ps->velocity[2] /= 10; pm->ps->velocity[2] += JUMP_VELOCITY; pm->ps->pm_flags |= PMF_JUMP_HELD; } - else if ( curHeight > forceJumpHeight[0] && curHeight < forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] - forceJumpHeight[0] ) + else if (curHeight > forceJumpHeight[0] && curHeight < forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] * jumpMultiplier - forceJumpHeight[0]) {//still have some headroom, don't totally stop it - if ( pm->ps->velocity[2] > JUMP_VELOCITY ) + if (pm->ps->velocity[2] > JUMP_VELOCITY) { pm->ps->velocity[2] = JUMP_VELOCITY; } @@ -1417,79 +1463,79 @@ static qboolean PM_CheckJump( void ) #endif //Not jumping - if ( pm->cmd.upmove < 10 ) { + if (pm->cmd.upmove < 10) { return qfalse; } // must wait for jump to be released - if ( pm->ps->pm_flags & PMF_JUMP_HELD ) + if (pm->ps->pm_flags & PMF_JUMP_HELD) { // clear upmove so cmdscale doesn't lower running speed pm->cmd.upmove = 0; return qfalse; } - if ( pm->ps->gravity <= 0 ) + if (pm->ps->gravity <= 0) {//in low grav, you push in the dir you're facing as long as there is something behind you to shove off of vec3_t forward, back; trace_t trace; - AngleVectors( pm->ps->viewangles, forward, NULL, NULL ); - VectorMA( pm->ps->origin, -8, forward, back ); - pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, back, pm->ps->clientNum, pm->tracemask&~(CONTENTS_PLAYERCLIP|CONTENTS_MONSTERCLIP), (EG2_Collision)0, 0 ); + AngleVectors(pm->ps->viewangles, forward, NULL, NULL); + VectorMA(pm->ps->origin, -8, forward, back); + pm->trace(&trace, pm->ps->origin, pm->mins, pm->maxs, back, pm->ps->clientNum, pm->tracemask&~(CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP), (EG2_Collision)0, 0); pm->cmd.upmove = 0; - if ( trace.fraction < 1.0f ) + if (trace.fraction < 1.0f) { - VectorMA( pm->ps->velocity, JUMP_VELOCITY/2, forward, pm->ps->velocity ); + VectorMA(pm->ps->velocity, JUMP_VELOCITY / 2, forward, pm->ps->velocity); //FIXME: kicking off wall anim? At least check what anim we're in? - PM_SetAnim(pm,SETANIM_LEGS,BOTH_FORCEJUMP1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_FORCEJUMP1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); } else {//else no surf close enough to push off of return qfalse; } - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE) {//need to set some things and return //Jumping pm->ps->forceJumpZStart = 0; pml.groundPlane = qfalse; pml.walking = qfalse; - pm->ps->pm_flags |= (PMF_JUMPING|PMF_JUMP_HELD); + pm->ps->pm_flags |= (PMF_JUMPING | PMF_JUMP_HELD); pm->ps->groundEntityNum = ENTITYNUM_NONE; pm->ps->jumpZStart = pm->ps->origin[2]; - if ( pm->gent ) + if (pm->gent) { - if ( !Q3_TaskIDPending( pm->gent, TID_CHAN_VOICE ) ) + if (!Q3_TaskIDPending(pm->gent, TID_CHAN_VOICE)) { - PM_AddEvent( EV_JUMP ); + PM_AddEvent(EV_JUMP); } } else { - PM_AddEvent( EV_JUMP ); + PM_AddEvent(EV_JUMP); } return qtrue; }//else no surf close enough to push off of } - else if ( pm->cmd.upmove > 0 //want to jump + else if (pm->cmd.upmove > 0 //want to jump && pm->waterlevel < 2 //not in water above ankles && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 //have force jump ability && !(pm->ps->pm_flags&PMF_JUMP_HELD)//not holding jump from a previous jump //&& !PM_InKnockDown( pm->ps )//not in a knockdown && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period && pm->gent && WP_ForcePowerAvailable( pm->gent, FP_LEVITATION, 0 ) //have enough force power to jump - && ((pm->ps->clientNum&&!PM_ControlledByPlayer())||((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) )) )// yes this locked weapons check also includes force powers, if we need a separate check later I'll make one + && ((pm->ps->clientNum&&!PM_ControlledByPlayer())||((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && BG_AllowThirdPersonSpecialMove( pm->ps ) && !cg.zoomMode && !(pm->gent->flags&FL_LOCK_PLAYER_WEAPONS) )) )// yes this locked weapons check also includes force powers, if we need a separate check later I'll make one { - if ( pm->gent->NPC && pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank <= RANK_LT_JG ) + if (pm->gent->NPC && pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank <= RANK_LT_JG) {//reborn who are not acrobats can't do any of these acrobatics //FIXME: extern these abilities in the .npc file! } - else if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + else if (pm->ps->groundEntityNum != ENTITYNUM_NONE) {//on the ground //check for left-wall and right-wall special jumps int anim = -1; @@ -1497,32 +1543,32 @@ static qboolean PM_CheckJump( void ) int forcePowerCostOverride = 0; // Cartwheels/ariels/butterflies - if ( (pm->ps->weapon==WP_SABER&&G_TryingCartwheel(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/&&(pm->cmd.buttons&BUTTON_ATTACK))//using saber and holding focus + attack -// ||(pm->ps->weapon!=WP_SABER&&((pm->cmd.buttons&BUTTON_ATTACK)||(pm->cmd.buttons&BUTTON_ALT_ATTACK)) ) )//using any other weapon and hitting either attack button - && (((pm->ps->clientNum>=MAX_CLIENTS&&!PM_ControlledByPlayer())&&pm->cmd.upmove > 0&& pm->ps->velocity[2] >= 0 )//jumping NPC, going up already - ||((pm->ps->clientNumgent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/))//focus-holding player - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR*/ )// have enough power + if ((pm->ps->weapon == WP_SABER&&G_TryingCartwheel(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ && (pm->cmd.buttons&BUTTON_ATTACK))//using saber and holding focus + attack + // ||(pm->ps->weapon!=WP_SABER&&((pm->cmd.buttons&BUTTON_ATTACK)||(pm->cmd.buttons&BUTTON_ALT_ATTACK)) ) )//using any other weapon and hitting either attack button + && (((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) && pm->cmd.upmove > 0 && pm->ps->velocity[2] >= 0)//jumping NPC, going up already + || ((pm->ps->clientNumgent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/))//focus-holding player + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_LR)/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_LR*/)// have enough power {//holding attack and jumping - if ( pm->cmd.rightmove > 0 ) + if (pm->cmd.rightmove > 0) { // If they're using the staff we do different anims. - if ( pm->ps->saberAnimLevel == SS_STAFF - && pm->ps->weapon == WP_SABER ) + if (pm->ps->saberAnimLevel == SS_STAFF + && pm->ps->weapon == WP_SABER) { - if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) - || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 ) + if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) + || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2) { anim = BOTH_BUTTERFLY_RIGHT; - forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR ); + forcePowerCostOverride = G_CostForSpecialMove(SABER_ALT_ATTACK_POWER_LR); } } - else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) - || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 ) + else if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) + || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1) { - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS))) {//okay to do cartwheels with this saber - if ( pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer() ) + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) {//player: since we're on the ground, always do a cartwheel /* anim = BOTH_CARTWHEEL_RIGHT; @@ -1532,40 +1578,40 @@ static qboolean PM_CheckJump( void ) else { vertPush = JUMP_VELOCITY; - if ( Q_irand( 0, 1 ) ) + if (Q_irand(0, 1)) { anim = BOTH_ARIAL_RIGHT; - forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR ); + forcePowerCostOverride = G_CostForSpecialMove(SABER_ALT_ATTACK_POWER_LR); } else { anim = BOTH_CARTWHEEL_RIGHT; - forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR ); + forcePowerCostOverride = G_CostForSpecialMove(SABER_ALT_ATTACK_POWER_LR); } } } } } - else if ( pm->cmd.rightmove < 0 ) + else if (pm->cmd.rightmove < 0) { // If they're using the staff we do different anims. - if ( pm->ps->saberAnimLevel == SS_STAFF - && pm->ps->weapon == WP_SABER ) + if (pm->ps->saberAnimLevel == SS_STAFF + && pm->ps->weapon == WP_SABER) { - if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) - || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 ) + if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) + || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2) { anim = BOTH_BUTTERFLY_LEFT; - forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR ); + forcePowerCostOverride = G_CostForSpecialMove(SABER_ALT_ATTACK_POWER_LR); } } - else if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) - || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 ) + else if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) + || pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1) { - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_CARTWHEELS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_CARTWHEELS))) {//okay to do cartwheels with this saber - if ( pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer() ) + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) {//player: since we're on the ground, always do a cartwheel /* anim = BOTH_CARTWHEEL_LEFT; @@ -1575,124 +1621,124 @@ static qboolean PM_CheckJump( void ) else { vertPush = JUMP_VELOCITY; - if ( Q_irand( 0, 1 ) ) + if (Q_irand(0, 1)) { anim = BOTH_ARIAL_LEFT; - forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR ); + forcePowerCostOverride = G_CostForSpecialMove(SABER_ALT_ATTACK_POWER_LR); } else { anim = BOTH_CARTWHEEL_LEFT; - forcePowerCostOverride = G_CostForSpecialMove( SABER_ALT_ATTACK_POWER_LR ); + forcePowerCostOverride = G_CostForSpecialMove(SABER_ALT_ATTACK_POWER_LR); } } } } } } - else if ( pm->cmd.rightmove > 0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 ) + else if (pm->cmd.rightmove > 0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1) {//strafing right - if ( pm->cmd.forwardmove > 0 ) + if (pm->cmd.forwardmove > 0) {//wall-run - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS))) {//okay to do wall-runs with this saber - vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f; + vertPush = forceJumpStrength[FORCE_LEVEL_2] / 2.0f; anim = BOTH_WALL_RUN_RIGHT; } } - else if ( pm->cmd.forwardmove == 0 ) + else if (pm->cmd.forwardmove == 0) {//wall-flip - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS))) {//okay to do wall-flips with this saber - vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f; + vertPush = forceJumpStrength[FORCE_LEVEL_2] / 2.25f; anim = BOTH_WALL_FLIP_RIGHT; } } } - else if ( pm->cmd.rightmove < 0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 ) + else if (pm->cmd.rightmove < 0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1) {//strafing left - if ( pm->cmd.forwardmove > 0 ) + if (pm->cmd.forwardmove > 0) {//wall-run - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS))) {//okay to do wall-runs with this saber - vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f; + vertPush = forceJumpStrength[FORCE_LEVEL_2] / 2.0f; anim = BOTH_WALL_RUN_LEFT; } } - else if ( pm->cmd.forwardmove == 0 ) + else if (pm->cmd.forwardmove == 0) {//wall-flip - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS))) {//okay to do wall-flips with this saber - vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f; + vertPush = forceJumpStrength[FORCE_LEVEL_2] / 2.25f; anim = BOTH_WALL_FLIP_LEFT; } } } else if ( /*pm->ps->clientNum >= MAX_CLIENTS//not the player - && !PM_ControlledByPlayer() //not controlled by player - &&*/ pm->cmd.forwardmove > 0 //pushing forward - && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 )//have jump 2 or higher + && !PM_ControlledByPlayer() //not controlled by player + &&*/ pm->cmd.forwardmove > 0 //pushing forward + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1)//have jump 2 or higher {//step off wall, flip backwards - if ( VectorLengthSquared( pm->ps->velocity ) > 40000 /*200*200*/) + if (VectorLengthSquared(pm->ps->velocity) > 40000 /*200*200*/) {//have to be moving... FIXME: make sure it's opposite the wall... or at least forward? - if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 ) + if (pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2) {//run all the way up wwall - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_WALL_RUNS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_RUNS))) {//okay to do wall-runs with this saber - vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.0f; + vertPush = forceJumpStrength[FORCE_LEVEL_2] / 2.0f; anim = BOTH_FORCEWALLRUNFLIP_START; } } else {//run just a couple steps up - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_WALL_FLIPS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_FLIPS))) {//okay to do wall-flips with this saber - vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f; + vertPush = forceJumpStrength[FORCE_LEVEL_2] / 2.25f; anim = BOTH_WALL_FLIP_BACK1; } } } } - else if ( pm->cmd.forwardmove < 0 //pushing back + else if (pm->cmd.forwardmove < 0 //pushing back //&& pm->ps->clientNum//not the player - && !(pm->cmd.buttons&BUTTON_ATTACK) )//not attacking + && !(pm->cmd.buttons&BUTTON_ATTACK))//not attacking {//back-jump does backflip... FIXME: always?! What about just doing a normal jump backwards? - if ( pm->ps->velocity[2] >= 0 ) + if (pm->ps->velocity[2] >= 0) {//must be going up already - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_FLIPS) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_FLIPS)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_FLIPS) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_FLIPS))) {//okay to do backstabs with this saber vertPush = JUMP_VELOCITY; - if ( pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand( 0, 2 ) ) + if (pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA && !Q_irand(0, 2)) { anim = BOTH_ALORA_FLIP_B; } else { - anim = PM_PickAnim( pm->gent, BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 ); + anim = PM_PickAnim(pm->gent, BOTH_FLIP_BACK1, BOTH_FLIP_BACK3); } } } } - else if ( VectorLengthSquared( pm->ps->velocity ) < 256 /*16 squared*/) + else if (VectorLengthSquared(pm->ps->velocity) < 256 /*16 squared*/) {//not moving - if ( pm->ps->weapon == WP_SABER && (pm->cmd.buttons & BUTTON_ATTACK) ) + if (pm->ps->weapon == WP_SABER && (pm->cmd.buttons & BUTTON_ATTACK)) { saberMoveName_t overrideJumpAttackUpMove = LS_INVALID; - if ( pm->ps->saber[0].jumpAtkUpMove != LS_INVALID ) + if (pm->ps->saber[0].jumpAtkUpMove != LS_INVALID) { - if ( pm->ps->saber[0].jumpAtkUpMove != LS_NONE ) + if (pm->ps->saber[0].jumpAtkUpMove != LS_NONE) {//actually overriding overrideJumpAttackUpMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkUpMove; } - else if ( pm->ps->dualSabers - && pm->ps->saber[1].jumpAtkUpMove > LS_NONE ) + else if (pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkUpMove > LS_NONE) {//would be cancelling it, but check the second saber, too overrideJumpAttackUpMove = (saberMoveName_t)pm->ps->saber[1].jumpAtkUpMove; } @@ -1701,144 +1747,144 @@ static qboolean PM_CheckJump( void ) overrideJumpAttackUpMove = LS_NONE; } } - else if ( pm->ps->dualSabers - && pm->ps->saber[1].jumpAtkUpMove != LS_INVALID ) + else if (pm->ps->dualSabers + && pm->ps->saber[1].jumpAtkUpMove != LS_INVALID) {//first saber not overridden, check second overrideJumpAttackUpMove = (saberMoveName_t)pm->ps->saber[0].jumpAtkUpMove; } - if ( overrideJumpAttackUpMove != LS_INVALID ) + if (overrideJumpAttackUpMove != LS_INVALID) {//do this move instead - if ( overrideJumpAttackUpMove != LS_NONE ) + if (overrideJumpAttackUpMove != LS_NONE) { anim = saberMoveData[overrideJumpAttackUpMove].animToUse; } } - else if ( pm->ps->saberAnimLevel == SS_MEDIUM ) + else if (pm->ps->saberAnimLevel == SS_MEDIUM) { /* //Only tavion does these now if ( pm->ps->clientNum && Q_irand( 0, 1 ) ) {//butterfly... FIXME: does direction matter? - vertPush = JUMP_VELOCITY; - if ( Q_irand( 0, 1 ) ) - { - anim = BOTH_BUTTERFLY_LEFT; - } - else - { - anim = BOTH_BUTTERFLY_RIGHT; - } + vertPush = JUMP_VELOCITY; + if ( Q_irand( 0, 1 ) ) + { + anim = BOTH_BUTTERFLY_LEFT; + } + else + { + anim = BOTH_BUTTERFLY_RIGHT; + } } else - */if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() )//NOTE: pretty much useless, so player never does these + */if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer())//NOTE: pretty much useless, so player never does these {//jump-spin FIXME: does direction matter? - vertPush = forceJumpStrength[FORCE_LEVEL_2]/1.5f; - if ( pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA ) + vertPush = forceJumpStrength[FORCE_LEVEL_2] / 1.5f; + if (pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA) { anim = BOTH_ALORA_SPIN; } else { - anim = Q_irand( BOTH_FJSS_TR_BL, BOTH_FJSS_TL_BR ); + anim = Q_irand(BOTH_FJSS_TR_BL, BOTH_FJSS_TL_BR); } } } } } - if ( anim != -1 && PM_HasAnimation( pm->gent, anim ) ) + if (anim != -1 && PM_HasAnimation(pm->gent, anim)) { - vec3_t fwd, right, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + vec3_t fwd, right, traceto, mins = { pm->mins[0], pm->mins[1], 0 }, maxs = { pm->maxs[0], pm->maxs[1], 24 }, fwdAngles = { 0, pm->ps->viewangles[YAW], 0 }; trace_t trace; qboolean doTrace = qfalse; int contents = CONTENTS_SOLID; - AngleVectors( fwdAngles, fwd, right, NULL ); + AngleVectors(fwdAngles, fwd, right, NULL); //trace-check for a wall, if necc. - switch ( anim ) + switch (anim) { case BOTH_WALL_FLIP_LEFT: - if ( g_debugMelee->integer ) + if (g_debugMelee->integer) { contents |= CONTENTS_BODY; } //NOTE: purposely falls through to next case! case BOTH_WALL_RUN_LEFT: doTrace = qtrue; - VectorMA( pm->ps->origin, -16, right, traceto ); + VectorMA(pm->ps->origin, -16, right, traceto); break; case BOTH_WALL_FLIP_RIGHT: - if ( g_debugMelee->integer ) + if (g_debugMelee->integer) { contents |= CONTENTS_BODY; } //NOTE: purposely falls through to next case! case BOTH_WALL_RUN_RIGHT: doTrace = qtrue; - VectorMA( pm->ps->origin, 16, right, traceto ); + VectorMA(pm->ps->origin, 16, right, traceto); break; case BOTH_WALL_FLIP_BACK1: - if ( g_debugMelee->integer ) + if (g_debugMelee->integer) { contents |= CONTENTS_BODY; } doTrace = qtrue; - VectorMA( pm->ps->origin, 32, fwd, traceto );//was 16 + VectorMA(pm->ps->origin, 32, fwd, traceto);//was 16 break; case BOTH_FORCEWALLRUNFLIP_START: - if ( g_debugMelee->integer ) + if (g_debugMelee->integer) { contents |= CONTENTS_BODY; } doTrace = qtrue; - VectorMA( pm->ps->origin, 32, fwd, traceto );//was 16 + VectorMA(pm->ps->origin, 32, fwd, traceto);//was 16 break; } - vec3_t idealNormal={0}, wallNormal={0}; - if ( doTrace ) + vec3_t idealNormal = { 0 }, wallNormal = { 0 }; + if (doTrace) { //FIXME: all these jump ones should check for head clearance - pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 ); - VectorCopy( trace.plane.normal, wallNormal ); - VectorNormalize( wallNormal ); - VectorSubtract( pm->ps->origin, traceto, idealNormal ); - VectorNormalize( idealNormal ); - if ( anim == BOTH_WALL_FLIP_LEFT ) + pm->trace(&trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0); + VectorCopy(trace.plane.normal, wallNormal); + VectorNormalize(wallNormal); + VectorSubtract(pm->ps->origin, traceto, idealNormal); + VectorNormalize(idealNormal); + if (anim == BOTH_WALL_FLIP_LEFT) {//sigh.. check for bottomless pit to the right trace_t trace2; vec3_t start; - VectorMA( pm->ps->origin, 128, right, traceto ); - pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 ); - if ( !trace2.allsolid && !trace2.startsolid ) + VectorMA(pm->ps->origin, 128, right, traceto); + pm->trace(&trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0); + if (!trace2.allsolid && !trace2.startsolid) { - VectorCopy( trace2.endpos, traceto ); - VectorCopy( traceto, start ); + VectorCopy(trace2.endpos, traceto); + VectorCopy(traceto, start); traceto[2] -= 384; - pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 ); - if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f ) + pm->trace(&trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0); + if (!trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f) {//bottomless pit! trace.fraction = 1.0f;//way to stop it from doing the side-flip } } } - else if ( anim == BOTH_WALL_FLIP_RIGHT ) + else if (anim == BOTH_WALL_FLIP_RIGHT) {//sigh.. check for bottomless pit to the left trace_t trace2; vec3_t start; - VectorMA( pm->ps->origin, -128, right, traceto ); - pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 ); - if ( !trace2.allsolid && !trace2.startsolid ) + VectorMA(pm->ps->origin, -128, right, traceto); + pm->trace(&trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0); + if (!trace2.allsolid && !trace2.startsolid) { - VectorCopy( trace2.endpos, traceto ); - VectorCopy( traceto, start ); + VectorCopy(trace2.endpos, traceto); + VectorCopy(traceto, start); traceto[2] -= 384; - pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 ); - if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f ) + pm->trace(&trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0); + if (!trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f) {//bottomless pit! trace.fraction = 1.0f;//way to stop it from doing the side-flip } @@ -1846,54 +1892,54 @@ static qboolean PM_CheckJump( void ) } else { - if ( anim == BOTH_WALL_FLIP_BACK1 - || anim == BOTH_FORCEWALLRUNFLIP_START ) + if (anim == BOTH_WALL_FLIP_BACK1 + || anim == BOTH_FORCEWALLRUNFLIP_START) {//trace up and forward a little to make sure the wall it at least 64 tall - if ( (contents&CONTENTS_BODY)//included entitied + if ((contents&CONTENTS_BODY)//included entitied && (trace.contents&CONTENTS_BODY) //hit an entity - && g_entities[trace.entityNum].client )//hit a client + && g_entities[trace.entityNum].client)//hit a client {//no need to trace up, it's all good... - if ( PM_InOnGroundAnim( &g_entities[trace.entityNum].client->ps ) )//on the ground, no jump + if (PM_InOnGroundAnim(&g_entities[trace.entityNum].client->ps))//on the ground, no jump {//can't jump off guys on ground trace.fraction = 1.0f;//way to stop if from doing the jump } - else if ( anim == BOTH_FORCEWALLRUNFLIP_START ) + else if (anim == BOTH_FORCEWALLRUNFLIP_START) {//instead of wall-running up, do the backflip anim = BOTH_WALL_FLIP_BACK1; - vertPush = forceJumpStrength[FORCE_LEVEL_2]/2.25f; + vertPush = forceJumpStrength[FORCE_LEVEL_2] / 2.25f; } } - else if ( anim == BOTH_WALL_FLIP_BACK1 ) + else if (anim == BOTH_WALL_FLIP_BACK1) { trace_t trace2; vec3_t start; - VectorCopy( pm->ps->origin, start ); + VectorCopy(pm->ps->origin, start); start[2] += 64; - VectorMA( start, 32, fwd, traceto ); - pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 ); - if ( trace2.allsolid + VectorMA(start, 32, fwd, traceto); + pm->trace(&trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0); + if (trace2.allsolid || trace2.startsolid - || trace2.fraction >= 1.0f ) + || trace2.fraction >= 1.0f) {//no room above or no wall in front at that height trace.fraction = 1.0f;//way to stop if from doing the jump } } } - if ( trace.fraction < 1.0f ) + if (trace.fraction < 1.0f) {//still valid to jump - if ( anim == BOTH_WALL_FLIP_BACK1 ) + if (anim == BOTH_WALL_FLIP_BACK1) {//sigh.. check for bottomless pit to the rear trace_t trace2; vec3_t start; - VectorMA( pm->ps->origin, -128, fwd, traceto ); - pm->trace( &trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 ); - if ( !trace2.allsolid && !trace2.startsolid ) + VectorMA(pm->ps->origin, -128, fwd, traceto); + pm->trace(&trace2, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0); + if (!trace2.allsolid && !trace2.startsolid) { - VectorCopy( trace2.endpos, traceto ); - VectorCopy( traceto, start ); + VectorCopy(trace2.endpos, traceto); + VectorCopy(traceto, start); traceto[2] -= 384; - pm->trace( &trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0 ); - if ( !trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f ) + pm->trace(&trace2, start, mins, maxs, traceto, pm->ps->clientNum, contents, (EG2_Collision)0, 0); + if (!trace2.allsolid && !trace2.startsolid && trace2.fraction >= 1.0f) {//bottomless pit! trace.fraction = 1.0f;//way to stop it from doing the side-flip } @@ -1904,143 +1950,143 @@ static qboolean PM_CheckJump( void ) } gentity_t *traceEnt = &g_entities[trace.entityNum]; - if ( !doTrace || (trace.fraction < 1.0f&&((trace.entityNums.solid!=SOLID_BMODEL)||DotProduct(wallNormal,idealNormal)>0.7)) ) + if (!doTrace || (trace.fraction < 1.0f && ((trace.entityNums.solid != SOLID_BMODEL) || DotProduct(wallNormal, idealNormal)>0.7))) {//there is a wall there - if ( (anim != BOTH_WALL_RUN_LEFT - && anim != BOTH_WALL_RUN_RIGHT - && anim != BOTH_FORCEWALLRUNFLIP_START) - || (wallNormal[2] >= 0.0f && wallNormal[2] <= MAX_WALL_RUN_Z_NORMAL) ) + if ((anim != BOTH_WALL_RUN_LEFT + && anim != BOTH_WALL_RUN_RIGHT + && anim != BOTH_FORCEWALLRUNFLIP_START) + || (wallNormal[2] >= 0.0f && wallNormal[2] <= MAX_WALL_RUN_Z_NORMAL)) {//wall-runs can only run on relatively flat walls, sorry. - if ( anim == BOTH_ARIAL_LEFT || anim == BOTH_CARTWHEEL_LEFT ) + if (anim == BOTH_ARIAL_LEFT || anim == BOTH_CARTWHEEL_LEFT) { pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, -185, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, -185, right, pm->ps->velocity); } - else if ( anim == BOTH_ARIAL_RIGHT || anim == BOTH_CARTWHEEL_RIGHT ) + else if (anim == BOTH_ARIAL_RIGHT || anim == BOTH_CARTWHEEL_RIGHT) { pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, 185, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, 185, right, pm->ps->velocity); } - else if ( anim == BOTH_BUTTERFLY_LEFT ) + else if (anim == BOTH_BUTTERFLY_LEFT) { pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, -190, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, -190, right, pm->ps->velocity); } - else if ( anim == BOTH_BUTTERFLY_RIGHT ) + else if (anim == BOTH_BUTTERFLY_RIGHT) { pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, 190, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, 190, right, pm->ps->velocity); } //move me to side - else if ( anim == BOTH_WALL_FLIP_LEFT ) + else if (anim == BOTH_WALL_FLIP_LEFT) { pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, 150, right, pm->ps->velocity); } - else if ( anim == BOTH_WALL_FLIP_RIGHT ) + else if (anim == BOTH_WALL_FLIP_RIGHT) { pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, -150, right, pm->ps->velocity); } - else if ( anim == BOTH_FLIP_BACK1 + else if (anim == BOTH_FLIP_BACK1 || anim == BOTH_FLIP_BACK2 || anim == BOTH_FLIP_BACK3 || anim == BOTH_ALORA_FLIP_B - || anim == BOTH_WALL_FLIP_BACK1 ) + || anim == BOTH_WALL_FLIP_BACK1) { pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity ); + VectorMA(pm->ps->velocity, -150, fwd, pm->ps->velocity); } //kick if jumping off an ent - if ( doTrace + if (doTrace && anim != BOTH_WALL_RUN_LEFT && anim != BOTH_WALL_RUN_RIGHT && anim != BOTH_FORCEWALLRUNFLIP_START) { - if ( pm->gent && trace.entityNum < ENTITYNUM_WORLD ) + if (pm->gent && trace.entityNum < ENTITYNUM_WORLD) { - if ( traceEnt + if (traceEnt && traceEnt->client && traceEnt->health > 0 && traceEnt->takedamage && traceEnt->client->NPC_class != CLASS_GALAKMECH && traceEnt->client->NPC_class != CLASS_DESANN - && !(traceEnt->flags&FL_NO_KNOCKBACK) ) + && !(traceEnt->flags&FL_NO_KNOCKBACK)) {//push them away and do pain vec3_t oppDir, fxDir; - float strength = VectorNormalize2( pm->ps->velocity, oppDir ); - VectorScale( oppDir, -1, oppDir ); + float strength = VectorNormalize2(pm->ps->velocity, oppDir); + VectorScale(oppDir, -1, oppDir); //FIXME: need knockdown anim - G_Damage( traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR|DAMAGE_NO_HIT_LOC|DAMAGE_NO_KNOCKBACK, MOD_MELEE ); - VectorCopy( fwd, fxDir ); - VectorScale( fxDir, -1, fxDir ); - G_PlayEffect( G_EffectIndex( "melee/kick_impact" ), trace.endpos, fxDir ); + G_Damage(traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR | DAMAGE_NO_HIT_LOC | DAMAGE_NO_KNOCKBACK, MOD_MELEE); + VectorCopy(fwd, fxDir); + VectorScale(fxDir, -1, fxDir); + G_PlayEffect(G_EffectIndex("melee/kick_impact"), trace.endpos, fxDir); //G_Sound( traceEnt, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) ); - if ( traceEnt->health > 0 ) + if (traceEnt->health > 0) {//didn't kill him - if ( (traceEnt->s.number==0&&!Q_irand(0,g_spskill->integer)) - || (traceEnt->NPC!=NULL&&Q_irand(RANK_CIVILIAN,traceEnt->NPC->rank)+Q_irand(-2,2)s.number == 0 && !Q_irand(0, g_spskill->integer)) + || (traceEnt->NPC != NULL&&Q_irand(RANK_CIVILIAN, traceEnt->NPC->rank) + Q_irand(-2, 2)ps->velocity[2] = vertPush; } //animate me - if ( anim == BOTH_BUTTERFLY_RIGHT ) + if (anim == BOTH_BUTTERFLY_RIGHT) { - PM_SetSaberMove( LS_BUTTERFLY_RIGHT ); + PM_SetSaberMove(LS_BUTTERFLY_RIGHT); } - else if ( anim == BOTH_BUTTERFLY_LEFT ) + else if (anim == BOTH_BUTTERFLY_LEFT) { - PM_SetSaberMove( LS_BUTTERFLY_LEFT ); + PM_SetSaberMove(LS_BUTTERFLY_LEFT); } else {//not a proper saberMove, so do set all the details manually int parts = SETANIM_LEGS; if ( /*anim == BOTH_BUTTERFLY_LEFT || - anim == BOTH_BUTTERFLY_RIGHT ||*/ - anim == BOTH_FJSS_TR_BL || - anim == BOTH_FJSS_TL_BR ) + anim == BOTH_BUTTERFLY_RIGHT ||*/ + anim == BOTH_FJSS_TR_BL || + anim == BOTH_FJSS_TL_BR) { parts = SETANIM_BOTH; - pm->cmd.buttons&=~BUTTON_ATTACK; + pm->cmd.buttons &= ~BUTTON_ATTACK; pm->ps->saberMove = LS_NONE; - pm->gent->client->ps.SaberActivateTrail( 300 ); + pm->gent->client->ps.SaberActivateTrail(300); } - else if ( !pm->ps->weaponTime ) + else if (!pm->ps->weaponTime) { parts = SETANIM_BOTH; } - PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); + PM_SetAnim(pm, parts, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0); if ( /*anim == BOTH_BUTTERFLY_LEFT - || anim == BOTH_BUTTERFLY_RIGHT - ||*/ anim == BOTH_FJSS_TR_BL - || anim == BOTH_FJSS_TL_BR - || anim == BOTH_FORCEWALLRUNFLIP_START ) + || anim == BOTH_BUTTERFLY_RIGHT + ||*/ anim == BOTH_FJSS_TR_BL + || anim == BOTH_FJSS_TL_BR + || anim == BOTH_FORCEWALLRUNFLIP_START) { pm->ps->weaponTime = pm->ps->torsoAnimTimer; } - else if ( anim == BOTH_WALL_FLIP_LEFT - || anim == BOTH_WALL_FLIP_RIGHT - || anim == BOTH_WALL_FLIP_BACK1 ) + else if (anim == BOTH_WALL_FLIP_LEFT + || anim == BOTH_WALL_FLIP_RIGHT + || anim == BOTH_WALL_FLIP_BACK1) {//let us do some more moves after this pm->ps->saberAttackChainCount = 0; } } pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height - pm->ps->pm_flags |= (PMF_JUMPING|PMF_SLOW_MO_FALL); + pm->ps->pm_flags |= (PMF_JUMPING | PMF_SLOW_MO_FALL); pm->cmd.upmove = 0; - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); - WP_ForcePowerDrain( pm->gent, FP_LEVITATION, forcePowerCostOverride ); + G_SoundOnEnt(pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav"); + WP_ForcePowerDrain(pm->gent, FP_LEVITATION, forcePowerCostOverride); } } } @@ -2049,300 +2095,300 @@ static qboolean PM_CheckJump( void ) {//in the air int legsAnim = pm->ps->legsAnim; - if ( legsAnim == BOTH_WALL_RUN_LEFT || legsAnim == BOTH_WALL_RUN_RIGHT ) + if (legsAnim == BOTH_WALL_RUN_LEFT || legsAnim == BOTH_WALL_RUN_RIGHT) {//running on a wall - vec3_t right, traceto, mins = {pm->mins[0],pm->mins[0],0}, maxs = {pm->maxs[0],pm->maxs[0],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + vec3_t right, traceto, mins = { pm->mins[0], pm->mins[0], 0 }, maxs = { pm->maxs[0], pm->maxs[0], 24 }, fwdAngles = { 0, pm->ps->viewangles[YAW], 0 }; trace_t trace; int anim = -1; - AngleVectors( fwdAngles, NULL, right, NULL ); + AngleVectors(fwdAngles, NULL, right, NULL); - if ( legsAnim == BOTH_WALL_RUN_LEFT ) + if (legsAnim == BOTH_WALL_RUN_LEFT) { - if ( pm->ps->legsAnimTimer > 400 ) + if (pm->ps->legsAnimTimer > 400) {//not at the end of the anim - float animLen = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, BOTH_WALL_RUN_LEFT ); - if ( pm->ps->legsAnimTimer < animLen - 400 ) + float animLen = PM_AnimLength(pm->gent->client->clientInfo.animFileIndex, BOTH_WALL_RUN_LEFT); + if (pm->ps->legsAnimTimer < animLen - 400) {//not at start of anim - VectorMA( pm->ps->origin, -16, right, traceto ); + VectorMA(pm->ps->origin, -16, right, traceto); anim = BOTH_WALL_RUN_LEFT_FLIP; } } } - else if ( legsAnim == BOTH_WALL_RUN_RIGHT ) + else if (legsAnim == BOTH_WALL_RUN_RIGHT) { - if ( pm->ps->legsAnimTimer > 400 ) + if (pm->ps->legsAnimTimer > 400) {//not at the end of the anim - float animLen = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, BOTH_WALL_RUN_RIGHT ); - if ( pm->ps->legsAnimTimer < animLen - 400 ) + float animLen = PM_AnimLength(pm->gent->client->clientInfo.animFileIndex, BOTH_WALL_RUN_RIGHT); + if (pm->ps->legsAnimTimer < animLen - 400) {//not at start of anim - VectorMA( pm->ps->origin, 16, right, traceto ); + VectorMA(pm->ps->origin, 16, right, traceto); anim = BOTH_WALL_RUN_RIGHT_FLIP; } } } - if ( anim != -1 ) + if (anim != -1) { - pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 ); - if ( trace.fraction < 1.0f ) + pm->trace(&trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID | CONTENTS_BODY, (EG2_Collision)0, 0); + if (trace.fraction < 1.0f) {//flip off wall - if ( anim == BOTH_WALL_RUN_LEFT_FLIP ) + if (anim == BOTH_WALL_RUN_LEFT_FLIP) { pm->ps->velocity[0] *= 0.5f; pm->ps->velocity[1] *= 0.5f; - VectorMA( pm->ps->velocity, 150, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, 150, right, pm->ps->velocity); } - else if ( anim == BOTH_WALL_RUN_RIGHT_FLIP ) + else if (anim == BOTH_WALL_RUN_RIGHT_FLIP) { pm->ps->velocity[0] *= 0.5f; pm->ps->velocity[1] *= 0.5f; - VectorMA( pm->ps->velocity, -150, right, pm->ps->velocity ); + VectorMA(pm->ps->velocity, -150, right, pm->ps->velocity); } - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); - pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0); + pm->ps->pm_flags |= PMF_JUMPING | PMF_SLOW_MO_FALL; pm->cmd.upmove = 0; } } - if ( pm->cmd.upmove != 0 ) + if (pm->cmd.upmove != 0) {//jump failed, so don't try to do normal jump code, just return return qfalse; } } - else if ( pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START ) + else if (pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START) {//want to jump off wall - vec3_t fwd, traceto, mins = {pm->mins[0],pm->mins[0],0}, maxs = {pm->maxs[0],pm->maxs[0],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + vec3_t fwd, traceto, mins = { pm->mins[0], pm->mins[0], 0 }, maxs = { pm->maxs[0], pm->maxs[0], 24 }, fwdAngles = { 0, pm->ps->viewangles[YAW], 0 }; trace_t trace; int anim = -1; - AngleVectors( fwdAngles, fwd, NULL, NULL ); + AngleVectors(fwdAngles, fwd, NULL, NULL); - float animLen = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, BOTH_FORCEWALLRUNFLIP_START ); - if ( pm->ps->legsAnimTimer < animLen - 250 )//was 400 + float animLen = PM_AnimLength(pm->gent->client->clientInfo.animFileIndex, BOTH_FORCEWALLRUNFLIP_START); + if (pm->ps->legsAnimTimer < animLen - 250)//was 400 {//not at start of anim - VectorMA( pm->ps->origin, 16, fwd, traceto ); + VectorMA(pm->ps->origin, 16, fwd, traceto); anim = BOTH_FORCEWALLRUNFLIP_END; } - if ( anim != -1 ) + if (anim != -1) { - pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID|CONTENTS_BODY, (EG2_Collision)0, 0 ); - if ( trace.fraction < 1.0f ) + pm->trace(&trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID | CONTENTS_BODY, (EG2_Collision)0, 0); + if (trace.fraction < 1.0f) {//flip off wall pm->ps->velocity[0] *= 0.5f; pm->ps->velocity[1] *= 0.5f; - VectorMA( pm->ps->velocity, WALL_RUN_UP_BACKFLIP_SPEED, fwd, pm->ps->velocity ); + VectorMA(pm->ps->velocity, WALL_RUN_UP_BACKFLIP_SPEED, fwd, pm->ps->velocity); pm->ps->velocity[2] += 200; int parts = SETANIM_LEGS; - if ( !pm->ps->weaponTime ) + if (!pm->ps->weaponTime) {//not attacking, set anim on both parts = SETANIM_BOTH; } - PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); + PM_SetAnim(pm, parts, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 0); //FIXME: do damage to traceEnt, like above? - pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + pm->ps->pm_flags |= PMF_JUMPING | PMF_SLOW_MO_FALL; pm->cmd.upmove = 0; - PM_AddEvent( EV_JUMP ); + PM_AddEvent(EV_JUMP); } } - if ( pm->cmd.upmove != 0 ) + if (pm->cmd.upmove != 0) {//jump failed, so don't try to do normal jump code, just return return qfalse; } } /* else if ( pm->cmd.forwardmove < 0 - && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 - && !(pm->ps->pm_flags&PMF_JUMP_HELD) //not holding jump - && (level.time - pm->ps->lastOnGround) <= 250 //just jumped - )//&& !(pm->cmd.buttons&BUTTON_ATTACK) ) + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 + && !(pm->ps->pm_flags&PMF_JUMP_HELD) //not holding jump + && (level.time - pm->ps->lastOnGround) <= 250 //just jumped + )//&& !(pm->cmd.buttons&BUTTON_ATTACK) ) {//double-tap back-jump does backflip - vec3_t fwd, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + vec3_t fwd, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; - AngleVectors( fwdAngles, fwd, NULL, NULL ); - pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity ); - //pm->ps->velocity[2] = JUMP_VELOCITY; - int parts = SETANIM_LEGS; - if ( !pm->ps->weaponTime ) - { - parts = SETANIM_BOTH; - } - PM_SetAnim( pm, parts, PM_PickAnim( pm->gent, BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 ), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); - pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height - pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; - pm->cmd.upmove = 0; - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); - WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 ); + AngleVectors( fwdAngles, fwd, NULL, NULL ); + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity ); + //pm->ps->velocity[2] = JUMP_VELOCITY; + int parts = SETANIM_LEGS; + if ( !pm->ps->weaponTime ) + { + parts = SETANIM_BOTH; + } + PM_SetAnim( pm, parts, PM_PickAnim( pm->gent, BOTH_FLIP_BACK1, BOTH_FLIP_BACK3 ), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + pm->cmd.upmove = 0; + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 ); } */ /* else if ( pm->cmd.forwardmove > 0 //pushing forward - && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period - && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //have force jump 2 or higher - && (level.time - pm->ps->lastOnGround) <= 250//just jumped - && (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 )//not in a flip or spin or anything + && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 //have force jump 2 or higher + && (level.time - pm->ps->lastOnGround) <= 250//just jumped + && (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 )//not in a flip or spin or anything {//run up wall, flip backwards - //FIXME: have to be moving... make sure it's opposite the wall... or at least forward? - int wallWalkAnim = BOTH_WALL_FLIP_BACK1; - int parts = SETANIM_LEGS; - int contents = CONTENTS_SOLID; - qboolean kick = qtrue; - if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 ) - { - wallWalkAnim = BOTH_FORCEWALLRUNFLIP_START; - parts = SETANIM_BOTH; - kick = qfalse; - } - else - { - contents |= CONTENTS_BODY; - if ( !pm->ps->weaponTime ) - { - parts = SETANIM_BOTH; - } - } - if ( PM_HasAnimation( pm->gent, wallWalkAnim ) ) - { - vec3_t fwd, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; - trace_t trace; - vec3_t idealNormal; + //FIXME: have to be moving... make sure it's opposite the wall... or at least forward? + int wallWalkAnim = BOTH_WALL_FLIP_BACK1; + int parts = SETANIM_LEGS; + int contents = CONTENTS_SOLID; + qboolean kick = qtrue; + if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2 ) + { + wallWalkAnim = BOTH_FORCEWALLRUNFLIP_START; + parts = SETANIM_BOTH; + kick = qfalse; + } + else + { + contents |= CONTENTS_BODY; + if ( !pm->ps->weaponTime ) + { + parts = SETANIM_BOTH; + } + } + if ( PM_HasAnimation( pm->gent, wallWalkAnim ) ) + { + vec3_t fwd, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + trace_t trace; + vec3_t idealNormal; - AngleVectors( fwdAngles, fwd, NULL, NULL ); - VectorMA( pm->ps->origin, 32, fwd, traceto ); + AngleVectors( fwdAngles, fwd, NULL, NULL ); + VectorMA( pm->ps->origin, 32, fwd, traceto ); - pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );//FIXME: clip brushes too? - VectorSubtract( pm->ps->origin, traceto, idealNormal ); - VectorNormalize( idealNormal ); - gentity_t *traceEnt = &g_entities[trace.entityNum]; + pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, contents );//FIXME: clip brushes too? + VectorSubtract( pm->ps->origin, traceto, idealNormal ); + VectorNormalize( idealNormal ); + gentity_t *traceEnt = &g_entities[trace.entityNum]; - if ( trace.fraction < 1.0f - &&((trace.entityNums.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) ) - {//there is a wall there - pm->ps->velocity[0] = pm->ps->velocity[1] = 0; - if ( wallWalkAnim == BOTH_FORCEWALLRUNFLIP_START ) - { - pm->ps->velocity[2] = forceJumpStrength[FORCE_LEVEL_3]/2.0f; - } - else - { - VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity ); - } - //animate me - PM_SetAnim( pm, parts, wallWalkAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); - pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height - pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; - pm->cmd.upmove = 0; - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); - WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 ); - //kick if jumping off an ent - if ( kick && pm->gent && trace.entityNum < ENTITYNUM_WORLD ) - { - if ( traceEnt - && traceEnt->client - && traceEnt->health > 0 - && traceEnt->takedamage - && traceEnt->client->NPC_class != CLASS_GALAKMECH - && traceEnt->client->NPC_class != CLASS_DESANN - && !(traceEnt->flags&FL_NO_KNOCKBACK) ) - {//push them away and do pain - vec3_t oppDir; - float strength = VectorNormalize2( pm->ps->velocity, oppDir ); - VectorScale( oppDir, -1, oppDir ); - //FIXME: need knockdown anim - G_Damage( traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR|DAMAGE_NO_HIT_LOC|DAMAGE_NO_KNOCKBACK, MOD_MELEE ); - G_Sound( traceEnt, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) ); - if ( trace.fraction <= 0.5f ) - {//close to him - if ( traceEnt->health > 0 ) - {//didn't kill him - if ( (traceEnt->s.number==0&&!Q_irand(0,g_spskill->integer)) - || (traceEnt->NPC!=NULL&&Q_irand(RANK_CIVILIAN,traceEnt->NPC->rank)+Q_irand(-2,2)s.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) ) + {//there is a wall there + pm->ps->velocity[0] = pm->ps->velocity[1] = 0; + if ( wallWalkAnim == BOTH_FORCEWALLRUNFLIP_START ) + { + pm->ps->velocity[2] = forceJumpStrength[FORCE_LEVEL_3]/2.0f; + } + else + { + VectorMA( pm->ps->velocity, -150, fwd, pm->ps->velocity ); + } + //animate me + PM_SetAnim( pm, parts, wallWalkAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); + pm->ps->forceJumpZStart = pm->ps->origin[2];//so we don't take damage if we land at same height + pm->ps->pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL; + pm->cmd.upmove = 0; + G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + WP_ForcePowerDrain( pm->gent, FP_LEVITATION, 0 ); + //kick if jumping off an ent + if ( kick && pm->gent && trace.entityNum < ENTITYNUM_WORLD ) + { + if ( traceEnt + && traceEnt->client + && traceEnt->health > 0 + && traceEnt->takedamage + && traceEnt->client->NPC_class != CLASS_GALAKMECH + && traceEnt->client->NPC_class != CLASS_DESANN + && !(traceEnt->flags&FL_NO_KNOCKBACK) ) + {//push them away and do pain + vec3_t oppDir; + float strength = VectorNormalize2( pm->ps->velocity, oppDir ); + VectorScale( oppDir, -1, oppDir ); + //FIXME: need knockdown anim + G_Damage( traceEnt, pm->gent, pm->gent, oppDir, traceEnt->currentOrigin, 10, DAMAGE_NO_ARMOR|DAMAGE_NO_HIT_LOC|DAMAGE_NO_KNOCKBACK, MOD_MELEE ); + G_Sound( traceEnt, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) ); + if ( trace.fraction <= 0.5f ) + {//close to him + if ( traceEnt->health > 0 ) + {//didn't kill him + if ( (traceEnt->s.number==0&&!Q_irand(0,g_spskill->integer)) + || (traceEnt->NPC!=NULL&&Q_irand(RANK_CIVILIAN,traceEnt->NPC->rank)+Q_irand(-2,2)ps->legsAnimTimer<=100 - ||!PM_InSpecialJump( legsAnim )//not in a special jump anim - ||PM_InReboundJump( legsAnim )//we're already in a rebound - ||PM_InBackFlip( legsAnim ) )//a backflip (needed so you can jump off a wall behind you) - //&& pm->ps->velocity[2] <= 0 - && pm->ps->velocity[2] > -1200 //not falling down very fast - && !(pm->ps->pm_flags&PMF_JUMP_HELD)//have to have released jump since last press - && (pm->cmd.forwardmove||pm->cmd.rightmove)//pushing in a direction - //&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period - && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2//level 3 jump or better - && pm->ps->forcePower > 10 //have enough force power to do another one - && (level.time-pm->ps->lastOnGround) > 250 //haven't been on the ground in the last 1/4 of a second - && (!(pm->ps->pm_flags&PMF_JUMPING)//not jumping - || ( (level.time-pm->ps->lastOnGround) > 250 //we are jumping, but have been in the air for at least half a second - &&( g_debugMelee->integer//if you know kung fu, no height cap on wall-grab-jumps - || ((pm->ps->origin[2]-pm->ps->forceJumpZStart) < (forceJumpHeightMax[FORCE_LEVEL_3]-(G_ForceWallJumpStrength()/2.0f))) )//can fit at least one more wall jump in (yes, using "magic numbers"... for now) - ) - ) - //&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything - ) + else if ((pm->ps->legsAnimTimer <= 100 + || !PM_InSpecialJump(legsAnim)//not in a special jump anim + || PM_InReboundJump(legsAnim)//we're already in a rebound + || PM_InBackFlip(legsAnim))//a backflip (needed so you can jump off a wall behind you) + //&& pm->ps->velocity[2] <= 0 + && pm->ps->velocity[2] > -1200 //not falling down very fast + && !(pm->ps->pm_flags&PMF_JUMP_HELD)//have to have released jump since last press + && (pm->cmd.forwardmove || pm->cmd.rightmove)//pushing in a direction + //&& pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_2//level 3 jump or better + && pm->ps->forcePower > 10 //have enough force power to do another one + && (level.time - pm->ps->lastOnGround) > 250 //haven't been on the ground in the last 1/4 of a second + && (!(pm->ps->pm_flags&PMF_JUMPING)//not jumping + || ((level.time - pm->ps->lastOnGround) > 250 //we are jumping, but have been in the air for at least half a second + && (g_debugMelee->integer//if you know kung fu, no height cap on wall-grab-jumps + || ((pm->ps->origin[2] - pm->ps->forceJumpZStart) < (forceJumpHeightMax[FORCE_LEVEL_3] - (G_ForceWallJumpStrength() / 2.0f))))//can fit at least one more wall jump in (yes, using "magic numbers"... for now) + ) + ) + //&& (pm->ps->legsAnim == BOTH_JUMP1 || pm->ps->legsAnim == BOTH_INAIR1 ) )//not in a flip or spin or anything + ) {//see if we're pushing at a wall and jump off it if so - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_WALL_GRAB) - && ( !pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_GRAB) ) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_WALL_GRAB) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_WALL_GRAB))) {//okay to do wall-grabs with this saber //FIXME: make sure we have enough force power //FIXME: check to see if we can go any higher //FIXME: limit to a certain number of these in a row? //FIXME: maybe don't require a ucmd direction, just check all 4? //FIXME: should stick to the wall for a second, then push off... - vec3_t checkDir, traceto, mins = {pm->mins[0],pm->mins[1],0}, maxs = {pm->maxs[0],pm->maxs[1],24}, fwdAngles = {0, pm->ps->viewangles[YAW], 0}; + vec3_t checkDir, traceto, mins = { pm->mins[0], pm->mins[1], 0 }, maxs = { pm->maxs[0], pm->maxs[1], 24 }, fwdAngles = { 0, pm->ps->viewangles[YAW], 0 }; trace_t trace; vec3_t idealNormal; int anim = -1; - if ( pm->cmd.rightmove ) + if (pm->cmd.rightmove) { - if ( pm->cmd.rightmove > 0 ) + if (pm->cmd.rightmove > 0) { anim = BOTH_FORCEWALLREBOUND_RIGHT; - AngleVectors( fwdAngles, NULL, checkDir, NULL ); + AngleVectors(fwdAngles, NULL, checkDir, NULL); } - else if ( pm->cmd.rightmove < 0 ) + else if (pm->cmd.rightmove < 0) { anim = BOTH_FORCEWALLREBOUND_LEFT; - AngleVectors( fwdAngles, NULL, checkDir, NULL ); - VectorScale( checkDir, -1, checkDir ); + AngleVectors(fwdAngles, NULL, checkDir, NULL); + VectorScale(checkDir, -1, checkDir); } } - else if ( pm->cmd.forwardmove > 0 ) + else if (pm->cmd.forwardmove > 0) { anim = BOTH_FORCEWALLREBOUND_FORWARD; - AngleVectors( fwdAngles, checkDir, NULL, NULL ); + AngleVectors(fwdAngles, checkDir, NULL, NULL); } - else if ( pm->cmd.forwardmove < 0 ) + else if (pm->cmd.forwardmove < 0) { anim = BOTH_FORCEWALLREBOUND_BACK; - AngleVectors( fwdAngles, checkDir, NULL, NULL ); - VectorScale( checkDir, -1, checkDir ); + AngleVectors(fwdAngles, checkDir, NULL, NULL); + VectorScale(checkDir, -1, checkDir); } - if ( anim != -1 ) + if (anim != -1) {//trace in the dir we're pushing in and see if there's a vertical wall there - VectorMA( pm->ps->origin, 16, checkDir, traceto );//was 8 - pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID, (EG2_Collision)0, 0 );//FIXME: clip brushes too? - VectorSubtract( pm->ps->origin, traceto, idealNormal ); - VectorNormalize( idealNormal ); + VectorMA(pm->ps->origin, 16, checkDir, traceto);//was 8 + pm->trace(&trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID, (EG2_Collision)0, 0);//FIXME: clip brushes too? + VectorSubtract(pm->ps->origin, traceto, idealNormal); + VectorNormalize(idealNormal); gentity_t *traceEnt = &g_entities[trace.entityNum]; - if ( trace.fraction < 1.0f + if (trace.fraction < 1.0f && fabs(trace.plane.normal[2]) <= MAX_WALL_GRAB_SLOPE - &&((trace.entityNums.solid!=SOLID_BMODEL)||DotProduct(trace.plane.normal,idealNormal)>0.7) ) + && ((trace.entityNums.solid != SOLID_BMODEL) || DotProduct(trace.plane.normal, idealNormal)>0.7)) {//there is a wall there - float dot = DotProduct( pm->ps->velocity, trace.plane.normal ); - if ( dot < 1.0f ) + float dot = DotProduct(pm->ps->velocity, trace.plane.normal); + if (dot < 1.0f) {//can't be heading *away* from the wall! //grab it! - PM_GrabWallForJump( anim ); + PM_GrabWallForJump(anim); } } } @@ -2355,70 +2401,70 @@ static qboolean PM_CheckJump( void ) } } - if ( pm->gent + if (pm->gent //&& pm->cmd.upmove > 0 && pm->ps->forceRageRecoveryTime < pm->cmd.serverTime //not in a force Rage recovery period && pm->ps->weapon == WP_SABER && (pm->ps->weaponTime > 0||(pm->cmd.buttons&BUTTON_ATTACK)) - && ((pm->ps->clientNum&&!PM_ControlledByPlayer())||((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.renderingThirdPerson && !cg.zoomMode)) ) + && ((pm->ps->clientNum&&!PM_ControlledByPlayer())||((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && BG_AllowThirdPersonSpecialMove( pm->ps ) && !cg.zoomMode)) ) {//okay, we just jumped and we're in an attack - if ( !PM_RollingAnim( pm->ps->legsAnim ) - && !PM_InKnockDown( pm->ps ) + if (!PM_RollingAnim(pm->ps->legsAnim) + && !PM_InKnockDown(pm->ps) && !PM_InDeathAnim() - && !PM_PainAnim( pm->ps->torsoAnim ) - && !PM_FlippingAnim( pm->ps->legsAnim ) - && !PM_SpinningAnim( pm->ps->legsAnim ) - && !PM_SaberInSpecialAttack( pm->ps->torsoAnim ) ) + && !PM_PainAnim(pm->ps->torsoAnim) + && !PM_FlippingAnim(pm->ps->legsAnim) + && !PM_SpinningAnim(pm->ps->legsAnim) + && !PM_SaberInSpecialAttack(pm->ps->torsoAnim)) {//HMM... do NPCs need this logic? - if ( !PM_SaberInTransitionAny( pm->ps->saberMove ) //not going to/from/between an attack anim - && !PM_SaberInAttack( pm->ps->saberMove ) //not in attack anim + if (!PM_SaberInTransitionAny(pm->ps->saberMove) //not going to/from/between an attack anim + && !PM_SaberInAttack(pm->ps->saberMove) //not in attack anim && pm->ps->weaponTime <= 0//not busy - && (pm->cmd.buttons&BUTTON_ATTACK) )//want to attack + && (pm->cmd.buttons&BUTTON_ATTACK))//want to attack {//not in an attack or finishing/starting/transitioning one - if ( PM_CheckBackflipAttackMove() ) + if (PM_CheckBackflipAttackMove()) { - PM_SetSaberMove( PM_SaberBackflipAttackMove() );//backflip attack + PM_SetSaberMove(PM_SaberBackflipAttackMove());//backflip attack } /* else if ( PM_CheckSaberDualJumpAttackMove() ) { - PM_SetSaberMove( PM_SaberDualJumpAttackMove() );//jump forward sideways flip attack + PM_SetSaberMove( PM_SaberDualJumpAttackMove() );//jump forward sideways flip attack } */ } /* else if ( ( PM_SaberInTransitionAny( pm->ps->saberMove ) || PM_SaberInAttack( pm->ps->saberMove ) ) - && PM_InAnimForSaberMove( pm->ps->torsoAnim, pm->ps->saberMove ) ) + && PM_InAnimForSaberMove( pm->ps->torsoAnim, pm->ps->saberMove ) ) {//not in an anim we shouldn't interrupt - //see if it's not too late to start a special jump-attack - float animLength = PM_AnimLength( g_entities[pm->ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)pm->ps->torsoAnim ); - if ( animLength - pm->ps->torsoAnimTimer < 500 ) - {//just started the saberMove - //check for special-case jump attacks - if ( PM_CheckFlipOverAttackMove( qtrue ) ) - { - PM_SetSaberMove( PM_SaberFlipOverAttackMove() ); - } - else if ( PM_CheckJumpAttackMove() ) - { - PM_SetSaberMove( PM_SaberJumpAttackMove() ); - } - } + //see if it's not too late to start a special jump-attack + float animLength = PM_AnimLength( g_entities[pm->ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)pm->ps->torsoAnim ); + if ( animLength - pm->ps->torsoAnimTimer < 500 ) + {//just started the saberMove + //check for special-case jump attacks + if ( PM_CheckFlipOverAttackMove( qtrue ) ) + { + PM_SetSaberMove( PM_SaberFlipOverAttackMove() ); + } + else if ( PM_CheckJumpAttackMove() ) + { + PM_SetSaberMove( PM_SaberJumpAttackMove() ); + } + } } */ } } - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE) { return qfalse; } - if ( pm->cmd.upmove > 0 ) + if (pm->cmd.upmove > 0) {//no special jumps /* gentity_t *groundEnt = &g_entities[pm->ps->groundEntityNum]; if ( groundEnt && groundEnt->NPC ) {//Can't jump off of someone's head - return qfalse; + return qfalse; } */ @@ -2427,11 +2473,11 @@ static qboolean PM_CheckJump( void ) pm->ps->pm_flags |= PMF_JUMPING; } - if ( d_JediAI->integer ) + if (d_JediAI->integer) { - if ( pm->ps->clientNum && pm->ps->weapon == WP_SABER ) + if (pm->ps->clientNum && pm->ps->weapon == WP_SABER) { - Com_Printf( "jumping\n" ); + Com_Printf("jumping\n"); } } //Jumping @@ -2441,20 +2487,20 @@ static qboolean PM_CheckJump( void ) pm->ps->groundEntityNum = ENTITYNUM_NONE; pm->ps->jumpZStart = pm->ps->origin[2]; - if ( pm->gent ) + if (pm->gent) { - if ( !Q3_TaskIDPending( pm->gent, TID_CHAN_VOICE ) ) + if (!Q3_TaskIDPending(pm->gent, TID_CHAN_VOICE)) { - PM_AddEvent( EV_JUMP ); + PM_AddEvent(EV_JUMP); } } else { - PM_AddEvent( EV_JUMP ); + PM_AddEvent(EV_JUMP); } //Set the animations - if ( pm->ps->gravity > 0 && !PM_InSpecialJump( pm->ps->legsAnim ) && !PM_InGetUp( pm->ps ) ) + if (pm->ps->gravity > 0 && !PM_InSpecialJump(pm->ps->legsAnim) && !PM_InGetUp(pm->ps)) { PM_JumpForDir(); } @@ -2467,7 +2513,7 @@ static qboolean PM_CheckJump( void ) PM_CheckWaterJump ============= */ -static qboolean PM_CheckWaterJump( void ) { +static qboolean PM_CheckWaterJump(void) { vec3_t spot; int cont; vec3_t flatforward; @@ -2476,16 +2522,16 @@ static qboolean PM_CheckWaterJump( void ) { return qfalse; } - if ( pm->cmd.forwardmove <= 0 && pm->cmd.upmove <= 0 ) + if (pm->cmd.forwardmove <= 0 && pm->cmd.upmove <= 0) {//they must not want to get out? return qfalse; } // check for water jump - if ( pm->waterlevel != 2 ) { + if (pm->waterlevel != 2) { return qfalse; } - if ( pm->watertype & CONTENTS_LADDER ) { + if (pm->watertype & CONTENTS_LADDER) { if (pm->ps->velocity[2] <= 0) return qfalse; } @@ -2493,24 +2539,24 @@ static qboolean PM_CheckWaterJump( void ) { flatforward[0] = pml.forward[0]; flatforward[1] = pml.forward[1]; flatforward[2] = 0; - VectorNormalize( flatforward ); + VectorNormalize(flatforward); - VectorMA( pm->ps->origin, 30, flatforward, spot ); + VectorMA(pm->ps->origin, 30, flatforward, spot); spot[2] += 24; - cont = pm->pointcontents (spot, pm->ps->clientNum ); - if ( !(cont & CONTENTS_SOLID) ) { + cont = pm->pointcontents(spot, pm->ps->clientNum); + if (!(cont & CONTENTS_SOLID)) { return qfalse; } spot[2] += 16; - cont = pm->pointcontents( spot, pm->ps->clientNum ); - if ( cont&(CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA|CONTENTS_BODY) ) { + cont = pm->pointcontents(spot, pm->ps->clientNum); + if (cont&(CONTENTS_SOLID | CONTENTS_PLAYERCLIP | CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA | CONTENTS_BODY)) { return qfalse; } // jump out of water - VectorScale( pml.forward, 200, pm->ps->velocity ); - pm->ps->velocity[2] = 350+((pm->ps->waterheight-pm->ps->origin[2])*2); + VectorScale(pml.forward, 200, pm->ps->velocity); + pm->ps->velocity[2] = 350 + ((pm->ps->waterheight - pm->ps->origin[2]) * 2); pm->ps->pm_flags |= PMF_TIME_WATERJUMP; pm->ps->pm_time = 2000; @@ -2528,11 +2574,11 @@ PM_WaterJumpMove Flying out of the water =================== */ -static void PM_WaterJumpMove( void ) +static void PM_WaterJumpMove(void) { // waterjump has no control, but falls - PM_StepSlideMove( 1 ); + PM_StepSlideMove(1); pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; if (pm->ps->velocity[2] < 0) @@ -2549,7 +2595,7 @@ PM_WaterMove =================== */ -static void PM_WaterMove( void ) { +static void PM_WaterMove(void) { int i; vec3_t wishvel; float wishspeed; @@ -2557,68 +2603,72 @@ static void PM_WaterMove( void ) { float scale; float vel; - if ( PM_CheckWaterJump() ) { + if (PM_CheckWaterJump()) { PM_WaterJumpMove(); return; } - else if ( pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && pm->waterlevel < 3 ) + else if (pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && pm->waterlevel < 3) { - if ( PM_CheckJump () ) { + if (PM_CheckJump()) { // jumped away return; } } #if 0 // jump = head for surface - if ( pm->cmd.upmove >= 10 ) { + if (pm->cmd.upmove >= 10) { if (pm->ps->velocity[2] > -300) { - if ( pm->watertype == CONTENTS_WATER ) { + if (pm->watertype == CONTENTS_WATER) { pm->ps->velocity[2] = 100; - } else if (pm->watertype == CONTENTS_SLIME) { + } + else if (pm->watertype == CONTENTS_SLIME) { pm->ps->velocity[2] = 80; - } else { + } + else { pm->ps->velocity[2] = 50; } } } #endif - PM_Friction (); + PM_Friction(); - scale = PM_CmdScale( &pm->cmd ); + scale = PM_CmdScale(&pm->cmd); // // user intentions // - if ( !scale ) { + if (!scale) { wishvel[0] = 0; wishvel[1] = 0; - if ( pm->watertype & CONTENTS_LADDER ) { + if (pm->watertype & CONTENTS_LADDER) { wishvel[2] = 0; - } else { + } + else { wishvel[2] = -60; // sink towards bottom } - } else { - for (i=0 ; i<3 ; i++) { - wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; + } + else { + for (i = 0; i<3; i++) { + wishvel[i] = scale * pml.forward[i] * pm->cmd.forwardmove + scale * pml.right[i] * pm->cmd.rightmove; } wishvel[2] += scale * pm->cmd.upmove; - if ( !(pm->watertype&CONTENTS_LADDER) ) //ladder + if (!(pm->watertype&CONTENTS_LADDER)) //ladder { - float depth = (pm->ps->origin[2]+pm->gent->client->standheight)-pm->ps->waterheight; - if ( depth >= 12 ) + float depth = (pm->ps->origin[2] + pm->gent->client->standheight) - pm->ps->waterheight; + if (depth >= 12) {//too high! wishvel[2] -= 120; // sink towards bottom - if ( wishvel[2] > 0 ) + if (wishvel[2] > 0) { wishvel[2] = 0; } } - else if ( pm->ps->waterHeightLevel >= WHL_UNDER )//!depth && pm->waterlevel == 3 ) + else if (pm->ps->waterHeightLevel >= WHL_UNDER)//!depth && pm->waterlevel == 3 ) { } - else if ( depth < 12 ) + else if (depth < 12) {//still deep wishvel[2] -= 60; // sink towards bottom - if ( wishvel[2] > 30 ) + if (wishvel[2] > 30) { wishvel[2] = 30; } @@ -2626,38 +2676,39 @@ static void PM_WaterMove( void ) { } } - VectorCopy (wishvel, wishdir); + VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); - if ( pm->watertype & CONTENTS_LADDER ) //ladder + if (pm->watertype & CONTENTS_LADDER) //ladder { - if ( wishspeed > pm->ps->speed * pm_ladderScale ) { + if (wishspeed > pm->ps->speed * pm_ladderScale) { wishspeed = pm->ps->speed * pm_ladderScale; } - PM_Accelerate( wishdir, wishspeed, pm_flyaccelerate ); - } else { - if ( pm->ps->gravity < 0 ) + PM_Accelerate(wishdir, wishspeed, pm_flyaccelerate); + } + else { + if (pm->ps->gravity < 0) {//float up pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; } - if ( wishspeed > pm->ps->speed * pm_swimScale ) { + if (wishspeed > pm->ps->speed * pm_swimScale) { wishspeed = pm->ps->speed * pm_swimScale; } - PM_Accelerate( wishdir, wishspeed, pm_wateraccelerate ); + PM_Accelerate(wishdir, wishspeed, pm_wateraccelerate); } // make sure we can go up slopes easily under water - if ( pml.groundPlane && DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ) < 0 ) { + if (pml.groundPlane && DotProduct(pm->ps->velocity, pml.groundTrace.plane.normal) < 0) { vel = VectorLength(pm->ps->velocity); // slide along the ground plane - PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, - pm->ps->velocity, OVERCLIP ); + PM_ClipVelocity(pm->ps->velocity, pml.groundTrace.plane.normal, + pm->ps->velocity, OVERCLIP); VectorNormalize(pm->ps->velocity); VectorScale(pm->ps->velocity, vel, pm->ps->velocity); } - PM_SlideMove( qfalse ); + PM_SlideMove(qfalse); } @@ -2667,7 +2718,7 @@ PM_FlyVehicleMove =================== */ -static void PM_FlyVehicleMove( void ) +static void PM_FlyVehicleMove(void) { int i; vec3_t wishvel; @@ -2683,39 +2734,39 @@ static void PM_FlyVehicleMove( void ) //smove = pm->cmd.rightmove; // normal slowdown - if ( pm->ps->gravity && pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum == ENTITYNUM_NONE ) + if (pm->ps->gravity && pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum == ENTITYNUM_NONE) {//falling zVel = pm->ps->velocity[2]; - PM_Friction (); + PM_Friction(); pm->ps->velocity[2] = zVel; } else { - PM_Friction (); - if ( pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE ) + PM_Friction(); + if (pm->ps->velocity[2] < 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE) { pm->ps->velocity[2] = 0; // ignore slope movement } } - scale = PM_CmdScale( &pm->cmd ); + scale = PM_CmdScale(&pm->cmd); // Get The WishVel And WishSpeed //------------------------------- - if ( pm->ps->clientNum && (USENEWNAVSYSTEM || !VectorCompare( pm->ps->moveDir, vec3_origin )) ) + if (pm->ps->clientNum && (USENEWNAVSYSTEM || !VectorCompare(pm->ps->moveDir, vec3_origin))) {//NPC // If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds //-------------------------------------------------------------------------------------------- - if ((fmove!=0.0f || smove!=0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin)) + if ((fmove != 0.0f || smove != 0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin)) { //gi.Printf("Generating MoveDir\n"); - for ( i = 0 ; i < 3 ; i++ ) + for (i = 0; i < 3; i++) { - wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; + wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; } - VectorCopy( wishvel, wishdir ); + VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; } @@ -2724,38 +2775,38 @@ static void PM_FlyVehicleMove( void ) else { wishspeed = pm->ps->speed; - VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel ); - VectorCopy( pm->ps->moveDir, wishdir ); + VectorScale(pm->ps->moveDir, pm->ps->speed, wishvel); + VectorCopy(pm->ps->moveDir, wishdir); } } else { - for ( i = 0 ; i < 3 ; i++ ) { - wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; + for (i = 0; i < 3; i++) { + wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; } // when going up or down slopes the wish velocity should Not be zero - // wishvel[2] = 0; + // wishvel[2] = 0; - VectorCopy (wishvel, wishdir); + VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; } // Handle negative speed. - if ( wishspeed < 0 ) + if (wishspeed < 0) { wishspeed = wishspeed * -1.0f; - VectorScale( wishvel, -1.0f, wishvel ); - VectorScale( wishdir, -1.0f, wishdir ); + VectorScale(wishvel, -1.0f, wishvel); + VectorScale(wishdir, -1.0f, wishdir); } - VectorCopy( wishvel, wishdir ); - wishspeed = VectorNormalize( wishdir ); + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); - PM_Accelerate( wishdir, wishspeed, 100 ); + PM_Accelerate(wishdir, wishspeed, 100); - PM_StepSlideMove( 1 ); + PM_StepSlideMove(1); } /* @@ -2765,7 +2816,7 @@ PM_FlyMove Only with the flight powerup =================== */ -static void PM_FlyMove( void ) +static void PM_FlyMove(void) { int i; vec3_t wishvel; @@ -2777,18 +2828,19 @@ static void PM_FlyMove( void ) qboolean jetPackMove = qfalse; // normal slowdown - PM_Friction (); + PM_Friction(); - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->gent && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) && pm->gent->client->moveType == MT_FLYSWIM ) + && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER || + NPC->client->NPC_class == CLASS_MANDA) && pm->gent->client->moveType == MT_FLYSWIM) {//jetpack accel accel = pm_flyaccelerate; jetPackMove = qtrue; } - else if ( pm->ps->gravity <= 0 - && ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || (pm->gent&&pm->gent->client&&pm->gent->client->moveType == MT_RUNJUMP)) ) + else if (pm->ps->gravity <= 0 + && ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) || (pm->gent&&pm->gent->client&&pm->gent->client->moveType == MT_RUNJUMP))) { PM_CheckJump(); accel = 1.0f; @@ -2801,11 +2853,11 @@ static void PM_FlyMove( void ) accel = pm_flyaccelerate; } - scale = PM_CmdScale( &pm->cmd ); + scale = PM_CmdScale(&pm->cmd); // // user intentions // - if ( !scale ) + if (!scale) { wishvel[0] = 0; wishvel[1] = 0; @@ -2813,36 +2865,36 @@ static void PM_FlyMove( void ) } else { - for (i=0 ; i<3 ; i++) + for (i = 0; i<3; i++) { - wishvel[i] = scale * pml.forward[i]*pm->cmd.forwardmove + scale * pml.right[i]*pm->cmd.rightmove; + wishvel[i] = scale * pml.forward[i] * pm->cmd.forwardmove + scale * pml.right[i] * pm->cmd.rightmove; } - if ( jetPackMove ) + if (jetPackMove) { wishvel[2] += pm->cmd.upmove; } - else if ( lowGravMove ) + else if (lowGravMove) { wishvel[2] += scale * pm->cmd.upmove; - VectorScale( wishvel, 0.5f, wishvel ); + VectorScale(wishvel, 0.5f, wishvel); } } - VectorCopy( wishvel, wishdir ); - wishspeed = VectorNormalize( wishdir ); + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); - PM_Accelerate( wishdir, wishspeed, accel ); + PM_Accelerate(wishdir, wishspeed, accel); - PM_StepSlideMove( 1 ); + PM_StepSlideMove(1); } -qboolean PM_GroundSlideOkay( float zNormal ) +qboolean PM_GroundSlideOkay(float zNormal) { - if ( zNormal > 0 ) + if (zNormal > 0) { - if ( pm->ps->velocity[2] > 0 ) + if (pm->ps->velocity[2] > 0) { - if ( pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT + if (pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT || pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT_STOP || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT_STOP @@ -2850,7 +2902,7 @@ qboolean PM_GroundSlideOkay( float zNormal ) || pm->ps->legsAnim == BOTH_FORCELONGLEAP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK || pm->ps->legsAnim == BOTH_FORCELONGLEAP_LAND - || PM_InReboundJump( pm->ps->legsAnim )) + || PM_InReboundJump(pm->ps->legsAnim)) { return qfalse; } @@ -2864,7 +2916,7 @@ PM_AirMove =================== */ -static void PM_AirMove( void ) { +static void PM_AirMove(void) { int i; vec3_t wishvel; float fmove, smove; @@ -2873,6 +2925,20 @@ static void PM_AirMove( void ) { usercmd_t cmd; float gravMod = 1.0f; + float jumpMultiplier; + if (g_moonJump->integer) + { + jumpMultiplier = 5.0; + } + else if (g_forceNewPowers->integer) + { + jumpMultiplier = 1.25; + } + else + { + jumpMultiplier = 1.0; + } + #if METROID_JUMP PM_CheckJump(); #endif @@ -2883,7 +2949,7 @@ static void PM_AirMove( void ) { smove = pm->cmd.rightmove; cmd = pm->cmd; - PM_CmdScale( &cmd ); + PM_CmdScale(&cmd); // set the movementDir so clients can rotate the legs for strafing PM_SetMovementDir(); @@ -2891,33 +2957,33 @@ static void PM_AirMove( void ) { // project moves down to flat plane pml.forward[2] = 0; pml.right[2] = 0; - VectorNormalize (pml.forward); - VectorNormalize (pml.right); + VectorNormalize(pml.forward); + VectorNormalize(pml.right); Vehicle_t *pVeh = NULL; - if ( pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) + if (pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) { pVeh = pm->gent->m_pVehicle; } - if ( pVeh && pVeh->m_pVehicleInfo->hoverHeight > 0 ) + if (pVeh && pVeh->m_pVehicleInfo->hoverHeight > 0) {//in a hovering vehicle, have air control // Flying Or Breaking, No Control //-------------------------------- - if ( pVeh->m_ulFlags&VEH_FLYING || pVeh->m_ulFlags&VEH_SLIDEBREAKING) + if (pVeh->m_ulFlags&VEH_FLYING || pVeh->m_ulFlags&VEH_SLIDEBREAKING) { wishspeed = 0.0f; - VectorClear( wishvel ); - VectorClear( wishdir ); + VectorClear(wishvel); + VectorClear(wishdir); } // Out Of Control - Maintain pos3 Velocity //----------------------------------------- else if ((pVeh->m_ulFlags&VEH_OUTOFCONTROL) || (pVeh->m_ulFlags&VEH_STRAFERAM)) { - VectorCopy(pm->gent->pos3, wishvel); + VectorCopy(pm->gent->pos3, wishvel); VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); } @@ -2936,42 +3002,42 @@ static void PM_AirMove( void ) { else { wishspeed = pm->ps->speed; - VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel ); - VectorCopy( pm->ps->moveDir, wishdir ); + VectorScale(pm->ps->moveDir, pm->ps->speed, wishvel); + VectorCopy(pm->ps->moveDir, wishdir); } } - else if ( (pm->ps->pm_flags&PMF_SLOW_MO_FALL) ) + else if ((pm->ps->pm_flags&PMF_SLOW_MO_FALL)) {//no air-control - VectorClear( wishvel ); + VectorClear(wishvel); } else { - for ( i = 0 ; i < 2 ; i++ ) + for (i = 0; i < 2; i++) { - wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; + wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; } wishvel[2] = 0; } - VectorCopy (wishvel, wishdir); - wishspeed = VectorNormalize(wishdir); + VectorCopy(wishvel, wishdir); + wishspeed = VectorNormalize(wishdir); - if ( ( DotProduct (pm->ps->velocity, wishdir) ) < 0.0f ) + if ((DotProduct(pm->ps->velocity, wishdir)) < 0.0f) {//Encourage deceleration away from the current velocity wishspeed *= pm_airDecelRate; } // not on ground, so little effect on velocity float accelerate = pm_airaccelerate; - if ( pVeh && pVeh->m_pVehicleInfo->type == VH_SPEEDER ) + if (pVeh && pVeh->m_pVehicleInfo->type == VH_SPEEDER) {//speeders have more control in air //in mid-air accelerate = pVeh->m_pVehicleInfo->acceleration; - if ( pml.groundPlane ) + if (pml.groundPlane) {//on a slope of some kind, shouldn't have much control and should slide a lot accelerate *= 0.5f; } - if (pVeh->m_ulFlags & VEH_SLIDEBREAKING) + if (pVeh->m_ulFlags & VEH_SLIDEBREAKING) { VectorScale(pm->ps->velocity, 0.80f, pm->ps->velocity); } @@ -2980,47 +3046,51 @@ static void PM_AirMove( void ) { pm->ps->velocity[2] = 1000.0f; } } - PM_Accelerate( wishdir, wishspeed, accelerate ); + PM_Accelerate(wishdir, wishspeed, accelerate); // we may have a ground plane that is very steep, even // though we don't have a groundentity // slide along the steep plane - if ( pml.groundPlane ) + if (pml.groundPlane) { - if ( PM_GroundSlideOkay( pml.groundTrace.plane.normal[2] ) ) + if (PM_GroundSlideOkay(pml.groundTrace.plane.normal[2])) { - PM_ClipVelocity( pm->ps->velocity, pml.groundTrace.plane.normal, - pm->ps->velocity, OVERCLIP ); + PM_ClipVelocity(pm->ps->velocity, pml.groundTrace.plane.normal, + pm->ps->velocity, OVERCLIP); } } - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 && pm->ps->forceJumpZStart - && pm->ps->velocity[2] > 0 ) + && pm->ps->velocity[2] > 0) {//I am force jumping and I'm not holding the button anymore - float curHeight = pm->ps->origin[2] - pm->ps->forceJumpZStart + (pm->ps->velocity[2]*pml.frametime); - float maxJumpHeight = forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]]; - if ( curHeight >= maxJumpHeight ) + float curHeight = pm->ps->origin[2] - pm->ps->forceJumpZStart + (pm->ps->velocity[2] * pml.frametime); + float maxJumpHeight = forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] * jumpMultiplier; + if (g_playerCheatPowers->integer || g_moonJump->integer) + { + maxJumpHeight *= 30; + } + if (curHeight >= maxJumpHeight) {//reached top, cut velocity pm->ps->velocity[2] = 0; } } - if ( (pm->ps->pm_flags&PMF_STUCK_TO_WALL) ) + if ((pm->ps->pm_flags&PMF_STUCK_TO_WALL)) { gravMod = 0.0f; } - PM_StepSlideMove( gravMod ); + PM_StepSlideMove(gravMod); - if (pVeh && pm->ps->pm_flags&PMF_BUMPED) + if (pVeh && pm->ps->pm_flags&PMF_BUMPED) { -/* + /* // Turn Vehicle In Direction Of Collision //---------------------------------------- vec3_t nAngles; - vectoangles(pm->ps->velocity, nAngles); + vectoangles(pm->ps->velocity, nAngles); nAngles[0] = pVeh->m_pParentEntity->client->ps.viewangles[0]; nAngles[2] = pVeh->m_pParentEntity->client->ps.viewangles[2]; @@ -3031,17 +3101,17 @@ static void PM_AirMove( void ) { SetClientViewAngle( pVeh->m_pParentEntity, nAngles ); if (pVeh->m_pPilot) { - SetClientViewAngle( pVeh->m_pPilot, nAngles ); - } + SetClientViewAngle( pVeh->m_pPilot, nAngles ); + } VectorCopy(nAngles, pVeh->m_vPrevOrientation); VectorCopy(nAngles, pVeh->m_vOrientation); pVeh->m_vAngularVelocity = 0.0f; -*/ + */ // Reduce "Bounce Up Wall" Velocity //---------------------------------- - if (pm->ps->velocity[2]>0) + if (pm->ps->velocity[2]>0) { pm->ps->velocity[2] *= 0.1f; } @@ -3055,7 +3125,7 @@ PM_WalkMove =================== */ -static void PM_WalkMove( void ) { +static void PM_WalkMove(void) { int i; vec3_t wishvel; float fmove, smove; @@ -3066,69 +3136,71 @@ static void PM_WalkMove( void ) { float accelerate; float vel; - if ( pm->ps->gravity < 0 ) + if (pm->ps->gravity < 0) {//float away pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; pm->ps->groundEntityNum = ENTITYNUM_NONE; pml.groundPlane = qfalse; pml.walking = qfalse; - if ( pm->waterlevel > 1 ) { + if (pm->waterlevel > 1) { PM_WaterMove(); - } else { + } + else { PM_AirMove(); } return; } - if ( pm->waterlevel > 2 && DotProduct( pml.forward, pml.groundTrace.plane.normal ) > 0 ) { + if (pm->waterlevel > 2 && DotProduct(pml.forward, pml.groundTrace.plane.normal) > 0) { // begin swimming PM_WaterMove(); return; } - if ( PM_CheckJump () ) { + if (PM_CheckJump()) { // jumped away - if ( pm->waterlevel > 1 ) { + if (pm->waterlevel > 1) { PM_WaterMove(); - } else { + } + else { PM_AirMove(); } return; } - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE &&//on ground + if (pm->ps->groundEntityNum != ENTITYNUM_NONE &&//on ground pm->ps->velocity[2] <= 0 &&//not going up - pm->ps->pm_flags&PMF_TIME_KNOCKBACK )//knockback fimter on (stops friction) + pm->ps->pm_flags&PMF_TIME_KNOCKBACK)//knockback fimter on (stops friction) { pm->ps->pm_flags &= ~PMF_TIME_KNOCKBACK; } qboolean slide = qfalse; - if ( pm->ps->pm_type == PM_DEAD ) + if (pm->ps->pm_type == PM_DEAD) {//corpse - if ( g_entities[pm->ps->groundEntityNum].client ) + if (g_entities[pm->ps->groundEntityNum].client) {//on a client - if ( g_entities[pm->ps->groundEntityNum].health > 0 ) + if (g_entities[pm->ps->groundEntityNum].health > 0) {//a living client //no friction slide = qtrue; } } } - if ( !slide ) + if (!slide) { - PM_Friction (); + PM_Friction(); } - if ( g_debugMelee->integer ) + if (g_debugMelee->integer) { if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//player - && cg.renderingThirdPerson//in third person + && BG_AllowThirdPersonSpecialMove( pm->ps )//in third person && ((pm->cmd.buttons&BUTTON_USE)||pm->ps->leanStopDebounceTime)//holding use or leaning //&& (pm->ps->forcePowersActive&(1<ps->groundEntityNum != ENTITYNUM_NONE//on ground - && !cg_usingInFrontOf )//nothing to use + && !cg_usingInFrontOf)//nothing to use {//holding use stops you from moving return; } @@ -3137,7 +3209,7 @@ static void PM_WalkMove( void ) { smove = pm->cmd.rightmove; cmd = pm->cmd; - scale = PM_CmdScale( &cmd ); + scale = PM_CmdScale(&cmd); // set the movementDir so clients can rotate the legs for strafing PM_SetMovementDir(); @@ -3147,51 +3219,51 @@ static void PM_WalkMove( void ) { pml.right[2] = 0; // project the forward and right directions onto the ground plane - PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP ); - PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP ); + PM_ClipVelocity(pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP); + PM_ClipVelocity(pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP); // - VectorNormalize (pml.forward); - VectorNormalize (pml.right); + VectorNormalize(pml.forward); + VectorNormalize(pml.right); -/* if ( ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) && pm->gent->NPC ) + /* if ( ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) && pm->gent->NPC ) {//speeder control scheme - vec3_t vfwd, vrt; - AngleVectors( ((CVehicleNPC *)pm->gent->NPC)->m_vOrientation, vfwd, vrt, NULL ); + vec3_t vfwd, vrt; + AngleVectors( ((CVehicleNPC *)pm->gent->NPC)->m_vOrientation, vfwd, vrt, NULL ); - float speed = pm->ps->speed; - if ( fmove < 0 ) - {//going backwards - if ( speed < 0 ) - {//speed is negative, but since our command is reverse, make speed positive - speed = fabs( speed ); - } - else if ( speed > 0 ) - {//trying to move back but speed is still positive, so keep moving forward (we'll slow down eventually) - speed *= -1; - } - } - VectorScale( vfwd, speed*fmove/127.0f, wishvel ); - //VectorMA( wishvel, pm->ps->speed*smove/127.0f, vrt, wishvel ); - wishspeed = VectorNormalize2( wishvel, wishdir ); + float speed = pm->ps->speed; + if ( fmove < 0 ) + {//going backwards + if ( speed < 0 ) + {//speed is negative, but since our command is reverse, make speed positive + speed = fabs( speed ); + } + else if ( speed > 0 ) + {//trying to move back but speed is still positive, so keep moving forward (we'll slow down eventually) + speed *= -1; + } + } + VectorScale( vfwd, speed*fmove/127.0f, wishvel ); + //VectorMA( wishvel, pm->ps->speed*smove/127.0f, vrt, wishvel ); + wishspeed = VectorNormalize2( wishvel, wishdir ); } else*/ // Get The WishVel And WishSpeed //------------------------------- - if ( pm->ps->clientNum && (USENEWNAVSYSTEM || !VectorCompare( pm->ps->moveDir, vec3_origin )) ) + if (pm->ps->clientNum && (USENEWNAVSYSTEM || !VectorCompare(pm->ps->moveDir, vec3_origin))) {//NPC // If The UCmds Were Set, But Never Converted Into A MoveDir, Then Make The WishDir From UCmds //-------------------------------------------------------------------------------------------- - if ((fmove!=0.0f || smove!=0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin)) + if ((fmove != 0.0f || smove != 0.0f) && VectorCompare(pm->ps->moveDir, vec3_origin)) { //gi.Printf("Generating MoveDir\n"); - for ( i = 0 ; i < 3 ; i++ ) + for (i = 0; i < 3; i++) { - wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; + wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; } - VectorCopy( wishvel, wishdir ); + VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; } @@ -3200,59 +3272,59 @@ static void PM_WalkMove( void ) { else { wishspeed = pm->ps->speed; - VectorScale( pm->ps->moveDir, pm->ps->speed, wishvel ); - VectorCopy( pm->ps->moveDir, wishdir ); + VectorScale(pm->ps->moveDir, pm->ps->speed, wishvel); + VectorCopy(pm->ps->moveDir, wishdir); } } else { - for ( i = 0 ; i < 3 ; i++ ) { - wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; + for (i = 0; i < 3; i++) { + wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; } // when going up or down slopes the wish velocity should Not be zero - // wishvel[2] = 0; + // wishvel[2] = 0; - VectorCopy (wishvel, wishdir); + VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; } // Handle negative speed. - if ( wishspeed < 0 ) + if (wishspeed < 0) { wishspeed = wishspeed * -1.0f; - VectorScale( wishvel, -1.0f, wishvel ); - VectorScale( wishdir, -1.0f, wishdir ); + VectorScale(wishvel, -1.0f, wishvel); + VectorScale(wishdir, -1.0f, wishdir); } // clamp the speed lower if ducking - if ( pm->ps->pm_flags & PMF_DUCKED && !PM_InKnockDown( pm->ps ) ) + if (pm->ps->pm_flags & PMF_DUCKED && !PM_InKnockDown(pm->ps)) { - if ( wishspeed > pm->ps->speed * pm_duckScale ) + if (wishspeed > pm->ps->speed * pm_duckScale) { wishspeed = pm->ps->speed * pm_duckScale; } } // clamp the speed lower if wading or walking on the bottom - if ( pm->waterlevel ) { + if (pm->waterlevel) { float waterScale; waterScale = pm->waterlevel / 3.0; - waterScale = 1.0 - ( 1.0 - pm_swimScale ) * waterScale; - if ( wishspeed > pm->ps->speed * waterScale ) { + waterScale = 1.0 - (1.0 - pm_swimScale) * waterScale; + if (wishspeed > pm->ps->speed * waterScale) { wishspeed = pm->ps->speed * waterScale; } } // when a player gets hit, they temporarily lose // full control, which allows them to be moved a bit - if ( Flying == FLY_HOVER ) + if (Flying == FLY_HOVER) { accelerate = pm_vehicleaccelerate; } - else if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || (pm->ps->pm_flags&PMF_TIME_KNOCKBACK) || (pm->ps->pm_flags&PMF_TIME_NOFRICTION) ) + else if ((pml.groundTrace.surfaceFlags & SURF_SLICK) || (pm->ps->pm_flags&PMF_TIME_KNOCKBACK) || (pm->ps->pm_flags&PMF_TIME_NOFRICTION)) { accelerate = pm_airaccelerate; } @@ -3272,7 +3344,7 @@ static void PM_WalkMove( void ) { if (gi.WE_IsOutside(pm->gent->currentOrigin)) { VectorScale(windDir, -1.0f, windDir); - accelerate *= (1.0f - (DotProduct(wishdir, windDir)*0.55f)); + accelerate *= (1.0f - (DotProduct(wishdir, windDir)*0.55f)); } } } @@ -3280,49 +3352,50 @@ static void PM_WalkMove( void ) { //=================================================== } - PM_Accelerate (wishdir, wishspeed, accelerate); + PM_Accelerate(wishdir, wishspeed, accelerate); //Com_Printf("velocity = %1.1f %1.1f %1.1f\n", pm->ps->velocity[0], pm->ps->velocity[1], pm->ps->velocity[2]); //Com_Printf("velocity1 = %1.1f\n", VectorLength(pm->ps->velocity)); - if ( ( pml.groundTrace.surfaceFlags & SURF_SLICK ) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK || (pm->ps->pm_flags&PMF_TIME_NOFRICTION) ) { - if ( pm->ps->gravity >= 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE && !VectorLengthSquared( pm->ps->velocity ) && pml.groundTrace.plane.normal[2] == 1.0 ) + if ((pml.groundTrace.surfaceFlags & SURF_SLICK) || pm->ps->pm_flags & PMF_TIME_KNOCKBACK || (pm->ps->pm_flags&PMF_TIME_NOFRICTION)) { + if (pm->ps->gravity >= 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE && !VectorLengthSquared(pm->ps->velocity) && pml.groundTrace.plane.normal[2] == 1.0) {//on ground and not moving and on level ground, no reason to do stupid fucking gravity with the clipvelocity!!!! } else { - if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED) && !(pm->ps->eFlags&EF_FORCE_DRAINED) ) + if (!(pm->ps->eFlags&EF_FORCE_GRIPPED) && !(pm->ps->eFlags&EF_FORCE_DRAINED)) { pm->ps->velocity[2] -= pm->ps->gravity * pml.frametime; } } - } else { + } + else { // don't reset the z velocity for slopes -// pm->ps->velocity[2] = 0; + // pm->ps->velocity[2] = 0; } vel = VectorLength(pm->ps->velocity); // slide along the ground plane - PM_ClipVelocity (pm->ps->velocity, pml.groundTrace.plane.normal, - pm->ps->velocity, OVERCLIP ); + PM_ClipVelocity(pm->ps->velocity, pml.groundTrace.plane.normal, + pm->ps->velocity, OVERCLIP); // don't decrease velocity when going up or down a slope VectorNormalize(pm->ps->velocity); VectorScale(pm->ps->velocity, vel, pm->ps->velocity); // don't do anything if standing still - if ( !pm->ps->velocity[0] && !pm->ps->velocity[1] ) { + if (!pm->ps->velocity[0] && !pm->ps->velocity[1]) { return; } - if ( pm->ps->gravity <= 0 ) + if (pm->ps->gravity <= 0) {//need to apply gravity since we're going to float up from ground - PM_StepSlideMove( 1 ); + PM_StepSlideMove(1); } else { - PM_StepSlideMove( 0 ); + PM_StepSlideMove(0); } //Com_Printf("velocity2 = %1.1f\n", VectorLength(pm->ps->velocity)); @@ -3335,33 +3408,34 @@ static void PM_WalkMove( void ) { PM_DeadMove ============== */ -static void PM_DeadMove( void ) { +static void PM_DeadMove(void) { float forward; // If this is a vehicle, tell him he's dead, but give him a little while to do his things. -/* if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->NPC && pm->gent->health != -99999 ) + /* if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->NPC && pm->gent->health != -99999 ) { - pm->gent->health = 1; - ((CVehicleNPC *)pm->gent->NPC)->StartDeathDelay( 0 ); + pm->gent->health = 1; + ((CVehicleNPC *)pm->gent->NPC)->StartDeathDelay( 0 ); } else { - pm->gent->health = 0; + pm->gent->health = 0; }*/ - if ( !pml.walking ) { + if (!pml.walking) { return; } // extra friction - forward = VectorLength (pm->ps->velocity); + forward = VectorLength(pm->ps->velocity); forward -= 20; - if ( forward <= 0 ) { - VectorClear (pm->ps->velocity); - } else { - VectorNormalize (pm->ps->velocity); - VectorScale (pm->ps->velocity, forward, pm->ps->velocity); + if (forward <= 0) { + VectorClear(pm->ps->velocity); + } + else { + VectorNormalize(pm->ps->velocity); + VectorScale(pm->ps->velocity, forward, pm->ps->velocity); } } @@ -3371,7 +3445,7 @@ static void PM_DeadMove( void ) { PM_NoclipMove =============== */ -static void PM_NoclipMove( void ) { +static void PM_NoclipMove(void) { float speed, drop, friction, control, newspeed; int i; vec3_t wishvel; @@ -3380,22 +3454,22 @@ static void PM_NoclipMove( void ) { float wishspeed; float scale; - if(pm->gent && pm->gent->client) + if (pm->gent && pm->gent->client) { pm->ps->viewheight = pm->gent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET; -// if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] ) -// { -// assert(0); -// } + // if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] ) + // { + // assert(0); + // } - VectorCopy( pm->gent->mins, pm->mins ); - VectorCopy( pm->gent->maxs, pm->maxs ); + VectorCopy(pm->gent->mins, pm->mins); + VectorCopy(pm->gent->maxs, pm->maxs); } else { pm->ps->viewheight = DEFAULT_MAXS_2 + STANDARD_VIEWHEIGHT_OFFSET;//DEFAULT_VIEWHEIGHT; - if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 ) + if (!DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2) { assert(0); } @@ -3411,10 +3485,10 @@ static void PM_NoclipMove( void ) { // friction - speed = VectorLength (pm->ps->velocity); + speed = VectorLength(pm->ps->velocity); if (speed < 1) { - VectorCopy (vec3_origin, pm->ps->velocity); + VectorCopy(vec3_origin, pm->ps->velocity); } else { @@ -3430,11 +3504,11 @@ static void PM_NoclipMove( void ) { newspeed = 0; newspeed /= speed; - VectorScale (pm->ps->velocity, newspeed, pm->ps->velocity); + VectorScale(pm->ps->velocity, newspeed, pm->ps->velocity); } // accelerate - scale = PM_CmdScale( &pm->cmd ); + scale = PM_CmdScale(&pm->cmd); if (pm->cmd.buttons & BUTTON_ATTACK) { //turbo boost scale *= 10; } @@ -3445,38 +3519,38 @@ static void PM_NoclipMove( void ) { fmove = pm->cmd.forwardmove; smove = pm->cmd.rightmove; - for (i=0 ; i<3 ; i++) - wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; + for (i = 0; i<3; i++) + wishvel[i] = pml.forward[i] * fmove + pml.right[i] * smove; wishvel[2] += pm->cmd.upmove; - VectorCopy (wishvel, wishdir); + VectorCopy(wishvel, wishdir); wishspeed = VectorNormalize(wishdir); wishspeed *= scale; - PM_Accelerate( wishdir, wishspeed, pm_accelerate ); + PM_Accelerate(wishdir, wishspeed, pm_accelerate); // move - VectorMA (pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin); + VectorMA(pm->ps->origin, pml.frametime, pm->ps->velocity, pm->ps->origin); } //============================================================================ -static float PM_DamageForDelta( int delta ) +static float PM_DamageForDelta(int delta) { float damage = delta; - if ( pm->gent->NPC ) + if (pm->gent->NPC) { - if ( pm->ps->weapon == WP_SABER - || (pm->gent->client && pm->gent->client->NPC_class == CLASS_REBORN) ) + if (pm->ps->weapon == WP_SABER + || (pm->gent->client && pm->gent->client->NPC_class == CLASS_REBORN)) {//FIXME: for now Jedi take no falling damage, but really they should if pushed off? damage = 0; } } - else if ( pm->ps->clientNum < MAX_CLIENTS ) + else if (pm->ps->clientNum < MAX_CLIENTS) { - if ( damage < 50 ) + if (damage < 50) { - if ( damage > 24 ) + if (damage > 24) { damage = damage - 25; } @@ -3489,28 +3563,25 @@ static float PM_DamageForDelta( int delta ) return damage * 0.5f; } -static void PM_CrashLandDamage( int damage ) +static void PM_CrashLandDamage(int damage) { - if ( pm->gent ) + if (pm->gent) { int dflags = DAMAGE_NO_ARMOR; - if ( pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) ) + if (pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT)) { damage = 1000; dflags |= DAMAGE_DIE_ON_IMPACT; } - else + else if (!(pm->gent->flags&FL_NO_IMPACT_DMG)) { - damage = PM_DamageForDelta( damage ); - - if ( (pm->gent->flags&FL_NO_IMPACT_DMG) ) - return; + damage = PM_DamageForDelta(damage); } - if ( damage ) + if (damage) { pm->gent->painDebounceTime = level.time + 200; // no normal pain sound - G_Damage( pm->gent, NULL, player, NULL, NULL, damage, dflags, MOD_FALLING ); + G_Damage(pm->gent, NULL, player, NULL, NULL, damage, dflags, MOD_FALLING); } } } @@ -3518,66 +3589,66 @@ static void PM_CrashLandDamage( int damage ) /* static float PM_CrashLandDelta( vec3_t org, vec3_t prevOrg, vec3_t prev_vel, float grav, int waterlevel ) { - float delta; - float dist; - float vel, acc; - float t; - float a, b, c, den; +float delta; +float dist; +float vel, acc; +float t; +float a, b, c, den; - // calculate the exact velocity on landing - dist = org[2] - prevOrg[2]; - vel = prev_vel[2]; - acc = -grav; +// calculate the exact velocity on landing +dist = org[2] - prevOrg[2]; +vel = prev_vel[2]; +acc = -grav; - a = acc / 2; - b = vel; - c = -dist; +a = acc / 2; +b = vel; +c = -dist; - den = b * b - 4 * a * c; - if ( den < 0 ) - { - return 0; - } - t = (-b - sqrt( den ) ) / ( 2 * a ); +den = b * b - 4 * a * c; +if ( den < 0 ) +{ +return 0; +} +t = (-b - sqrt( den ) ) / ( 2 * a ); - delta = vel + t * acc; - delta = delta*delta * 0.0001; +delta = vel + t * acc; +delta = delta*delta * 0.0001; - // never take falling damage if completely underwater - if ( waterlevel == 3 ) - { - return 0; - } - // reduce falling damage if there is standing water - if ( waterlevel == 2 ) - { - delta *= 0.25; - } - if ( waterlevel == 1 ) - { - delta *= 0.5; - } +// never take falling damage if completely underwater +if ( waterlevel == 3 ) +{ +return 0; +} +// reduce falling damage if there is standing water +if ( waterlevel == 2 ) +{ +delta *= 0.25; +} +if ( waterlevel == 1 ) +{ +delta *= 0.5; +} - return delta; +return delta; } */ -static float PM_CrashLandDelta( vec3_t prev_vel, int waterlevel ) +static float PM_CrashLandDelta(vec3_t prev_vel, int waterlevel) { float delta; - if ( pm->waterlevel == 3 ) + if (pm->waterlevel == 3) { return 0; } - delta = fabs(prev_vel[2])/10;//VectorLength( prev_vel ) + delta = fabs(prev_vel[2]) / 10;//VectorLength( prev_vel ) // reduce falling damage if there is standing water - if ( pm->waterlevel == 2 ) + if (pm->waterlevel == 2) { delta *= 0.25; } - if ( pm->waterlevel == 1 ) + if (pm->waterlevel == 1) { delta *= 0.5; } @@ -3585,26 +3656,28 @@ static float PM_CrashLandDelta( vec3_t prev_vel, int waterlevel ) return delta; } -int PM_GetLandingAnim( void ) +int PM_GetLandingAnim(void) { int anim = pm->ps->legsAnim; //special cases: - if ( anim == BOTH_FLIP_ATTACK7 - || anim == BOTH_FLIP_HOLD7 ) + if (anim == BOTH_FLIP_ATTACK7 + || anim == BOTH_FLIP_HOLD7) { - return BOTH_FLIP_LAND; + return BOTH_FLIP_LAND; } - else if ( anim == BOTH_FLIP_LAND ) + else if (anim == BOTH_FLIP_LAND) { - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } return BOTH_LAND1; } - else if ( PM_InAirKickingAnim( anim ) ) + else if (PM_InAirKickingAnim(anim)) { - switch ( anim ) + switch (anim) { case BOTH_A7_KICK_F_AIR: return BOTH_FORCELAND1; @@ -3621,67 +3694,83 @@ int PM_GetLandingAnim( void ) } } - if ( PM_SpinningAnim( anim ) || PM_SaberInSpecialAttack( anim ) ) + if (PM_SpinningAnim(anim) || PM_SaberInSpecialAttack(anim)) { return -1; } - switch ( anim ) + switch (anim) { case BOTH_FORCEJUMPLEFT1: case BOTH_FORCEINAIRLEFT1: anim = BOTH_FORCELANDLEFT1; - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } break; case BOTH_FORCEJUMPRIGHT1: case BOTH_FORCEINAIRRIGHT1: anim = BOTH_FORCELANDRIGHT1; - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } break; case BOTH_FORCEJUMP1: case BOTH_FORCEINAIR1: - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } anim = BOTH_FORCELAND1; break; case BOTH_FORCEJUMPBACK1: case BOTH_FORCEINAIRBACK1: - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } anim = BOTH_FORCELANDBACK1; break; case BOTH_JUMPLEFT1: case BOTH_INAIRLEFT1: anim = BOTH_LANDLEFT1; - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } break; case BOTH_JUMPRIGHT1: case BOTH_INAIRRIGHT1: anim = BOTH_LANDRIGHT1; - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } break; case BOTH_JUMP1: case BOTH_INAIR1: anim = BOTH_LAND1; - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } break; case BOTH_JUMPBACK1: case BOTH_INAIRBACK1: anim = BOTH_LANDBACK1; - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } break; case BOTH_BUTTERFLY_LEFT: case BOTH_BUTTERFLY_RIGHT: @@ -3729,13 +3818,13 @@ int PM_GetLandingAnim( void ) break; case BOTH_WALL_RUN_LEFT://# case BOTH_WALL_RUN_RIGHT://# - if ( pm->ps->legsAnimTimer > 500 ) + if (pm->ps->legsAnimTimer > 500) {//only land at end of anim return -1; } //NOTE: falls through on purpose! default: - if ( pm->ps->pm_flags & PMF_BACKWARDS_JUMP ) + if (pm->ps->pm_flags & PMF_BACKWARDS_JUMP) { anim = BOTH_LANDBACK1; } @@ -3743,30 +3832,32 @@ int PM_GetLandingAnim( void ) { anim = BOTH_LAND1; } - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } break; } return anim; } -void G_StartRoll( gentity_t *ent, int anim ) +void G_StartRoll(gentity_t *ent, int anim) { - NPC_SetAnim(ent,SETANIM_BOTH,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS); + NPC_SetAnim(ent, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS); ent->client->ps.weaponTime = ent->client->ps.torsoAnimTimer - 200;//just to make sure it's cleared when roll is done - G_AddEvent( ent, EV_ROLL, 0 ); + G_AddEvent(ent, EV_ROLL, 0); ent->client->ps.saberMove = LS_NONE; } -static qboolean PM_TryRoll( void ) +static qboolean PM_TryRoll(void) { float rollDist = 192;//was 64; - if ( PM_SaberInAttack( pm->ps->saberMove ) || PM_SaberInSpecialAttack( pm->ps->torsoAnim ) - || PM_SpinningSaberAnim( pm->ps->legsAnim ) - || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&PM_SaberInStart( pm->ps->saberMove )) ) + if (PM_SaberInAttack(pm->ps->saberMove) || PM_SaberInSpecialAttack(pm->ps->torsoAnim) + || PM_SpinningSaberAnim(pm->ps->legsAnim) + || ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && PM_SaberInStart(pm->ps->saberMove))) {//attacking or spinning (or, if player, starting an attack) - if ( PM_CanRollFromSoulCal( pm->ps ) ) + if (PM_CanRollFromSoulCal(pm->ps)) {//hehe } else @@ -3774,11 +3865,16 @@ static qboolean PM_TryRoll( void ) return qfalse; } } - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && (!cg.renderingThirdPerson || cg.zoomMode) ) + if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && (!BG_AllowThirdPersonSpecialMove( pm->ps )) ) {//player can't do this in 1st person + // now you can - Dusty ;) return qfalse; } - if ( !pm->gent ) + if (!pm->gent) + { + return qfalse; + } + if ( pm->ps->weapon == WP_EMPLACED_GUN && !(pm->ps->eFlags & EF_LOCKED_TO_WEAPON)) { return qfalse; } @@ -3786,32 +3882,34 @@ static qboolean PM_TryRoll( void ) { return qfalse; } - if ( pm->ps->dualSabers - && (pm->ps->saber[1].saberFlags&SFL_NO_ROLLS) ) + if (pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_ROLLS)) { return qfalse; } - if ( pm->ps->clientNum && pm->gent->NPC ) + if (pm->ps->clientNum && pm->gent->NPC) {//NPC - if ( pm->gent->NPC->scriptFlags&SCF_NO_ACROBATICS ) + if (pm->gent->NPC->scriptFlags&SCF_NO_ACROBATICS) {//scripted to never do acrobatics return qfalse; } - if ( pm->ps->weapon == WP_SABER ) + if (pm->ps->weapon == WP_SABER) {//jedi/reborn - if ( pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank < RANK_LT_JG ) + if (pm->gent->NPC->rank != RANK_CREWMAN && pm->gent->NPC->rank < RANK_LT_JG) {//reborn/jedi who are not acrobats or fencers can't do any of these acrobatics return qfalse; } } else {//non-jedi/reborn - if ( pm->ps->weapon != WP_NONE )//not empty-handed...who would that be??? + if (pm->ps->weapon != WP_NONE)//not empty-handed...who would that be??? {//only jedi/reborn NPCs should be able to do rolls (with a few exceptions) - if ( !pm->gent + if (!pm->gent || !pm->gent->client - || (pm->gent->client->NPC_class != CLASS_BOBAFETT //boba can roll with it, baby + || (pm->gent->client->NPC_class != CLASS_BOBAFETT + && pm->gent->client->NPC_class != CLASS_MANDA + && pm->gent->client->NPC_class != CLASS_COMMANDO//boba can roll with it, baby && pm->gent->client->NPC_class != CLASS_REBORN //reborn using weapons other than saber can still roll )) {//can't roll @@ -3827,45 +3925,45 @@ static qboolean PM_TryRoll( void ) fwdAngles = { 0, pm->ps->viewangles[YAW], 0 }; trace_t trace; int anim = -1; - AngleVectors( fwdAngles, fwd, right, NULL ); + AngleVectors(fwdAngles, fwd, right, NULL); //FIXME: trace ahead for clearance to roll - if ( pm->cmd.forwardmove ) + if (pm->cmd.forwardmove) { - if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) + if (pm->ps->pm_flags & PMF_BACKWARDS_RUN) { anim = BOTH_ROLL_B; - VectorMA( pm->ps->origin, -rollDist, fwd, traceto ); + VectorMA(pm->ps->origin, -rollDist, fwd, traceto); } else { anim = BOTH_ROLL_F; - VectorMA( pm->ps->origin, rollDist, fwd, traceto ); + VectorMA(pm->ps->origin, rollDist, fwd, traceto); } } - else if ( pm->cmd.rightmove > 0 ) + else if (pm->cmd.rightmove > 0) { anim = BOTH_ROLL_R; - VectorMA( pm->ps->origin, rollDist, right, traceto ); + VectorMA(pm->ps->origin, rollDist, right, traceto); } - else if ( pm->cmd.rightmove < 0 ) + else if (pm->cmd.rightmove < 0) { anim = BOTH_ROLL_L; - VectorMA( pm->ps->origin, -rollDist, right, traceto ); + VectorMA(pm->ps->origin, -rollDist, right, traceto); } else {//??? } - if ( anim != -1 ) + if (anim != -1) { qboolean roll = qfalse; int clipmask = CONTENTS_SOLID; - if ( pm->ps->clientNum ) + if (pm->ps->clientNum) { - clipmask |= (CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP); + clipmask |= (CONTENTS_MONSTERCLIP | CONTENTS_BOTCLIP); } else { - if ( pm->gent && pm->gent->enemy && pm->gent->enemy->health > 0 ) + if (pm->gent && pm->gent->enemy && pm->gent->enemy->health > 0) {//player can always roll in combat roll = qtrue; } @@ -3874,32 +3972,32 @@ static qboolean PM_TryRoll( void ) clipmask |= CONTENTS_PLAYERCLIP; } } - if ( !roll ) + if (!roll) { - pm->trace( &trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, clipmask, (EG2_Collision)0, 0 ); - if ( trace.fraction >= 1.0f ) + pm->trace(&trace, pm->ps->origin, mins, maxs, traceto, pm->ps->clientNum, clipmask, (EG2_Collision)0, 0); + if (trace.fraction >= 1.0f) {//okay, clear, check for a bottomless drop vec3_t top; - VectorCopy( traceto, top ); + VectorCopy(traceto, top); traceto[2] -= 256; - pm->trace( &trace, top, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID, (EG2_Collision)0, 0 ); - if ( trace.fraction < 1.0f ) + pm->trace(&trace, top, mins, maxs, traceto, pm->ps->clientNum, CONTENTS_SOLID, (EG2_Collision)0, 0); + if (trace.fraction < 1.0f) {//not a bottomless drop roll = qtrue; } } else {//hit an architectural obstruction - if ( pm->ps->clientNum ) + if (pm->ps->clientNum) {//NPCs don't care about rolling into walls, just off ledges - if ( !(trace.contents&CONTENTS_BOTCLIP) ) + if (!(trace.contents&CONTENTS_BOTCLIP)) { roll = qtrue; } } - else if ( G_EntIsDoor( trace.entityNum ) ) + else if (G_EntIsDoor(trace.entityNum)) {//okay to roll into a door - if ( G_EntIsUnlockedDoor( trace.entityNum ) ) + if (G_EntIsUnlockedDoor(trace.entityNum)) {//if it's an auto-door roll = qtrue; } @@ -3907,34 +4005,34 @@ static qboolean PM_TryRoll( void ) else {//check other conditions gentity_t *traceEnt = &g_entities[trace.entityNum]; - if ( traceEnt && (traceEnt->svFlags&SVF_GLASS_BRUSH) ) + if (traceEnt && (traceEnt->svFlags&SVF_GLASS_BRUSH)) {//okay to roll through glass roll = qtrue; } } } } - if ( roll ) + if (roll) { - G_StartRoll( pm->gent, anim ); + G_StartRoll(pm->gent, anim); return qtrue; } } return qfalse; } -extern void CG_LandingEffect( vec3_t origin, vec3_t normal, int material ); -static void PM_CrashLandEffect( void ) +extern void CG_LandingEffect(vec3_t origin, vec3_t normal, int material); +static void PM_CrashLandEffect(void) { - if ( pm->waterlevel ) + if (pm->waterlevel) { return; } - float delta = fabs(pml.previous_velocity[2])/10;//VectorLength( pml.previous_velocity );? - if ( delta >= 30 ) + float delta = fabs(pml.previous_velocity[2]) / 10;//VectorLength( pml.previous_velocity );? + if (delta >= 30) { - vec3_t bottom = {pm->ps->origin[0],pm->ps->origin[1],pm->ps->origin[2]+pm->mins[2]+1}; - CG_LandingEffect( bottom, pml.groundTrace.plane.normal, (pml.groundTrace.surfaceFlags&MATERIAL_MASK) ); + vec3_t bottom = { pm->ps->origin[0], pm->ps->origin[1], pm->ps->origin[2] + pm->mins[2] + 1 }; + CG_LandingEffect(bottom, pml.groundTrace.plane.normal, (pml.groundTrace.surfaceFlags&MATERIAL_MASK)); } } /* @@ -3944,76 +4042,90 @@ PM_CrashLand Check for hard landings that generate sound events ================= */ -static void PM_CrashLand( void ) +static void PM_CrashLand(void) { float delta = 0; qboolean forceLanding = qfalse; - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) + float jumpMultiplier; + if (g_playerCheatPowers->integer >= 3) + { + jumpMultiplier = 5.0; + } + else if (g_forceNewPowers->integer) + { + jumpMultiplier = 1.25; + } + else + { + jumpMultiplier = 1.0; + } + + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) { - if ( pm->gent->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL ) + if (pm->gent->m_pVehicle->m_pVehicleInfo->type != VH_ANIMAL) { - float dot = DotProduct( pm->ps->velocity, pml.groundTrace.plane.normal ); + float dot = DotProduct(pm->ps->velocity, pml.groundTrace.plane.normal); //Com_Printf("%i:crashland %4.2f\n", c_pmove, pm->ps->velocity[2]); - if ( dot < -100.0f ) + if (dot < -100.0f) { //NOTE: never hits this anyway - if ( pm->gent->m_pVehicle->m_pVehicleInfo->iImpactFX ) + if (pm->gent->m_pVehicle->m_pVehicleInfo->iImpactFX) {//make sparks - if ( !Q_irand( 0, 3 ) ) + if (!Q_irand(0, 3)) {//FIXME: debounce - G_PlayEffect( pm->gent->m_pVehicle->m_pVehicleInfo->iImpactFX, pm->ps->origin, pml.groundTrace.plane.normal ); + G_PlayEffect(pm->gent->m_pVehicle->m_pVehicleInfo->iImpactFX, pm->ps->origin, pml.groundTrace.plane.normal); } } - int damage = floor(fabs(dot+100)/10.0f); - if ( damage >= 0 ) + int damage = floor(fabs(dot + 100) / 10.0f); + if (damage >= 0) { - G_Damage( pm->gent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING ); + G_Damage(pm->gent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING); } } } return; } - if ( (pm->ps->pm_flags&PMF_TRIGGER_PUSHED) ) + if ((pm->ps->pm_flags&PMF_TRIGGER_PUSHED)) { delta = 21;//? forceLanding = qtrue; } else { - if ( pm->gent && pm->gent->NPC && pm->gent->NPC->aiFlags & NPCAI_DIE_ON_IMPACT ) + if (pm->gent && pm->gent->NPC && pm->gent->NPC->aiFlags & NPCAI_DIE_ON_IMPACT) {//have to do death on impact if we are falling to our death, FIXME: should we avoid any additional damage this func? - PM_CrashLandDamage( 1000 ); + PM_CrashLandDamage(1000); } - else if ( pm->gent + else if (pm->gent && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) ) + && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER || pm->gent->client->NPC_class == CLASS_MANDA)) { - if ( JET_Flying( pm->gent ) ) + if (JET_Flying(pm->gent)) { - if ( pm->gent->client->NPC_class == CLASS_BOBAFETT - || (pm->gent->client->NPC_class == CLASS_ROCKETTROOPER&&pm->gent->NPC&&pm->gent->NPC->rankgent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_MANDA + || (pm->gent->client->NPC_class == CLASS_ROCKETTROOPER&&pm->gent->NPC&&pm->gent->NPC->rankgent ); + JET_FlyStop(pm->gent); } else { - pm->ps->velocity[2] += Q_flrand( 100, 200 ); + pm->ps->velocity[2] += Q_flrand(100, 200); } - PM_AddEvent( EV_FALL_SHORT ); + PM_AddEvent(EV_FALL_SHORT); } - if ( pm->ps->forceJumpZStart ) + if (pm->ps->forceJumpZStart) {//we were force-jumping forceLanding = qtrue; } delta = 1; } - else if ( pm->ps->jumpZStart && (pm->ps->forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_1||(pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())) ) + else if (pm->ps->jumpZStart && (pm->ps->forcePowerLevel[FP_LEVITATION] >= FORCE_LEVEL_1 || (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()))) {//we were force-jumping - if ( pm->ps->origin[2] >= pm->ps->jumpZStart ) + if (pm->ps->origin[2] >= pm->ps->jumpZStart) {//we landed at same height or higher than we landed - if ( pm->ps->forceJumpZStart ) + if (pm->ps->forceJumpZStart) {//we were force-jumping forceLanding = qtrue; } @@ -4021,37 +4133,37 @@ static void PM_CrashLand( void ) } else {//take off some of it, at least - delta = (pm->ps->jumpZStart-pm->ps->origin[2]); - float dropAllow = forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]]; - if ( dropAllow < 128 ) + delta = (pm->ps->jumpZStart - pm->ps->origin[2]); + float dropAllow = forceJumpHeight[pm->ps->forcePowerLevel[FP_LEVITATION]] * jumpMultiplier; + if (dropAllow < 128) {//always allow a drop from 128, at least dropAllow = 128; } - if ( delta > forceJumpHeight[FORCE_LEVEL_1] ) + if (delta > forceJumpHeight[FORCE_LEVEL_1] * jumpMultiplier) {//will have to use force jump ability to absorb some of it forceLanding = qtrue;//absorbed some - just to force the correct animation to play below } - delta = (delta - dropAllow)/2; + delta = (delta - dropAllow) / 2; } - if ( delta < 1 ) + if (delta < 1) { delta = 1; } } - if ( !delta ) + if (!delta) { - delta = PM_CrashLandDelta( pml.previous_velocity, pm->waterlevel ); + delta = PM_CrashLandDelta(pml.previous_velocity, pm->waterlevel); } } PM_CrashLandEffect(); - if ( (pm->ps->pm_flags&PMF_DUCKED) && (level.time-pm->ps->lastOnGround)>500 ) + if ((pm->ps->pm_flags&PMF_DUCKED) && (level.time - pm->ps->lastOnGround)>500) {//must be crouched and have been inthe air for half a second minimum - if( !PM_InOnGroundAnim( pm->ps ) && !PM_InKnockDown( pm->ps ) ) + if (!PM_InOnGroundAnim(pm->ps) && !PM_InKnockDown(pm->ps)) {//roll! - if ( PM_TryRoll() ) + if (PM_TryRoll()) {//absorb some impact delta *= 0.5f; } @@ -4059,103 +4171,105 @@ static void PM_CrashLand( void ) } // If he just entered the water (from a fall presumably), absorb some of the impact. - if ( pm->waterlevel >= 2 ) + if (pm->waterlevel >= 2) { delta *= 0.4f; } - if ( delta < 1 ) + if (delta < 1) { - AddSoundEvent( pm->gent, pm->ps->origin, 32, AEL_MINOR, qfalse, qtrue ); + AddSoundEvent(pm->gent, pm->ps->origin, 32, AEL_MINOR, qfalse, qtrue); return; } qboolean deadFallSound = qfalse; - if( !PM_InDeathAnim() ) + if (!PM_InDeathAnim()) { - if ( PM_InAirKickingAnim( pm->ps->legsAnim ) - && pm->ps->torsoAnim == pm->ps->legsAnim ) + if (PM_InAirKickingAnim(pm->ps->legsAnim) + && pm->ps->torsoAnim == pm->ps->legsAnim) { int anim = PM_GetLandingAnim(); - if ( anim != -1 ) + if (anim != -1) {//interrupting a kick clears everything - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); // Only blend over 100ms pm->ps->saberMove = LS_READY; pm->ps->weaponTime = 0; - //stick landings some - pm->ps->velocity[0] *= 0.5f; - pm->ps->velocity[1] *= 0.5f; + if ( !g_allowBunnyhopping->integer ) { + //stick landings some + pm->ps->velocity[0] *= 0.5f; + pm->ps->velocity[1] *= 0.5f; + } } } - else if ( pm->gent + else if (pm->gent && pm->gent->client - && pm->gent->client->NPC_class == CLASS_ROCKETTROOPER ) + && pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) {//rockettroopers are simpler int anim = PM_GetLandingAnim(); - if ( anim != -1 ) + if (anim != -1) { - if ( pm->gent->NPC - && pm->gent->NPC->rank < RANK_LT ) + if (pm->gent->NPC + && pm->gent->NPC->rank < RANK_LT) {//special case: ground-based rocket troopers *always* play land anim on whole body - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); // Only blend over 100ms } else { - PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); // Only blend over 100ms } } } - else if ( pm->cmd.upmove >= 0 && !PM_InKnockDown( pm->ps ) && !PM_InRoll( pm->ps )) + else if (pm->cmd.upmove >= 0 && !PM_InKnockDown(pm->ps) && !PM_InRoll(pm->ps)) {//not crouching - if ( delta > 10 + if (delta > 10 || pm->ps->pm_flags & PMF_BACKWARDS_JUMP - || (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1 << FP_LEVITATION)) + || forceLanding) //EV_FALL_SHORT or jumping back or force-land {// decide which landing animation to use - if ( pm->gent + if (pm->gent && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_RANCOR || pm->gent->client->NPC_class == CLASS_WAMPA ) ) + && (pm->gent->client->NPC_class == CLASS_RANCOR || pm->gent->client->NPC_class == CLASS_WAMPA)) { } else { int anim = PM_GetLandingAnim(); - if ( anim != -1 ) + if (anim != -1) { - if ( PM_FlippingAnim( pm->ps->torsoAnim ) - || PM_SpinningAnim( pm->ps->torsoAnim ) - || pm->ps->torsoAnim == BOTH_FLIP_LAND ) + if (PM_FlippingAnim(pm->ps->torsoAnim) + || PM_SpinningAnim(pm->ps->torsoAnim) + || pm->ps->torsoAnim == BOTH_FLIP_LAND) {//interrupt these if we're going to play a land pm->ps->torsoAnimTimer = 0; } - if ( anim == BOTH_FORCELONGLEAP_LAND ) + if (anim == BOTH_FORCELONGLEAP_LAND) { - if ( pm->gent ) + if (pm->gent) { - G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/player/slide.wav" ); + G_SoundOnEnt(pm->gent, CHAN_AUTO, "sound/player/slide.wav"); } - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); // Only blend over 100ms } - else if ( anim == BOTH_FLIP_LAND - || (pm->ps->torsoAnim == BOTH_FLIP_LAND) ) + else if (anim == BOTH_FLIP_LAND + || (pm->ps->torsoAnim == BOTH_FLIP_LAND)) { - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); // Only blend over 100ms } - else if ( PM_InAirKickingAnim( pm->ps->legsAnim ) - && pm->ps->torsoAnim == pm->ps->legsAnim ) + else if (PM_InAirKickingAnim(pm->ps->legsAnim) + && pm->ps->torsoAnim == pm->ps->legsAnim) {//interrupting a kick clears everything - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); // Only blend over 100ms pm->ps->saberMove = LS_READY; pm->ps->weaponTime = 0; } - else if ( PM_ForceJumpingAnim( pm->ps->legsAnim ) - && pm->ps->torsoAnim == pm->ps->legsAnim ) + else if (PM_ForceJumpingAnim(pm->ps->legsAnim) + && pm->ps->torsoAnim == pm->ps->legsAnim) {//special case: if land during one of these, set the torso, too, if it's not doing something else - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); // Only blend over 100ms } else { - PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 100); // Only blend over 100ms } } } @@ -4166,173 +4280,173 @@ static void PM_CrashLand( void ) { pm->ps->gravity = 1.0; //PM_CrashLandDamage( delta ); - if ( pm->gent ) + if (pm->gent) { if ((!(pml.groundTrace.surfaceFlags & SURF_NODAMAGE)) && -// (!(pml.groundTrace.contents & CONTENTS_NODROP)) && - (!(pm->pointcontents(pm->ps->origin,pm->ps->clientNum) & CONTENTS_NODROP))) + // (!(pml.groundTrace.contents & CONTENTS_NODROP)) && + (!(pm->pointcontents(pm->ps->origin, pm->ps->clientNum) & CONTENTS_NODROP))) { - if ( pm->waterlevel < 2 ) + if (pm->waterlevel < 2) {//don't play fallsplat when impact in the water deadFallSound = qtrue; - if ( !(pm->ps->eFlags&EF_NODRAW) ) + if (!(pm->ps->eFlags&EF_NODRAW)) {//no sound if no draw - if ( delta >= 75 ) + if (delta >= 75) { - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/player/fallsplat.wav" ); + G_SoundOnEnt(pm->gent, CHAN_BODY, "sound/player/fallsplat.wav"); } else { - G_SoundOnEnt( pm->gent, CHAN_BODY, va("sound/player/bodyfall_human%d.wav",Q_irand(1,3)) ); + G_SoundOnEnt(pm->gent, CHAN_BODY, va("sound/player/bodyfall_human%d.wav", Q_irand(1, 3))); } } } else { - G_SoundOnEnt( pm->gent, CHAN_BODY, va("sound/player/bodyfall_water%d.wav",Q_irand(1,3)) ); + G_SoundOnEnt(pm->gent, CHAN_BODY, va("sound/player/bodyfall_water%d.wav", Q_irand(1, 3))); } - if ( gi.VoiceVolume[pm->ps->clientNum] - && pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) ) + if (gi.VoiceVolume[pm->ps->clientNum] + && pm->gent->NPC && (pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT)) {//I was talking, so cut it off... with a jump sound? - if ( !(pm->ps->eFlags&EF_NODRAW) ) + if (!(pm->ps->eFlags&EF_NODRAW)) {//no sound if no draw - G_SoundOnEnt( pm->gent, CHAN_VOICE_ATTEN, "*pain100.wav" ); + G_SoundOnEnt(pm->gent, CHAN_VOICE_ATTEN, "*pain100.wav"); } } } } - if( pm->ps->legsAnim == BOTH_FALLDEATH1 || pm->ps->legsAnim == BOTH_FALLDEATH1INAIR) + if (pm->ps->legsAnim == BOTH_FALLDEATH1 || pm->ps->legsAnim == BOTH_FALLDEATH1INAIR) {//FIXME: add a little bounce? //FIXME: cut voice channel? int old_pm_type = pm->ps->pm_type; pm->ps->pm_type = PM_NORMAL; //Hack because for some reason PM_SetAnim just returns if you're dead...??? - PM_SetAnim(pm, SETANIM_BOTH, BOTH_FALLDEATH1LAND, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_FALLDEATH1LAND, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); pm->ps->pm_type = old_pm_type; - AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_SUSPICIOUS, qfalse, qtrue ); + AddSoundEvent(pm->gent, pm->ps->origin, 256, AEL_SUSPICIOUS, qfalse, qtrue); return; } } // create a local entity event to play the sound - if ( pm->gent && pm->gent->client && pm->gent->client->respawnTime >= level.time - 500 ) + if (pm->gent && pm->gent->client && pm->gent->client->respawnTime >= level.time - 500) {//just spawned in, don't make a noise return; } - if ( delta >= 75 ) + if (delta >= 75) { - if ( !deadFallSound ) + if (!deadFallSound) { - if ( !(pm->ps->eFlags&EF_NODRAW) ) + if (!(pm->ps->eFlags&EF_NODRAW)) {//no sound if no draw - PM_AddEvent( EV_FALL_FAR ); + PM_AddEvent(EV_FALL_FAR); } } - if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) + if (!(pml.groundTrace.surfaceFlags & SURF_NODAMAGE)) { - PM_CrashLandDamage( delta ); + PM_CrashLandDamage(delta); } - if ( pm->gent ) + if (pm->gent) { - if ( pm->gent->s.number == 0 ) + if (pm->gent->s.number == 0) { vec3_t bottom; - VectorCopy( pm->ps->origin, bottom ); + VectorCopy(pm->ps->origin, bottom); bottom[2] += pm->mins[2]; - // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) + // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) { - AddSoundEvent( pm->gent, bottom, 256, AEL_SUSPICIOUS, qfalse, qtrue ); + AddSoundEvent(pm->gent, bottom, 256, AEL_SUSPICIOUS, qfalse, qtrue); } } - else if ( pm->ps->stats[STAT_HEALTH] <= 0 && pm->gent && pm->gent->enemy ) + else if (pm->ps->stats[STAT_HEALTH] <= 0 && pm->gent && pm->gent->enemy) { - AddSoundEvent( pm->gent->enemy, pm->ps->origin, 256, AEL_DISCOVERED, qfalse, qtrue ); + AddSoundEvent(pm->gent->enemy, pm->ps->origin, 256, AEL_DISCOVERED, qfalse, qtrue); } } } - else if ( delta >= 50 ) + else if (delta >= 50) { // this is a pain grunt, so don't play it if dead - if ( pm->ps->stats[STAT_HEALTH] > 0 ) + if (pm->ps->stats[STAT_HEALTH] > 0) { - if ( !deadFallSound ) + if (!deadFallSound) { - if ( !(pm->ps->eFlags&EF_NODRAW) ) + if (!(pm->ps->eFlags&EF_NODRAW)) {//no sound if no draw - PM_AddEvent( EV_FALL_MEDIUM );//damage is dealt in g_active, ClientEvents + PM_AddEvent(EV_FALL_MEDIUM);//damage is dealt in g_active, ClientEvents } } - if ( pm->gent ) + if (pm->gent) { - if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) + if (!(pml.groundTrace.surfaceFlags & SURF_NODAMAGE)) { - PM_CrashLandDamage( delta ); + PM_CrashLandDamage(delta); } - if ( pm->gent->s.number == 0 ) + if (pm->gent->s.number == 0) { vec3_t bottom; - VectorCopy( pm->ps->origin, bottom ); + VectorCopy(pm->ps->origin, bottom); bottom[2] += pm->mins[2]; - // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) + // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) { - AddSoundEvent( pm->gent, bottom, 256, AEL_MINOR, qfalse, qtrue ); + AddSoundEvent(pm->gent, bottom, 256, AEL_MINOR, qfalse, qtrue); } } } } } - else if ( delta >= 30 ) + else if (delta >= 30) { - if ( !deadFallSound ) + if (!deadFallSound) { - if ( !(pm->ps->eFlags&EF_NODRAW) ) + if (!(pm->ps->eFlags&EF_NODRAW)) {//no sound if no draw - PM_AddEvent( EV_FALL_SHORT ); + PM_AddEvent(EV_FALL_SHORT); } } - if ( pm->gent ) + if (pm->gent) { - if ( pm->gent->s.number == 0 ) + if (pm->gent->s.number == 0) { vec3_t bottom; - VectorCopy( pm->ps->origin, bottom ); + VectorCopy(pm->ps->origin, bottom); bottom[2] += pm->mins[2]; - // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) + // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) { - AddSoundEvent( pm->gent, bottom, 128, AEL_MINOR, qfalse, qtrue ); + AddSoundEvent(pm->gent, bottom, 128, AEL_MINOR, qfalse, qtrue); } } else { - if ( !(pml.groundTrace.surfaceFlags & SURF_NODAMAGE) ) + if (!(pml.groundTrace.surfaceFlags & SURF_NODAMAGE)) { - PM_CrashLandDamage( delta ); + PM_CrashLandDamage(delta); } } } } else { - if ( !deadFallSound ) + if (!deadFallSound) { - if ( !(pm->ps->pm_flags&PMF_DUCKED) || !Q_irand( 0, 3 ) ) + if (!(pm->ps->pm_flags&PMF_DUCKED) || !Q_irand(0, 3)) {//only 25% chance of making landing alert when ducked - AddSoundEvent( pm->gent, pm->ps->origin, 32, AEL_MINOR, qfalse, qtrue ); + AddSoundEvent(pm->gent, pm->ps->origin, 32, AEL_MINOR, qfalse, qtrue); } - if ( !(pm->ps->eFlags&EF_NODRAW) ) + if (!(pm->ps->eFlags&EF_NODRAW)) {//no sound if no draw - if ( forceLanding ) + if (forceLanding) {//we were force-jumping - PM_AddEvent( EV_FALL_SHORT ); + PM_AddEvent(EV_FALL_SHORT); } else { -//moved to cg_player PM_AddEvent( PM_FootstepForSurface() ); + //moved to cg_player PM_AddEvent( PM_FootstepForSurface() ); } } } @@ -4340,7 +4454,7 @@ static void PM_CrashLand( void ) // start footstep cycle over pm->ps->bobCycle = 0; - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) {//stop the force push effect when you land pm->gent->forcePushTime = 0; } @@ -4353,8 +4467,8 @@ static void PM_CrashLand( void ) PM_CorrectAllSolid ============= */ -static void PM_CorrectAllSolid( void ) { - if ( pm->debugLevel ) { +static void PM_CorrectAllSolid(void) { + if (pm->debugLevel) { Com_Printf("%i:allsolid\n", c_pmove); //NOTENOTE: If this ever happens, I'd really like to see this print! } @@ -4365,24 +4479,24 @@ static void PM_CorrectAllSolid( void ) { pml.walking = qfalse; } -qboolean FlyingCreature( gentity_t *ent ) +qboolean FlyingCreature(gentity_t *ent) { - if ( ent->client->ps.gravity <= 0 && (ent->svFlags&SVF_CUSTOM_GRAVITY) ) + if (ent->client->ps.gravity <= 0 && (ent->svFlags&SVF_CUSTOM_GRAVITY)) { return qtrue; } return qfalse; } -qboolean PM_RocketeersAvoidDangerousFalls( void ) +qboolean PM_RocketeersAvoidDangerousFalls(void) { - if ( pm->gent->NPC + if (pm->gent->NPC && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) ) + && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_MANDA || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER)) {//fixme: fall through if jetpack broken? - if ( JET_Flying( pm->gent ) ) + if (JET_Flying(pm->gent)) { - if ( pm->gent->client->NPC_class == CLASS_BOBAFETT ) + if (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_MANDA) { pm->gent->client->jetPackTime = level.time + 2000; //Wait, what if the effect is already playing, how do we know??? @@ -4395,46 +4509,46 @@ qboolean PM_RocketeersAvoidDangerousFalls( void ) } else { - TIMER_Set( pm->gent, "jetRecharge", 0 ); - JET_FlyStart( pm->gent ); + TIMER_Set(pm->gent, "jetRecharge", 0); + JET_FlyStart(pm->gent); } return qtrue; } return qfalse; } -static void PM_FallToDeath( void ) +static void PM_FallToDeath(void) { - if ( !pm->gent ) + if (!pm->gent) { return; } - if ( PM_RocketeersAvoidDangerousFalls() ) + if (PM_RocketeersAvoidDangerousFalls()) { return; } // If this is a vehicle, more precisely an animal... - if ( pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL ) + if (pm->gent->client->NPC_class == CLASS_VEHICLE && pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL) { Vehicle_t *pVeh = pm->gent->m_pVehicle; - pVeh->m_pVehicleInfo->EjectAll( pVeh ); + pVeh->m_pVehicleInfo->EjectAll(pVeh); } else { - if ( PM_HasAnimation( pm->gent, BOTH_FALLDEATH1 ) ) + if (PM_HasAnimation(pm->gent, BOTH_FALLDEATH1)) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_FALLDEATH1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_FALLDEATH1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_DEATH1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_DEATH1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } - G_SoundOnEnt( pm->gent, CHAN_VOICE, "*falling1.wav" );//CHAN_VOICE_ATTEN + G_SoundOnEnt(pm->gent, CHAN_VOICE, "*falling1.wav");//CHAN_VOICE_ATTEN } - if ( pm->gent->NPC ) + if (pm->gent->NPC) { pm->gent->NPC->aiFlags |= NPCAI_DIE_ON_IMPACT; pm->gent->NPC->nextBStateThink = Q3_INFINITE; @@ -4442,9 +4556,9 @@ static void PM_FallToDeath( void ) pm->ps->friction = 1; } -int PM_ForceJumpAnimForJumpAnim( int anim ) +int PM_ForceJumpAnimForJumpAnim(int anim) { - switch( anim ) + switch (anim) { case BOTH_JUMP1: //# Jump - wind-up and leave ground anim = BOTH_FORCEJUMP1; //# Jump - wind-up and leave ground @@ -4486,9 +4600,9 @@ int PM_ForceJumpAnimForJumpAnim( int anim ) return anim; } -static void PM_SetVehicleAngles( vec3_t normal ) +static void PM_SetVehicleAngles(vec3_t normal) { - if ( !pm->gent->client || pm->gent->client->NPC_class != CLASS_VEHICLE ) + if (!pm->gent->client || pm->gent->client->NPC_class != CLASS_VEHICLE) return; Vehicle_t *pVeh = pm->gent->m_pVehicle; @@ -4497,8 +4611,8 @@ static void PM_SetVehicleAngles( vec3_t normal ) float vehicleBankingSpeed = pVeh->m_pVehicleInfo->bankingSpeed;//0.25f vec3_t vAngles; - if ( vehicleBankingSpeed <= 0 - || ( pVeh->m_pVehicleInfo->pitchLimit <= 0 && pVeh->m_pVehicleInfo->rollLimit <= 0 ) ) + if (vehicleBankingSpeed <= 0 + || (pVeh->m_pVehicleInfo->pitchLimit <= 0 && pVeh->m_pVehicleInfo->rollLimit <= 0)) {//don't bother, this vehicle doesn't bank return; } @@ -4509,18 +4623,18 @@ static void PM_SetVehicleAngles( vec3_t normal ) //center of gravity affects pitch in air/water (FIXME: what about roll?) //float pitchBias = 90.0f*pVeh->m_pVehicleInfo->centerOfGravity[0];//if centerOfGravity is all the way back (-1.0f), vehicle pitches up 90 degrees when in air - VectorClear( vAngles ); + VectorClear(vAngles); - if (pm->waterlevel>0 || (normal && (pml.groundTrace.contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)))) + if (pm->waterlevel>0 || (normal && (pml.groundTrace.contents&(CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA)))) {//in water - // vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.75f + (pitchBias*0.5); + // vAngles[PITCH] += (pm->ps->viewangles[PITCH]-vAngles[PITCH])*0.75f + (pitchBias*0.5); } - else if ( normal ) + else if (normal) {//have a valid surface below me - pitch_roll_for_slope( pm->gent, normal, vAngles ); + pitch_roll_for_slope(pm->gent, normal, vAngles); float deltaPitch = (vAngles[PITCH] - pVeh->m_vOrientation[PITCH]); - if (deltaPitch< -10.0f) + if (deltaPitch< -10.0f) { vAngles[PITCH] = pVeh->m_vOrientation[PITCH] - 10.0f; } @@ -4532,10 +4646,10 @@ static void PM_SetVehicleAngles( vec3_t normal ) else {//in air, let pitch match view...? //FIXME: take center of gravity into account - vAngles[PITCH] = pVeh->m_vOrientation[PITCH] - 1; + vAngles[PITCH] = pVeh->m_vOrientation[PITCH] - 1; if (vAngles[PITCH]<-15) { - vAngles[PITCH]=-15; + vAngles[PITCH] = -15; } //don't bank so fast when in the air vehicleBankingSpeed *= 0.125f; @@ -4550,23 +4664,23 @@ static void PM_SetVehicleAngles( vec3_t normal ) { //vAngles[ROLL] = pVeh->m_vOrientation[ROLL] + 5; } - else if ( pVeh->m_pVehicleInfo->rollLimit > 0 ) + else if (pVeh->m_pVehicleInfo->rollLimit > 0) { //roll when banking vec3_t velocity; float speed; - VectorCopy( pm->ps->velocity, velocity ); - speed = VectorNormalize( velocity ); - if ( speed>0.01f ) + VectorCopy(pm->ps->velocity, velocity); + speed = VectorNormalize(velocity); + if (speed>0.01f) { vec3_t rt, tempVAngles; float side, dotp; - VectorCopy( pVeh->m_vOrientation, tempVAngles ); + VectorCopy(pVeh->m_vOrientation, tempVAngles); tempVAngles[ROLL] = 0; - AngleVectors( tempVAngles, NULL, rt, NULL ); - dotp = DotProduct( velocity, rt ); + AngleVectors(tempVAngles, NULL, rt, NULL); + dotp = DotProduct(velocity, rt); //if (fabsf(dotp)>0.5f) { speed *= dotp; @@ -4584,31 +4698,31 @@ static void PM_SetVehicleAngles( vec3_t normal ) speed /= pVeh->m_pVehicleInfo->speedMax; } - /// if (pm->cmd.forwardmove==0) - // { - // speed *= 0.5; - // } - // if (pm->cmd.forwardmove<0) - // { - // speed *= 0.1f; - // } + /// if (pm->cmd.forwardmove==0) + // { + // speed *= 0.5; + // } + // if (pm->cmd.forwardmove<0) + // { + // speed *= 0.1f; + // } if (pVeh->m_ulFlags & VEH_SLIDEBREAKING) { speed *= 3.0f; } - side = speed * 75.0f; - // if (pVeh->m_ulFlags & VEH_STRAFERAM) - // { - // vAngles[ROLL] += side; - // } - // else + side = speed * 75.0f; + // if (pVeh->m_ulFlags & VEH_STRAFERAM) + // { + // vAngles[ROLL] += side; + // } + // else { - vAngles[ROLL] -= side; + vAngles[ROLL] -= side; } - //gi.Printf("VAngles(%f)", vAngles[2]); + //gi.Printf("VAngles(%f)", vAngles[2]); } if (fabsf(vAngles[ROLL])<0.001f) { @@ -4618,36 +4732,36 @@ static void PM_SetVehicleAngles( vec3_t normal ) } //cap - if ( vAngles[PITCH] > pVeh->m_pVehicleInfo->pitchLimit ) + if (vAngles[PITCH] > pVeh->m_pVehicleInfo->pitchLimit) { vAngles[PITCH] = pVeh->m_pVehicleInfo->pitchLimit; } - else if ( vAngles[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit ) + else if (vAngles[PITCH] < -pVeh->m_pVehicleInfo->pitchLimit) { vAngles[PITCH] = -pVeh->m_pVehicleInfo->pitchLimit; } if (!(pVeh->m_ulFlags & VEH_SPINNING)) { - if ( vAngles[ROLL] > pVeh->m_pVehicleInfo->rollLimit ) + if (vAngles[ROLL] > pVeh->m_pVehicleInfo->rollLimit) { vAngles[ROLL] = pVeh->m_pVehicleInfo->rollLimit; } - else if ( vAngles[ROLL] < -pVeh->m_pVehicleInfo->rollLimit ) + else if (vAngles[ROLL] < -pVeh->m_pVehicleInfo->rollLimit) { vAngles[ROLL] = -pVeh->m_pVehicleInfo->rollLimit; } } //do it - for ( int i = 0; i < 3; i++ ) + for (int i = 0; i < 3; i++) { - if ( i == YAW ) + if (i == YAW) {//yawing done elsewhere continue; } - if ( i == ROLL && pVeh->m_ulFlags & VEH_STRAFERAM) + if (i == ROLL && pVeh->m_ulFlags & VEH_STRAFERAM) {//during strafe ram, roll is done elsewhere continue; } @@ -4655,92 +4769,92 @@ static void PM_SetVehicleAngles( vec3_t normal ) /* else if ( i == PITCH ) { - curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[PITCH], pVeh->m_vOrientation[PITCH] )))/(g_vehicleInfo[pm->ps->vehicleIndex].pitchLimit/2.0f); + curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[PITCH], pVeh->m_vOrientation[PITCH] )))/(g_vehicleInfo[pm->ps->vehicleIndex].pitchLimit/2.0f); } else if ( i == ROLL ) { - curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[ROLL], pVeh->m_vOrientation[ROLL] )))/(g_vehicleInfo[pm->ps->vehicleIndex].rollLimit/2.0f); + curVehicleBankingSpeed = vehicleBankingSpeed*fabs(AngleNormalize180(AngleSubtract( vAngles[ROLL], pVeh->m_vOrientation[ROLL] )))/(g_vehicleInfo[pm->ps->vehicleIndex].rollLimit/2.0f); } if ( curVehicleBankingSpeed ) */ { - // if ( pVeh->m_vOrientation[i] >= vAngles[i] + vehicleBankingSpeed ) - // { - // pVeh->m_vOrientation[i] -= vehicleBankingSpeed; - // } - // else if ( pVeh->m_vOrientation[i] <= vAngles[i] - vehicleBankingSpeed ) - // { - // pVeh->m_vOrientation[i] += vehicleBankingSpeed; - // } - // else + // if ( pVeh->m_vOrientation[i] >= vAngles[i] + vehicleBankingSpeed ) + // { + // pVeh->m_vOrientation[i] -= vehicleBankingSpeed; + // } + // else if ( pVeh->m_vOrientation[i] <= vAngles[i] - vehicleBankingSpeed ) + // { + // pVeh->m_vOrientation[i] += vehicleBankingSpeed; + // } + // else { pVeh->m_vOrientation[i] = vAngles[i]; } } } - //gi.Printf("Orientation(%f)", pVeh->m_vOrientation[2]); + //gi.Printf("Orientation(%f)", pVeh->m_vOrientation[2]); } -void BG_ExternThisSoICanRecompileInDebug( Vehicle_t *pVeh, playerState_t *riderPS ) +void BG_ExternThisSoICanRecompileInDebug(Vehicle_t *pVeh, playerState_t *riderPS) {/* - float pitchSubtract, pitchDelta, yawDelta; - //Com_Printf( S_COLOR_RED"PITCH: %4.2f, YAW: %4.2f, ROLL: %4.2f\n", riderPS->viewangles[0],riderPS->viewangles[1],riderPS->viewangles[2]); - yawDelta = AngleSubtract(riderPS->viewangles[YAW],pVeh->m_vPrevRiderViewAngles[YAW]); -#ifndef QAGAME - if ( !cg_paused.integer ) - { - //Com_Printf( "%d - yawDelta %4.2f\n", pm->cmd.serverTime, yawDelta ); - } -#endif - yawDelta *= (4.0f*pVeh->m_fTimeModifier); - pVeh->m_vOrientation[ROLL] -= yawDelta; - - pitchDelta = AngleSubtract(riderPS->viewangles[PITCH],pVeh->m_vPrevRiderViewAngles[PITCH]); - pitchDelta *= (2.0f*pVeh->m_fTimeModifier); - pitchSubtract = pitchDelta * (fabs(pVeh->m_vOrientation[ROLL])/90.0f); - pVeh->m_vOrientation[PITCH] += pitchDelta-pitchSubtract; - if ( pVeh->m_vOrientation[ROLL] > 0 ) - { - pVeh->m_vOrientation[YAW] += pitchSubtract; - } - else - { - pVeh->m_vOrientation[YAW] -= pitchSubtract; - } - pVeh->m_vOrientation[PITCH] = AngleNormalize180( pVeh->m_vOrientation[PITCH] ); - pVeh->m_vOrientation[YAW] = AngleNormalize360( pVeh->m_vOrientation[YAW] ); - pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] ); - - VectorCopy( riderPS->viewangles, pVeh->m_vPrevRiderViewAngles );*/ -} - -void BG_VehicleTurnRateForSpeed( Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride ) -{ - if ( pVeh && pVeh->m_pVehicleInfo ) + float pitchSubtract, pitchDelta, yawDelta; + //Com_Printf( S_COLOR_RED"PITCH: %4.2f, YAW: %4.2f, ROLL: %4.2f\n", riderPS->viewangles[0],riderPS->viewangles[1],riderPS->viewangles[2]); + yawDelta = AngleSubtract(riderPS->viewangles[YAW],pVeh->m_vPrevRiderViewAngles[YAW]); + #ifndef QAGAME + if ( !cg_paused.integer ) + { + //Com_Printf( "%d - yawDelta %4.2f\n", pm->cmd.serverTime, yawDelta ); + } + #endif + yawDelta *= (4.0f*pVeh->m_fTimeModifier); + pVeh->m_vOrientation[ROLL] -= yawDelta; + + pitchDelta = AngleSubtract(riderPS->viewangles[PITCH],pVeh->m_vPrevRiderViewAngles[PITCH]); + pitchDelta *= (2.0f*pVeh->m_fTimeModifier); + pitchSubtract = pitchDelta * (fabs(pVeh->m_vOrientation[ROLL])/90.0f); + pVeh->m_vOrientation[PITCH] += pitchDelta-pitchSubtract; + if ( pVeh->m_vOrientation[ROLL] > 0 ) + { + pVeh->m_vOrientation[YAW] += pitchSubtract; + } + else + { + pVeh->m_vOrientation[YAW] -= pitchSubtract; + } + pVeh->m_vOrientation[PITCH] = AngleNormalize180( pVeh->m_vOrientation[PITCH] ); + pVeh->m_vOrientation[YAW] = AngleNormalize360( pVeh->m_vOrientation[YAW] ); + pVeh->m_vOrientation[ROLL] = AngleNormalize180( pVeh->m_vOrientation[ROLL] ); + + VectorCopy( riderPS->viewangles, pVeh->m_vPrevRiderViewAngles );*/ +} + +void BG_VehicleTurnRateForSpeed(Vehicle_t *pVeh, float speed, float *mPitchOverride, float *mYawOverride) +{ + if (pVeh && pVeh->m_pVehicleInfo) { float speedFrac = 1.0f; - if ( pVeh->m_pVehicleInfo->speedDependantTurning ) + if (pVeh->m_pVehicleInfo->speedDependantTurning) { - if ( pVeh->m_LandTrace.fraction >= 1.0f - || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE ) + if (pVeh->m_LandTrace.fraction >= 1.0f + || pVeh->m_LandTrace.plane.normal[2] < MIN_LANDING_SLOPE) { - speedFrac = (speed/(pVeh->m_pVehicleInfo->speedMax*0.75f)); - if ( speedFrac < 0.25f ) + speedFrac = (speed / (pVeh->m_pVehicleInfo->speedMax*0.75f)); + if (speedFrac < 0.25f) { speedFrac = 0.25f; } - else if ( speedFrac > 1.0f ) + else if (speedFrac > 1.0f) { speedFrac = 1.0f; } } } - if ( pVeh->m_pVehicleInfo->mousePitch ) + if (pVeh->m_pVehicleInfo->mousePitch) { *mPitchOverride = pVeh->m_pVehicleInfo->mousePitch*speedFrac; } - if ( pVeh->m_pVehicleInfo->mouseYaw ) + if (pVeh->m_pVehicleInfo->mouseYaw) { *mYawOverride = pVeh->m_pVehicleInfo->mouseYaw*speedFrac; } @@ -4753,41 +4867,41 @@ PM_GroundTraceMissed The ground trace didn't hit a surface, so we are in freefall ============= */ -static void PM_GroundTraceMissed( void ) { +static void PM_GroundTraceMissed(void) { trace_t trace; vec3_t point; qboolean cliff_fall = qfalse; - if ( Flying != FLY_HOVER ) + if (Flying != FLY_HOVER) { - if ( !(pm->ps->eFlags&EF_FORCE_DRAINED) ) + if (!(pm->ps->eFlags&EF_FORCE_DRAINED)) { //FIXME: if in a contents_falldeath brush, play the falling death anim and sound? - if ( pm->ps->clientNum != 0 && pm->gent && pm->gent->NPC && pm->gent->client && pm->gent->client->NPC_class != CLASS_DESANN )//desann never falls to his death + if (pm->ps->clientNum != 0 && pm->gent && pm->gent->NPC && pm->gent->client && pm->gent->client->NPC_class != CLASS_DESANN)//desann never falls to his death { - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE) { - if ( pm->ps->stats[STAT_HEALTH] > 0 + if (pm->ps->stats[STAT_HEALTH] > 0 && !(pm->gent->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) && !(pm->gent->NPC->aiFlags&NPCAI_JUMP) // doing a path jump && !(pm->gent->NPC->scriptFlags&SCF_NO_FALLTODEATH) - && pm->gent->NPC->behaviorState != BS_JUMP )//not being scripted to jump + && pm->gent->NPC->behaviorState != BS_JUMP)//not being scripted to jump { - if ( (level.time - pm->gent->client->respawnTime > 2000)//been in the world for at least 2 seconds + if ((level.time - pm->gent->client->respawnTime > 2000)//been in the world for at least 2 seconds && (!pm->gent->NPC->timeOfDeath || level.time - pm->gent->NPC->timeOfDeath < 1000) && pm->gent->e_ThinkFunc != thinkF_NPC_RemoveBody //Have to do this now because timeOfDeath is used by thinkF_NPC_RemoveBody to debounce removal checks - && !(pm->gent->client->ps.forcePowersActive&(1<gent->client->ps.forcePowersActive&(1 << FP_LEVITATION))) { - if ( !FlyingCreature( pm->gent ) - && g_gravity->value > 0 ) + if (!FlyingCreature(pm->gent) + && g_gravity->value > 0) { - if ( !(pm->gent->flags&FL_UNDYING) - && !(pm->gent->flags&FL_GODMODE) ) + if (!(pm->gent->flags&FL_UNDYING) + && !(pm->gent->flags&FL_GODMODE)) { - if ( !(pm->ps->eFlags&EF_FORCE_GRIPPED) + if (!(pm->ps->eFlags&EF_FORCE_GRIPPED) && !(pm->ps->eFlags&EF_FORCE_DRAINED) - && !(pm->ps->pm_flags&PMF_TRIGGER_PUSHED) ) + && !(pm->ps->pm_flags&PMF_TRIGGER_PUSHED)) { - if ( !pm->ps->forceJumpZStart || pm->ps->forceJumpZStart > pm->ps->origin[2] )// && fabs(pm->ps->velocity[0])<10 && fabs(pm->ps->velocity[1])<10 && pm->ps->velocity[2]<0)//either not force-jumping or force-jumped and now fell below original jump start height + if (!pm->ps->forceJumpZStart || pm->ps->forceJumpZStart > pm->ps->origin[2])// && fabs(pm->ps->velocity[0])<10 && fabs(pm->ps->velocity[1])<10 && pm->ps->velocity[2]<0)//either not force-jumping or force-jumped and now fell below original jump start height { /*if ( pm->ps->legsAnim = BOTH_FALLDEATH1 && pm->ps->legsAnim != BOTH_DEATH1 @@ -4796,60 +4910,60 @@ static void PM_GroundTraceMissed( void ) { vec3_t vel; float time; - VectorCopy( pm->ps->velocity, vel ); - float speed = VectorLength( vel ); - if ( !speed ) + VectorCopy(pm->ps->velocity, vel); + float speed = VectorLength(vel); + if (!speed) {//damn divide by zero speed = 1; } - time = 400/speed; + time = 400 / speed; vel[2] -= 0.5 * time * pm->ps->gravity; - speed = VectorLength( vel ); - if ( !speed ) + speed = VectorLength(vel); + if (!speed) {//damn divide by zero speed = 1; } - time = 400/speed; - VectorScale( vel, time, vel ); - VectorAdd( pm->ps->origin, vel, point ); + time = 400 / speed; + VectorScale(vel, time, vel); + VectorAdd(pm->ps->origin, vel, point); - pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 ); + pm->trace(&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); - if ( (trace.contents&CONTENTS_LAVA) - && PM_RocketeersAvoidDangerousFalls() ) + if ((trace.contents&CONTENTS_LAVA) + && PM_RocketeersAvoidDangerousFalls()) {//got out of it } - else if ( !trace.allsolid && !trace.startsolid && (pm->ps->origin[2] - trace.endpos[2]) >= 128 )//>=128 so we don't die on steps! + else if (!trace.allsolid && !trace.startsolid && (pm->ps->origin[2] - trace.endpos[2]) >= 128)//>=128 so we don't die on steps! { - if ( trace.fraction == 1.0 ) + if (trace.fraction == 1.0) {//didn't hit, we're probably going to die - if ( pm->ps->velocity[2] < 0 && pm->ps->origin[2] - point[2] > 256 ) + if (pm->ps->velocity[2] < 0 && pm->ps->origin[2] - point[2] > 256) {//going down, into a bottomless pit, apparently PM_FallToDeath(); cliff_fall = qtrue; } } - else if ( trace.entityNum < ENTITYNUM_NONE + else if (trace.entityNum < ENTITYNUM_NONE && pm->ps->weapon != WP_SABER - && (!pm->gent || !pm->gent->client || (pm->gent->client->NPC_class != CLASS_BOBAFETT&&pm->gent->client->NPC_class!=CLASS_REBORN&&pm->gent->client->NPC_class!=CLASS_ROCKETTROOPER)) ) + && (!pm->gent || !pm->gent->client || (pm->gent->client->NPC_class != CLASS_BOBAFETT&&pm->gent->client->NPC_class != CLASS_REBORN&&pm->gent->client->NPC_class != CLASS_ROCKETTROOPER && pm->gent->client->NPC_class != CLASS_MANDA))) {//Jedi don't scream and die if they're heading for a hard impact gentity_t *traceEnt = &g_entities[trace.entityNum]; - if ( trace.entityNum == ENTITYNUM_WORLD || (traceEnt && traceEnt->bmodel) ) + if (trace.entityNum == ENTITYNUM_WORLD || (traceEnt && traceEnt->bmodel)) {//hit architecture, find impact force float dmg; - VectorCopy( pm->ps->velocity, vel ); - time = Distance( trace.endpos, pm->ps->origin )/VectorLength( vel ); + VectorCopy(pm->ps->velocity, vel); + time = Distance(trace.endpos, pm->ps->origin) / VectorLength(vel); vel[2] -= 0.5 * time * pm->ps->gravity; - if ( trace.plane.normal[2] > 0.5 ) + if (trace.plane.normal[2] > 0.5) {//use falling damage int waterlevel, junk; - PM_SetWaterLevelAtPoint( trace.endpos, &waterlevel, &junk ); - dmg = PM_CrashLandDelta( vel, waterlevel ); - if ( dmg >= 30 ) + PM_SetWaterLevelAtPoint(trace.endpos, &waterlevel, &junk); + dmg = PM_CrashLandDelta(vel, waterlevel); + if (dmg >= 30) {//there is a minimum fall threshhold - dmg = PM_DamageForDelta( dmg ); + dmg = PM_DamageForDelta(dmg); } else { @@ -4859,30 +4973,30 @@ static void PM_GroundTraceMissed( void ) { else {//use impact damage //guestimate - if ( pm->gent->client && pm->gent->client->ps.forceJumpZStart ) + if (pm->gent->client && pm->gent->client->ps.forceJumpZStart) {//we were force-jumping - if ( pm->gent->currentOrigin[2] >= pm->gent->client->ps.forceJumpZStart ) + if (pm->gent->currentOrigin[2] >= pm->gent->client->ps.forceJumpZStart) {//we landed at same height or higher than we landed dmg = 0; } else {//FIXME: take off some of it, at least? - dmg = (pm->gent->client->ps.forceJumpZStart-pm->gent->currentOrigin[2])/3; + dmg = (pm->gent->client->ps.forceJumpZStart - pm->gent->currentOrigin[2]) / 3; } } - dmg = 10 * VectorLength( pm->ps->velocity ); - if ( pm->ps->clientNum < MAX_CLIENTS ) + dmg = 10 * VectorLength(pm->ps->velocity); + if (pm->ps->clientNum < MAX_CLIENTS) { dmg /= 2; } dmg *= 0.01875f;//magic number } - float maxDmg = pm->ps->stats[STAT_HEALTH]>20?pm->ps->stats[STAT_HEALTH]:20;//a fall that would do less than 20 points of damage should never make us scream to our deaths - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_HOWLER ) + float maxDmg = pm->ps->stats[STAT_HEALTH]>20 ? pm->ps->stats[STAT_HEALTH] : 20;//a fall that would do less than 20 points of damage should never make us scream to our deaths + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_HOWLER) {//howlers can survive long falls, despire their health maxDmg *= 5; } - if ( dmg >= pm->ps->stats[STAT_HEALTH] )//armor? + if (dmg >= pm->ps->stats[STAT_HEALTH])//armor? { PM_FallToDeath(); cliff_fall = qtrue; @@ -4898,7 +5012,7 @@ static void PM_GroundTraceMissed( void ) { VectorCopy( pm->ps->origin, start ); if ( pm->ps->forceJumpZStart < start[2] ) {//Jedi who are force-jumping should only do this from landing point down? - start[2] = pm->ps->forceJumpZStart; + start[2] = pm->ps->forceJumpZStart; } VectorCopy( start, point ); point[2] -= 400;//320 @@ -4906,8 +5020,8 @@ static void PM_GroundTraceMissed( void ) { //FIXME: somehow, people can still get stuck on ledges and not splat when hit...? if ( !trace.allsolid && !trace.startsolid && trace.fraction == 1.0 ) { - PM_FallToDeath(); - cliff_fall = qtrue; + PM_FallToDeath(); + cliff_fall = qtrue; } */ } @@ -4918,18 +5032,18 @@ static void PM_GroundTraceMissed( void ) { } } } - if ( !cliff_fall ) + if (!cliff_fall) { - if ( pm->ps->legsAnim == BOTH_FORCELONGLEAP_START + if (pm->ps->legsAnim == BOTH_FORCELONGLEAP_START || pm->ps->legsAnim == BOTH_FORCELONGLEAP_ATTACK || pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START - || pm->ps->legsAnim == BOTH_FLIP_LAND ) + || pm->ps->legsAnim == BOTH_FLIP_LAND) {//let it stay on this anim } - else if ( PM_KnockDownAnimExtended( pm->ps->legsAnim ) ) + else if (PM_KnockDownAnimExtended(pm->ps->legsAnim)) {//no in-air anims when in knockdown anim } - else if ( ( pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT + else if ((pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT || pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT_STOP || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT_STOP @@ -4941,71 +5055,71 @@ static void PM_GroundTraceMissed( void ) { || pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_LEFT || pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_BACK || pm->ps->legsAnim == BOTH_FORCEWALLREBOUND_RIGHT - || pm->ps->legsAnim == BOTH_CEILING_DROP ) - && !pm->ps->legsAnimTimer ) + || pm->ps->legsAnim == BOTH_CEILING_DROP) + && !pm->ps->legsAnimTimer) {//if flip anim is done, okay to use inair - PM_SetAnim( pm, SETANIM_LEGS, BOTH_FORCEINAIR1, SETANIM_FLAG_OVERRIDE, 350 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_LEGS, BOTH_FORCEINAIR1, SETANIM_FLAG_OVERRIDE, 350); // Only blend over 100ms } - else if ( pm->ps->legsAnim == BOTH_FLIP_ATTACK7 - || pm->ps->legsAnim == BOTH_FLIP_HOLD7 ) + else if (pm->ps->legsAnim == BOTH_FLIP_ATTACK7 + || pm->ps->legsAnim == BOTH_FLIP_HOLD7) { - if ( !pm->ps->legsAnimTimer ) + if (!pm->ps->legsAnimTimer) {//done? - PM_SetAnim( pm, SETANIM_BOTH, BOTH_FLIP_HOLD7, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 350 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_BOTH, BOTH_FLIP_HOLD7, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD, 350); // Only blend over 100ms } } - else if ( PM_InCartwheel( pm->ps->legsAnim ) ) + else if (PM_InCartwheel(pm->ps->legsAnim)) { - if ( pm->ps->legsAnimTimer > 0 ) + if (pm->ps->legsAnimTimer > 0) {//still playing on bottom } else { - PM_SetAnim( pm, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_NORMAL, 350 ); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_NORMAL, 350); } } - else if ( PM_InAirKickingAnim( pm->ps->legsAnim ) ) + else if (PM_InAirKickingAnim(pm->ps->legsAnim)) { - if ( pm->ps->legsAnimTimer > 0 ) + if (pm->ps->legsAnimTimer > 0) {//still playing on bottom } else { - PM_SetAnim( pm, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_NORMAL, 350 ); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_INAIR1, SETANIM_FLAG_NORMAL, 350); pm->ps->saberMove = LS_READY; pm->ps->weaponTime = 0; } } - else if ( !PM_InRoll( pm->ps ) - && !PM_SpinningAnim( pm->ps->legsAnim ) - && !PM_FlippingAnim( pm->ps->legsAnim ) - && !PM_InSpecialJump( pm->ps->legsAnim ) ) + else if (!PM_InRoll(pm->ps) + && !PM_SpinningAnim(pm->ps->legsAnim) + && !PM_FlippingAnim(pm->ps->legsAnim) + && !PM_InSpecialJump(pm->ps->legsAnim)) { - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum != ENTITYNUM_NONE) { // we just transitioned into freefall - if ( pm->debugLevel ) + if (pm->debugLevel) { Com_Printf("%i:lift\n", c_pmove); } // if they aren't in a jumping animation and the ground is a ways away, force into it // if we didn't do the trace, the player would be backflipping down staircases - VectorCopy( pm->ps->origin, point ); + VectorCopy(pm->ps->origin, point); point[2] -= 64; - pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); - if ( trace.fraction == 1.0 ) + pm->trace(&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); + if (trace.fraction == 1.0) {//FIXME: if velocity[2] < 0 and didn't jump, use some falling anim - if ( pm->ps->velocity[2] <= 0 && !(pm->ps->pm_flags&PMF_JUMP_HELD)) + if (pm->ps->velocity[2] <= 0 && !(pm->ps->pm_flags&PMF_JUMP_HELD)) { - if(!PM_InDeathAnim()) + if (!PM_InDeathAnim()) { // If we're a vehicle, set our falling flag. - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) { // We're flying in the air. - if ( pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL ) + if (pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL) { pm->gent->m_pVehicle->m_ulFlags |= VEH_FLYING; } @@ -5015,31 +5129,31 @@ static void PM_GroundTraceMissed( void ) { vec3_t moveDir, lookAngles, lookDir, lookRight; int anim = BOTH_INAIR1; - VectorCopy( pm->ps->velocity, moveDir ); + VectorCopy(pm->ps->velocity, moveDir); moveDir[2] = 0; - VectorNormalize( moveDir ); + VectorNormalize(moveDir); - VectorCopy( pm->ps->viewangles, lookAngles ); + VectorCopy(pm->ps->viewangles, lookAngles); lookAngles[PITCH] = lookAngles[ROLL] = 0; - AngleVectors( lookAngles, lookDir, lookRight, NULL ); + AngleVectors(lookAngles, lookDir, lookRight, NULL); - float dot = DotProduct( moveDir, lookDir ); - if ( dot > 0.5 ) + float dot = DotProduct(moveDir, lookDir); + if (dot > 0.5) {//redundant anim = BOTH_INAIR1; } - else if ( dot < -0.5 ) + else if (dot < -0.5) { anim = BOTH_INAIRBACK1; } else { - dot = DotProduct( moveDir, lookRight ); - if ( dot > 0.5 ) + dot = DotProduct(moveDir, lookRight); + if (dot > 0.5) { anim = BOTH_INAIRRIGHT1; } - else if ( dot < -0.5 ) + else if (dot < -0.5) { anim = BOTH_INAIRLEFT1; } @@ -5048,30 +5162,30 @@ static void PM_GroundTraceMissed( void ) { anim = BOTH_INAIR1; } } - if ( pm->ps->forcePowersActive & ( 1 << FP_LEVITATION ) ) + if (pm->ps->forcePowersActive & (1 << FP_LEVITATION)) { - anim = PM_ForceJumpAnimForJumpAnim( anim ); + anim = PM_ForceJumpAnimForJumpAnim(anim); } - PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE, 100 ); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms } } pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } - else if ( !(pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1 << FP_LEVITATION))) { - if ( pm->cmd.forwardmove >= 0 ) + if (pm->cmd.forwardmove >= 0) { - if(!PM_InDeathAnim()) + if (!PM_InDeathAnim()) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMP1,SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_LEGS, BOTH_JUMP1, SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms } pm->ps->pm_flags &= ~PMF_BACKWARDS_JUMP; } - else if ( pm->cmd.forwardmove < 0 ) + else if (pm->cmd.forwardmove < 0) { - if(!PM_InDeathAnim()) + if (!PM_InDeathAnim()) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_JUMPBACK1,SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms + PM_SetAnim(pm, SETANIM_LEGS, BOTH_JUMPBACK1, SETANIM_FLAG_OVERRIDE, 100); // Only blend over 100ms } pm->ps->pm_flags |= PMF_BACKWARDS_JUMP; } @@ -5080,10 +5194,10 @@ static void PM_GroundTraceMissed( void ) { else { // If we're a vehicle, set our falling flag. - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) { // We're on the ground. - if ( pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL ) + if (pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL) { pm->gent->m_pVehicle->m_ulFlags &= ~VEH_FLYING; } @@ -5094,7 +5208,7 @@ static void PM_GroundTraceMissed( void ) { } } - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum != ENTITYNUM_NONE) { pm->ps->jumpZStart = pm->ps->origin[2]; } @@ -5110,15 +5224,15 @@ static void PM_GroundTraceMissed( void ) { PM_GroundTrace ============= */ -static void PM_GroundTrace( void ) { +static void PM_GroundTrace(void) { vec3_t point; trace_t trace; - if ( (pm->ps->eFlags&EF_LOCKED_TO_WEAPON) + if ((pm->ps->eFlags&EF_LOCKED_TO_WEAPON) || (pm->ps->eFlags&EF_HELD_BY_RANCOR) || (pm->ps->eFlags&EF_HELD_BY_WAMPA) || (pm->ps->eFlags&EF_HELD_BY_SAND_CREATURE) - || PM_RidingVehicle() ) + || PM_RidingVehicle()) { pml.groundPlane = qtrue; pml.walking = qtrue; @@ -5141,10 +5255,10 @@ static void PM_GroundTrace( void ) { */ return; } - else if ( pm->ps->legsAnimTimer > 300 + else if (pm->ps->legsAnimTimer > 300 && (pm->ps->legsAnim == BOTH_WALL_RUN_RIGHT - || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT - || pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START) ) + || pm->ps->legsAnim == BOTH_WALL_RUN_LEFT + || pm->ps->legsAnim == BOTH_FORCEWALLRUNFLIP_START)) {//wall-running forces you to be in the air pml.groundPlane = qfalse; pml.walking = qfalse; @@ -5153,7 +5267,7 @@ static void PM_GroundTrace( void ) { } float minNormal = (float)MIN_WALK_NORMAL; - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) {//FIXME: extern this as part of vehicle data minNormal = pm->gent->m_pVehicle->m_pVehicleInfo->maxSlope; } @@ -5162,17 +5276,17 @@ static void PM_GroundTrace( void ) { point[1] = pm->ps->origin[1]; point[2] = pm->ps->origin[2] - 0.25f; - pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); + pm->trace(&trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); pml.groundTrace = trace; // do something corrective if the trace starts in a solid... - if ( trace.allsolid ) { + if (trace.allsolid) { PM_CorrectAllSolid(); return; } // if the trace didn't hit anything, we are in free fall - if ( trace.fraction == 1.0 || g_gravity->value <= 0 ) + if (trace.fraction == 1.0 || g_gravity->value <= 0) { PM_GroundTraceMissed(); pml.groundPlane = qfalse; @@ -5180,43 +5294,43 @@ static void PM_GroundTrace( void ) { /* if ( pm->ps->vehicleIndex != VEHICLE_NONE ) { - PM_SetVehicleAngles( NULL ); + PM_SetVehicleAngles( NULL ); } */ return; } // Not a vehicle and not riding one. - if ( pm->gent + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class != CLASS_SAND_CREATURE - && (pm->gent->client->NPC_class != CLASS_VEHICLE && !PM_RidingVehicle() ) ) + && (pm->gent->client->NPC_class != CLASS_VEHICLE && !PM_RidingVehicle())) { // check if getting thrown off the ground - if ( ((pm->ps->velocity[2]>0&&(pm->ps->pm_flags&PMF_TIME_KNOCKBACK))||pm->ps->velocity[2]>100) && DotProduct( pm->ps->velocity, trace.plane.normal ) > 10 ) + if (((pm->ps->velocity[2]>0 && (pm->ps->pm_flags&PMF_TIME_KNOCKBACK)) || pm->ps->velocity[2]>100) && DotProduct(pm->ps->velocity, trace.plane.normal) > 10) {//either thrown off ground (PMF_TIME_KNOCKBACK) or going off the ground at a large velocity - if ( pm->debugLevel ) { + if (pm->debugLevel) { Com_Printf("%i:kickoff\n", c_pmove); } // go into jump animation - if ( PM_FlippingAnim( pm->ps->legsAnim) ) + if (PM_FlippingAnim(pm->ps->legsAnim)) {//we're flipping } - else if ( PM_InSpecialJump( pm->ps->legsAnim ) ) + else if (PM_InSpecialJump(pm->ps->legsAnim)) {//special jumps } - else if ( PM_InKnockDown( pm->ps ) ) + else if (PM_InKnockDown(pm->ps)) {//in knockdown } - else if ( PM_InRoll( pm->ps ) ) + else if (PM_InRoll(pm->ps)) {//in knockdown } - else if ( PM_KickingAnim( pm->ps->legsAnim ) ) + else if (PM_KickingAnim(pm->ps->legsAnim)) {//in kick } - else if ( pm->gent + else if (pm->gent && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_RANCOR || pm->gent->client->NPC_class == CLASS_WAMPA) ) + && (pm->gent->client->NPC_class == CLASS_RANCOR || pm->gent->client->NPC_class == CLASS_WAMPA)) { } else @@ -5234,13 +5348,13 @@ static void PM_GroundTrace( void ) { /* if ( pm->ps->vehicleIndex != VEHICLE_NONE ) { - PM_SetVehicleAngles( trace.plane.normal ); + PM_SetVehicleAngles( trace.plane.normal ); } */ // slopes that are too steep will not be considered onground - if ( trace.plane.normal[2] < minNormal ) { - if ( pm->debugLevel ) { + if (trace.plane.normal[2] < minNormal) { + if (pm->debugLevel) { Com_Printf("%i:steep\n", c_pmove); } // FIXME: if they can't slide down the slope, let them @@ -5262,9 +5376,9 @@ static void PM_GroundTrace( void ) { pm->ps->pm_time = 0; } - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) { + if (pm->ps->groundEntityNum == ENTITYNUM_NONE) { // just hit the ground - if ( pm->debugLevel ) { + if (pm->debugLevel) { Com_Printf("%i:Land\n", c_pmove); } @@ -5273,13 +5387,13 @@ static void PM_GroundTrace( void ) { PM_CrashLand(); // don't do landing time if we were just going down a slope - if ( pml.previous_velocity[2] < -200 ) { + if (pml.previous_velocity[2] < -200) { // don't allow another jump for a little while pm->ps->pm_flags |= PMF_TIME_LAND; pm->ps->pm_time = 250; } if (!pm->cmd.forwardmove && !pm->cmd.rightmove) { - if ( Flying != FLY_HOVER ) + if (Flying != FLY_HOVER) { pm->ps->velocity[2] = 0; //wouldn't normally want this because of slopes, but we aren't tyring to move... } @@ -5289,23 +5403,23 @@ static void PM_GroundTrace( void ) { pm->ps->groundEntityNum = trace.entityNum; pm->ps->lastOnGround = level.time; - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) {//if a player, clear the jumping "flag" so can't double-jump pm->ps->forceJumpCharge = 0; } // don't reset the z velocity for slopes -// pm->ps->velocity[2] = 0; + // pm->ps->velocity[2] = 0; - PM_AddTouchEnt( trace.entityNum ); + PM_AddTouchEnt(trace.entityNum); } int LastMatrixJumpTime = 0; #define DEBUGMATRIXJUMP 0 -void PM_HoverTrace( void ) +void PM_HoverTrace(void) { - if ( !pm->gent || !pm->gent->client || pm->gent->client->NPC_class != CLASS_VEHICLE ) + if (!pm->gent || !pm->gent->client || pm->gent->client->NPC_class != CLASS_VEHICLE) { return; } @@ -5318,35 +5432,35 @@ void PM_HoverTrace( void ) pml.groundPlane = qfalse; - float relativeWaterLevel = (pm->ps->waterheight - (pm->ps->origin[2]+pm->mins[2])); - if ( pm->waterlevel && relativeWaterLevel >= 0 ) + float relativeWaterLevel = (pm->ps->waterheight - (pm->ps->origin[2] + pm->mins[2])); + if (pm->waterlevel && relativeWaterLevel >= 0) {//in water - if ( pVeh->m_pVehicleInfo->bouyancy <= 0.0f ) + if (pVeh->m_pVehicleInfo->bouyancy <= 0.0f) {//sink like a rock } else {//rise up - float floatHeight = (pVeh->m_pVehicleInfo->bouyancy * ((pm->maxs[2]-pm->mins[2])*0.5f)) - (hoverHeight*0.5f);//1.0f should make you float half-in, half-out of water - if ( relativeWaterLevel > floatHeight ) + float floatHeight = (pVeh->m_pVehicleInfo->bouyancy * ((pm->maxs[2] - pm->mins[2])*0.5f)) - (hoverHeight*0.5f);//1.0f should make you float half-in, half-out of water + if (relativeWaterLevel > floatHeight) {//too low, should rise up pm->ps->velocity[2] += (relativeWaterLevel - floatHeight) * pVeh->m_fTimeModifier; } } - if ( pm->ps->waterheight < pm->ps->origin[2]+pm->maxs[2] ) + if (pm->ps->waterheight < pm->ps->origin[2] + pm->maxs[2]) {//part of us is sticking out of water - if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 ) + if (fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100) {//moving at a decent speed - if ( Q_irand( pml.frametime, 100 ) >= 50 ) + if (Q_irand(pml.frametime, 100) >= 50) {//splash vAng[PITCH] = vAng[ROLL] = 0; vAng[YAW] = pVeh->m_vOrientation[YAW]; - AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] ); + AngleVectors(vAng, fxAxis[2], fxAxis[1], fxAxis[0]); vec3_t wakeOrg; - VectorCopy( pm->ps->origin, wakeOrg ); + VectorCopy(pm->ps->origin, wakeOrg); wakeOrg[2] = pm->ps->waterheight; - if ( pVeh->m_pVehicleInfo->iWakeFX ) + if (pVeh->m_pVehicleInfo->iWakeFX) { - G_PlayEffect( pVeh->m_pVehicleInfo->iWakeFX, wakeOrg, fxAxis ); + G_PlayEffect(pVeh->m_pVehicleInfo->iWakeFX, wakeOrg, fxAxis); } } } @@ -5367,35 +5481,35 @@ void PM_HoverTrace( void ) //NOTE: if bouyancy is 2.0f or higher, you float over water like it's solid ground. // if it's 1.0f, you sink halfway into water. If it's 0, you sink... - if ( pVeh->m_pVehicleInfo->bouyancy >= 2.0f ) + if (pVeh->m_pVehicleInfo->bouyancy >= 2.0f) {//sit on water - traceContents |= (CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA); + traceContents |= (CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA); } - pm->trace( trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, traceContents, (EG2_Collision)0, 0 ); - if ( trace->plane.normal[2] >= minNormal ) + pm->trace(trace, pm->ps->origin, pm->mins, pm->maxs, point, pm->ps->clientNum, traceContents, (EG2_Collision)0, 0); + if (trace->plane.normal[2] >= minNormal) {//not a steep slope, so push us up - if ( trace->fraction < 0.3f ) + if (trace->fraction < 0.3f) {//push up off ground - float hoverForce = pVeh->m_pVehicleInfo->hoverStrength; - pm->ps->velocity[2] += (0.3f-trace->fraction)*hoverForce*pVeh->m_fTimeModifier; + float hoverForce = pVeh->m_pVehicleInfo->hoverStrength; + pm->ps->velocity[2] += (0.3f - trace->fraction)*hoverForce*pVeh->m_fTimeModifier; - // if (pm->ps->velocity[2]>60.0f) - // { - // pm->ps->velocity[2] = 60.0f; - // } + // if (pm->ps->velocity[2]>60.0f) + // { + // pm->ps->velocity[2] = 60.0f; + // } - if ( (trace->contents&(CONTENTS_WATER|CONTENTS_SLIME|CONTENTS_LAVA)) ) + if ((trace->contents&(CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA))) {//hovering on water, make a spash if moving - if ( fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100 ) + if (fabs(pm->ps->velocity[0]) + fabs(pm->ps->velocity[1]) > 100) {//moving at a decent speed - if ( Q_irand( pml.frametime, 100 ) >= 50 ) + if (Q_irand(pml.frametime, 100) >= 50) {//splash vAng[PITCH] = vAng[ROLL] = 0; vAng[YAW] = pVeh->m_vOrientation[YAW]; - AngleVectors( vAng, fxAxis[2], fxAxis[1], fxAxis[0] ); - if ( pVeh->m_pVehicleInfo->iWakeFX ) + AngleVectors(vAng, fxAxis[2], fxAxis[1], fxAxis[0]); + if (pVeh->m_pVehicleInfo->iWakeFX) { - G_PlayEffect( pVeh->m_pVehicleInfo->iWakeFX, trace->endpos, fxAxis ); + G_PlayEffect(pVeh->m_pVehicleInfo->iWakeFX, trace->endpos, fxAxis); } } } @@ -5403,13 +5517,13 @@ void PM_HoverTrace( void ) if (pVeh->m_ulFlags & VEH_SLIDEBREAKING) { - if ( Q_irand( pml.frametime, 100 ) >= 50 ) + if (Q_irand(pml.frametime, 100) >= 50) {//dust VectorClear(fxAxis[0]); - fxAxis[0][2] = 1; + fxAxis[0][2] = 1; VectorCopy(pm->ps->velocity, fxAxis[1]); - fxAxis[1][2] *= 0.01f; + fxAxis[1][2] *= 0.01f; VectorMA(pm->ps->origin, 0.25f, fxAxis[1], point); G_PlayEffect("ships/swoop_dust", point, fxAxis[0]); } @@ -5418,25 +5532,25 @@ void PM_HoverTrace( void ) } } } - if ( pml.groundPlane ) + if (pml.groundPlane) { - PM_SetVehicleAngles( pml.groundTrace.plane.normal ); + PM_SetVehicleAngles(pml.groundTrace.plane.normal); // We're on the ground. -// if (pVeh->m_ulFlags&VEH_FLYING && level.timem_iTurboTime) -// { -// pVeh->m_iTurboTime = 0; // stop turbo -// } + // if (pVeh->m_ulFlags&VEH_FLYING && level.timem_iTurboTime) + // { + // pVeh->m_iTurboTime = 0; // stop turbo + // } pVeh->m_ulFlags &= ~VEH_FLYING; pVeh->m_vAngularVelocity = 0.0f; } else { - PM_SetVehicleAngles( NULL ); + PM_SetVehicleAngles(NULL); // We're flying in the air. pVeh->m_ulFlags |= VEH_FLYING; //groundTrace - if (pVeh->m_vAngularVelocity==0.0f) + if (pVeh->m_vAngularVelocity == 0.0f) { pVeh->m_vAngularVelocity = pVeh->m_vOrientation[YAW] - pVeh->m_vPrevOrientation[YAW]; if (pVeh->m_vAngularVelocity<-15.0f) @@ -5445,45 +5559,45 @@ void PM_HoverTrace( void ) } if (pVeh->m_vAngularVelocity> 15.0f) { - pVeh->m_vAngularVelocity = 15.0f; + pVeh->m_vAngularVelocity = 15.0f; } // BEGIN MATRIX MODE INIT FOR JUMP //================================= - if (pm->gent && + if (pm->gent && pm->gent->owner && - (pm->gent->owner->s.numbergent->owner)) && - pVeh->m_pVehicleInfo->type==VH_SPEEDER && - level.time>(LastMatrixJumpTime+5000) && VectorLength(pm->ps->velocity)>30.0f) + (pm->gent->owner->s.numbergent->owner)) && + pVeh->m_pVehicleInfo->type == VH_SPEEDER && + level.time>(LastMatrixJumpTime + 5000) && VectorLength(pm->ps->velocity)>30.0f) { - LastMatrixJumpTime = level.time; - vec3_t predictedApx; + LastMatrixJumpTime = level.time; + vec3_t predictedApx; vec3_t predictedFallVelocity; vec3_t predictedLandPosition; - VectorScale(pm->ps->velocity, 2.0f, predictedFallVelocity); // take friction into account - predictedFallVelocity[2] = -(pm->ps->gravity * 1.1f); // take gravity into account + VectorScale(pm->ps->velocity, 2.0f, predictedFallVelocity); // take friction into account + predictedFallVelocity[2] = -(pm->ps->gravity * 1.1f); // take gravity into account - VectorMA(pm->ps->origin, 0.25f, pm->ps->velocity, predictedApx); - VectorMA(predictedApx, 0.25f, predictedFallVelocity, predictedLandPosition); + VectorMA(pm->ps->origin, 0.25f, pm->ps->velocity, predictedApx); + VectorMA(predictedApx, 0.25f, predictedFallVelocity, predictedLandPosition); trace_t trace2; - gi.trace( &trace2, predictedApx, pm->mins, pm->maxs, predictedLandPosition, pm->ps->clientNum, traceContents, (EG2_Collision)0, 0); - if (!trace2.startsolid && !trace2.allsolid && trace2.fraction>0.75 && Q_irand(0, 3)==0) + gi.trace(&trace2, predictedApx, pm->mins, pm->maxs, predictedLandPosition, pm->ps->clientNum, traceContents, (EG2_Collision)0, 0); + if (!trace2.startsolid && !trace2.allsolid && trace2.fraction>0.75 && Q_irand(0, 3) == 0) { LastMatrixJumpTime += 20000; - G_StartMatrixEffect(pm->gent, MEF_HIT_GROUND_STOP); -// CG_DrawEdge(pm->ps->origin, predictedApx, EDGE_WHITE_TWOSECOND); -// CG_DrawEdge(predictedApx, predictedLandPosition, EDGE_WHITE_TWOSECOND); + G_StartMatrixEffect(pm->gent, MEF_HIT_GROUND_STOP); + // CG_DrawEdge(pm->ps->origin, predictedApx, EDGE_WHITE_TWOSECOND); + // CG_DrawEdge(predictedApx, predictedLandPosition, EDGE_WHITE_TWOSECOND); } -// else -// { -// CG_DrawEdge(pm->ps->origin, predictedApx, EDGE_RED_TWOSECOND); -// CG_DrawEdge(predictedApx, predictedLandPosition, EDGE_RED_TWOSECOND); -// } + // else + // { + // CG_DrawEdge(pm->ps->origin, predictedApx, EDGE_RED_TWOSECOND); + // CG_DrawEdge(predictedApx, predictedLandPosition, EDGE_RED_TWOSECOND); + // } } //================================= } @@ -5497,7 +5611,7 @@ void PM_HoverTrace( void ) PM_SetWaterLevelAtPoint FIXME: avoid this twice? certainly if not moving ============= */ -static void PM_SetWaterLevelAtPoint( vec3_t org, int *waterlevel, int *watertype ) +static void PM_SetWaterLevelAtPoint(vec3_t org, int *waterlevel, int *watertype) { vec3_t point; int cont; @@ -5513,11 +5627,11 @@ static void PM_SetWaterLevelAtPoint( vec3_t org, int *waterlevel, int *watertype point[0] = org[0]; point[1] = org[1]; point[2] = org[2] + DEFAULT_MINS_2 + 1; - if (gi.totalMapContents() & (MASK_WATER|CONTENTS_LADDER)) + if (gi.totalMapContents() & (MASK_WATER | CONTENTS_LADDER)) { - cont = pm->pointcontents( point, pm->ps->clientNum ); + cont = pm->pointcontents(point, pm->ps->clientNum); - if ( cont & (MASK_WATER|CONTENTS_LADDER) ) + if (cont & (MASK_WATER | CONTENTS_LADDER)) { sample2 = pm->ps->viewheight - DEFAULT_MINS_2; sample1 = sample2 / 2; @@ -5525,13 +5639,13 @@ static void PM_SetWaterLevelAtPoint( vec3_t org, int *waterlevel, int *watertype *watertype = cont; *waterlevel = 1; point[2] = org[2] + DEFAULT_MINS_2 + sample1; - cont = pm->pointcontents( point, pm->ps->clientNum ); - if ( cont & (MASK_WATER|CONTENTS_LADDER) ) + cont = pm->pointcontents(point, pm->ps->clientNum); + if (cont & (MASK_WATER | CONTENTS_LADDER)) { *waterlevel = 2; point[2] = org[2] + DEFAULT_MINS_2 + sample2; - cont = pm->pointcontents( point, pm->ps->clientNum ); - if ( cont & (MASK_WATER|CONTENTS_LADDER) ) + cont = pm->pointcontents(point, pm->ps->clientNum); + if (cont & (MASK_WATER | CONTENTS_LADDER)) { *waterlevel = 3; } @@ -5540,10 +5654,10 @@ static void PM_SetWaterLevelAtPoint( vec3_t org, int *waterlevel, int *watertype } } -void PM_SetWaterHeight( void ) +void PM_SetWaterHeight(void) { pm->ps->waterHeightLevel = WHL_NONE; - if ( pm->waterlevel < 1 ) + if (pm->waterlevel < 1) { pm->ps->waterheight = pm->ps->origin[2] + DEFAULT_MINS_2 - 4; return; @@ -5551,22 +5665,22 @@ void PM_SetWaterHeight( void ) trace_t trace; vec3_t top, bottom; - VectorCopy( pm->ps->origin, top ); - VectorCopy( pm->ps->origin, bottom ); + VectorCopy(pm->ps->origin, top); + VectorCopy(pm->ps->origin, bottom); top[2] += pm->gent->client->standheight; bottom[2] += DEFAULT_MINS_2; - gi.trace( &trace, top, pm->mins, pm->maxs, bottom, pm->ps->clientNum, MASK_WATER, (EG2_Collision)0, 0 ); + gi.trace(&trace, top, pm->mins, pm->maxs, bottom, pm->ps->clientNum, MASK_WATER, (EG2_Collision)0, 0); - if ( trace.startsolid ) + if (trace.startsolid) {//under water pm->ps->waterheight = top[2] + 4; } - else if ( trace.fraction < 1.0f ) + else if (trace.fraction < 1.0f) {//partially in and partially out of water - pm->ps->waterheight = trace.endpos[2]+pm->mins[2]; + pm->ps->waterheight = trace.endpos[2] + pm->mins[2]; } - else if ( trace.contents&MASK_WATER ) + else if (trace.contents&MASK_WATER) {//water is above me pm->ps->waterheight = top[2] + 4; } @@ -5574,36 +5688,36 @@ void PM_SetWaterHeight( void ) {//water is below me pm->ps->waterheight = bottom[2] - 4; } - float distFromEyes = (pm->ps->origin[2]+pm->gent->client->standheight)-pm->ps->waterheight; + float distFromEyes = (pm->ps->origin[2] + pm->gent->client->standheight) - pm->ps->waterheight; - if ( distFromEyes < 0 ) + if (distFromEyes < 0) { pm->ps->waterHeightLevel = WHL_UNDER; } - else if ( distFromEyes < 6 ) + else if (distFromEyes < 6) { pm->ps->waterHeightLevel = WHL_HEAD; } - else if ( distFromEyes < 18 ) + else if (distFromEyes < 18) { pm->ps->waterHeightLevel = WHL_SHOULDERS; } - else if ( distFromEyes < pm->gent->client->standheight-8 ) + else if (distFromEyes < pm->gent->client->standheight - 8) {//at least 8 above origin pm->ps->waterHeightLevel = WHL_TORSO; } else { - float distFromOrg = pm->ps->origin[2]-pm->ps->waterheight; - if ( distFromOrg < 6 ) + float distFromOrg = pm->ps->origin[2] - pm->ps->waterheight; + if (distFromOrg < 6) { pm->ps->waterHeightLevel = WHL_WAIST; } - else if ( distFromOrg < 16 ) + else if (distFromOrg < 16) { pm->ps->waterHeightLevel = WHL_KNEES; } - else if ( distFromOrg > fabs(pm->mins[2]) ) + else if (distFromOrg > fabs(pm->mins[2])) { pm->ps->waterHeightLevel = WHL_NONE; } @@ -5622,21 +5736,21 @@ PM_SetBounds Sets mins, maxs ============== */ -static void PM_SetBounds (void) +static void PM_SetBounds(void) { - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { - if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] ) + if (!pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2]) { //assert(0); } - VectorCopy( pm->gent->mins, pm->mins ); - VectorCopy( pm->gent->maxs, pm->maxs ); + VectorCopy(pm->gent->mins, pm->mins); + VectorCopy(pm->gent->maxs, pm->maxs); } else { - if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 ) + if (!DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2) { assert(0); } @@ -5659,23 +5773,23 @@ PM_CheckDuck Sets mins, maxs, and pm->ps->viewheight ============== */ -static void PM_CheckDuck (void) +static void PM_CheckDuck(void) { trace_t trace; int standheight; int crouchheight; int oldHeight; - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { - if ( !pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2] ) + if (!pm->gent->mins[0] || !pm->gent->mins[1] || !pm->gent->mins[2] || !pm->gent->maxs[0] || !pm->gent->maxs[1] || !pm->gent->maxs[2]) { //assert(0); } if ( pm->ps->clientNum < MAX_CLIENTS && (pm->gent->client->NPC_class == CLASS_ATST ||pm->gent->client->NPC_class == CLASS_RANCOR) - && !cg.renderingThirdPerson ) + && !BG_AllowThirdPersonSpecialMove( pm->ps ) ) { standheight = crouchheight = 128; } @@ -5687,7 +5801,7 @@ static void PM_CheckDuck (void) } else { - if ( !DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2 ) + if (!DEFAULT_MINS_0 || !DEFAULT_MINS_1 || !DEFAULT_MAXS_0 || !DEFAULT_MAXS_1 || !DEFAULT_MINS_2 || !DEFAULT_MAXS_2) { assert(0); } @@ -5696,7 +5810,7 @@ static void PM_CheckDuck (void) crouchheight = CROUCH_MAXS_2; } - if ( PM_RidingVehicle() || (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) ) + if (PM_RidingVehicle() || (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE)) {//riding a vehicle or are a vehicle //no ducking or rolling when on a vehicle //right? not even on ones that you just ride on top of? @@ -5710,9 +5824,9 @@ static void PM_CheckDuck (void) return; } - if ( PM_InGetUp( pm->ps ) ) + if (PM_InGetUp(pm->ps)) {//can't do any kind of crouching when getting up - if ( pm->ps->legsAnim == BOTH_GETUP_CROUCH_B1 || pm->ps->legsAnim == BOTH_GETUP_CROUCH_F1 ) + if (pm->ps->legsAnim == BOTH_GETUP_CROUCH_B1 || pm->ps->legsAnim == BOTH_GETUP_CROUCH_F1) {//crouched still pm->ps->pm_flags |= PMF_DUCKED; pm->maxs[2] = crouchheight; @@ -5723,16 +5837,16 @@ static void PM_CheckDuck (void) oldHeight = pm->maxs[2]; - if ( PM_InRoll( pm->ps ) ) + if (PM_InRoll(pm->ps)) { /* if ( pm->ps->clientNum && pm->gent && pm->gent->client ) { - pm->maxs[2] = pm->gent->client->renderInfo.eyePoint[2]-pm->ps->origin[2] + 4; - if ( crouchheight > pm->maxs[2] ) - { - pm->maxs[2] = crouchheight; - } + pm->maxs[2] = pm->gent->client->renderInfo.eyePoint[2]-pm->ps->origin[2] + 4; + if ( crouchheight > pm->maxs[2] ) + { + pm->maxs[2] = crouchheight; + } } else */ @@ -5743,14 +5857,14 @@ static void PM_CheckDuck (void) pm->ps->pm_flags |= PMF_DUCKED; return; } - if ( PM_GettingUpFromKnockDown( standheight, crouchheight ) ) + if (PM_GettingUpFromKnockDown(standheight, crouchheight)) { pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET; return; } - if ( PM_InKnockDown( pm->ps ) ) + if (PM_InKnockDown(pm->ps)) {//forced crouch - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) {//interrupted any potential delayed weapon fires pm->gent->client->fireDelay = 0; } @@ -5759,42 +5873,42 @@ static void PM_CheckDuck (void) pm->ps->pm_flags |= PMF_DUCKED; return; } - if ( pm->cmd.upmove < 0 ) + if (pm->cmd.upmove < 0) { // trying to duck pm->maxs[2] = crouchheight; pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;//CROUCH_VIEWHEIGHT; - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE && !PM_SwimmingAnim( pm->ps->legsAnim ) ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE && !PM_SwimmingAnim(pm->ps->legsAnim)) {//Not ducked already and trying to duck in mid-air //will raise your feet, unducking whilst in air will drop feet - if ( !(pm->ps->pm_flags&PMF_DUCKED) ) + if (!(pm->ps->pm_flags&PMF_DUCKED)) { pm->ps->eFlags ^= EF_TELEPORT_BIT; } - if ( pm->gent ) + if (pm->gent) { pm->ps->origin[2] += oldHeight - pm->maxs[2];//diff will be zero if were already ducking //Don't worry, we know we fit in a smaller size } } pm->ps->pm_flags |= PMF_DUCKED; - if ( d_JediAI->integer ) + if (d_JediAI->integer) { - if ( pm->ps->clientNum && pm->ps->weapon == WP_SABER ) + if (pm->ps->clientNum && pm->ps->weapon == WP_SABER) { - Com_Printf( "ducking\n" ); + Com_Printf("ducking\n"); } } } else { // want to stop ducking, stand up if possible - if ( pm->ps->pm_flags & PMF_DUCKED ) + if (pm->ps->pm_flags & PMF_DUCKED) {//Was ducking - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE) {//unducking whilst in air will try to drop feet pm->maxs[2] = standheight; pm->ps->origin[2] += oldHeight - pm->maxs[2]; - pm->trace (&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 ); - if ( !trace.allsolid ) + pm->trace(&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); + if (!trace.allsolid) { pm->ps->eFlags ^= EF_TELEPORT_BIT; pm->ps->pm_flags &= ~PMF_DUCKED; @@ -5811,15 +5925,15 @@ static void PM_CheckDuck (void) { // try to stand up pm->maxs[2] = standheight; - pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 ); - if ( !trace.allsolid ) + pm->trace(&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); + if (!trace.allsolid) { pm->ps->pm_flags &= ~PMF_DUCKED; } } } - if ( pm->ps->pm_flags & PMF_DUCKED ) + if (pm->ps->pm_flags & PMF_DUCKED) {//Still ducking pm->maxs[2] = crouchheight; pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET;//CROUCH_VIEWHEIGHT; @@ -5836,9 +5950,9 @@ static void PM_CheckDuck (void) //=================================================================== -qboolean PM_SaberLockAnim( int anim ) +qboolean PM_SaberLockAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_BF2LOCK: //# case BOTH_BF1LOCK: //# @@ -5849,9 +5963,9 @@ qboolean PM_SaberLockAnim( int anim ) return qfalse; } -qboolean PM_ForceAnim( int anim ) +qboolean PM_ForceAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_CHOKE1: //being choked...??? case BOTH_GESTURE1: //taunting... @@ -5870,7 +5984,7 @@ qboolean PM_ForceAnim( int anim ) case BOTH_FORCEGRIP1: //# temp force-grip anim (actually re-using push) case BOTH_FORCEGRIP_HOLD: //# temp force-grip anim (actually re-using push) case BOTH_FORCEGRIP_RELEASE: //# temp force-grip anim (actually re-using push) - //case BOTH_FORCEGRIP3: //# force-gripping + //case BOTH_FORCEGRIP3: //# force-gripping case BOTH_FORCE_RAGE: case BOTH_FORCE_2HANDEDLIGHTNING: case BOTH_FORCE_2HANDEDLIGHTNING_START: @@ -5891,18 +6005,18 @@ qboolean PM_ForceAnim( int anim ) return qfalse; } -qboolean PM_InSaberAnim( int anim ) +qboolean PM_InSaberAnim(int anim) { - if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR ) + if (anim >= BOTH_A1_T__B_ && anim <= BOTH_H1_S1_BR) { return qtrue; } return qfalse; } -qboolean PM_InForceGetUp( playerState_t *ps ) +qboolean PM_InForceGetUp(playerState_t *ps) { - switch ( ps->legsAnim ) + switch (ps->legsAnim) { case BOTH_FORCE_GETUP_F1: case BOTH_FORCE_GETUP_F2: @@ -5920,7 +6034,7 @@ qboolean PM_InForceGetUp( playerState_t *ps ) case BOTH_GETUP_FROLL_F: case BOTH_GETUP_FROLL_L: case BOTH_GETUP_FROLL_R: - if ( ps->legsAnimTimer ) + if (ps->legsAnimTimer) { return qtrue; } @@ -5929,9 +6043,9 @@ qboolean PM_InForceGetUp( playerState_t *ps ) return qfalse; } -qboolean PM_InGetUp( playerState_t *ps ) +qboolean PM_InGetUp(playerState_t *ps) { - switch ( ps->legsAnim ) + switch (ps->legsAnim) { case BOTH_GETUP1: case BOTH_GETUP2: @@ -5948,22 +6062,22 @@ qboolean PM_InGetUp( playerState_t *ps ) case BOTH_GETUP_FROLL_F: case BOTH_GETUP_FROLL_L: case BOTH_GETUP_FROLL_R: - if ( ps->legsAnimTimer ) + if (ps->legsAnimTimer) { return qtrue; } break; default: - return PM_InForceGetUp( ps ); + return PM_InForceGetUp(ps); break; } //what the hell, redundant, but... return qfalse; } -qboolean PM_InGetUpNoRoll( playerState_t *ps ) +qboolean PM_InGetUpNoRoll(playerState_t *ps) { - switch ( ps->legsAnim ) + switch (ps->legsAnim) { case BOTH_GETUP1: case BOTH_GETUP2: @@ -5980,7 +6094,7 @@ qboolean PM_InGetUpNoRoll( playerState_t *ps ) case BOTH_FORCE_GETUP_B4: case BOTH_FORCE_GETUP_B5: case BOTH_FORCE_GETUP_B6: - if ( ps->legsAnimTimer ) + if (ps->legsAnimTimer) { return qtrue; } @@ -5989,74 +6103,74 @@ qboolean PM_InGetUpNoRoll( playerState_t *ps ) return qfalse; } -qboolean PM_InKnockDown( playerState_t *ps ) +qboolean PM_InKnockDown(playerState_t *ps) { - switch ( ps->legsAnim ) + switch (ps->legsAnim) { case BOTH_KNOCKDOWN1: case BOTH_KNOCKDOWN2: case BOTH_KNOCKDOWN3: case BOTH_KNOCKDOWN4: case BOTH_KNOCKDOWN5: - //special anims: + //special anims: case BOTH_RELEASED: return qtrue; break; case BOTH_LK_DL_ST_T_SB_1_L: - if ( ps->legsAnimTimer < 550 ) + if (ps->legsAnimTimer < 550) { return qtrue; } break; case BOTH_PLAYER_PA_3_FLY: - if ( ps->legsAnimTimer < 300 ) + if (ps->legsAnimTimer < 300) { return qtrue; } /* else if ( ps->clientNum < MAX_CLIENTS - && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) + && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) { - return qtrue; + return qtrue; } */ break; default: - return PM_InGetUp( ps ); + return PM_InGetUp(ps); break; } return qfalse; } -qboolean PM_InKnockDownNoGetup( playerState_t *ps ) +qboolean PM_InKnockDownNoGetup(playerState_t *ps) { - switch ( ps->legsAnim ) + switch (ps->legsAnim) { case BOTH_KNOCKDOWN1: case BOTH_KNOCKDOWN2: case BOTH_KNOCKDOWN3: case BOTH_KNOCKDOWN4: case BOTH_KNOCKDOWN5: - //special anims: + //special anims: case BOTH_RELEASED: return qtrue; break; case BOTH_LK_DL_ST_T_SB_1_L: - if ( ps->legsAnimTimer < 550 ) + if (ps->legsAnimTimer < 550) { return qtrue; } break; case BOTH_PLAYER_PA_3_FLY: - if ( ps->legsAnimTimer < 300 ) + if (ps->legsAnimTimer < 300) { return qtrue; } /* else if ( ps->clientNum < MAX_CLIENTS - && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) + && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) { - return qtrue; + return qtrue; } */ break; @@ -6064,9 +6178,9 @@ qboolean PM_InKnockDownNoGetup( playerState_t *ps ) return qfalse; } -qboolean PM_InKnockDownOnGround( playerState_t *ps ) +qboolean PM_InKnockDownOnGround(playerState_t *ps) { - switch ( ps->legsAnim ) + switch (ps->legsAnim) { case BOTH_KNOCKDOWN1: case BOTH_KNOCKDOWN2: @@ -6075,9 +6189,9 @@ qboolean PM_InKnockDownOnGround( playerState_t *ps ) case BOTH_KNOCKDOWN5: case BOTH_RELEASED: //if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->legsAnimTimer > 300 ) - {//at end of fall down anim - return qtrue; - } + {//at end of fall down anim + return qtrue; + } break; case BOTH_GETUP1: case BOTH_GETUP2: @@ -6094,7 +6208,7 @@ qboolean PM_InKnockDownOnGround( playerState_t *ps ) case BOTH_FORCE_GETUP_B4: case BOTH_FORCE_GETUP_B5: case BOTH_FORCE_GETUP_B6: - if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->legsAnimTimer < 500 ) + if (PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim) - ps->legsAnimTimer < 500) {//at beginning of getup anim return qtrue; } @@ -6107,27 +6221,27 @@ qboolean PM_InKnockDownOnGround( playerState_t *ps ) case BOTH_GETUP_FROLL_F: case BOTH_GETUP_FROLL_L: case BOTH_GETUP_FROLL_R: - if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->legsAnimTimer < 500 ) + if (PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim) - ps->legsAnimTimer < 500) {//at beginning of getup anim return qtrue; } break; case BOTH_LK_DL_ST_T_SB_1_L: - if ( ps->legsAnimTimer < 1000 ) + if (ps->legsAnimTimer < 1000) { return qtrue; } break; case BOTH_PLAYER_PA_3_FLY: - if ( ps->legsAnimTimer < 300 ) + if (ps->legsAnimTimer < 300) { return qtrue; } /* else if ( ps->clientNum < MAX_CLIENTS - && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) + && ps->legsAnimTimer < 300 + PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME ) { - return qtrue; + return qtrue; } */ break; @@ -6135,12 +6249,12 @@ qboolean PM_InKnockDownOnGround( playerState_t *ps ) return qfalse; } -qboolean PM_CrouchGetup( float crouchheight ) +qboolean PM_CrouchGetup(float crouchheight) { pm->maxs[2] = crouchheight; pm->ps->viewheight = crouchheight + STANDARD_VIEWHEIGHT_OFFSET; int anim = -1; - switch ( pm->ps->legsAnim ) + switch (pm->ps->legsAnim) { case BOTH_KNOCKDOWN1: case BOTH_KNOCKDOWN2: @@ -6155,56 +6269,56 @@ qboolean PM_CrouchGetup( float crouchheight ) anim = BOTH_GETUP_CROUCH_F1; break; } - if ( anim == -1 ) + if (anim == -1) {//WTF? stay down? pm->ps->legsAnimTimer = 100;//hold this anim for another 10th of a second return qfalse; } else {//get up into crouch anim - if ( PM_LockedAnim( pm->ps->torsoAnim ) ) + if (PM_LockedAnim(pm->ps->torsoAnim)) {//need to be able to override this anim pm->ps->torsoAnimTimer = 0; } - if ( PM_LockedAnim( pm->ps->legsAnim ) ) + if (PM_LockedAnim(pm->ps->legsAnim)) {//need to be able to override this anim pm->ps->legsAnimTimer = 0; } - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS ); + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS); pm->ps->saberMove = pm->ps->saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in pm->ps->saberBlocked = BLOCKED_NONE; return qtrue; } } -extern qboolean PM_GoingToAttackDown( playerState_t *ps ); -qboolean PM_CheckRollGetup( void ) +extern qboolean PM_GoingToAttackDown(playerState_t *ps); +qboolean PM_CheckRollGetup(void) { - if ( pm->ps->legsAnim == BOTH_KNOCKDOWN1 + if (pm->ps->legsAnim == BOTH_KNOCKDOWN1 || pm->ps->legsAnim == BOTH_KNOCKDOWN2 || pm->ps->legsAnim == BOTH_KNOCKDOWN3 || pm->ps->legsAnim == BOTH_KNOCKDOWN4 || pm->ps->legsAnim == BOTH_KNOCKDOWN5 || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L || pm->ps->legsAnim == BOTH_PLAYER_PA_3_FLY - || pm->ps->legsAnim == BOTH_RELEASED ) + || pm->ps->legsAnim == BOTH_RELEASED) {//lying on back or front - if ( ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && ( pm->cmd.rightmove || (pm->cmd.forwardmove&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) ) )//player pressing left or right - || ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) && pm->gent->NPC//an NPC - && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have at least force jump 1 - && pm->gent->enemy //I have an enemy - && pm->gent->enemy->client//a client - && pm->gent->enemy->enemy == pm->gent//he's mad at me! - && (PM_GoingToAttackDown( &pm->gent->enemy->client->ps )||!Q_irand(0,2))//he's attacking downward! (or we just feel like doing it this time) - && ((pm->gent->client&&pm->gent->client->NPC_class==CLASS_ALORA)||Q_irand( 0, RANK_CAPTAIN )gent->NPC->rank) ) )//higher rank I am, more likely I am to roll away! + if (((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && (pm->cmd.rightmove || (pm->cmd.forwardmove&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0)))//player pressing left or right + || ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) && pm->gent->NPC//an NPC + && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0//have at least force jump 1 + && pm->gent->enemy //I have an enemy + && pm->gent->enemy->client//a client + && pm->gent->enemy->enemy == pm->gent//he's mad at me! + && (PM_GoingToAttackDown(&pm->gent->enemy->client->ps) || !Q_irand(0, 2))//he's attacking downward! (or we just feel like doing it this time) + && ((pm->gent->client&&pm->gent->client->NPC_class == CLASS_ALORA) || Q_irand(0, RANK_CAPTAIN)gent->NPC->rank)))//higher rank I am, more likely I am to roll away! {//roll away! int anim; qboolean forceGetUp = qfalse; - if ( pm->cmd.forwardmove > 0 ) + if (pm->cmd.forwardmove > 0) { - if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3 + if (pm->ps->legsAnim == BOTH_KNOCKDOWN3 || pm->ps->legsAnim == BOTH_KNOCKDOWN5 - || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L ) + || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L) { anim = BOTH_GETUP_FROLL_F; } @@ -6214,11 +6328,11 @@ qboolean PM_CheckRollGetup( void ) } forceGetUp = qtrue; } - else if ( pm->cmd.forwardmove < 0 ) + else if (pm->cmd.forwardmove < 0) { - if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3 + if (pm->ps->legsAnim == BOTH_KNOCKDOWN3 || pm->ps->legsAnim == BOTH_KNOCKDOWN5 - || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L ) + || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L) { anim = BOTH_GETUP_FROLL_B; } @@ -6228,11 +6342,11 @@ qboolean PM_CheckRollGetup( void ) } forceGetUp = qtrue; } - else if ( pm->cmd.rightmove > 0 ) + else if (pm->cmd.rightmove > 0) { - if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3 + if (pm->ps->legsAnim == BOTH_KNOCKDOWN3 || pm->ps->legsAnim == BOTH_KNOCKDOWN5 - || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L ) + || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L) { anim = BOTH_GETUP_FROLL_R; } @@ -6241,11 +6355,11 @@ qboolean PM_CheckRollGetup( void ) anim = BOTH_GETUP_BROLL_R; } } - else if ( pm->cmd.rightmove < 0 ) + else if (pm->cmd.rightmove < 0) { - if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3 + if (pm->ps->legsAnim == BOTH_KNOCKDOWN3 || pm->ps->legsAnim == BOTH_KNOCKDOWN5 - || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L ) + || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L) { anim = BOTH_GETUP_FROLL_L; } @@ -6256,34 +6370,34 @@ qboolean PM_CheckRollGetup( void ) } else { - if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3 + if (pm->ps->legsAnim == BOTH_KNOCKDOWN3 || pm->ps->legsAnim == BOTH_KNOCKDOWN5 - || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L ) + || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L) {//on your front - anim = Q_irand( BOTH_GETUP_FROLL_B, BOTH_GETUP_FROLL_R ); + anim = Q_irand(BOTH_GETUP_FROLL_B, BOTH_GETUP_FROLL_R); } else { - anim = Q_irand( BOTH_GETUP_BROLL_B, BOTH_GETUP_BROLL_R ); + anim = Q_irand(BOTH_GETUP_BROLL_B, BOTH_GETUP_BROLL_R); } } - if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())) { - if ( !G_CheckRollSafety( pm->gent, anim, 64 ) ) + if (!G_CheckRollSafety(pm->gent, anim, 64)) {//oops, try other one - if ( pm->ps->legsAnim == BOTH_KNOCKDOWN3 + if (pm->ps->legsAnim == BOTH_KNOCKDOWN3 || pm->ps->legsAnim == BOTH_KNOCKDOWN5 - || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L ) + || pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L) { - if ( anim == BOTH_GETUP_FROLL_R ) + if (anim == BOTH_GETUP_FROLL_R) { anim = BOTH_GETUP_FROLL_L; } - else if ( anim == BOTH_GETUP_FROLL_F ) + else if (anim == BOTH_GETUP_FROLL_F) { anim = BOTH_GETUP_FROLL_B; } - else if ( anim == BOTH_GETUP_FROLL_B ) + else if (anim == BOTH_GETUP_FROLL_B) { anim = BOTH_GETUP_FROLL_F; } @@ -6291,22 +6405,22 @@ qboolean PM_CheckRollGetup( void ) { anim = BOTH_GETUP_FROLL_L; } - if ( !G_CheckRollSafety( pm->gent, anim, 64 ) ) + if (!G_CheckRollSafety(pm->gent, anim, 64)) {//neither side is clear, screw it return qfalse; } } else { - if ( anim == BOTH_GETUP_BROLL_R ) + if (anim == BOTH_GETUP_BROLL_R) { anim = BOTH_GETUP_BROLL_L; } - else if ( anim == BOTH_GETUP_BROLL_F ) + else if (anim == BOTH_GETUP_BROLL_F) { anim = BOTH_GETUP_BROLL_B; } - else if ( anim == BOTH_GETUP_FROLL_B ) + else if (anim == BOTH_GETUP_FROLL_B) { anim = BOTH_GETUP_BROLL_F; } @@ -6314,7 +6428,7 @@ qboolean PM_CheckRollGetup( void ) { anim = BOTH_GETUP_BROLL_L; } - if ( !G_CheckRollSafety( pm->gent, anim, 64 ) ) + if (!G_CheckRollSafety(pm->gent, anim, 64)) {//neither side is clear, screw it return qfalse; } @@ -6322,28 +6436,28 @@ qboolean PM_CheckRollGetup( void ) } } pm->cmd.rightmove = pm->cmd.forwardmove = 0; - if ( PM_LockedAnim( pm->ps->torsoAnim ) ) + if (PM_LockedAnim(pm->ps->torsoAnim)) {//need to be able to override this anim pm->ps->torsoAnimTimer = 0; } - if ( PM_LockedAnim( pm->ps->legsAnim ) ) + if (PM_LockedAnim(pm->ps->legsAnim)) {//need to be able to override this anim pm->ps->legsAnimTimer = 0; } - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS ); + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS); pm->ps->weaponTime = pm->ps->torsoAnimTimer - 300;//don't attack until near end of this anim pm->ps->saberMove = pm->ps->saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in pm->ps->saberBlocked = BLOCKED_NONE; - if ( forceGetUp ) + if (forceGetUp) { - if ( pm->gent && pm->gent->client && pm->gent->client->playerTeam == TEAM_ENEMY + if (pm->gent && pm->gent->client && pm->gent->client->playerTeam == TEAM_ENEMY && pm->gent->NPC && pm->gent->NPC->blockedSpeechDebounceTime < level.time - && !Q_irand( 0, 1 ) ) + && !Q_irand(0, 1)) { - PM_AddEvent( Q_irand( EV_COMBAT1, EV_COMBAT3 ) ); + PM_AddEvent(Q_irand(EV_COMBAT1, EV_COMBAT3)); pm->gent->NPC->blockedSpeechDebounceTime = level.time + 1000; } - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + G_SoundOnEnt(pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav"); //launch off ground? pm->ps->weaponTime = 300;//just to make sure it's cleared } @@ -6353,64 +6467,64 @@ qboolean PM_CheckRollGetup( void ) return qfalse; } -extern int G_MinGetUpTime( gentity_t *ent ); -qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight ) +extern int G_MinGetUpTime(gentity_t *ent); +qboolean PM_GettingUpFromKnockDown(float standheight, float crouchheight) { int legsAnim = pm->ps->legsAnim; - if ( legsAnim == BOTH_KNOCKDOWN1 - ||legsAnim == BOTH_KNOCKDOWN2 - ||legsAnim == BOTH_KNOCKDOWN3 - ||legsAnim == BOTH_KNOCKDOWN4 - ||legsAnim == BOTH_KNOCKDOWN5 - ||legsAnim == BOTH_PLAYER_PA_3_FLY - ||legsAnim == BOTH_LK_DL_ST_T_SB_1_L - ||legsAnim == BOTH_RELEASED ) + if (legsAnim == BOTH_KNOCKDOWN1 + || legsAnim == BOTH_KNOCKDOWN2 + || legsAnim == BOTH_KNOCKDOWN3 + || legsAnim == BOTH_KNOCKDOWN4 + || legsAnim == BOTH_KNOCKDOWN5 + || legsAnim == BOTH_PLAYER_PA_3_FLY + || legsAnim == BOTH_LK_DL_ST_T_SB_1_L + || legsAnim == BOTH_RELEASED) {//in a knockdown - int minTimeLeft = G_MinGetUpTime( pm->gent ); - if ( pm->ps->legsAnimTimer <= minTimeLeft ) + int minTimeLeft = G_MinGetUpTime(pm->gent); + if (pm->ps->legsAnimTimer <= minTimeLeft) {//if only a quarter of a second left, allow roll-aways - if ( PM_CheckRollGetup() ) + if (PM_CheckRollGetup()) { pm->cmd.rightmove = pm->cmd.forwardmove = 0; return qtrue; } } - if ( TIMER_Exists( pm->gent, "noGetUpStraight" ) ) + if (TIMER_Exists(pm->gent, "noGetUpStraight")) { - if ( !TIMER_Done2( pm->gent, "noGetUpStraight", qtrue ) ) + if (!TIMER_Done2(pm->gent, "noGetUpStraight", qtrue)) {//not allowed to do straight get-ups for another few seconds - if ( pm->ps->legsAnimTimer <= minTimeLeft ) + if (pm->ps->legsAnimTimer <= minTimeLeft) {//hold it for a bit - pm->ps->legsAnimTimer = minTimeLeft+1; + pm->ps->legsAnimTimer = minTimeLeft + 1; } } } - if ( !pm->ps->legsAnimTimer || (pm->ps->legsAnimTimer<=minTimeLeft&&(pm->cmd.upmove>0||(pm->gent&&pm->gent->client&&pm->gent->client->NPC_class==CLASS_ALORA))) ) + if (!pm->ps->legsAnimTimer || (pm->ps->legsAnimTimer <= minTimeLeft && (pm->cmd.upmove>0 || (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class == CLASS_ALORA)))) {//done with the knockdown - FIXME: somehow this is allowing an *instant* getup...??? //FIXME: if trying to crouch (holding button?), just get up into a crouch? - if ( pm->cmd.upmove < 0 ) + if (pm->cmd.upmove < 0) { - return PM_CrouchGetup( crouchheight ); + return PM_CrouchGetup(crouchheight); } else { trace_t trace; // try to stand up pm->maxs[2] = standheight; - pm->trace( &trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 ); - if ( !trace.allsolid ) + pm->trace(&trace, pm->ps->origin, pm->mins, pm->maxs, pm->ps->origin, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); + if (!trace.allsolid) {//stand up int anim = BOTH_GETUP1; qboolean forceGetUp = qfalse; pm->maxs[2] = standheight; pm->ps->viewheight = standheight + STANDARD_VIEWHEIGHT_OFFSET; //NOTE: the force power checks will stop fencers and grunts from getting up using force jump - switch ( pm->ps->legsAnim ) + switch (pm->ps->legsAnim) { case BOTH_KNOCKDOWN1: - if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) ) + if ((pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->cmd.upmove>0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0))//FORCE_LEVEL_1) )FORCE_LEVEL_1) ) { - anim = Q_irand( BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6 );//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end + anim = Q_irand(BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6);//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end forceGetUp = qtrue; } else @@ -6420,9 +6534,9 @@ qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight ) break; case BOTH_KNOCKDOWN2: case BOTH_PLAYER_PA_3_FLY: - if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) ) + if ((pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->cmd.upmove>0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0))//FORCE_LEVEL_1) )FORCE_LEVEL_1) ) { - anim = Q_irand( BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6 );//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end + anim = Q_irand(BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6);//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end forceGetUp = qtrue; } else @@ -6431,9 +6545,9 @@ qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight ) } break; case BOTH_KNOCKDOWN3: - if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) ) + if ((pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->cmd.upmove>0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0))//FORCE_LEVEL_1) ) { - anim = Q_irand( BOTH_FORCE_GETUP_F1, BOTH_FORCE_GETUP_F2 ); + anim = Q_irand(BOTH_FORCE_GETUP_F1, BOTH_FORCE_GETUP_F2); forceGetUp = qtrue; } else @@ -6443,9 +6557,9 @@ qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight ) break; case BOTH_KNOCKDOWN4: case BOTH_RELEASED: - if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) ) + if ((pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->cmd.upmove>0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0))//FORCE_LEVEL_1) )FORCE_LEVEL_1) ) { - anim = Q_irand( BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6 );//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end + anim = Q_irand(BOTH_FORCE_GETUP_B1, BOTH_FORCE_GETUP_B6);//NOTE: BOTH_FORCE_GETUP_B5 takes soe steps forward at end forceGetUp = qtrue; } else @@ -6455,9 +6569,9 @@ qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight ) break; case BOTH_KNOCKDOWN5: case BOTH_LK_DL_ST_T_SB_1_L: - if ( (pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())&&pm->cmd.upmove>0&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) )//FORCE_LEVEL_1) )FORCE_LEVEL_1) ) + if ((pm->ps->clientNum&&pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0) || ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->cmd.upmove>0 && pm->ps->forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0))//FORCE_LEVEL_1) )FORCE_LEVEL_1) ) { - anim = Q_irand( BOTH_FORCE_GETUP_F1, BOTH_FORCE_GETUP_F2 ); + anim = Q_irand(BOTH_FORCE_GETUP_F1, BOTH_FORCE_GETUP_F2); forceGetUp = qtrue; } else @@ -6467,43 +6581,43 @@ qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight ) break; } //Com_Printf( "getupanim = %s\n", animTable[anim].name ); - if ( forceGetUp ) + if (forceGetUp) { - if ( pm->gent && pm->gent->client && pm->gent->client->playerTeam == TEAM_ENEMY + if (pm->gent && pm->gent->client && pm->gent->client->playerTeam == TEAM_ENEMY && pm->gent->NPC && pm->gent->NPC->blockedSpeechDebounceTime < level.time - && !Q_irand( 0, 1 ) ) + && !Q_irand(0, 1)) { - PM_AddEvent( Q_irand( EV_COMBAT1, EV_COMBAT3 ) ); + PM_AddEvent(Q_irand(EV_COMBAT1, EV_COMBAT3)); pm->gent->NPC->blockedSpeechDebounceTime = level.time + 1000; } - G_SoundOnEnt( pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + G_SoundOnEnt(pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav"); //launch off ground? pm->ps->weaponTime = 300;//just to make sure it's cleared } - if ( PM_LockedAnim( pm->ps->torsoAnim ) ) + if (PM_LockedAnim(pm->ps->torsoAnim)) {//need to be able to override this anim pm->ps->torsoAnimTimer = 0; } - if ( PM_LockedAnim( pm->ps->legsAnim ) ) + if (PM_LockedAnim(pm->ps->legsAnim)) {//need to be able to override this anim pm->ps->legsAnimTimer = 0; } - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS ); + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS); pm->ps->saberMove = pm->ps->saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in pm->ps->saberBlocked = BLOCKED_NONE; return qtrue; } else { - return PM_CrouchGetup( crouchheight ); + return PM_CrouchGetup(crouchheight); } } } else { - if ( pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L ) + if (pm->ps->legsAnim == BOTH_LK_DL_ST_T_SB_1_L) { - PM_CmdForRoll( pm->ps, &pm->cmd ); + PM_CmdForRoll(pm->ps, &pm->cmd); } else { @@ -6514,9 +6628,9 @@ qboolean PM_GettingUpFromKnockDown( float standheight, float crouchheight ) return qfalse; } -void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ) +void PM_CmdForRoll(playerState_t *ps, usercmd_t *pCmd) { - switch ( ps->legsAnim ) + switch (ps->legsAnim) { case BOTH_ROLL_F: pCmd->forwardmove = 127; @@ -6542,7 +6656,7 @@ void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ) break; case BOTH_GETUP_FROLL_R: - if ( ps->legsAnimTimer <= 250 ) + if (ps->legsAnimTimer <= 250) {//end of anim pCmd->forwardmove = pCmd->rightmove = 0; } @@ -6561,7 +6675,7 @@ void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ) break; case BOTH_GETUP_FROLL_L: - if ( ps->legsAnimTimer <= 250 ) + if (ps->legsAnimTimer <= 250) {//end of anim pCmd->forwardmove = pCmd->rightmove = 0; } @@ -6574,11 +6688,11 @@ void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ) break; case BOTH_GETUP_BROLL_B: - if ( ps->torsoAnimTimer <= 250 ) + if (ps->torsoAnimTimer <= 250) {//end of anim pCmd->forwardmove = pCmd->rightmove = 0; } - else if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->torsoAnimTimer < 350 ) + else if (PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim) - ps->torsoAnimTimer < 350) {//beginning of anim pCmd->forwardmove = pCmd->rightmove = 0; } @@ -6592,11 +6706,11 @@ void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ) break; case BOTH_GETUP_FROLL_B: - if ( ps->torsoAnimTimer <= 100 ) + if (ps->torsoAnimTimer <= 100) {//end of anim pCmd->forwardmove = pCmd->rightmove = 0; } - else if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->torsoAnimTimer < 200 ) + else if (PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim) - ps->torsoAnimTimer < 200) {//beginning of anim pCmd->forwardmove = pCmd->rightmove = 0; } @@ -6610,11 +6724,11 @@ void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ) break; case BOTH_GETUP_BROLL_F: - if ( ps->torsoAnimTimer <= 550 ) + if (ps->torsoAnimTimer <= 550) {//end of anim pCmd->forwardmove = pCmd->rightmove = 0; } - else if ( PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim ) - ps->torsoAnimTimer < 150 ) + else if (PM_AnimLength(g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)ps->legsAnim) - ps->torsoAnimTimer < 150) {//beginning of anim pCmd->forwardmove = pCmd->rightmove = 0; } @@ -6627,7 +6741,7 @@ void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ) break; case BOTH_GETUP_FROLL_F: - if ( ps->torsoAnimTimer <= 100 ) + if (ps->torsoAnimTimer <= 100) {//end of anim pCmd->forwardmove = pCmd->rightmove = 0; } @@ -6641,8 +6755,8 @@ void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ) break; case BOTH_LK_DL_ST_T_SB_1_L: //kicked backwards - if ( ps->legsAnimTimer < 3050//at least 10 frames in - && ps->legsAnimTimer > 550 )//at least 6 frames from end + if (ps->legsAnimTimer < 3050//at least 10 frames in + && ps->legsAnimTimer > 550)//at least 6 frames from end {//move backwards pCmd->forwardmove = -64; pCmd->rightmove = 0; @@ -6657,9 +6771,9 @@ void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd ) pCmd->upmove = 0; } -qboolean PM_InRollIgnoreTimer( playerState_t *ps ) +qboolean PM_InRollIgnoreTimer(playerState_t *ps) { - switch ( ps->legsAnim ) + switch (ps->legsAnim) { case BOTH_ROLL_F: case BOTH_ROLL_B: @@ -6678,9 +6792,9 @@ qboolean PM_InRollIgnoreTimer( playerState_t *ps ) return qfalse; } -qboolean PM_InRoll( playerState_t *ps ) +qboolean PM_InRoll(playerState_t *ps) { - if ( ps->legsAnimTimer && PM_InRollIgnoreTimer( ps ) ) + if (ps->legsAnimTimer && PM_InRollIgnoreTimer(ps)) { return qtrue; } @@ -6688,9 +6802,9 @@ qboolean PM_InRoll( playerState_t *ps ) return qfalse; } -qboolean PM_CrouchAnim( int anim ) +qboolean PM_CrouchAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_SIT1: //# Normal chair sit. case BOTH_SIT2: //# Lotus position. @@ -6704,7 +6818,7 @@ qboolean PM_CrouchAnim( int anim ) case BOTH_KNEES1: //# Tavion on her knees case BOTH_CROUCHATTACKBACK1://FIXME: not if in middle of anim? case BOTH_ROLL_STAB: - //??? + //??? case BOTH_STAND_TO_KNEEL: case BOTH_KNEEL_TO_STAND: case BOTH_TURNCROUCH1: @@ -6717,28 +6831,28 @@ qboolean PM_CrouchAnim( int anim ) return qfalse; } -qboolean PM_PainAnim( int anim ) -{ - switch ( anim ) - { - case BOTH_PAIN1: //# First take pain anim - case BOTH_PAIN2: //# Second take pain anim - case BOTH_PAIN3: //# Third take pain anim - case BOTH_PAIN4: //# Fourth take pain anim - case BOTH_PAIN5: //# Fifth take pain anim - from behind - case BOTH_PAIN6: //# Sixth take pain anim - from behind - case BOTH_PAIN7: //# Seventh take pain anim - from behind - case BOTH_PAIN8: //# Eigth take pain anim - from behind - case BOTH_PAIN9: //# - case BOTH_PAIN10: //# - case BOTH_PAIN11: //# - case BOTH_PAIN12: //# - case BOTH_PAIN13: //# - case BOTH_PAIN14: //# - case BOTH_PAIN15: //# - case BOTH_PAIN16: //# - case BOTH_PAIN17: //# - case BOTH_PAIN18: //# +qboolean PM_PainAnim(int anim) +{ + switch (anim) + { + case BOTH_PAIN1: //# First take pain anim + case BOTH_PAIN2: //# Second take pain anim + case BOTH_PAIN3: //# Third take pain anim + case BOTH_PAIN4: //# Fourth take pain anim + case BOTH_PAIN5: //# Fifth take pain anim - from behind + case BOTH_PAIN6: //# Sixth take pain anim - from behind + case BOTH_PAIN7: //# Seventh take pain anim - from behind + case BOTH_PAIN8: //# Eigth take pain anim - from behind + case BOTH_PAIN9: //# + case BOTH_PAIN10: //# + case BOTH_PAIN11: //# + case BOTH_PAIN12: //# + case BOTH_PAIN13: //# + case BOTH_PAIN14: //# + case BOTH_PAIN15: //# + case BOTH_PAIN16: //# + case BOTH_PAIN17: //# + case BOTH_PAIN18: //# return qtrue; break; } @@ -6746,9 +6860,9 @@ qboolean PM_PainAnim( int anim ) } -qboolean PM_DodgeHoldAnim( int anim ) +qboolean PM_DodgeHoldAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_DODGE_HOLD_FL: case BOTH_DODGE_HOLD_FR: @@ -6762,9 +6876,9 @@ qboolean PM_DodgeHoldAnim( int anim ) return qfalse; } -qboolean PM_DodgeAnim( int anim ) +qboolean PM_DodgeAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_DODGE_FL: //# lean-dodge forward left case BOTH_DODGE_FR: //# lean-dodge forward right @@ -6775,37 +6889,37 @@ qboolean PM_DodgeAnim( int anim ) return qtrue; break; default: - return PM_DodgeHoldAnim( anim ); + return PM_DodgeHoldAnim(anim); break; } //return qfalse; } -qboolean PM_ForceJumpingAnim( int anim ) +qboolean PM_ForceJumpingAnim(int anim) { - switch ( anim ) + switch (anim) { - case BOTH_FORCEJUMP1: //# Jump - wind-up and leave ground - case BOTH_FORCEINAIR1: //# In air loop (from jump) - case BOTH_FORCELAND1: //# Landing (from in air loop) - case BOTH_FORCEJUMPBACK1: //# Jump backwards - wind-up and leave ground - case BOTH_FORCEINAIRBACK1: //# In air loop (from jump back) - case BOTH_FORCELANDBACK1: //# Landing backwards(from in air loop) - case BOTH_FORCEJUMPLEFT1: //# Jump left - wind-up and leave ground - case BOTH_FORCEINAIRLEFT1: //# In air loop (from jump left) - case BOTH_FORCELANDLEFT1: //# Landing left(from in air loop) - case BOTH_FORCEJUMPRIGHT1: //# Jump right - wind-up and leave ground - case BOTH_FORCEINAIRRIGHT1: //# In air loop (from jump right) - case BOTH_FORCELANDRIGHT1: //# Landing right(from in air loop) + case BOTH_FORCEJUMP1: //# Jump - wind-up and leave ground + case BOTH_FORCEINAIR1: //# In air loop (from jump) + case BOTH_FORCELAND1: //# Landing (from in air loop) + case BOTH_FORCEJUMPBACK1: //# Jump backwards - wind-up and leave ground + case BOTH_FORCEINAIRBACK1: //# In air loop (from jump back) + case BOTH_FORCELANDBACK1: //# Landing backwards(from in air loop) + case BOTH_FORCEJUMPLEFT1: //# Jump left - wind-up and leave ground + case BOTH_FORCEINAIRLEFT1: //# In air loop (from jump left) + case BOTH_FORCELANDLEFT1: //# Landing left(from in air loop) + case BOTH_FORCEJUMPRIGHT1: //# Jump right - wind-up and leave ground + case BOTH_FORCEINAIRRIGHT1: //# In air loop (from jump right) + case BOTH_FORCELANDRIGHT1: //# Landing right(from in air loop) return qtrue; break; } return qfalse; } -qboolean PM_JumpingAnim( int anim ) +qboolean PM_JumpingAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_JUMP1: //# Jump - wind-up and leave ground case BOTH_INAIR1: //# In air loop (from jump) @@ -6823,38 +6937,38 @@ qboolean PM_JumpingAnim( int anim ) return qtrue; break; default: - if ( PM_InAirKickingAnim( anim ) ) + if (PM_InAirKickingAnim(anim)) { return qtrue; } - return PM_ForceJumpingAnim( anim ); + return PM_ForceJumpingAnim(anim); break; } //return qfalse; } -qboolean PM_LandingAnim( int anim ) +qboolean PM_LandingAnim(int anim) { - switch ( anim ) + switch (anim) { - case BOTH_LAND1: //# Landing (from in air loop) - case BOTH_LAND2: //# Landing Hard (from a great height) - case BOTH_LANDBACK1: //# Landing backwards(from in air loop) - case BOTH_LANDLEFT1: //# Landing left(from in air loop) - case BOTH_LANDRIGHT1: //# Landing right(from in air loop) - case BOTH_FORCELAND1: //# Landing (from in air loop) - case BOTH_FORCELANDBACK1: //# Landing backwards(from in air loop) - case BOTH_FORCELANDLEFT1: //# Landing left(from in air loop) - case BOTH_FORCELANDRIGHT1: //# Landing right(from in air loop) + case BOTH_LAND1: //# Landing (from in air loop) + case BOTH_LAND2: //# Landing Hard (from a great height) + case BOTH_LANDBACK1: //# Landing backwards(from in air loop) + case BOTH_LANDLEFT1: //# Landing left(from in air loop) + case BOTH_LANDRIGHT1: //# Landing right(from in air loop) + case BOTH_FORCELAND1: //# Landing (from in air loop) + case BOTH_FORCELANDBACK1: //# Landing backwards(from in air loop) + case BOTH_FORCELANDLEFT1: //# Landing left(from in air loop) + case BOTH_FORCELANDRIGHT1: //# Landing right(from in air loop) return qtrue; break; } return qfalse; } -qboolean PM_FlippingAnim( int anim ) +qboolean PM_FlippingAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_FLIP_F: //# Flip forward case BOTH_FLIP_B: //# Flip backwards @@ -6872,7 +6986,7 @@ qboolean PM_FlippingAnim( int anim ) case BOTH_FLIP_BACK3: case BOTH_WALL_FLIP_BACK1: case BOTH_ALORA_FLIP_B: - //Not really flips, but... + //Not really flips, but... case BOTH_WALL_RUN_RIGHT: case BOTH_WALL_RUN_LEFT: case BOTH_WALL_RUN_RIGHT_STOP: @@ -6881,7 +6995,7 @@ qboolean PM_FlippingAnim( int anim ) case BOTH_BUTTERFLY_RIGHT: case BOTH_BUTTERFLY_FL1: case BOTH_BUTTERFLY_FR1: - // + // case BOTH_ARIAL_LEFT: case BOTH_ARIAL_RIGHT: case BOTH_ARIAL_F1: @@ -6891,7 +7005,7 @@ qboolean PM_FlippingAnim( int anim ) case BOTH_JUMPFLIPSTABDOWN: case BOTH_JUMPATTACK6: case BOTH_JUMPATTACK7: - //JKA + //JKA case BOTH_FORCEWALLRUNFLIP_END: case BOTH_FORCEWALLRUNFLIP_ALT: case BOTH_FLIP_ATTACK7: @@ -6902,9 +7016,9 @@ qboolean PM_FlippingAnim( int anim ) return qfalse; } -qboolean PM_WalkingAnim( int anim ) +qboolean PM_WalkingAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_WALK1: //# Normal walk case BOTH_WALK2: //# Normal walk with saber @@ -6923,9 +7037,9 @@ qboolean PM_WalkingAnim( int anim ) return qfalse; } -qboolean PM_RunningAnim( int anim ) +qboolean PM_RunningAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_RUN1: case BOTH_RUN2: @@ -6945,9 +7059,9 @@ qboolean PM_RunningAnim( int anim ) return qfalse; } -qboolean PM_RollingAnim( int anim ) +qboolean PM_RollingAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_ROLL_F: //# Roll forward case BOTH_ROLL_B: //# Roll backward @@ -6967,9 +7081,9 @@ qboolean PM_RollingAnim( int anim ) return qfalse; } -qboolean PM_SwimmingAnim( int anim ) +qboolean PM_SwimmingAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_SWIM_IDLE1: //# Swimming Idle 1 case BOTH_SWIMFORWARD: //# Swim forward loop @@ -6980,11 +7094,11 @@ qboolean PM_SwimmingAnim( int anim ) return qfalse; } -qboolean PM_LeapingSaberAnim( int anim ) +qboolean PM_LeapingSaberAnim(int anim) { - switch ( anim ) + switch (anim) { - //level 7 + //level 7 case BOTH_T7_BR_TL: case BOTH_T7__L_BR: case BOTH_T7__L__R: @@ -6997,11 +7111,11 @@ qboolean PM_LeapingSaberAnim( int anim ) return qfalse; } -qboolean PM_SpinningSaberAnim( int anim ) +qboolean PM_SpinningSaberAnim(int anim) { - switch ( anim ) + switch (anim) { - //level 1 - FIXME: level 1 will have *no* spins + //level 1 - FIXME: level 1 will have *no* spins case BOTH_T1_BR_BL: case BOTH_T1__R__L: case BOTH_T1__R_BL: @@ -7014,28 +7128,28 @@ qboolean PM_SpinningSaberAnim( int anim ) case BOTH_T1_BL_BR: case BOTH_T1_BL__R: case BOTH_T1_BL_TR: - //level 2 + //level 2 case BOTH_T2_BR__L: case BOTH_T2_BR_BL: case BOTH_T2__R_BL: case BOTH_T2__L_BR: case BOTH_T2_BL_BR: case BOTH_T2_BL__R: - //level 3 + //level 3 case BOTH_T3_BR__L: case BOTH_T3_BR_BL: case BOTH_T3__R_BL: case BOTH_T3__L_BR: case BOTH_T3_BL_BR: case BOTH_T3_BL__R: - //level 4 + //level 4 case BOTH_T4_BR__L: case BOTH_T4_BR_BL: case BOTH_T4__R_BL: case BOTH_T4__L_BR: case BOTH_T4_BL_BR: case BOTH_T4_BL__R: - //level 5 + //level 5 case BOTH_T5_BR_BL: case BOTH_T5__R__L: case BOTH_T5__R_BL: @@ -7048,7 +7162,7 @@ qboolean PM_SpinningSaberAnim( int anim ) case BOTH_T5_BL_BR: case BOTH_T5_BL__R: case BOTH_T5_BL_TR: - //level 6 + //level 6 case BOTH_T6_BR_TL: case BOTH_T6__R_TL: case BOTH_T6__R__L: @@ -7071,7 +7185,7 @@ qboolean PM_SpinningSaberAnim( int anim ) case BOTH_T6_BL_BR: case BOTH_T6_BL__R: case BOTH_T6_BL_TR: - //level 7 + //level 7 case BOTH_T7_BR_TL: case BOTH_T7_BR__L: case BOTH_T7_BR_BL: @@ -7089,8 +7203,8 @@ qboolean PM_SpinningSaberAnim( int anim ) case BOTH_T7_T__BR: case BOTH_T7__L_TR: case BOTH_V7_BL_S7: - //special - //case BOTH_A2_STABBACK1: + //special + //case BOTH_A2_STABBACK1: case BOTH_ATTACK_BACK: case BOTH_CROUCHATTACKBACK1: case BOTH_BUTTERFLY_LEFT: @@ -7105,69 +7219,69 @@ qboolean PM_SpinningSaberAnim( int anim ) return qfalse; } -qboolean PM_SpinningAnim( int anim ) +qboolean PM_SpinningAnim(int anim) { /* switch ( anim ) { //FIXME: list any other spinning anims default: - break; + break; } */ - return PM_SpinningSaberAnim( anim ); + return PM_SpinningSaberAnim(anim); } -void PM_ResetAnkleAngles( void ) +void PM_ResetAnkleAngles(void) { - if ( !pm->gent || !pm->gent->client || pm->gent->client->NPC_class != CLASS_ATST ) + if (!pm->gent || !pm->gent->client || pm->gent->client->NPC_class != CLASS_ATST) { return; } - if ( pm->gent->footLBone != -1 ) + if (pm->gent->footLBone != -1) { - gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 ); + gi.G2API_SetBoneAnglesIndex(&pm->gent->ghoul2[0], pm->gent->footLBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0); } - if ( pm->gent->footRBone != -1 ) + if (pm->gent->footRBone != -1) { - gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 ); + gi.G2API_SetBoneAnglesIndex(&pm->gent->ghoul2[0], pm->gent->footRBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0); } } -void PM_AnglesForSlope( const float yaw, const vec3_t slope, vec3_t angles ) +void PM_AnglesForSlope(const float yaw, const vec3_t slope, vec3_t angles) { vec3_t nvf, ovf, ovr, new_angles; float pitch, mod, dot; - VectorSet( angles, 0, yaw, 0 ); - AngleVectors( angles, ovf, ovr, NULL ); + VectorSet(angles, 0, yaw, 0); + AngleVectors(angles, ovf, ovr, NULL); - vectoangles( slope, new_angles ); + vectoangles(slope, new_angles); pitch = new_angles[PITCH] + 90; new_angles[ROLL] = new_angles[PITCH] = 0; - AngleVectors( new_angles, nvf, NULL, NULL ); + AngleVectors(new_angles, nvf, NULL, NULL); - mod = DotProduct( nvf, ovr ); + mod = DotProduct(nvf, ovr); - if ( mod < 0 ) + if (mod < 0) mod = -1; else mod = 1; - dot = DotProduct( nvf, ovf ); + dot = DotProduct(nvf, ovf); angles[YAW] = 0; angles[PITCH] = dot * pitch; - angles[ROLL] = ((1-Q_fabs(dot)) * pitch * mod); + angles[ROLL] = ((1 - Q_fabs(dot)) * pitch * mod); } -void PM_FootSlopeTrace( float *pDiff, float *pInterval ) +void PM_FootSlopeTrace(float *pDiff, float *pInterval) { vec3_t footLOrg, footROrg, footLBot, footRBot; trace_t trace; float diff, interval; - if ( pm->gent->client->NPC_class == CLASS_ATST ) + if (pm->gent->client->NPC_class == CLASS_ATST) { interval = 10; } @@ -7176,29 +7290,29 @@ void PM_FootSlopeTrace( float *pDiff, float *pInterval ) interval = 4;//? } - if ( pm->gent->footLBolt == -1 || pm->gent->footRBolt == -1 ) + if (pm->gent->footLBolt == -1 || pm->gent->footRBolt == -1) { - if ( pDiff != NULL ) + if (pDiff != NULL) { *pDiff = 0; } - if ( pInterval != NULL ) + if (pInterval != NULL) { *pInterval = interval; } return; } #if 1 - for ( int i = 0; i < 3; i++ ) + for (int i = 0; i < 3; i++) { - if ( Q_isnan( pm->gent->client->renderInfo.footLPoint[i] ) - || Q_isnan( pm->gent->client->renderInfo.footRPoint[i] ) ) + if (Q_isnan(pm->gent->client->renderInfo.footLPoint[i]) + || Q_isnan(pm->gent->client->renderInfo.footRPoint[i])) { - if ( pDiff != NULL ) + if (pDiff != NULL) { *pDiff = 0; } - if ( pInterval != NULL ) + if (pInterval != NULL) { *pInterval = interval; } @@ -7209,104 +7323,104 @@ void PM_FootSlopeTrace( float *pDiff, float *pInterval ) //FIXME: these really should have been gotten on the cgame, but I guess sometimes they're not and we end up with qnan numbers! mdxaBone_t boltMatrix; - vec3_t G2Angles = {0, pm->gent->client->ps.legsYaw, 0}; + vec3_t G2Angles = { 0, pm->gent->client->ps.legsYaw, 0 }; //get the feet - gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footLBolt, - &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time), - NULL, pm->gent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footLPoint ); - - gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footRBolt, - &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time), - NULL, pm->gent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footRPoint ); + gi.G2API_GetBoltMatrix(pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footLBolt, + &boltMatrix, G2Angles, pm->ps->origin, (cg.time ? cg.time : level.time), + NULL, pm->gent->s.modelScale); + gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, pm->gent->client->renderInfo.footLPoint); + + gi.G2API_GetBoltMatrix(pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footRBolt, + &boltMatrix, G2Angles, pm->ps->origin, (cg.time ? cg.time : level.time), + NULL, pm->gent->s.modelScale); + gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, pm->gent->client->renderInfo.footRPoint); #endif //NOTE: on AT-STs, rotating the foot moves this point, so it will wiggle... // we have to do this extra work (more G2 transforms) to stop the wiggle... is it worth it? /* if ( pm->gent->client->NPC_class == CLASS_ATST ) { - mdxaBone_t boltMatrix; - vec3_t G2Angles = {0, pm->gent->client->ps.legsYaw, 0}; - //get the feet - gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL ); - gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footLBolt, - &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time), - NULL, pm->gent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footLPoint ); - - gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL ); - gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footRBolt, - &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time), - NULL, pm->gent->s.modelScale ); - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footRPoint ); + mdxaBone_t boltMatrix; + vec3_t G2Angles = {0, pm->gent->client->ps.legsYaw, 0}; + //get the feet + gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL ); + gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footLBolt, + &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time), + NULL, pm->gent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footLPoint ); + + gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, vec3_origin, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL ); + gi.G2API_GetBoltMatrix( pm->gent->ghoul2, pm->gent->playerModel, pm->gent->footRBolt, + &boltMatrix, G2Angles, pm->ps->origin, (cg.time?cg.time:level.time), + NULL, pm->gent->s.modelScale ); + gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, pm->gent->client->renderInfo.footRPoint ); } */ //get these on the cgame and store it, save ourselves a ghoul2 construct skel call - VectorCopy( pm->gent->client->renderInfo.footLPoint, footLOrg ); - VectorCopy( pm->gent->client->renderInfo.footRPoint, footROrg ); + VectorCopy(pm->gent->client->renderInfo.footLPoint, footLOrg); + VectorCopy(pm->gent->client->renderInfo.footRPoint, footROrg); //step 2: adjust foot tag z height to bottom of bbox+1 footLOrg[2] = pm->gent->currentOrigin[2] + pm->gent->mins[2] + 1; footROrg[2] = pm->gent->currentOrigin[2] + pm->gent->mins[2] + 1; - VectorSet( footLBot, footLOrg[0], footLOrg[1], footLOrg[2] - interval*10 ); - VectorSet( footRBot, footROrg[0], footROrg[1], footROrg[2] - interval*10 ); + VectorSet(footLBot, footLOrg[0], footLOrg[1], footLOrg[2] - interval * 10); + VectorSet(footRBot, footROrg[0], footROrg[1], footROrg[2] - interval * 10); //step 3: trace down from each, find difference vec3_t footMins, footMaxs; vec3_t footLSlope, footRSlope; - if ( pm->gent->client->NPC_class == CLASS_ATST ) + if (pm->gent->client->NPC_class == CLASS_ATST) { - VectorSet( footMins, -16, -16, 0 ); - VectorSet( footMaxs, 16, 16, 1 ); + VectorSet(footMins, -16, -16, 0); + VectorSet(footMaxs, 16, 16, 1); } else { - VectorSet( footMins, -3, -3, 0 ); - VectorSet( footMaxs, 3, 3, 1 ); + VectorSet(footMins, -3, -3, 0); + VectorSet(footMaxs, 3, 3, 1); } - pm->trace( &trace, footLOrg, footMins, footMaxs, footLBot, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 ); - VectorCopy( trace.endpos, footLBot ); - VectorCopy( trace.plane.normal, footLSlope ); + pm->trace(&trace, footLOrg, footMins, footMaxs, footLBot, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); + VectorCopy(trace.endpos, footLBot); + VectorCopy(trace.plane.normal, footLSlope); - pm->trace( &trace, footROrg, footMins, footMaxs, footRBot, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0 ); - VectorCopy( trace.endpos, footRBot ); - VectorCopy( trace.plane.normal, footRSlope ); + pm->trace(&trace, footROrg, footMins, footMaxs, footRBot, pm->ps->clientNum, pm->tracemask, (EG2_Collision)0, 0); + VectorCopy(trace.endpos, footRBot); + VectorCopy(trace.plane.normal, footRSlope); diff = footLBot[2] - footRBot[2]; //optional step: for atst, tilt the footpads to match the slopes under it... - if ( pm->gent->client->NPC_class == CLASS_ATST ) + if (pm->gent->client->NPC_class == CLASS_ATST) { vec3_t footAngles; - if ( !VectorCompare( footLSlope, vec3_origin ) ) + if (!VectorCompare(footLSlope, vec3_origin)) {//rotate the ATST's left foot pad to match the slope - PM_AnglesForSlope( pm->gent->client->renderInfo.legsYaw, footLSlope, footAngles ); + PM_AnglesForSlope(pm->gent->client->renderInfo.legsYaw, footLSlope, footAngles); //Hmm... lerp this? - gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footLBone, footAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 ); + gi.G2API_SetBoneAnglesIndex(&pm->gent->ghoul2[0], pm->gent->footLBone, footAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0); } - if ( !VectorCompare( footRSlope, vec3_origin ) ) + if (!VectorCompare(footRSlope, vec3_origin)) {//rotate the ATST's right foot pad to match the slope - PM_AnglesForSlope( pm->gent->client->renderInfo.legsYaw, footRSlope, footAngles ); + PM_AnglesForSlope(pm->gent->client->renderInfo.legsYaw, footRSlope, footAngles); //Hmm... lerp this? - gi.G2API_SetBoneAnglesIndex( &pm->gent->ghoul2[0], pm->gent->footRBone, footAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0 ); + gi.G2API_SetBoneAnglesIndex(&pm->gent->ghoul2[0], pm->gent->footRBone, footAngles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_Y, NEGATIVE_X, NULL, 0, 0); } } - if ( pDiff != NULL ) + if (pDiff != NULL) { *pDiff = diff; } - if ( pInterval != NULL ) + if (pInterval != NULL) { *pInterval = interval; } } -qboolean PM_InSlopeAnim( int anim ) +qboolean PM_InSlopeAnim(int anim) { - switch ( anim ) + switch (anim) { case LEGS_LEFTUP1: //# On a slope with left foot 4 higher than right case LEGS_LEFTUP2: //# On a slope with left foot 8 higher than right @@ -7384,9 +7498,9 @@ qboolean PM_InSlopeAnim( int anim ) return qfalse; } -qboolean PM_SaberStanceAnim( int anim ) +qboolean PM_SaberStanceAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_STAND1://not really a saberstance anim, actually... "saber off" stance case BOTH_STAND2://single-saber, medium style @@ -7400,9 +7514,9 @@ qboolean PM_SaberStanceAnim( int anim ) return qfalse; } -qboolean PM_SaberDrawPutawayAnim( int anim ) +qboolean PM_SaberDrawPutawayAnim(int anim) { - switch ( anim ) + switch (anim) { case BOTH_STAND1TO2: case BOTH_STAND2TO1: @@ -7417,71 +7531,71 @@ qboolean PM_SaberDrawPutawayAnim( int anim ) } #define SLOPE_RECALC_INT 100 -extern qboolean G_StandardHumanoid( gentity_t *self ); -qboolean PM_AdjustStandAnimForSlope( void ) +extern qboolean G_StandardHumanoid(gentity_t *self); +qboolean PM_AdjustStandAnimForSlope(void) { - if ( !pm->gent || !pm->gent->client ) + if (!pm->gent || !pm->gent->client) { return qfalse; } - if ( pm->gent->client->NPC_class != CLASS_ATST - && (!pm->gent||!G_StandardHumanoid( pm->gent )) ) + if (pm->gent->client->NPC_class != CLASS_ATST + && (!pm->gent || !G_StandardHumanoid(pm->gent))) {//only ATST and player does this return qfalse; } if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) - && (!cg.renderingThirdPerson || cg.zoomMode) ) + && (!BG_AllowThirdPersonSpecialMove( pm->ps )) ) {//first person doesn't do this return qfalse; } - if ( pm->gent->footLBolt == -1 || pm->gent->footRBolt == -1 ) + if (pm->gent->footLBolt == -1 || pm->gent->footRBolt == -1) {//need these bolts! return qfalse; } //step 1: find the 2 foot tags float diff; float interval; - PM_FootSlopeTrace( &diff, &interval ); + PM_FootSlopeTrace(&diff, &interval); //step 4: based on difference, choose one of the left/right slope-match intervals int destAnim; - if ( diff >= interval*5 ) + if (diff >= interval * 5) { destAnim = LEGS_LEFTUP5; } - else if ( diff >= interval*4 ) + else if (diff >= interval * 4) { destAnim = LEGS_LEFTUP4; } - else if ( diff >= interval*3 ) + else if (diff >= interval * 3) { destAnim = LEGS_LEFTUP3; } - else if ( diff >= interval*2 ) + else if (diff >= interval * 2) { destAnim = LEGS_LEFTUP2; } - else if ( diff >= interval ) + else if (diff >= interval) { destAnim = LEGS_LEFTUP1; } - else if ( diff <= interval*-5 ) + else if (diff <= interval*-5) { destAnim = LEGS_RIGHTUP5; } - else if ( diff <= interval*-4 ) + else if (diff <= interval*-4) { destAnim = LEGS_RIGHTUP4; } - else if ( diff <= interval*-3 ) + else if (diff <= interval*-3) { destAnim = LEGS_RIGHTUP3; } - else if ( diff <= interval*-2 ) + else if (diff <= interval*-2) { destAnim = LEGS_RIGHTUP2; } - else if ( diff <= interval*-1 ) + else if (diff <= interval*-1) { destAnim = LEGS_RIGHTUP1; } @@ -7491,10 +7605,10 @@ qboolean PM_AdjustStandAnimForSlope( void ) } int legsAnim = pm->ps->legsAnim; - if ( pm->gent->client->NPC_class != CLASS_ATST ) + if (pm->gent->client->NPC_class != CLASS_ATST) { //adjust for current legs anim - switch ( legsAnim ) + switch (legsAnim) { case BOTH_STAND1: case LEGS_S1_LUP1: @@ -7507,7 +7621,7 @@ qboolean PM_AdjustStandAnimForSlope( void ) case LEGS_S1_RUP3: case LEGS_S1_RUP4: case LEGS_S1_RUP5: - destAnim = LEGS_S1_LUP1 + (destAnim-LEGS_LEFTUP1); + destAnim = LEGS_S1_LUP1 + (destAnim - LEGS_LEFTUP1); break; case BOTH_STAND2: case BOTH_SABERFAST_STANCE: @@ -7537,7 +7651,7 @@ qboolean PM_AdjustStandAnimForSlope( void ) case LEGS_S3_RUP3: case LEGS_S3_RUP4: case LEGS_S3_RUP5: - destAnim = LEGS_S3_LUP1 + (destAnim-LEGS_LEFTUP1); + destAnim = LEGS_S3_LUP1 + (destAnim - LEGS_LEFTUP1); break; case BOTH_STAND4: case LEGS_S4_LUP1: @@ -7550,7 +7664,7 @@ qboolean PM_AdjustStandAnimForSlope( void ) case LEGS_S4_RUP3: case LEGS_S4_RUP4: case LEGS_S4_RUP5: - destAnim = LEGS_S4_LUP1 + (destAnim-LEGS_LEFTUP1); + destAnim = LEGS_S4_LUP1 + (destAnim - LEGS_LEFTUP1); break; case BOTH_STAND5: case LEGS_S5_LUP1: @@ -7563,7 +7677,7 @@ qboolean PM_AdjustStandAnimForSlope( void ) case LEGS_S5_RUP3: case LEGS_S5_RUP4: case LEGS_S5_RUP5: - destAnim = LEGS_S5_LUP1 + (destAnim-LEGS_LEFTUP1); + destAnim = LEGS_S5_LUP1 + (destAnim - LEGS_LEFTUP1); break; case BOTH_SABERDUAL_STANCE: case LEGS_S6_LUP1: @@ -7576,7 +7690,7 @@ qboolean PM_AdjustStandAnimForSlope( void ) case LEGS_S6_RUP3: case LEGS_S6_RUP4: case LEGS_S6_RUP5: - destAnim = LEGS_S6_LUP1 + (destAnim-LEGS_LEFTUP1); + destAnim = LEGS_S6_LUP1 + (destAnim - LEGS_LEFTUP1); break; case BOTH_SABERSTAFF_STANCE: case LEGS_S7_LUP1: @@ -7589,7 +7703,7 @@ qboolean PM_AdjustStandAnimForSlope( void ) case LEGS_S7_RUP3: case LEGS_S7_RUP4: case LEGS_S7_RUP5: - destAnim = LEGS_S7_LUP1 + (destAnim-LEGS_LEFTUP1); + destAnim = LEGS_S7_LUP1 + (destAnim - LEGS_LEFTUP1); break; case BOTH_STAND6: default: @@ -7599,20 +7713,20 @@ qboolean PM_AdjustStandAnimForSlope( void ) } //step 5: based on the chosen interval and the current legsAnim, pick the correct anim //step 6: increment/decrement to the dest anim, not instant - if ( (legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5) + if ((legsAnim >= LEGS_LEFTUP1 && legsAnim <= LEGS_LEFTUP5) || (legsAnim >= LEGS_S1_LUP1 && legsAnim <= LEGS_S1_LUP5) || (legsAnim >= LEGS_S3_LUP1 && legsAnim <= LEGS_S3_LUP5) || (legsAnim >= LEGS_S4_LUP1 && legsAnim <= LEGS_S4_LUP5) || (legsAnim >= LEGS_S5_LUP1 && legsAnim <= LEGS_S5_LUP5) || (legsAnim >= LEGS_S6_LUP1 && legsAnim <= LEGS_S6_LUP5) - || (legsAnim >= LEGS_S7_LUP1 && legsAnim <= LEGS_S7_LUP5) ) + || (legsAnim >= LEGS_S7_LUP1 && legsAnim <= LEGS_S7_LUP5)) {//already in left-side up - if ( destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time ) + if (destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time) { legsAnim++; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim < legsAnim && pm->gent->client->slopeRecalcTime < level.time ) + else if (destAnim < legsAnim && pm->gent->client->slopeRecalcTime < level.time) { legsAnim--; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7622,20 +7736,20 @@ qboolean PM_AdjustStandAnimForSlope( void ) destAnim = legsAnim; } } - else if ( (legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5) + else if ((legsAnim >= LEGS_RIGHTUP1 && legsAnim <= LEGS_RIGHTUP5) || (legsAnim >= LEGS_S1_RUP1 && legsAnim <= LEGS_S1_RUP5) || (legsAnim >= LEGS_S3_RUP1 && legsAnim <= LEGS_S3_RUP5) || (legsAnim >= LEGS_S4_RUP1 && legsAnim <= LEGS_S4_RUP5) || (legsAnim >= LEGS_S5_RUP1 && legsAnim <= LEGS_S5_RUP5) || (legsAnim >= LEGS_S6_RUP1 && legsAnim <= LEGS_S6_RUP5) - || (legsAnim >= LEGS_S7_RUP1 && legsAnim <= LEGS_S7_RUP5) ) + || (legsAnim >= LEGS_S7_RUP1 && legsAnim <= LEGS_S7_RUP5)) {//already in right-side up - if ( destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time ) + if (destAnim > legsAnim && pm->gent->client->slopeRecalcTime < level.time) { legsAnim++; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim < legsAnim && pm->gent->client->slopeRecalcTime < level.time ) + else if (destAnim < legsAnim && pm->gent->client->slopeRecalcTime < level.time) { legsAnim--; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7647,16 +7761,16 @@ qboolean PM_AdjustStandAnimForSlope( void ) } else {//in a stand of some sort? - if ( pm->gent->client->NPC_class == CLASS_ATST ) + if (pm->gent->client->NPC_class == CLASS_ATST) { - if ( legsAnim == BOTH_STAND1 || legsAnim == BOTH_STAND2 || legsAnim == BOTH_CROUCH1IDLE ) + if (legsAnim == BOTH_STAND1 || legsAnim == BOTH_STAND2 || legsAnim == BOTH_CROUCH1IDLE) { - if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 ) + if (destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5) {//going into left side up destAnim = LEGS_LEFTUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 ) + else if (destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5) {//going into right side up destAnim = LEGS_RIGHTUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7669,15 +7783,15 @@ qboolean PM_AdjustStandAnimForSlope( void ) } else { - switch ( legsAnim ) + switch (legsAnim) { case BOTH_STAND1: - if ( destAnim >= LEGS_S1_LUP1 && destAnim <= LEGS_S1_LUP5 ) + if (destAnim >= LEGS_S1_LUP1 && destAnim <= LEGS_S1_LUP5) {//going into left side up destAnim = LEGS_S1_LUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim >= LEGS_S1_RUP1 && destAnim <= LEGS_S1_RUP5 ) + else if (destAnim >= LEGS_S1_RUP1 && destAnim <= LEGS_S1_RUP5) {//going into right side up destAnim = LEGS_S1_RUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7691,12 +7805,12 @@ qboolean PM_AdjustStandAnimForSlope( void ) case BOTH_SABERFAST_STANCE: case BOTH_SABERSLOW_STANCE: case BOTH_CROUCH1IDLE: - if ( destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5 ) + if (destAnim >= LEGS_LEFTUP1 && destAnim <= LEGS_LEFTUP5) {//going into left side up destAnim = LEGS_LEFTUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5 ) + else if (destAnim >= LEGS_RIGHTUP1 && destAnim <= LEGS_RIGHTUP5) {//going into right side up destAnim = LEGS_RIGHTUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7707,12 +7821,12 @@ qboolean PM_AdjustStandAnimForSlope( void ) } break; case BOTH_STAND3: - if ( destAnim >= LEGS_S3_LUP1 && destAnim <= LEGS_S3_LUP5 ) + if (destAnim >= LEGS_S3_LUP1 && destAnim <= LEGS_S3_LUP5) {//going into left side up destAnim = LEGS_S3_LUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim >= LEGS_S3_RUP1 && destAnim <= LEGS_S3_RUP5 ) + else if (destAnim >= LEGS_S3_RUP1 && destAnim <= LEGS_S3_RUP5) {//going into right side up destAnim = LEGS_S3_RUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7723,12 +7837,12 @@ qboolean PM_AdjustStandAnimForSlope( void ) } break; case BOTH_STAND4: - if ( destAnim >= LEGS_S4_LUP1 && destAnim <= LEGS_S4_LUP5 ) + if (destAnim >= LEGS_S4_LUP1 && destAnim <= LEGS_S4_LUP5) {//going into left side up destAnim = LEGS_S4_LUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim >= LEGS_S4_RUP1 && destAnim <= LEGS_S4_RUP5 ) + else if (destAnim >= LEGS_S4_RUP1 && destAnim <= LEGS_S4_RUP5) {//going into right side up destAnim = LEGS_S4_RUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7739,12 +7853,12 @@ qboolean PM_AdjustStandAnimForSlope( void ) } break; case BOTH_STAND5: - if ( destAnim >= LEGS_S5_LUP1 && destAnim <= LEGS_S5_LUP5 ) + if (destAnim >= LEGS_S5_LUP1 && destAnim <= LEGS_S5_LUP5) {//going into left side up destAnim = LEGS_S5_LUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim >= LEGS_S5_RUP1 && destAnim <= LEGS_S5_RUP5 ) + else if (destAnim >= LEGS_S5_RUP1 && destAnim <= LEGS_S5_RUP5) {//going into right side up destAnim = LEGS_S5_RUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7755,12 +7869,12 @@ qboolean PM_AdjustStandAnimForSlope( void ) } break; case BOTH_SABERDUAL_STANCE: - if ( destAnim >= LEGS_S6_LUP1 && destAnim <= LEGS_S6_LUP5 ) + if (destAnim >= LEGS_S6_LUP1 && destAnim <= LEGS_S6_LUP5) {//going into left side up destAnim = LEGS_S6_LUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim >= LEGS_S6_RUP1 && destAnim <= LEGS_S6_RUP5 ) + else if (destAnim >= LEGS_S6_RUP1 && destAnim <= LEGS_S6_RUP5) {//going into right side up destAnim = LEGS_S6_RUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7771,12 +7885,12 @@ qboolean PM_AdjustStandAnimForSlope( void ) } break; case BOTH_SABERSTAFF_STANCE: - if ( destAnim >= LEGS_S7_LUP1 && destAnim <= LEGS_S7_LUP5 ) + if (destAnim >= LEGS_S7_LUP1 && destAnim <= LEGS_S7_LUP5) {//going into left side up destAnim = LEGS_S7_LUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; } - else if ( destAnim >= LEGS_S7_RUP1 && destAnim <= LEGS_S7_RUP5 ) + else if (destAnim >= LEGS_S7_RUP1 && destAnim <= LEGS_S7_RUP5) {//going into right side up destAnim = LEGS_S7_RUP1; pm->gent->client->slopeRecalcTime = level.time + SLOPE_RECALC_INT; @@ -7794,108 +7908,108 @@ qboolean PM_AdjustStandAnimForSlope( void ) } } //step 7: set the anim - PM_SetAnim( pm, SETANIM_LEGS, destAnim, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_LEGS, destAnim, SETANIM_FLAG_NORMAL); return qtrue; } -void PM_JetPackAnim( void ) +void PM_JetPackAnim(void) { - if ( !PM_ForceJumpingAnim( pm->ps->legsAnim ) )//haven't started forcejump yet + if (!PM_ForceJumpingAnim(pm->ps->legsAnim))//haven't started forcejump yet { - vec3_t facingFwd, facingRight, facingAngles = {0, pm->ps->viewangles[YAW], 0}; + vec3_t facingFwd, facingRight, facingAngles = { 0, pm->ps->viewangles[YAW], 0 }; int anim = BOTH_FORCEJUMP1; - AngleVectors( facingAngles, facingFwd, facingRight, NULL ); - float dotR = DotProduct( facingRight, pm->ps->velocity ); - float dotF = DotProduct( facingFwd, pm->ps->velocity ); - if ( fabs(dotR) > fabs(dotF) * 1.5 ) + AngleVectors(facingAngles, facingFwd, facingRight, NULL); + float dotR = DotProduct(facingRight, pm->ps->velocity); + float dotF = DotProduct(facingFwd, pm->ps->velocity); + if (fabs(dotR) > fabs(dotF) * 1.5) { - if ( dotR > 150 ) + if (dotR > 150) { anim = BOTH_FORCEJUMPRIGHT1; } - else if ( dotR < -150 ) + else if (dotR < -150) { anim = BOTH_FORCEJUMPLEFT1; } } else { - if ( dotF > 150 ) + if (dotF > 150) { anim = BOTH_FORCEJUMP1; } - else if ( dotF < -150 ) + else if (dotF < -150) { anim = BOTH_FORCEJUMPBACK1; } } int parts = SETANIM_BOTH; - if ( pm->ps->weaponTime ) + if (pm->ps->weaponTime) {//FIXME: really only care if we're in a saber attack anim... parts = SETANIM_LEGS; } - PM_SetAnim( pm, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, parts, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } /* else { - if ( !pm->ps->legsAnimTimer ) - {//not in the middle of a legsAnim - int anim = pm->ps->legsAnim; - int newAnim = -1; - switch ( anim ) - { - case BOTH_FORCEJUMP1: - newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1; - break; - case BOTH_FORCEJUMPBACK1: - newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1; - break; - case BOTH_FORCEJUMPLEFT1: - newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1; - break; - case BOTH_FORCEJUMPRIGHT1: - newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1; - break; - } - if ( newAnim != -1 ) - { - int parts = SETANIM_BOTH; - if ( pm->ps->weaponTime ) - {//FIXME: really only care if we're in a saber attack anim... - parts = SETANIM_LEGS; - } + if ( !pm->ps->legsAnimTimer ) + {//not in the middle of a legsAnim + int anim = pm->ps->legsAnim; + int newAnim = -1; + switch ( anim ) + { + case BOTH_FORCEJUMP1: + newAnim = BOTH_FORCELAND1;//BOTH_FORCEINAIR1; + break; + case BOTH_FORCEJUMPBACK1: + newAnim = BOTH_FORCELANDBACK1;//BOTH_FORCEINAIRBACK1; + break; + case BOTH_FORCEJUMPLEFT1: + newAnim = BOTH_FORCELANDLEFT1;//BOTH_FORCEINAIRLEFT1; + break; + case BOTH_FORCEJUMPRIGHT1: + newAnim = BOTH_FORCELANDRIGHT1;//BOTH_FORCEINAIRRIGHT1; + break; + } + if ( newAnim != -1 ) + { + int parts = SETANIM_BOTH; + if ( pm->ps->weaponTime ) + {//FIXME: really only care if we're in a saber attack anim... + parts = SETANIM_LEGS; + } - PM_SetAnim( pm, parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - } - } + PM_SetAnim( pm, parts, newAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + } + } } */ } -void PM_SwimFloatAnim( void ) +void PM_SwimFloatAnim(void) { int legsAnim = pm->ps->legsAnim; //FIXME: no start or stop anims - if ( pm->cmd.forwardmove || pm->cmd.rightmove || pm->cmd.upmove ) + if (pm->cmd.forwardmove || pm->cmd.rightmove || pm->cmd.upmove) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIMFORWARD,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_SWIMFORWARD, SETANIM_FLAG_NORMAL); } else {//stopping - if ( legsAnim == BOTH_SWIMFORWARD ) + if (legsAnim == BOTH_SWIMFORWARD) {//I was swimming - if ( !pm->ps->legsAnimTimer ) + if (!pm->ps->legsAnimTimer) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_SWIM_IDLE1, SETANIM_FLAG_NORMAL); } } else {//idle - if ( !(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0 ) + if (!(pm->ps->pm_flags&PMF_DUCKED) && pm->cmd.upmove >= 0) {//not crouching - PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_SWIM_IDLE1, SETANIM_FLAG_NORMAL); } } } @@ -7906,7 +8020,7 @@ void PM_SwimFloatAnim( void ) PM_Footsteps =============== */ -static void PM_Footsteps( void ) +static void PM_Footsteps(void) { float bobmove; int old, oldAnim; @@ -7915,19 +8029,19 @@ static void PM_Footsteps( void ) qboolean flipping = qfalse; int setAnimFlags = SETANIM_FLAG_NORMAL; - if( pm->gent == NULL || pm->gent->client == NULL ) + if (pm->gent == NULL || pm->gent->client == NULL) return; - if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + if ((pm->ps->eFlags&EF_HELD_BY_WAMPA)) { - PM_SetAnim( pm, SETANIM_BOTH, BOTH_HANG_IDLE, SETANIM_FLAG_NORMAL ); - if ( pm->ps->legsAnim == BOTH_HANG_IDLE ) + PM_SetAnim(pm, SETANIM_BOTH, BOTH_HANG_IDLE, SETANIM_FLAG_NORMAL); + if (pm->ps->legsAnim == BOTH_HANG_IDLE) { - if ( pm->ps->torsoAnimTimer < 100 ) + if (pm->ps->torsoAnimTimer < 100) { pm->ps->torsoAnimTimer = 100; } - if ( pm->ps->legsAnimTimer < 100 ) + if (pm->ps->legsAnimTimer < 100) { pm->ps->legsAnimTimer = 100; } @@ -7935,21 +8049,21 @@ static void PM_Footsteps( void ) return; } - if ( PM_SpinningSaberAnim( pm->ps->legsAnim ) && pm->ps->legsAnimTimer ) + if (PM_SpinningSaberAnim(pm->ps->legsAnim) && pm->ps->legsAnimTimer) {//spinning return; } - if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps )) + if (PM_InKnockDown(pm->ps) || PM_InRoll(pm->ps)) {//in knockdown return; } - if ( (pm->ps->eFlags&EF_FORCE_DRAINED) ) + if ((pm->ps->eFlags&EF_FORCE_DRAINED)) {//being drained //PM_SetAnim( pm, SETANIM_LEGS, BOTH_HUGGEE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); return; } - if ( (pm->ps->forcePowersActive&(1<ps->forceDrainEntityNum < ENTITYNUM_WORLD ) + if ((pm->ps->forcePowersActive&(1 << FP_DRAIN)) + && pm->ps->forceDrainEntityNum < ENTITYNUM_WORLD) {//draining //PM_SetAnim( pm, SETANIM_LEGS, BOTH_HUGGER1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); return; @@ -7959,7 +8073,7 @@ static void PM_Footsteps( void ) return; } - if( pm->gent->NPC != NULL ) + if (pm->gent->NPC != NULL) { validNPC = qtrue; } @@ -7971,34 +8085,34 @@ static void PM_Footsteps( void ) // calculate speed and cycle to be used for // all cyclic walking effects // - pm->xyspeed = sqrt( pm->ps->velocity[0] * pm->ps->velocity[0] - + pm->ps->velocity[1] * pm->ps->velocity[1] ); + pm->xyspeed = sqrt(pm->ps->velocity[0] * pm->ps->velocity[0] + + pm->ps->velocity[1] * pm->ps->velocity[1]); - if ( pm->ps->legsAnim == BOTH_FLIP_F || + if (pm->ps->legsAnim == BOTH_FLIP_F || pm->ps->legsAnim == BOTH_FLIP_B || pm->ps->legsAnim == BOTH_FLIP_L || pm->ps->legsAnim == BOTH_FLIP_R || pm->ps->legsAnim == BOTH_ALORA_FLIP_1 || pm->ps->legsAnim == BOTH_ALORA_FLIP_2 || - pm->ps->legsAnim == BOTH_ALORA_FLIP_3 ) + pm->ps->legsAnim == BOTH_ALORA_FLIP_3) { flipping = qtrue; } - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE - || ( pm->watertype & CONTENTS_LADDER ) - || pm->ps->waterHeightLevel >= WHL_TORSO ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE + || (pm->watertype & CONTENTS_LADDER) + || pm->ps->waterHeightLevel >= WHL_TORSO) {//in air or submerged in water or in ladder // airborne leaves position in cycle intact, but doesn't advance - if ( pm->waterlevel > 0 ) + if (pm->waterlevel > 0) { - if ( pm->watertype & CONTENTS_LADDER ) + if (pm->watertype & CONTENTS_LADDER) {//FIXME: check for watertype, save waterlevel for whether to play //the get off ladder transition anim! - if ( pm->ps->velocity[2] ) + if (pm->ps->velocity[2]) {//going up or down it int anim; - if ( pm->ps->velocity[2] > 0 ) + if (pm->ps->velocity[2] > 0) { anim = BOTH_LADDER_UP1; } @@ -8006,10 +8120,10 @@ static void PM_Footsteps( void ) { anim = BOTH_LADDER_DWN1; } - PM_SetAnim( pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - if ( pm->waterlevel >= 2 ) //arms on ladder + PM_SetAnim(pm, SETANIM_LEGS, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); + if (pm->waterlevel >= 2) //arms on ladder { - PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } if (fabs(pm->ps->velocity[2]) >5) { bobmove = 0.005 * fabs(pm->ps->velocity[2]); // climbing bobs slow @@ -8020,55 +8134,55 @@ static void PM_Footsteps( void ) } else { - PM_SetAnim( pm, SETANIM_LEGS, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); pm->ps->legsAnimTimer += 300; - if ( pm->waterlevel >= 2 ) //arms on ladder + if (pm->waterlevel >= 2) //arms on ladder { - PM_SetAnim( pm, SETANIM_TORSO, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_LADDER_IDLE, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); pm->ps->torsoAnimTimer += 300; } } return; } - else if ( pm->ps->waterHeightLevel >= WHL_TORSO - && ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) - ||pm->ps->weapon==WP_SABER||pm->ps->weapon==WP_NONE||pm->ps->weapon==WP_MELEE) )//pm->waterlevel > 1 ) //in deep water + else if (pm->ps->waterHeightLevel >= WHL_TORSO + && ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + || pm->ps->weapon == WP_SABER || pm->ps->weapon == WP_NONE || pm->ps->weapon == WP_MELEE))//pm->waterlevel > 1 ) //in deep water { - if ( !PM_ForceJumpingUp( pm->gent ) ) + if (!PM_ForceJumpingUp(pm->gent)) { - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE && (pm->ps->pm_flags&PMF_DUCKED) ) + if (pm->ps->groundEntityNum != ENTITYNUM_NONE && (pm->ps->pm_flags&PMF_DUCKED)) { - if ( !flipping ) + if (!flipping) {//you can crouch under water if feet are on ground - if ( pm->cmd.forwardmove || pm->cmd.rightmove ) + if (pm->cmd.forwardmove || pm->cmd.rightmove) { - if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) + if (pm->ps->pm_flags & PMF_BACKWARDS_RUN) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALKBACK,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags); } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALK,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags); } } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_CROUCH1, SETANIM_FLAG_NORMAL); } return; } } PM_SwimFloatAnim(); - if ( pm->ps->legsAnim != BOTH_SWIM_IDLE1 ) + if (pm->ps->legsAnim != BOTH_SWIM_IDLE1) {//moving old = pm->ps->bobCycle; bobmove = 0.15f; // swim is a slow cycle - pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255; + pm->ps->bobCycle = (int)(old + bobmove * pml.msec) & 255; // if we just crossed a cycle boundary, play an apropriate footstep event - if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) + if (((old + 64) ^ (pm->ps->bobCycle + 64)) & 128) { - PM_AddEvent( EV_SWIM ); + PM_AddEvent(EV_SWIM); } } } @@ -8077,22 +8191,22 @@ static void PM_Footsteps( void ) else {//hmm, in water, but not high enough to swim //fall through to walk/run/stand - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE) {//unless in the air //NOTE: this is a dupe of the code just below... for when you are not in the water at all - if ( pm->ps->pm_flags & PMF_DUCKED ) + if (pm->ps->pm_flags & PMF_DUCKED) { - if ( !flipping ) + if (!flipping) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_CROUCH1, SETANIM_FLAG_NORMAL); } } - else if ( pm->ps->gravity <= 0 )//FIXME: or just less than normal? + else if (pm->ps->gravity <= 0)//FIXME: or just less than normal? { - if ( pm->gent + if (pm->gent && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_BOBAFETT ||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) - && pm->gent->client->moveType == MT_FLYSWIM ) + && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_MANDA || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) + && pm->gent->client->moveType == MT_FLYSWIM) {//flying around with jetpack //do something else? PM_JetPackAnim(); @@ -8108,19 +8222,19 @@ static void PM_Footsteps( void ) } else { - if ( pm->ps->pm_flags & PMF_DUCKED ) + if (pm->ps->pm_flags & PMF_DUCKED) { - if ( !flipping ) + if (!flipping) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_CROUCH1, SETANIM_FLAG_NORMAL); } } - else if ( pm->ps->gravity <= 0 )//FIXME: or just less than normal? + else if (pm->ps->gravity <= 0)//FIXME: or just less than normal? { - if ( pm->gent + if (pm->gent && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_BOBAFETT||pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) - && pm->gent->client->moveType == MT_FLYSWIM ) + && (pm->gent->client->NPC_class == CLASS_BOBAFETT || pm->gent->client->NPC_class == CLASS_MANDA || pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) + && pm->gent->client->moveType == MT_FLYSWIM) {//flying around with jetpack //do something else? PM_JetPackAnim(); @@ -8134,101 +8248,103 @@ static void PM_Footsteps( void ) } } - if ( PM_SwimmingAnim( pm->ps->legsAnim ) && pm->waterlevel < 2 ) + if (PM_SwimmingAnim(pm->ps->legsAnim) && pm->waterlevel < 2) {//legs are in swim anim, and not swimming, be sure to override it setAnimFlags |= SETANIM_FLAG_OVERRIDE; } // if not trying to move - if ( !pm->cmd.forwardmove && !pm->cmd.rightmove ) + if (!pm->cmd.forwardmove && !pm->cmd.rightmove) { - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST) { - if ( !PM_AdjustStandAnimForSlope() ) + if (!PM_AdjustStandAnimForSlope()) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } - else if ( pm->ps->pm_flags & PMF_DUCKED ) + else if (pm->ps->pm_flags & PMF_DUCKED) { - if( !PM_InOnGroundAnim( pm->ps ) ) + if (!PM_InOnGroundAnim(pm->ps)) { - if ( !PM_AdjustStandAnimForSlope() ) + if (!PM_AdjustStandAnimForSlope()) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_CROUCH1, SETANIM_FLAG_NORMAL); } } } else { - if ( pm->ps->legsAnimTimer && PM_LandingAnim( pm->ps->legsAnim ) ) + if (pm->ps->legsAnimTimer && PM_LandingAnim(pm->ps->legsAnim)) {//still in a landing anim, let it play return; } - if ( pm->ps->legsAnimTimer + if (pm->ps->legsAnimTimer && (pm->ps->legsAnim == BOTH_THERMAL_READY - ||pm->ps->legsAnim == BOTH_THERMAL_THROW - ||pm->ps->legsAnim == BOTH_ATTACK10) ) + || pm->ps->legsAnim == BOTH_THERMAL_THROW + || pm->ps->legsAnim == BOTH_ATTACK10)) {//still in a thermal anim, let it play return; } qboolean saberInAir = qtrue; - if ( pm->ps->saberInFlight ) + if (pm->ps->saberInFlight) {//guiding saber - if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + if (PM_SaberInBrokenParry(pm->ps->saberMove) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim(pm->ps->torsoAnim)) {//we're stuck in a broken parry saberInAir = qfalse; } - if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + if (pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0)//player is 0 {// - if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + if (&g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY) {//fell to the ground and we're not trying to pull it back saberInAir = qfalse; } } } - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH) {//NOTE: stand1 is with the helmet retracted, stand1to2 is the helmet going into place - PM_SetAnim( pm, SETANIM_BOTH, BOTH_STAND2, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_STAND2, SETANIM_FLAG_NORMAL); } - else if ( pm->ps->weapon == WP_SABER + else if (pm->ps->weapon == WP_SABER && pm->ps->saberInFlight && saberInAir && (!pm->ps->dualSabers || !pm->ps->saber[1].Active())) { - if ( !PM_AdjustStandAnimForSlope() ) + if (!PM_AdjustStandAnimForSlope()) { - if ( pm->ps->legsAnim != BOTH_LOSE_SABER - || !pm->ps->legsAnimTimer ) + if (pm->ps->legsAnim != BOTH_LOSE_SABER + || !pm->ps->legsAnimTimer) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_SABERPULL,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_SABERPULL, SETANIM_FLAG_NORMAL); } } } - else if ( (pm->ps->weapon == WP_SABER + else if ((pm->ps->weapon == WP_SABER &&pm->ps->SaberLength()>0 + && (pm->ps->SaberActive() || !g_noIgniteTwirl->integer) &&!pm->ps->saberInFlight &&!PM_SaberDrawPutawayAnim( pm->ps->legsAnim )) ) { - if ( !PM_AdjustStandAnimForSlope() ) + if (!PM_AdjustStandAnimForSlope()) { int legsAnim; - if ( (pm->ps->torsoAnim == BOTH_SPINATTACK6 - || pm->ps->torsoAnim == BOTH_SPINATTACK7 - || PM_SaberInAttack( pm->ps->saberMove ) - || PM_SaberInTransitionAny( pm->ps->saberMove )) + if ((pm->ps->torsoAnim == BOTH_SPINATTACK6 + || pm->ps->torsoAnim == BOTH_SPINATTACK7 + || PM_SaberInAttack(pm->ps->saberMove) + || PM_SaberInTransitionAny(pm->ps->saberMove)) && pm->ps->legsAnim != BOTH_FORCELONGLEAP_LAND && (pm->ps->groundEntityNum == ENTITYNUM_NONE//in air - || (!PM_JumpingAnim( pm->ps->torsoAnim )&&!PM_InAirKickingAnim( pm->ps->torsoAnim ))) )//OR: on ground and torso not in a jump anim + || (!PM_JumpingAnim(pm->ps->torsoAnim) && !PM_InAirKickingAnim(pm->ps->torsoAnim))))//OR: on ground and torso not in a jump anim { legsAnim = pm->ps->torsoAnim; } else { - switch ( pm->ps->saberAnimLevel ) + switch (pm->ps->saberAnimLevel) { case SS_FAST: case SS_TAVION: + case SS_KATARN: legsAnim = BOTH_SABERFAST_STANCE; break; case SS_STRONG: @@ -8248,19 +8364,20 @@ static void PM_Footsteps( void ) break; } } - PM_SetAnim(pm,SETANIM_LEGS,legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, legsAnim, SETANIM_FLAG_NORMAL); } } - else if( (validNPC && pm->ps->weapon > WP_SABER && pm->ps->weapon < WP_DET_PACK ))// && pm->gent->client->race != RACE_BORG))//Being careful or carrying a 2-handed weapon + + else if( (validNPC && pm->ps->weapon > WP_SABER && pm->ps->weapon < WP_DET_PACK ))//Being careful or carrying a 2-handed weapon {//Squadmates use BOTH_STAND3 oldAnim = pm->ps->legsAnim; - if(oldAnim != BOTH_GUARD_LOOKAROUND1 && oldAnim != BOTH_GUARD_IDLE1 + if (oldAnim != BOTH_GUARD_LOOKAROUND1 && oldAnim != BOTH_GUARD_IDLE1 && oldAnim != BOTH_STAND2TO4 - && oldAnim != BOTH_STAND4TO2 && oldAnim != BOTH_STAND4 ) + && oldAnim != BOTH_STAND4TO2 && oldAnim != BOTH_STAND4) {//Don't auto-override the guard idles - if ( !PM_AdjustStandAnimForSlope() ) + if (!PM_AdjustStandAnimForSlope()) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND3,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND3, SETANIM_FLAG_NORMAL); //if(oldAnim != BOTH_STAND2 && pm->ps->legsAnim == BOTH_STAND2) //{ // pm->ps->legsAnimTimer = 500; @@ -8270,48 +8387,48 @@ static void PM_Footsteps( void ) } else { - if ( !PM_AdjustStandAnimForSlope() ) + if (!PM_AdjustStandAnimForSlope()) { // FIXME: Do we need this here... The imps stand is 4, not 1... - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_IMPERIAL ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_IMPERIAL) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND4, SETANIM_FLAG_NORMAL); } - else if ( pm->ps->weapon == WP_TUSKEN_STAFF ) + else if (pm->ps->weapon == WP_TUSKEN_STAFF) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND1, SETANIM_FLAG_NORMAL); } else { - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR) { - if ( pm->gent->count ) + if (pm->gent->count) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND4,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND4, SETANIM_FLAG_NORMAL); } - else if ( pm->gent->enemy || pm->gent->wait ) + else if (pm->gent->enemy || pm->gent->wait) {//have an enemy or have had one since we spawned - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND2, SETANIM_FLAG_NORMAL); } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA) { - if ( pm->gent->count ) + if (pm->gent->count) {//holding a victim - PM_SetAnim(pm,SETANIM_LEGS,BOTH_HOLD_IDLE/*BOTH_STAND2*/,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_HOLD_IDLE/*BOTH_STAND2*/, SETANIM_FLAG_NORMAL); } else {//not holding a victim - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } } @@ -8321,27 +8438,27 @@ static void PM_Footsteps( void ) } //maybe call this every frame, even when moving? - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST) { - PM_FootSlopeTrace( NULL, NULL ); + PM_FootSlopeTrace(NULL, NULL); } //trying to move laterally - if ( (pm->ps->eFlags&EF_IN_ATST) - || (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class==CLASS_RANCOR)//does this catch NPCs, too? - || (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class==CLASS_WAMPA) )//does this catch NPCs, too? + if ((pm->ps->eFlags&EF_IN_ATST) + || (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class == CLASS_RANCOR)//does this catch NPCs, too? + || (pm->gent&&pm->gent->client&&pm->gent->client->NPC_class == CLASS_WAMPA))//does this catch NPCs, too? {//atst, Rancor & Wampa, only override turn anims on legs (no torso) - if ( pm->ps->legsAnim == BOTH_TURN_LEFT1 || - pm->ps->legsAnim == BOTH_TURN_RIGHT1 ) + if (pm->ps->legsAnim == BOTH_TURN_LEFT1 || + pm->ps->legsAnim == BOTH_TURN_RIGHT1) {//moving overrides turning setAnimFlags |= SETANIM_FLAG_OVERRIDE; } } else {//all other NPCs... - if ( (PM_InSaberAnim( pm->ps->legsAnim ) && !PM_SpinningSaberAnim( pm->ps->legsAnim )) - || PM_SaberStanceAnim( pm->ps->legsAnim ) - || PM_SaberDrawPutawayAnim( pm->ps->legsAnim ) + if ((PM_InSaberAnim(pm->ps->legsAnim) && !PM_SpinningSaberAnim(pm->ps->legsAnim)) + || PM_SaberStanceAnim(pm->ps->legsAnim) + || PM_SaberDrawPutawayAnim(pm->ps->legsAnim) || pm->ps->legsAnim == BOTH_SPINATTACK6//not a full-body spin, just spinning the saber || pm->ps->legsAnim == BOTH_SPINATTACK7//not a full-body spin, just spinning the saber || pm->ps->legsAnim == BOTH_BUTTON_HOLD @@ -8349,191 +8466,193 @@ static void PM_Footsteps( void ) || pm->ps->legsAnim == BOTH_THERMAL_READY || pm->ps->legsAnim == BOTH_THERMAL_THROW || pm->ps->legsAnim == BOTH_ATTACK10 - || PM_LandingAnim( pm->ps->legsAnim ) - || PM_PainAnim( pm->ps->legsAnim ) - || PM_ForceAnim( pm->ps->legsAnim )) + || PM_LandingAnim(pm->ps->legsAnim) + || PM_PainAnim(pm->ps->legsAnim) + || PM_ForceAnim(pm->ps->legsAnim)) {//legs are in a saber anim, and not spinning, be sure to override it setAnimFlags |= SETANIM_FLAG_OVERRIDE; } } - if ( pm->ps->pm_flags & PMF_DUCKED ) + if (pm->ps->pm_flags & PMF_DUCKED) { bobmove = 0.5; // ducked characters bob much faster - if( !PM_InOnGroundAnim( pm->ps ) //not on the ground - && ( !PM_InRollIgnoreTimer( pm->ps )||(!pm->ps->legsAnimTimer&&pm->cmd.upmove<0) ) )//not in a roll (or you just finished one and you're still holding crouch) + if (!PM_InOnGroundAnim(pm->ps) //not on the ground + && (!PM_InRollIgnoreTimer(pm->ps) || (!pm->ps->legsAnimTimer&&pm->cmd.upmove<0)))//not in a roll (or you just finished one and you're still holding crouch) { qboolean rolled = qfalse; - if ( PM_RunningAnim( pm->ps->legsAnim ) + + if (((PM_RunningAnim(pm->ps->legsAnim) || pm->ps->legsAnim == BOTH_FORCEHEAL_START - || PM_CanRollFromSoulCal( pm->ps )) + || PM_CanRollFromSoulCal(pm->ps)) && + (g_autoRoll->integer || pm->cmd.buttons&BUTTON_USE))) {//roll! rolled = PM_TryRoll(); } - if ( !rolled ) + if (!rolled) { - if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) + if (pm->ps->pm_flags & PMF_BACKWARDS_RUN) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALKBACK,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_CROUCH1WALKBACK, setAnimFlags); } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_CROUCH1WALK,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_CROUCH1WALK, setAnimFlags); } - if ( !Q_irand( 0, 19 ) ) + if (!Q_irand(0, 19)) {//5% chance of making an alert - AddSoundEvent( pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue ); + AddSoundEvent(pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue); } } else {//rolling is a little noisy - AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_MINOR, qtrue, qtrue ); + AddSoundEvent(pm->gent, pm->ps->origin, 128, AEL_MINOR, qtrue, qtrue); } } // ducked characters never play footsteps } - else if ( pm->ps->pm_flags & PMF_BACKWARDS_RUN ) + else if (pm->ps->pm_flags & PMF_BACKWARDS_RUN) {//Moving backwards - if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) + if (!(pm->cmd.buttons & BUTTON_WALKING)) {//running backwards bobmove = 0.4F; // faster speeds bob faster - if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() ) + if (pm->ps->weapon == WP_SABER && pm->ps->SaberActive()) { /* if ( pm->ps->saberAnimLevel == SS_STAFF ) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK_STAFF,setAnimFlags); + PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK_STAFF,setAnimFlags); } else */ { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK2,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUNBACK2, setAnimFlags); } } - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR) {//no run anim - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK1,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALKBACK1, setAnimFlags); } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUNBACK1,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUNBACK1, setAnimFlags); } footstep = qtrue; } else {//walking backwards bobmove = 0.3F; // faster speeds bob faster - if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() ) + if (pm->ps->weapon == WP_SABER && pm->ps->SaberActive()) { - if ( pm->ps->saberAnimLevel == SS_DUAL ) + if (pm->ps->saberAnimLevel == SS_DUAL) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK_DUAL,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALKBACK_DUAL, setAnimFlags); } - else if ( pm->ps->saberAnimLevel == SS_STAFF ) + else if (pm->ps->saberAnimLevel == SS_STAFF) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK_STAFF,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALKBACK_STAFF, setAnimFlags); } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK2,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALKBACK2, setAnimFlags); } } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALKBACK1,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALKBACK1, setAnimFlags); } - if ( !Q_irand( 0, 9 ) ) + if (!Q_irand(0, 9)) {//10% chance of a small alert, mainly for the sand_creature - AddSoundEvent( pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue ); + AddSoundEvent(pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue); } } } else { - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH) { bobmove = 0.3F; // walking bobs slow - if ( pm->ps->weapon == WP_NONE ) + if (pm->ps->weapon == WP_NONE) {//helmet retracted - PM_SetAnim( pm, SETANIM_BOTH, BOTH_WALK1, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_WALK1, SETANIM_FLAG_NORMAL); } else {//helmet in place - PM_SetAnim( pm, SETANIM_BOTH, BOTH_WALK2, SETANIM_FLAG_NORMAL ); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_WALK2, SETANIM_FLAG_NORMAL); } - AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_SUSPICIOUS, qtrue, qtrue ); + AddSoundEvent(pm->gent, pm->ps->origin, 128, AEL_SUSPICIOUS, qtrue, qtrue); } else { - if ( !( pm->cmd.buttons & BUTTON_WALKING ) ) + if (!(pm->cmd.buttons & BUTTON_WALKING)) {//running bobmove = 0.4F; // faster speeds bob faster - if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() ) + if (pm->ps->weapon == WP_SABER && pm->ps->SaberActive()) { - if ( pm->ps->saberAnimLevel == SS_DUAL ) + if (pm->ps->saberAnimLevel == SS_DUAL) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN_DUAL,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN_DUAL, setAnimFlags); } - else if ( pm->ps->saberAnimLevel == SS_STAFF ) + else if (pm->ps->saberAnimLevel == SS_STAFF) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN_STAFF,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN_STAFF, setAnimFlags); } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN2,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN2, setAnimFlags); } } else { - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_JAWA ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_JAWA) { //if ( pm->gent->enemy && (pm->ps->weapon == WP_NONE || pm->ps->weapon == WP_MELEE) ) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN4,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN4, setAnimFlags); } /* else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN1,setAnimFlags); + PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN1,setAnimFlags); } */ } - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST) { - if ( pm->ps->legsAnim != BOTH_RUN1 ) + if (pm->ps->legsAnim != BOTH_RUN1) { - if ( pm->ps->legsAnim != BOTH_RUN1START ) + if (pm->ps->legsAnim != BOTH_RUN1START) {//Hmm, he should really start slow and have to accelerate... also need to do this for stopping - PM_SetAnim( pm,SETANIM_LEGS, BOTH_RUN1START, setAnimFlags|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN1START, setAnimFlags | SETANIM_FLAG_HOLD); } - else if ( !pm->ps->legsAnimTimer ) + else if (!pm->ps->legsAnimTimer) { - PM_SetAnim( pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags); } } else { - PM_SetAnim( pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags); } } - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA) { - if ( pm->gent->NPC && pm->gent->NPC->stats.runSpeed == 300 ) + if (pm->gent->NPC && pm->gent->NPC->stats.runSpeed == 300) {//full on run, on all fours - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN1, SETANIM_FLAG_NORMAL); } else {//regular, upright run - PM_SetAnim(pm,SETANIM_LEGS,BOTH_RUN2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN2, SETANIM_FLAG_NORMAL); } } - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_RANCOR) {//no run anim - PM_SetAnim( pm, SETANIM_LEGS, BOTH_WALK1, setAnimFlags ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALK1, setAnimFlags); } else { - PM_SetAnim( pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_RUN1, setAnimFlags); } } footstep = qtrue; @@ -8541,59 +8660,59 @@ static void PM_Footsteps( void ) else {//walking forward bobmove = 0.3F; // walking bobs slow - if ( pm->ps->weapon == WP_SABER && pm->ps->SaberActive() ) + if (pm->ps->weapon == WP_SABER && pm->ps->SaberActive()) { - if ( pm->ps->saberAnimLevel == SS_DUAL ) + if (pm->ps->saberAnimLevel == SS_DUAL) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK_DUAL,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALK_DUAL, setAnimFlags); } - else if ( pm->ps->saberAnimLevel == SS_STAFF ) + else if (pm->ps->saberAnimLevel == SS_STAFF) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK_STAFF,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALK_STAFF, setAnimFlags); } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK2,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALK2, setAnimFlags); } } - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_WAMPA) { - if ( pm->gent->health <= 50 ) + if (pm->gent->health <= 50) {//hurt walk - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALK2, SETANIM_FLAG_NORMAL); } else {//normal walk - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALK1, SETANIM_FLAG_NORMAL); } } else { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_WALK1,setAnimFlags); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_WALK1, setAnimFlags); } //Enemy NPCs always make footsteps for the benefit of the player - if ( pm->gent + if (pm->gent && pm->gent->NPC && pm->gent->client - && pm->gent->client->playerTeam != TEAM_PLAYER ) + && pm->gent->client->playerTeam != TEAM_PLAYER) { footstep = qtrue; } - else if ( !Q_irand( 0, 9 ) ) + else if (!Q_irand(0, 9)) {//10% chance of a small alert, mainly for the sand_creature - AddSoundEvent( pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue ); + AddSoundEvent(pm->gent, pm->ps->origin, 16, AEL_MINOR, qtrue, qtrue); } } } } - if(pm->gent != NULL) + if (pm->gent != NULL) { - if( pm->gent->client->renderInfo.legsFpsMod > 2 ) + if (pm->gent->client->renderInfo.legsFpsMod > 2) { pm->gent->client->renderInfo.legsFpsMod = 2; } - else if(pm->gent->client->renderInfo.legsFpsMod < 0.5) + else if (pm->gent->client->renderInfo.legsFpsMod < 0.5) { pm->gent->client->renderInfo.legsFpsMod = 0.5; } @@ -8603,100 +8722,101 @@ static void PM_Footsteps( void ) // check for footstep / splash sounds old = pm->ps->bobCycle; - pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255; + pm->ps->bobCycle = (int)(old + bobmove * pml.msec) & 255; // if we just crossed a cycle boundary, play an apropriate footstep event - if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) + if (((old + 64) ^ (pm->ps->bobCycle + 64)) & 128) { - if ( pm->watertype & CONTENTS_LADDER ) + if (pm->watertype & CONTENTS_LADDER) { - if ( !pm->noFootsteps ) + if (!pm->noFootsteps) { if (pm->ps->groundEntityNum == ENTITYNUM_NONE) {// on ladder - PM_AddEvent( EV_FOOTSTEP_METAL ); - } else { + PM_AddEvent(EV_FOOTSTEP_METAL); + } + else { //PM_AddEvent( PM_FootstepForSurface() ); //still on ground } } - if ( pm->gent && pm->gent->s.number == 0 ) + if (pm->gent && pm->gent->s.number == 0) { -// if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) + // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) { - AddSoundEvent( pm->gent, pm->ps->origin, 128, AEL_MINOR, qtrue ); + AddSoundEvent(pm->gent, pm->ps->origin, 128, AEL_MINOR, qtrue); } } } - else if ( pm->waterlevel == 0 ) + else if (pm->waterlevel == 0) { // on ground will only play sounds if running - if ( footstep ) + if (footstep) { - if ( !pm->noFootsteps ) + if (!pm->noFootsteps) { //PM_AddEvent( PM_FootstepForSurface() ); } - if ( pm->gent && pm->gent->s.number == 0 ) + if (pm->gent && pm->gent->s.number == 0) { vec3_t bottom; - VectorCopy( pm->ps->origin, bottom ); + VectorCopy(pm->ps->origin, bottom); bottom[2] += pm->mins[2]; -// if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) + // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) { - AddSoundEvent( pm->gent, bottom, 256, AEL_MINOR, qtrue, qtrue ); + AddSoundEvent(pm->gent, bottom, 256, AEL_MINOR, qtrue, qtrue); } } } } - else if ( pm->waterlevel == 1 ) + else if (pm->waterlevel == 1) { // splashing - if ( pm->ps->waterHeightLevel >= WHL_KNEES ) + if (pm->ps->waterHeightLevel >= WHL_KNEES) { - PM_AddEvent( EV_FOOTWADE ); + PM_AddEvent(EV_FOOTWADE); } else { - PM_AddEvent( EV_FOOTSPLASH ); + PM_AddEvent(EV_FOOTSPLASH); } - if ( pm->gent && pm->gent->s.number == 0 ) + if (pm->gent && pm->gent->s.number == 0) { vec3_t bottom; - VectorCopy( pm->ps->origin, bottom ); + VectorCopy(pm->ps->origin, bottom); bottom[2] += pm->mins[2]; -// if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) + // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) { - AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS, qfalse, qtrue );//was bottom - AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_MINOR ); + AddSoundEvent(pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS, qfalse, qtrue);//was bottom + AddSightEvent(pm->gent, pm->ps->origin, 512, AEL_MINOR); } } } - else if ( pm->waterlevel == 2 ) + else if (pm->waterlevel == 2) { // wading / swimming at surface /* if ( pm->ps->waterHeightLevel >= WHL_TORSO ) { - PM_AddEvent( EV_SWIM ); + PM_AddEvent( EV_SWIM ); } else */ { - PM_AddEvent( EV_FOOTWADE ); + PM_AddEvent(EV_FOOTWADE); } - if ( pm->gent && pm->gent->s.number == 0 ) + if (pm->gent && pm->gent->s.number == 0) { -// if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) + // if ( pm->gent->client && pm->gent->client->playerTeam != TEAM_DISGUISE ) { - AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_MINOR, qfalse, qtrue ); - AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS ); + AddSoundEvent(pm->gent, pm->ps->origin, 256, AEL_MINOR, qfalse, qtrue); + AddSightEvent(pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS); } } } else {// or no sound when completely underwater...? - PM_AddEvent( EV_SWIM ); + PM_AddEvent(EV_SWIM); } } } @@ -8708,11 +8828,11 @@ PM_WaterEvents Generate sound events for entering and leaving water ============== */ -static void PM_WaterEvents( void ) { // FIXME? +static void PM_WaterEvents(void) { // FIXME? qboolean impact_splash = qfalse; - if ( pm->watertype & CONTENTS_LADDER ) //fake water for ladder + if (pm->watertype & CONTENTS_LADDER) //fake water for ladder { return; } @@ -8721,25 +8841,25 @@ static void PM_WaterEvents( void ) { // FIXME? // if (!pml.previous_waterlevel && pm->waterlevel) { - if ( (pm->watertype&CONTENTS_LAVA) ) + if ((pm->watertype&CONTENTS_LAVA)) { - PM_AddEvent( EV_LAVA_TOUCH ); + PM_AddEvent(EV_LAVA_TOUCH); } else { - PM_AddEvent( EV_WATER_TOUCH ); + PM_AddEvent(EV_WATER_TOUCH); } - if ( pm->gent ) + if (pm->gent) { - if ( VectorLengthSquared( pm->ps->velocity ) > 40000 ) + if (VectorLengthSquared(pm->ps->velocity) > 40000) { impact_splash = qtrue; } - if ( pm->ps->clientNum < MAX_CLIENTS ) + if (pm->ps->clientNum < MAX_CLIENTS) { - AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS ); - AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS ); + AddSoundEvent(pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS); + AddSightEvent(pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS); } } } @@ -8749,56 +8869,56 @@ static void PM_WaterEvents( void ) { // FIXME? // if (pml.previous_waterlevel && !pm->waterlevel) { - if ( (pm->watertype&CONTENTS_LAVA) ) + if ((pm->watertype&CONTENTS_LAVA)) { - PM_AddEvent( EV_LAVA_LEAVE ); + PM_AddEvent(EV_LAVA_LEAVE); } else { - PM_AddEvent( EV_WATER_LEAVE ); + PM_AddEvent(EV_WATER_LEAVE); } - if ( pm->gent && VectorLengthSquared( pm->ps->velocity ) > 40000 ) + if (pm->gent && VectorLengthSquared(pm->ps->velocity) > 40000) { impact_splash = qtrue; } - if ( pm->gent && pm->ps->clientNum < MAX_CLIENTS ) + if (pm->gent && pm->ps->clientNum < MAX_CLIENTS) { - AddSoundEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS ); - AddSightEvent( pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS ); + AddSoundEvent(pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS); + AddSightEvent(pm->gent, pm->ps->origin, 512, AEL_SUSPICIOUS); } } - if ( impact_splash ) + if (impact_splash) { //play the splash effect trace_t tr; vec3_t axis[3], angs, start, end; - VectorSet( angs, 0, pm->gent->currentAngles[YAW], 0 ); - AngleVectors( angs, axis[2], axis[1], axis[0] ); + VectorSet(angs, 0, pm->gent->currentAngles[YAW], 0); + AngleVectors(angs, axis[2], axis[1], axis[0]); - VectorCopy( pm->ps->origin, start ); - VectorCopy( pm->ps->origin, end ); + VectorCopy(pm->ps->origin, start); + VectorCopy(pm->ps->origin, end); // FIXME: set start and end better start[2] += 10; end[2] -= 40; - gi.trace( &tr, start, vec3_origin, vec3_origin, end, pm->gent->s.number, MASK_WATER, (EG2_Collision)0, 0 ); + gi.trace(&tr, start, vec3_origin, vec3_origin, end, pm->gent->s.number, MASK_WATER, (EG2_Collision)0, 0); - if ( tr.fraction < 1.0f ) + if (tr.fraction < 1.0f) { - if ( (tr.contents&CONTENTS_LAVA) ) + if ((tr.contents&CONTENTS_LAVA)) { - G_PlayEffect( "env/lava_splash", tr.endpos, axis ); + G_PlayEffect("env/lava_splash", tr.endpos, axis); } - else if ( (tr.contents&CONTENTS_SLIME) ) + else if ((tr.contents&CONTENTS_SLIME)) { - G_PlayEffect( "env/acid_splash", tr.endpos, axis ); + G_PlayEffect("env/acid_splash", tr.endpos, axis); } else //must be water { - G_PlayEffect( "env/water_impact", tr.endpos, axis ); + G_PlayEffect("env/water_impact", tr.endpos, axis); } } } @@ -8807,19 +8927,19 @@ static void PM_WaterEvents( void ) { // FIXME? // check for head just going under water // if (pml.previous_waterlevel != 3 && pm->waterlevel == 3) { - if ( (pm->watertype&CONTENTS_LAVA) ) + if ((pm->watertype&CONTENTS_LAVA)) { - PM_AddEvent( EV_LAVA_UNDER ); + PM_AddEvent(EV_LAVA_UNDER); } else { - PM_AddEvent( EV_WATER_UNDER ); + PM_AddEvent(EV_WATER_UNDER); } - if ( pm->gent && pm->ps->clientNum < MAX_CLIENTS ) + if (pm->gent && pm->ps->clientNum < MAX_CLIENTS) { - AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_MINOR ); - AddSightEvent( pm->gent, pm->ps->origin, 384, AEL_MINOR ); + AddSoundEvent(pm->gent, pm->ps->origin, 256, AEL_MINOR); + AddSightEvent(pm->gent, pm->ps->origin, 384, AEL_MINOR); } } @@ -8827,25 +8947,25 @@ static void PM_WaterEvents( void ) { // FIXME? // check for head just coming out of water // if (pml.previous_waterlevel == 3 && pm->waterlevel != 3) { - if ( !pm->gent || !pm->gent->client || pm->gent->client->airOutTime < level.time + 2000 ) + if (!pm->gent || !pm->gent->client || pm->gent->client->airOutTime < level.time + 2000) {//only do this if we were drowning or about to start drowning - PM_AddEvent( EV_WATER_CLEAR ); + PM_AddEvent(EV_WATER_CLEAR); } else { - if ( (pm->watertype&CONTENTS_LAVA) ) + if ((pm->watertype&CONTENTS_LAVA)) { - PM_AddEvent( EV_LAVA_LEAVE ); + PM_AddEvent(EV_LAVA_LEAVE); } else { - PM_AddEvent( EV_WATER_LEAVE ); + PM_AddEvent(EV_WATER_LEAVE); } } - if ( pm->gent && pm->ps->clientNum < MAX_CLIENTS ) + if (pm->gent && pm->ps->clientNum < MAX_CLIENTS) { - AddSoundEvent( pm->gent, pm->ps->origin, 256, AEL_MINOR ); - AddSightEvent( pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS ); + AddSoundEvent(pm->gent, pm->ps->origin, 256, AEL_MINOR); + AddSightEvent(pm->gent, pm->ps->origin, 384, AEL_SUSPICIOUS); } } } @@ -8856,79 +8976,79 @@ static void PM_WaterEvents( void ) { // FIXME? PM_BeginWeaponChange =============== */ -static void PM_BeginWeaponChange( int weapon ) { +static void PM_BeginWeaponChange(int weapon) { - if ( pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500 ) + if (pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500) {//just entered map - if ( weapon == WP_NONE && pm->ps->weapon != weapon ) + if (weapon == WP_NONE && pm->ps->weapon != weapon) {//don't switch to weapon none if just entered map return; } } - if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) { + if (weapon < WP_NONE || weapon >= WP_NUM_WEAPONS) { return; } - if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) { + if ( !( pm->ps->weapons[weapon] ) ) { return; } - if ( pm->ps->weaponstate == WEAPON_DROPPING ) { + if (pm->ps->weaponstate == WEAPON_DROPPING) { return; } - if ( cg.time > 0 ) + if (cg.time > 0) {//this way we don't get that annoying change weapon sound every time a map starts - PM_AddEvent( EV_CHANGE_WEAPON ); + PM_AddEvent(EV_CHANGE_WEAPON); } pm->ps->weaponstate = WEAPON_DROPPING; pm->ps->weaponTime += 200; - if ( !(pm->ps->eFlags&EF_HELD_BY_WAMPA) && !G_IsRidingVehicle(pm->gent)) + if (!(pm->ps->eFlags&EF_HELD_BY_WAMPA) && !G_IsRidingVehicle(pm->gent)) { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_DROPWEAP1,SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_DROPWEAP1, SETANIM_FLAG_HOLD); } // turn of any kind of zooming when weapon switching....except the LA Goggles // eezstreet edit: also ignore if we change to WP_NONE..sorta hacky fix for binoculars using WP_SABER - if ( pm->ps->clientNum == 0 && cg.weaponSelect != WP_NONE ) + if (pm->ps->clientNum == 0 && cg.weaponSelect != WP_NONE) { - if ( cg.zoomMode > 0 && cg.zoomMode < 3 ) + if (cg.zoomMode > 0 && cg.zoomMode < 3) { cg.zoomMode = 0; cg.zoomTime = cg.time; } } - if ( pm->gent + if (pm->gent && pm->gent->client - && (pm->gent->client->NPC_class == CLASS_ATST||pm->gent->client->NPC_class == CLASS_RANCOR) ) + && (pm->gent->client->NPC_class == CLASS_ATST || pm->gent->client->NPC_class == CLASS_RANCOR)) { - if ( pm->ps->clientNum < MAX_CLIENTS ) + if (pm->ps->clientNum < MAX_CLIENTS) { - gi.cvar_set( "cg_thirdperson", "1" ); + gi.cvar_set("cg_thirdperson", "1"); } } - else if ( weapon == WP_SABER ) + else if (weapon == WP_SABER) {//going to switch to lightsaber } else { - if ( pm->ps->weapon == WP_SABER ) + if (pm->ps->weapon == WP_SABER) {//going to switch away from saber - if ( pm->gent ) + if (pm->gent) { - G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/saber/saberoffquick.wav" ); + G_SoundOnEnt(pm->gent, CHAN_WEAPON, "sound/weapons/saber/saberoffquick.wav"); } if (!G_IsRidingVehicle(pm->gent)) { - PM_SetSaberMove(LS_PUTAWAY); + // PM_SetSaberMove(LS_PUTAWAY); } } //put this back in because saberActive isn't being set somewhere else anymore pm->ps->SaberDeactivate(); - pm->ps->SetSaberLength( 0.0f ); + pm->ps->SetSaberLength(0.0f); } } @@ -8938,38 +9058,87 @@ static void PM_BeginWeaponChange( int weapon ) { PM_FinishWeaponChange =============== */ -static void PM_FinishWeaponChange( void ) { +static void PM_FinishWeaponChange(void) { int weapon; qboolean trueSwitch = qtrue; - if ( pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500 ) + if (pm->gent && pm->gent->client && pm->gent->client->pers.enterTime >= level.time - 500) {//just entered map - if ( pm->cmd.weapon == WP_NONE && pm->ps->weapon != pm->cmd.weapon ) + if (pm->cmd.weapon == WP_NONE && pm->ps->weapon != pm->cmd.weapon) {//don't switch to weapon none if just entered map return; } } weapon = pm->cmd.weapon; - if ( weapon < WP_NONE || weapon >= WP_NUM_WEAPONS ) { + if (weapon < WP_NONE || weapon >= WP_NUM_WEAPONS) { weapon = WP_NONE; } - if ( !( pm->ps->stats[STAT_WEAPONS] & ( 1 << weapon ) ) ) { + if ( !( pm->ps->weapons[weapon] ) ) { weapon = WP_NONE; } - if ( pm->ps->weapon == weapon ) + if (pm->ps->weapon == weapon) { trueSwitch = qfalse; } + + if ( trueSwitch && pm->ps->weapon == WP_EMPLACED_GUN && !(pm->ps->eFlags & EF_LOCKED_TO_WEAPON) ) + { + gitem_t *item; + item = FindItemForWeapon( WP_EMPLACED_GUN ); + gentity_t *dropped = Drop_Item(pm->gent, item, 0, qfalse); + dropped->count = pm->ps->ammo[AMMO_EMPLACED]; + gi.G2API_InitGhoul2Model( dropped->ghoul2, "models/map_objects/hoth/eweb_model.glm", G_ModelIndex( "models/map_objects/hoth/eweb_model.glm" ), NULL_HANDLE, NULL_HANDLE, 0, 0); + gi.G2API_SetSurfaceOnOff(&dropped->ghoul2[0], "eweb_cannon", 0x00000002); + dropped->s.radius = 10; + dropped->delay = level.time + 1000; + pm->ps->ammo[AMMO_EMPLACED] = 0; + pm->ps->weapons[WP_EMPLACED_GUN] = 0; + } + //int oldWeap = pm->ps->weapon; pm->ps->weapon = weapon; pm->ps->weaponstate = WEAPON_RAISING; pm->ps->weaponTime += 250; - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ATST) {//do nothing } + else if ( weapon == WP_EMPLACED_GUN && !(pm->ps->eFlags & EF_LOCKED_TO_WEAPON) ) + { + if ( pm->gent ) + { + // remove the sabre if we had it. + G_RemoveWeaponModels( pm->gent ); + //holster sabers + WP_SaberAddHolsteredG2SaberModels( pm->gent ); + G_CreateG2AttachedWeaponModel( pm->gent, "models/map_objects/hoth/eweb_model.glm", pm->gent->handRBolt, 0 ); + } + + if ( !(pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + { + if ( pm->ps->weapon != WP_THERMAL + && pm->ps->weapon != WP_TRIP_MINE + && pm->ps->weapon != WP_DET_PACK + && !G_IsRidingVehicle(pm->gent)) + { + PM_SetAnim(pm,SETANIM_TORSO,TORSO_RAISEWEAP1,SETANIM_FLAG_HOLD); + } + } + + if ( pm->ps->clientNum < MAX_CLIENTS + && cg_gunAutoFirst.integer + && !PM_RidingVehicle() + // && oldWeap == WP_SABER + && weapon != WP_NONE ) + { + gi.cvar_set( "cg_thirdperson", "0" ); + } + pm->ps->saberMove = LS_NONE; + pm->ps->saberBlocking = BLK_NO; + pm->ps->saberBlocked = BLOCKED_NONE; + } else if ( weapon == WP_SABER ) {//turn on the lightsaber //FIXME: somehow sometimes I still end up with 2 weapons in hand... usually if I @@ -8978,46 +9147,48 @@ static void PM_FinishWeaponChange( void ) { // making my saber drop to the ground... when I switch back to the saber, it // does not remove the current weapon model and then, when I pull the saber // back to my hand, I have 2 weaponModels active...? - if ( pm->gent ) + if (pm->gent) {// remove gun if we had it. G_RemoveWeaponModels( pm->gent ); + //and remove holstered saber models + G_RemoveHolsterModels( pm->gent ); } - if ( !pm->ps->saberInFlight || pm->ps->dualSabers ) + if (!pm->ps->saberInFlight || pm->ps->dualSabers) {//if it's not in flight or lying around, turn it on! //FIXME: AddSound/Sight Event //FIXME: don't do this if just loaded a game - if ( trueSwitch ) + if (trueSwitch) {//actually did switch weapons, turn it on - if ( PM_RidingVehicle() ) + if (PM_RidingVehicle()) {//only turn on the first saber's first blade...? - pm->ps->SaberBladeActivate( 0, 0 ); + pm->ps->SaberBladeActivate(0, 0); } else { pm->ps->SaberActivate(); } - pm->ps->SetSaberLength( 0.0f ); + pm->ps->SetSaberLength(0.0f); } - if ( pm->gent ) + if (pm->gent) { - WP_SaberAddG2SaberModels( pm->gent ); + WP_SaberAddG2SaberModels(pm->gent); } } else {//FIXME: pull it back to us? } - if ( pm->gent ) + if (pm->gent) { - WP_SaberInitBladeData( pm->gent ); - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + WP_SaberInitBladeData(pm->gent); + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) { - gi.cvar_set( "cg_thirdperson", "1" ); + gi.cvar_set("cg_thirdperson", "1"); } } - if ( trueSwitch ) + if (trueSwitch) {//actually did switch weapons, play anim if (!G_IsRidingVehicle(pm->gent)) { @@ -9027,33 +9198,35 @@ static void PM_FinishWeaponChange( void ) { } else {//switched away from saber - if ( pm->gent ) + if (pm->gent) { // remove the sabre if we had it. G_RemoveWeaponModels( pm->gent ); - if (weaponData[weapon].weaponMdl[0]) { //might be NONE, so check if it has a model - G_CreateG2AttachedWeaponModel( pm->gent, weaponData[weapon].weaponMdl, pm->gent->handRBolt, 0 ); + //holster sabers + WP_SaberAddHolsteredG2SaberModels( pm->gent ); + if (weaponData[weapon].worldModel[0]) { //might be NONE, so check if it has a model + G_CreateG2AttachedWeaponModel( pm->gent, weaponData[weapon].worldModel, pm->gent->handRBolt, 0 ); } } - if ( !(pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + if (!(pm->ps->eFlags&EF_HELD_BY_WAMPA)) { - if ( pm->ps->weapon != WP_THERMAL + if (pm->ps->weapon != WP_THERMAL && pm->ps->weapon != WP_TRIP_MINE && pm->ps->weapon != WP_DET_PACK && !G_IsRidingVehicle(pm->gent)) { - PM_SetAnim(pm,SETANIM_TORSO,TORSO_RAISEWEAP1,SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_RAISEWEAP1, SETANIM_FLAG_HOLD); } } - if ( pm->ps->clientNum < MAX_CLIENTS + if (pm->ps->clientNum < MAX_CLIENTS && cg_gunAutoFirst.integer && !PM_RidingVehicle() -// && oldWeap == WP_SABER - && weapon != WP_NONE ) + // && oldWeap == WP_SABER + && weapon != WP_NONE) { - gi.cvar_set( "cg_thirdperson", "0" ); + gi.cvar_set("cg_thirdperson", "0"); } pm->ps->saberMove = LS_NONE; pm->ps->saberBlocking = BLK_NO; @@ -9061,14 +9234,14 @@ static void PM_FinishWeaponChange( void ) { } } -int PM_ReadyPoseForSaberAnimLevel( void ) +int PM_ReadyPoseForSaberAnimLevel(void) { int anim = BOTH_STAND2; - if (PM_RidingVehicle()) + if (PM_RidingVehicle()) { return -1; } - switch ( pm->ps->saberAnimLevel ) + switch (pm->ps->saberAnimLevel) { case SS_DUAL: anim = BOTH_SABERDUAL_STANCE; @@ -9076,6 +9249,9 @@ int PM_ReadyPoseForSaberAnimLevel( void ) case SS_STAFF: anim = BOTH_SABERSTAFF_STANCE; break; + case SS_KATARN: + anim = BOTH_STAND6; + break; case SS_FAST: case SS_TAVION: anim = BOTH_SABERFAST_STANCE; @@ -9093,34 +9269,34 @@ int PM_ReadyPoseForSaberAnimLevel( void ) return anim; } -qboolean PM_CanDoDualDoubleAttacks( void ) +qboolean PM_CanDoDualDoubleAttacks(void) { - if ( (pm->ps->saber[0].saberFlags&SFL_NO_MIRROR_ATTACKS) ) + if ((pm->ps->saber[0].saberFlags&SFL_NO_MIRROR_ATTACKS)) { return qfalse; } - if ( pm->ps->dualSabers - && (pm->ps->saber[1].saberFlags&SFL_NO_MIRROR_ATTACKS) ) + if (pm->ps->dualSabers + && (pm->ps->saber[1].saberFlags&SFL_NO_MIRROR_ATTACKS)) { return qfalse; } //NOTE: assumes you're using SS_DUAL style and have both sabers on... - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) {//player return qtrue; } - if ( pm->gent && pm->gent->NPC && pm->gent->NPC->rank >= Q_irand( RANK_LT_COMM, RANK_CAPTAIN+2 ) ) + if (pm->gent && pm->gent->NPC && pm->gent->NPC->rank >= Q_irand(RANK_LT_COMM, RANK_CAPTAIN + 2)) {//high-rank NPC return qtrue; } - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ALORA) {//Alora return qtrue; } return qfalse; } -void PM_SetJumped( float height, qboolean force ) +void PM_SetJumped(float height, qboolean force) { pm->ps->velocity[2] = height; pml.groundPlane = qfalse; @@ -9130,17 +9306,17 @@ void PM_SetJumped( float height, qboolean force ) pm->ps->pm_flags |= PMF_JUMPING; pm->cmd.upmove = 0; - if ( force ) + if (force) { pm->ps->jumpZStart = pm->ps->origin[2]; pm->ps->pm_flags |= PMF_SLOW_MO_FALL; //start force jump - pm->ps->forcePowersActive |= (1<gent, CHAN_BODY, "sound/weapons/force/jump.wav" ); + pm->ps->forcePowersActive |= (1 << FP_LEVITATION); + G_SoundOnEnt(pm->gent, CHAN_BODY, "sound/weapons/force/jump.wav"); } else { - PM_AddEvent( EV_JUMP ); + PM_AddEvent(EV_JUMP); } } @@ -9152,7 +9328,7 @@ void PM_SetSaberMove(saberMoveName_t newMove) int parts = SETANIM_TORSO; qboolean manualBlocking = qfalse; - if ( newMove < LS_NONE || newMove >= LS_MOVE_MAX ) + if (newMove < LS_NONE || newMove >= LS_MOVE_MAX) { assert(0); return; @@ -9161,42 +9337,42 @@ void PM_SetSaberMove(saberMoveName_t newMove) setflags = saberMoveData[newMove].animSetFlags; anim = saberMoveData[newMove].animToUse; - if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + if ((pm->ps->eFlags&EF_HELD_BY_WAMPA)) {//no anim return; } - if ( cg_debugSaber.integer&0x01 && (newMove != LS_READY) ) + if (cg_debugSaber.integer & 0x01 && (newMove != LS_READY)) { Com_Printf("SetSaberMove: From '%s' to '%s'\n", - saberMoveData[pm->ps->saberMove].name, - saberMoveData[newMove].name); + saberMoveData[pm->ps->saberMove].name, + saberMoveData[newMove].name); } - if ( newMove == LS_READY || newMove == LS_A_FLIP_STAB || newMove == LS_A_FLIP_SLASH ) + if (newMove == LS_READY || newMove == LS_A_FLIP_STAB || newMove == LS_A_FLIP_SLASH) {//finished with a kata (or in a special move) reset attack counter pm->ps->saberAttackChainCount = 0; } - else if ( PM_SaberInAttack( newMove ) ) + else if (PM_SaberInAttack(newMove)) {//continuing with a kata, increment attack counter //FIXME: maybe some contextual/style-specific logic in here pm->ps->saberAttackChainCount++; } - if ( newMove == LS_READY ) + if (newMove == LS_READY) { - if ( pm->ps->saberBlockingTime > cg.time ) + if (pm->ps->saberBlockingTime > cg.time) { manualBlocking = qtrue; - if ( !pm->ps->SaberActive() ) + if (!pm->ps->SaberActive()) {//turn on all blades and sabers if none are currently on pm->ps->SaberActivate(); } - if ( pm->ps->saber[0].type == SABER_CLAW ) + if (pm->ps->saber[0].type == SABER_CLAW) { anim = BOTH_INAIR1;//FIXME: is there a better anim for this? } - else if ( pm->ps->dualSabers && pm->ps->saber[1].Active() ) + else if (pm->ps->dualSabers && pm->ps->saber[1].Active()) { anim = BOTH_INAIR1; } @@ -9205,29 +9381,29 @@ void PM_SetSaberMove(saberMoveName_t newMove) anim = BOTH_P1_S1_T_; } } - else if ( pm->ps->saber[0].readyAnim != -1 ) + else if (pm->ps->saber[0].readyAnim != -1) { anim = pm->ps->saber[0].readyAnim; } - else if ( pm->ps->dualSabers - && pm->ps->saber[1].readyAnim != -1 ) + else if (pm->ps->dualSabers + && pm->ps->saber[1].readyAnim != -1) { anim = pm->ps->saber[1].readyAnim; } - else if ( pm->ps->saber[0].type == SABER_ARC ) + else if (pm->ps->saber[0].type == SABER_ARC) {//FIXME: need it's own style? anim = BOTH_SABERFAST_STANCE; } - else if ( (pm->ps->dualSabers && pm->ps->saber[1].Active()) ) + else if ((pm->ps->dualSabers && pm->ps->saber[1].Active())) { anim = BOTH_SABERDUAL_STANCE; } - else if ( (pm->ps->SaberStaff() && (!pm->ps->saber[0].singleBladeStyle||pm->ps->saber[0].blade[1].active))//saber staff with more than first blade active - || pm->ps->saber[0].type == SABER_ARC ) + else if ((pm->ps->SaberStaff() && (!pm->ps->saber[0].singleBladeStyle || pm->ps->saber[0].blade[1].active))//saber staff with more than first blade active + || pm->ps->saber[0].type == SABER_ARC) { anim = BOTH_SABERSTAFF_STANCE; } - else if ( pm->ps->saber[0].type == SABER_LANCE || pm->ps->saber[0].type == SABER_TRIDENT ) + else if (pm->ps->saber[0].type == SABER_LANCE || pm->ps->saber[0].type == SABER_TRIDENT) {//FIXME: need some 2-handed forward-pointing anim anim = BOTH_STAND1; } @@ -9236,67 +9412,103 @@ void PM_SetSaberMove(saberMoveName_t newMove) anim = PM_ReadyPoseForSaberAnimLevel(); } } - else if ( newMove == LS_DRAW ) + else if (newMove == LS_DRAW) { - if ( PM_RunningAnim( pm->ps->torsoAnim ) ) + if (PM_RunningAnim(pm->ps->torsoAnim)) { pm->ps->saberMove = newMove; return; } - if ( pm->ps->saber[0].drawAnim != -1 ) + if ( pm->ps->saber[0].holsterPlace == HOLSTER_LHIP && !pm->ps->dualSabers && pm->ps->saber[0].drawAnim == -1 ) + { + //TODO: left hip draw anim + } + else if ( pm->ps->saber[0].holsterPlace == HOLSTER_LHIP + && pm->ps->dualSabers + && ((pm->ps->saber[1].holsterPlace == HOLSTER_LHIP) || (pm->ps->saber[1].holsterPlace == HOLSTER_HIPS)) + && !(pm->ps->saber[0].stylesForbidden&(1<ps->saber[1].stylesForbidden&(1<ps->saber[0].drawAnim != -1 ) { anim = pm->ps->saber[0].drawAnim; } - else if ( pm->ps->dualSabers - && pm->ps->saber[1].drawAnim != -1 ) + else if (pm->ps->dualSabers + && pm->ps->saber[1].drawAnim != -1) { anim = pm->ps->saber[1].drawAnim; } + else if ( pm->ps->saber[0].holsterPlace == HOLSTER_BACK ) + { + //TODO: nice back draw anim + anim = BOTH_S1_S7; + } else if ( pm->ps->saber[0].stylesLearned==(1<ps->dualSabers - && !(pm->ps->saber[0].stylesForbidden&(1<ps->saber[1].stylesForbidden&(1<ps->dualSabers + && !(pm->ps->saber[0].stylesForbidden&(1 << SS_DUAL)) + && !(pm->ps->saber[1].stylesForbidden&(1 << SS_DUAL))) { anim = BOTH_S1_S6; } - if ( pm->ps->torsoAnim == BOTH_STAND1IDLE1 ) + if (pm->ps->torsoAnim == BOTH_STAND1IDLE1) { setflags |= SETANIM_FLAG_OVERRIDE; } } - else if ( newMove == LS_PUTAWAY ) + else if (newMove == LS_PUTAWAY) { - if ( pm->ps->saber[0].putawayAnim != -1 ) + if ( pm->ps->saber[0].holsterPlace == HOLSTER_LHIP && !pm->ps->dualSabers && pm->ps->saber[0].putawayAnim == -1 ) + { + //TODO: left hip putaway anim + } + else if ( pm->ps->saber[0].holsterPlace == HOLSTER_LHIP + && pm->ps->dualSabers + && ((pm->ps->saber[1].holsterPlace == HOLSTER_LHIP) || (pm->ps->saber[1].holsterPlace == HOLSTER_HIPS)) + && !(pm->ps->saber[0].stylesForbidden&(1<ps->saber[1].stylesForbidden&(1<ps->saber[1].Active() ) + { + anim = BOTH_S6_S1; + //TODO: nice cross putaway anim + } + else if ( pm->ps->saber[0].putawayAnim != -1 ) { anim = pm->ps->saber[0].putawayAnim; } - else if ( pm->ps->dualSabers - && pm->ps->saber[1].putawayAnim != -1 ) + else if (pm->ps->dualSabers + && pm->ps->saber[1].putawayAnim != -1) { anim = pm->ps->saber[1].putawayAnim; } + else if ( pm->ps->saber[0].holsterPlace == HOLSTER_BACK ) + { + anim = BOTH_S7_S1; + } else if ( pm->ps->saber[0].stylesLearned==(1<ps->saber[0].blade[1].active ) { anim = BOTH_S7_S1; } - else if ( pm->ps->dualSabers - && !(pm->ps->saber[0].stylesForbidden&(1<ps->saber[1].stylesForbidden&(1<ps->saber[1].Active() ) + else if (pm->ps->dualSabers + && !(pm->ps->saber[0].stylesForbidden&(1 << SS_DUAL)) + && !(pm->ps->saber[1].stylesForbidden&(1 << SS_DUAL)) + && pm->ps->saber[1].Active()) { anim = BOTH_S6_S1; } - if ( PM_SaberStanceAnim( pm->ps->legsAnim ) && pm->ps->legsAnim != BOTH_STAND1 ) + if (PM_SaberStanceAnim(pm->ps->legsAnim) && pm->ps->legsAnim != BOTH_STAND1) { parts = SETANIM_BOTH; } else { - if ( PM_RunningAnim( pm->ps->torsoAnim ) ) + if (PM_RunningAnim(pm->ps->torsoAnim)) { pm->ps->saberMove = newMove; return; @@ -9305,47 +9517,54 @@ void PM_SetSaberMove(saberMoveName_t newMove) } //FIXME: also dual } - else if ( pm->ps->saberAnimLevel == SS_STAFF && newMove >= LS_S_TL2BR && newMove < LS_REFLECT_LL ) + else if (pm->ps->saberAnimLevel == SS_STAFF && newMove >= LS_S_TL2BR && newMove < LS_REFLECT_LL) {//staff has an entirely new set of anims, besides special attacks //FIXME: include ready and draw/putaway? //FIXME: get hand-made bounces and deflections? - if ( newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL ) + if (newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL) {//there aren't 1-7, just 1, 6 and 7, so just set it - anim = BOTH_P7_S7_T_ + (anim-BOTH_P1_S1_T_);//shift it up to the proper set + anim = BOTH_P7_S7_T_ + (anim - BOTH_P1_S1_T_);//shift it up to the proper set } else {//add the appropriate animLevel - anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE; + if (pm->ps->saberAnimLevel < SS_KATARN) + { + anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE; + } + else + { + anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1-1) * SABER_ANIM_GROUP_SIZE; + } } } - else if ( (pm->ps->saberAnimLevel == SS_DUAL - || (pm->ps->dualSabers&& pm->ps->saber[1].Active())) + else if ((pm->ps->saberAnimLevel == SS_DUAL + || (pm->ps->dualSabers&& pm->ps->saber[1].Active())) && newMove >= LS_S_TL2BR - && newMove < LS_REFLECT_LL ) + && newMove < LS_REFLECT_LL) {//staff has an entirely new set of anims, besides special attacks //FIXME: include ready and draw/putaway? //FIXME: get hand-made bounces and deflections? //FIXME: only do the dual FB & LR attacks when on ground? - if ( newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL ) + if (newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL) {//there aren't 1-7, just 1, 6 and 7, so just set it - anim = BOTH_P6_S6_T_ + (anim-BOTH_P1_S1_T_);//shift it up to the proper set + anim = BOTH_P6_S6_T_ + (anim - BOTH_P1_S1_T_);//shift it up to the proper set } - else if ( ( newMove == LS_A_R2L || newMove == LS_S_R2L - || newMove == LS_A_L2R || newMove == LS_S_L2R ) + else if ((newMove == LS_A_R2L || newMove == LS_S_R2L + || newMove == LS_A_L2R || newMove == LS_S_L2R) && PM_CanDoDualDoubleAttacks() - && G_CheckEnemyPresence( pm->gent, DIR_RIGHT, 150.0f ) - && G_CheckEnemyPresence( pm->gent, DIR_LEFT, 150.0f ) ) + && G_CheckEnemyPresence(pm->gent, DIR_RIGHT, 150.0f) + && G_CheckEnemyPresence(pm->gent, DIR_LEFT, 150.0f)) {//enemy both on left and right newMove = LS_DUAL_LR; anim = saberMoveData[newMove].animToUse; //probably already moved, but... pm->cmd.rightmove = 0; } - else if ( (newMove == LS_A_T2B || newMove == LS_S_T2B - || newMove == LS_A_BACK || newMove == LS_A_BACK_CR ) + else if ((newMove == LS_A_T2B || newMove == LS_S_T2B + || newMove == LS_A_BACK || newMove == LS_A_BACK_CR) && PM_CanDoDualDoubleAttacks() - && G_CheckEnemyPresence( pm->gent, DIR_FRONT, 150.0f ) - && G_CheckEnemyPresence( pm->gent, DIR_BACK, 150.0f ) ) + && G_CheckEnemyPresence(pm->gent, DIR_FRONT, 150.0f) + && G_CheckEnemyPresence(pm->gent, DIR_BACK, 150.0f)) {//enemy both in front and back newMove = LS_DUAL_FB; anim = saberMoveData[newMove].animToUse; @@ -9354,56 +9573,70 @@ void PM_SetSaberMove(saberMoveName_t newMove) } else {//add the appropriate animLevel - anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE; + if (pm->ps->saberAnimLevel < SS_KATARN) + { + anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE; + } + else + { + anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1-1) * SABER_ANIM_GROUP_SIZE; + } } } /* else if ( newMove == LS_DRAW && pm->ps->saberAnimLevel == SS_STAFF )//pm->ps->SaberStaff() ) {//hold saber out front as we turn it on - //FIXME: need a real "draw" anim for this (and put-away) - anim = BOTH_SABERSTAFF_STANCE; + //FIXME: need a real "draw" anim for this (and put-away) + anim = BOTH_SABERSTAFF_STANCE; } */ - else if ( pm->ps->saberAnimLevel > FORCE_LEVEL_1 && - !PM_SaberInIdle( newMove ) && !PM_SaberInParry( newMove ) && !PM_SaberInKnockaway( newMove ) && !PM_SaberInBrokenParry( newMove ) && !PM_SaberInReflect( newMove ) && !PM_SaberInSpecial( newMove )) + else if (pm->ps->saberAnimLevel > FORCE_LEVEL_1 && + !PM_SaberInIdle(newMove) && !PM_SaberInParry(newMove) && !PM_SaberInKnockaway(newMove) && !PM_SaberInBrokenParry(newMove) && !PM_SaberInReflect(newMove) && !PM_SaberInSpecial(newMove)) {//readies, parries and reflections have only 1 level - if ( pm->ps->saber[0].type == SABER_LANCE || pm->ps->saber[0].type == SABER_TRIDENT ) + if (pm->ps->saber[0].type == SABER_LANCE || pm->ps->saber[0].type == SABER_TRIDENT) {//FIXME: hack for now - these use the fast anims, but slowed down. Should have own style } else {//increment the anim to the next level of saber anims - anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE; + if (pm->ps->saberAnimLevel < SS_KATARN) + { + anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE; + } + else + { + anim += (pm->ps->saberAnimLevel-FORCE_LEVEL_1-1) * SABER_ANIM_GROUP_SIZE; + } } } - else if ( newMove == LS_KICK_F_AIR + else if (newMove == LS_KICK_F_AIR || newMove == LS_KICK_B_AIR || newMove == LS_KICK_R_AIR - || newMove == LS_KICK_L_AIR ) + || newMove == LS_KICK_L_AIR) { - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum != ENTITYNUM_NONE) { - PM_SetJumped( 200, qtrue ); + PM_SetJumped(200, qtrue); } } // If the move does the same animation as the last one, we need to force a restart... -// if ( saberMoveData[pm->ps->saberMove].animToUse == anim && newMove > LS_PUTAWAY) - if ( ( pm->ps->torsoAnim == anim || pm->ps->legsAnim == anim ) - && newMove > LS_PUTAWAY ) + // if ( saberMoveData[pm->ps->saberMove].animToUse == anim && newMove > LS_PUTAWAY) + if ((pm->ps->torsoAnim == anim || pm->ps->legsAnim == anim) + && newMove > LS_PUTAWAY) { setflags |= SETANIM_FLAG_RESTART; } - if ( (anim == BOTH_STAND1 && (pm->ps->saber[0].type == SABER_ARC || (pm->ps->dualSabers && pm->ps->saber[1].Active())) ) + if ((anim == BOTH_STAND1 && (pm->ps->saber[0].type == SABER_ARC || (pm->ps->dualSabers && pm->ps->saber[1].Active()))) || anim == BOTH_STAND2 //FIXME: temp hack to stop it from using run2 with staff || (0 && anim == BOTH_SABERSTAFF_STANCE) || anim == BOTH_SABERDUAL_STANCE || anim == BOTH_SABERFAST_STANCE - || anim == BOTH_SABERSLOW_STANCE ) + || anim == BOTH_SABERSLOW_STANCE) {//match torso anim to walk/run anim if newMove is just LS_READY //FIXME: play both_stand2_random1 when you've been idle for a while - switch ( pm->ps->legsAnim ) + switch (pm->ps->legsAnim) { case BOTH_WALK1: case BOTH_WALK2: @@ -9425,11 +9658,11 @@ void PM_SetSaberMove(saberMoveName_t newMove) } } - if ( !PM_RidingVehicle() ) + if (!PM_RidingVehicle()) { - if ( !manualBlocking ) + if (!manualBlocking) { - if ( newMove == LS_A_LUNGE + if (newMove == LS_A_LUNGE || newMove == LS_A_JUMP_T__B_ || newMove == LS_A_BACKSTAB || newMove == LS_A_BACK @@ -9458,32 +9691,32 @@ void PM_SetSaberMove(saberMoveName_t newMove) || newMove == LS_UPSIDE_DOWN_ATTACK || newMove == LS_PULL_ATTACK_STAB || newMove == LS_PULL_ATTACK_SWING - || PM_KickMove( newMove ) ) + || PM_KickMove(newMove)) { parts = SETANIM_BOTH; } - else if ( PM_SpinningSaberAnim( anim ) ) + else if (PM_SpinningSaberAnim(anim)) {//spins must be played on entire body parts = SETANIM_BOTH; } - else if ( (!pm->cmd.forwardmove&&!pm->cmd.rightmove&&!pm->cmd.upmove)) + else if ((!pm->cmd.forwardmove&&!pm->cmd.rightmove&&!pm->cmd.upmove)) {//not trying to run, duck or jump - if ( !PM_FlippingAnim( pm->ps->legsAnim ) && - !PM_InRoll( pm->ps ) && - !PM_InKnockDown( pm->ps ) && - !PM_JumpingAnim( pm->ps->legsAnim ) && - !PM_PainAnim( pm->ps->legsAnim ) && - !PM_InSpecialJump( pm->ps->legsAnim ) && - !PM_InSlopeAnim( pm->ps->legsAnim ) && + if (!PM_FlippingAnim(pm->ps->legsAnim) && + !PM_InRoll(pm->ps) && + !PM_InKnockDown(pm->ps) && + !PM_JumpingAnim(pm->ps->legsAnim) && + !PM_PainAnim(pm->ps->legsAnim) && + !PM_InSpecialJump(pm->ps->legsAnim) && + !PM_InSlopeAnim(pm->ps->legsAnim) && //!PM_CrouchAnim( pm->ps->legsAnim ) && //pm->cmd.upmove >= 0 && !(pm->ps->pm_flags & PMF_DUCKED) && - newMove != LS_PUTAWAY ) + newMove != LS_PUTAWAY) { parts = SETANIM_BOTH; } - else if ( !(pm->ps->pm_flags & PMF_DUCKED) - && ( newMove == LS_SPINATTACK_DUAL || newMove == LS_SPINATTACK ) ) + else if (!(pm->ps->pm_flags & PMF_DUCKED) + && (newMove == LS_SPINATTACK_DUAL || newMove == LS_SPINATTACK)) { parts = SETANIM_BOTH; } @@ -9498,70 +9731,71 @@ void PM_SetSaberMove(saberMoveName_t newMove) setflags &= ~SETANIM_FLAG_RESTART; } } - if (anim!=-1) + if (anim != -1) { - PM_SetAnim( pm, parts, anim, setflags, saberMoveData[newMove].blendTime ); + PM_SetAnim(pm, parts, anim, setflags, saberMoveData[newMove].blendTime); } - if ( pm->ps->torsoAnim == anim ) + if (pm->ps->torsoAnim == anim) {//successfully changed anims - //special check for *starting* a saber swing - if ( pm->gent && pm->ps->SaberLength() > 1 ) + //special check for *starting* a saber swing + if (pm->gent && pm->ps->SaberLength() > 1) { - if ( PM_SaberInAttack( newMove ) || PM_SaberInSpecialAttack( anim ) ) + if (PM_SaberInAttack(newMove) || PM_SaberInSpecialAttack(anim)) {//playing an attack - if ( pm->ps->saberMove != newMove ) + if (pm->ps->saberMove != newMove) {//wasn't playing that attack before - if ( PM_SaberInSpecialAttack( anim ) ) + if (PM_SaberInSpecialAttack(anim)) { - WP_SaberSwingSound( pm->gent, 0, SWING_FAST ); - if ( !PM_InCartwheel( pm->ps->torsoAnim ) ) + WP_SaberSwingSound(pm->gent, 0, SWING_FAST); + if (!PM_InCartwheel(pm->ps->torsoAnim)) {//can still attack during a cartwheel/arial pm->ps->weaponTime = pm->ps->torsoAnimTimer;//so we know our weapon is busy } } else { - switch ( pm->ps->saberAnimLevel ) + switch (pm->ps->saberAnimLevel) { case SS_DESANN: case SS_STRONG: - WP_SaberSwingSound( pm->gent, 0, SWING_STRONG ); + WP_SaberSwingSound(pm->gent, 0, SWING_STRONG); break; case SS_MEDIUM: case SS_DUAL: case SS_STAFF: - WP_SaberSwingSound( pm->gent, 0, SWING_MEDIUM ); + WP_SaberSwingSound(pm->gent, 0, SWING_MEDIUM); break; case SS_TAVION: case SS_FAST: + case SS_KATARN: WP_SaberSwingSound( pm->gent, 0, SWING_FAST ); break; } } } - else if ( (setflags&SETANIM_FLAG_RESTART) && PM_SaberInSpecialAttack( anim ) ) + else if ((setflags&SETANIM_FLAG_RESTART) && PM_SaberInSpecialAttack(anim)) {//sigh, if restarted a special, then set the weaponTime *again* - if ( !PM_InCartwheel( pm->ps->torsoAnim ) ) + if (!PM_InCartwheel(pm->ps->torsoAnim)) {//can still attack during a cartwheel/arial pm->ps->weaponTime = pm->ps->torsoAnimTimer;//so we know our weapon is busy } } } - else if ( PM_SaberInStart( newMove ) ) + else if (PM_SaberInStart(newMove)) { //if ( g_saberRealisticCombat->integer < 1 ) {//don't damage on the first few frames of a start anim because it may pop from one position to some drastically different one, killing the enemy without hitting them. int damageDelay = 150; - if ( pm->ps->torsoAnimTimer < damageDelay ) + if (pm->ps->torsoAnimTimer < damageDelay) { damageDelay = pm->ps->torsoAnimTimer; } //ent->client->ps.saberDamageDebounceTime = level.time + damageDelay; } - if ( pm->ps->saberAnimLevel == SS_STRONG ) + if (pm->ps->saberAnimLevel == SS_STRONG) { - WP_SaberSwingSound( pm->gent, 0, SWING_FAST ); + WP_SaberSwingSound(pm->gent, 0, SWING_FAST); } } } @@ -9569,27 +9803,27 @@ void PM_SetSaberMove(saberMoveName_t newMove) /* //wtf... getting stuck with weaponTime set even though we're not in an attack...? if ( PM_SaberInAttack( pm->ps->saberMove ) - && !PM_SaberInAttack( newMove ) ) + && !PM_SaberInAttack( newMove ) ) { - pm->ps->weaponTime = 0; + pm->ps->weaponTime = 0; } */ //Some special attacks can be started when sabers are off, make sure we turn them on, first! - switch ( newMove ) + switch (newMove) {//make sure the saber is on! case LS_A_LUNGE: case LS_ROLL_STAB: - if ( PM_InSecondaryStyle() ) + if (PM_InSecondaryStyle()) {//staff as medium or dual as fast - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) {//only force on the first saber pm->ps->saber[0].Activate(); } - else if ( pm->ps->saber[0].numBlades > 1 ) + else if (pm->ps->saber[0].numBlades > 1) {//only force on the first saber's first blade - pm->ps->SaberBladeActivate(0,0); + pm->ps->SaberBladeActivate(0, 0); } } else @@ -9615,10 +9849,10 @@ void PM_SetSaberMove(saberMoveName_t newMove) pm->ps->saberMove = newMove; pm->ps->saberBlocking = saberMoveData[newMove].blocking; - if ( pm->ps->clientNum == 0 || PM_ControlledByPlayer() ) + if (pm->ps->clientNum == 0 || PM_ControlledByPlayer()) { - if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ - && newMove >= LS_REFLECT_UP && newMove <= LS_REFLECT_LL ) + if (pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ + && newMove >= LS_REFLECT_UP && newMove <= LS_REFLECT_LL) {//don't clear it when blocking projectiles } else @@ -9626,20 +9860,20 @@ void PM_SetSaberMove(saberMoveName_t newMove) pm->ps->saberBlocked = BLOCKED_NONE; } } - else if ( pm->ps->saberBlocked <= BLOCKED_ATK_BOUNCE || !pm->ps->SaberActive() || (newMove < LS_PARRY_UR || newMove > LS_REFLECT_LL) ) + else if (pm->ps->saberBlocked <= BLOCKED_ATK_BOUNCE || !pm->ps->SaberActive() || (newMove < LS_PARRY_UR || newMove > LS_REFLECT_LL)) {//NPCs only clear blocked if not blocking? pm->ps->saberBlocked = BLOCKED_NONE; } - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { - if ( saberMoveData[newMove].trailLength > 0 ) + if (saberMoveData[newMove].trailLength > 0) { - pm->gent->client->ps.SaberActivateTrail( saberMoveData[newMove].trailLength ); // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter + pm->gent->client->ps.SaberActivateTrail(saberMoveData[newMove].trailLength); // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter } else { - pm->gent->client->ps.SaberDeactivateTrail( 0 ); + pm->gent->client->ps.SaberDeactivateTrail(0); } } } @@ -9655,22 +9889,22 @@ Generates a use event */ #define USE_DELAY 250 -void PM_Use( void ) +void PM_Use(void) { - if ( pm->ps->useTime > 0 ) + if (pm->ps->useTime > 0) { pm->ps->useTime -= pml.msec; - if ( pm->ps->useTime < 0 ) + if (pm->ps->useTime < 0) { pm->ps->useTime = 0; } } - if ( pm->ps->useTime > 0 ) { + if (pm->ps->useTime > 0) { return; } - if ( ! (pm->cmd.buttons & BUTTON_USE ) ) + if (!(pm->cmd.buttons & BUTTON_USE)) { pm->useEvent = 0; pm->ps->useTime = 0; @@ -9681,28 +9915,28 @@ void PM_Use( void ) pm->ps->useTime = USE_DELAY; } -extern saberMoveName_t PM_AttackForEnemyPos( qboolean allowFB, qboolean allowStabDown ); -saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ) +extern saberMoveName_t PM_AttackForEnemyPos(qboolean allowFB, qboolean allowStabDown); +saberMoveName_t PM_NPCSaberAttackFromQuad(int quad) { //FIXME: this should be an AI decision // It should be based on the enemy's current LS_ move, saberAnimLevel, // the jedi's difficulty level, rank and FP_OFFENSE skill... saberMoveName_t autoMove = LS_NONE; - if ( pm->gent && ((pm->gent->NPC && pm->gent->NPC->rank != RANK_ENSIGN && pm->gent->NPC->rank != RANK_CIVILIAN ) || (pm->gent->client && (pm->gent->client->NPC_class == CLASS_TAVION||pm->gent->client->NPC_class == CLASS_ALORA))) ) + if (pm->gent && ((pm->gent->NPC && pm->gent->NPC->rank != RANK_ENSIGN && pm->gent->NPC->rank != RANK_CIVILIAN) || (pm->gent->client && (pm->gent->client->NPC_class == CLASS_TAVION || pm->gent->client->NPC_class == CLASS_ALORA)))) { - autoMove = PM_AttackForEnemyPos( qtrue, qtrue ); + autoMove = PM_AttackForEnemyPos(qtrue, qtrue); } - if ( autoMove != LS_NONE && PM_SaberInSpecial( autoMove ) ) + if (autoMove != LS_NONE && PM_SaberInSpecial(autoMove)) {//if have opportunity to do a special attack, do one return autoMove; } else {//pick another one saberMoveName_t newmove = LS_NONE; - switch( quad ) + switch (quad) { case Q_T://blocked top - if ( Q_irand( 0, 1 ) ) + if (Q_irand(0, 1)) { newmove = LS_A_T2B; } @@ -9712,11 +9946,11 @@ saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ) } break; case Q_TR: - if ( !Q_irand( 0, 2 ) ) + if (!Q_irand(0, 2)) { newmove = LS_A_R2L; } - else if ( !Q_irand( 0, 1 ) ) + else if (!Q_irand(0, 1)) { newmove = LS_A_TR2BL; } @@ -9726,11 +9960,11 @@ saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ) } break; case Q_TL: - if ( !Q_irand( 0, 2 ) ) + if (!Q_irand(0, 2)) { newmove = LS_A_L2R; } - else if ( !Q_irand( 0, 1 ) ) + else if (!Q_irand(0, 1)) { newmove = LS_A_TL2BR; } @@ -9740,11 +9974,11 @@ saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ) } break; case Q_BR: - if ( !Q_irand( 0, 2 ) ) + if (!Q_irand(0, 2)) { newmove = LS_A_BR2TL; } - else if ( !Q_irand( 0, 1 ) ) + else if (!Q_irand(0, 1)) { newmove = LS_T1_BR_TR; } @@ -9754,11 +9988,11 @@ saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ) } break; case Q_BL: - if ( !Q_irand( 0, 2 ) ) + if (!Q_irand(0, 2)) { newmove = LS_A_BL2TR; } - else if ( !Q_irand( 0, 1 ) ) + else if (!Q_irand(0, 1)) { newmove = LS_T1_BL_TL; } @@ -9768,11 +10002,11 @@ saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ) } break; case Q_L: - if ( !Q_irand( 0, 2 ) ) + if (!Q_irand(0, 2)) { newmove = LS_A_L2R; } - else if ( !Q_irand( 0, 1 ) ) + else if (!Q_irand(0, 1)) { newmove = LS_T1__L_T_; } @@ -9782,11 +10016,11 @@ saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ) } break; case Q_R: - if ( !Q_irand( 0, 2 ) ) + if (!Q_irand(0, 2)) { newmove = LS_A_R2L; } - else if ( !Q_irand( 0, 1 ) ) + else if (!Q_irand(0, 1)) { newmove = LS_T1__R_T_; } @@ -9796,13 +10030,13 @@ saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ) } break; case Q_B: - if ( pm->gent + if (pm->gent && pm->gent->NPC - && pm->gent->NPC->rank >= RANK_LT_JG ) + && pm->gent->NPC->rank >= RANK_LT_JG) {//fencers and above can do bottom-up attack - if ( Q_irand( 0, pm->gent->NPC->rank ) >= RANK_LT_JG ) + if (Q_irand(0, pm->gent->NPC->rank) >= RANK_LT_JG) {//but not overly likely - newmove = PM_SaberLungeAttackMove( qtrue ); + newmove = PM_SaberLungeAttackMove(qtrue); } } break; @@ -9813,15 +10047,15 @@ saberMoveName_t PM_NPCSaberAttackFromQuad( int quad ) } } -int PM_SaberMoveQuadrantForMovement( usercmd_t *ucmd ) +int PM_SaberMoveQuadrantForMovement(usercmd_t *ucmd) { - if ( ucmd->rightmove > 0 ) + if (ucmd->rightmove > 0) {//moving right - if ( ucmd->forwardmove > 0 ) + if (ucmd->forwardmove > 0) {//forward right = TL2BR slash return Q_TL; } - else if ( ucmd->forwardmove < 0 ) + else if (ucmd->forwardmove < 0) {//backward right = BL2TR uppercut return Q_BL; } @@ -9830,13 +10064,13 @@ int PM_SaberMoveQuadrantForMovement( usercmd_t *ucmd ) return Q_L; } } - else if ( ucmd->rightmove < 0 ) + else if (ucmd->rightmove < 0) {//moving left - if ( ucmd->forwardmove > 0 ) + if (ucmd->forwardmove > 0) {//forward left = TR2BL slash return Q_TR; } - else if ( ucmd->forwardmove < 0 ) + else if (ucmd->forwardmove < 0) {//backward left = BR2TL uppercut return Q_BR; } @@ -9847,11 +10081,11 @@ int PM_SaberMoveQuadrantForMovement( usercmd_t *ucmd ) } else {//not moving left or right - if ( ucmd->forwardmove > 0 ) + if (ucmd->forwardmove > 0) {//forward= T2B slash return Q_T; } - else if ( ucmd->forwardmove < 0 ) + else if (ucmd->forwardmove < 0) {//backward= T2B slash //or B2T uppercut? return Q_T; } @@ -9863,47 +10097,62 @@ int PM_SaberMoveQuadrantForMovement( usercmd_t *ucmd ) //return Q_R;//???? } -void PM_SetAnimFrame( gentity_t *gent, int frame, qboolean torso, qboolean legs ) +void PM_SetAnimFrame(gentity_t *gent, int frame, qboolean torso, qboolean legs) { - if ( !gi.G2API_HaveWeGhoul2Models( gent->ghoul2 ) ) + if (!gi.G2API_HaveWeGhoul2Models(gent->ghoul2)) { return; } - int actualTime = (cg.time?cg.time:level.time); - if ( torso && gent->lowerLumbarBone != -1 )//gent->upperLumbarBone + int actualTime = (cg.time ? cg.time : level.time); + if (torso && gent->lowerLumbarBone != -1)//gent->upperLumbarBone { gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, //gent->upperLumbarBone - frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 ); - if ( gent->motionBone != -1 ) + frame, frame + 1, BONE_ANIM_OVERRIDE_FREEZE | BONE_ANIM_BLEND, 1, actualTime, frame, 150); + if (gent->motionBone != -1) { gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->motionBone, //gent->upperLumbarBone - frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 ); + frame, frame + 1, BONE_ANIM_OVERRIDE_FREEZE | BONE_ANIM_BLEND, 1, actualTime, frame, 150); + } + } + if ( torso && gent->headModel > 0 && gent->headLowerLumbarBone != -1) + { + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->headModel], gent->headLowerLumbarBone, //gent->upperLumbarBone + frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 ); + if ( gent->motionBone != -1 ) + { + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->headModel], gent->headMotionBone, //gent->upperLumbarBone + frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 ); } } if ( legs && gent->rootBone != -1 ) { gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->rootBone, + frame, frame + 1, BONE_ANIM_OVERRIDE_FREEZE | BONE_ANIM_BLEND, 1, actualTime, frame, 150); + } + if ( legs && gent->headModel > 0 && gent->headRootBone != -1 ) + { + gi.G2API_SetBoneAnimIndex(&gent->ghoul2[gent->headModel], gent->headRootBone, frame, frame+1, BONE_ANIM_OVERRIDE_FREEZE|BONE_ANIM_BLEND, 1, actualTime, frame, 150 ); } } -int PM_SaberLockWinAnim( saberLockResult_t result, int breakType ) +int PM_SaberLockWinAnim(saberLockResult_t result, int breakType) { int winAnim = -1; - switch ( pm->ps->torsoAnim ) + switch (pm->ps->torsoAnim) { -/* - default: -#ifndef FINAL_BUILD + /* + default: + #ifndef FINAL_BUILD Com_Printf( S_COLOR_RED"ERROR-PM_SaberLockBreak: %s not in saberlock anim, anim = (%d)%s\n", pm->gent->NPC_type, pm->ps->torsoAnim, animTable[pm->ps->torsoAnim].name ); -#endif -*/ + #endif + */ case BOTH_BF2LOCK: - if ( breakType == SABERLOCK_SUPERBREAK ) + if (breakType == SABERLOCK_SUPERBREAK) { winAnim = BOTH_LK_S_S_T_SB_1_W; } - else if ( result == LOCK_DRAW ) + else if (result == LOCK_DRAW) { winAnim = BOTH_BF1BREAK; } @@ -9914,11 +10163,11 @@ int PM_SaberLockWinAnim( saberLockResult_t result, int breakType ) } break; case BOTH_BF1LOCK: - if ( breakType == SABERLOCK_SUPERBREAK ) + if (breakType == SABERLOCK_SUPERBREAK) { winAnim = BOTH_LK_S_S_T_SB_1_W; } - else if ( result == LOCK_DRAW ) + else if (result == LOCK_DRAW) { winAnim = BOTH_KNOCKDOWN4; } @@ -9929,11 +10178,11 @@ int PM_SaberLockWinAnim( saberLockResult_t result, int breakType ) } break; case BOTH_CWCIRCLELOCK: - if ( breakType == SABERLOCK_SUPERBREAK ) + if (breakType == SABERLOCK_SUPERBREAK) { winAnim = BOTH_LK_S_S_S_SB_1_W; } - else if ( result == LOCK_DRAW ) + else if (result == LOCK_DRAW) { pm->ps->saberMove = pm->ps->saberBounceMove = LS_V1_BL; pm->ps->saberBlocked = BLOCKED_PARRY_BROKEN; @@ -9945,11 +10194,11 @@ int PM_SaberLockWinAnim( saberLockResult_t result, int breakType ) } break; case BOTH_CCWCIRCLELOCK: - if ( breakType == SABERLOCK_SUPERBREAK ) + if (breakType == SABERLOCK_SUPERBREAK) { winAnim = BOTH_LK_S_S_S_SB_1_W; } - else if ( result == LOCK_DRAW ) + else if (result == LOCK_DRAW) { pm->ps->saberMove = pm->ps->saberBounceMove = LS_V1_BR; pm->ps->saberBlocked = BLOCKED_PARRY_BROKEN; @@ -9964,44 +10213,44 @@ int PM_SaberLockWinAnim( saberLockResult_t result, int breakType ) //must be using new system: break; } - if ( winAnim != -1 ) + if (winAnim != -1) { - PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); pm->ps->weaponTime = pm->ps->torsoAnimTimer; pm->ps->saberBlocked = BLOCKED_NONE; pm->ps->weaponstate = WEAPON_FIRING; - if ( breakType == SABERLOCK_SUPERBREAK - && winAnim != BOTH_LK_ST_DL_T_SB_1_W ) + if (breakType == SABERLOCK_SUPERBREAK + && winAnim != BOTH_LK_ST_DL_T_SB_1_W) {//going to attack with saber, do a saber trail - pm->ps->SaberActivateTrail( 200 ); + pm->ps->SaberActivateTrail(200); } } return winAnim; } -int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int breakType ) +int PM_SaberLockLoseAnim(gentity_t *genemy, saberLockResult_t result, int breakType) { int loseAnim = -1; - switch ( genemy->client->ps.torsoAnim ) + switch (genemy->client->ps.torsoAnim) { -/* - default: -#ifndef FINAL_BUILD + /* + default: + #ifndef FINAL_BUILD Com_Printf( S_COLOR_RED"ERROR-PM_SaberLockBreak: %s not in saberlock anim, anim = (%d)%s\n", genemy->NPC_type, genemy->client->ps.torsoAnim, animTable[genemy->client->ps.torsoAnim].name ); -#endif -*/ + #endif + */ case BOTH_BF2LOCK: - if ( breakType == SABERLOCK_SUPERBREAK ) + if (breakType == SABERLOCK_SUPERBREAK) { loseAnim = BOTH_LK_S_S_T_SB_1_L; } - else if ( result == LOCK_DRAW ) + else if (result == LOCK_DRAW) { loseAnim = BOTH_BF1BREAK; } else { - if ( result == LOCK_STALEMATE ) + if (result == LOCK_STALEMATE) {//no-one won genemy->client->ps.saberMove = LS_K1_T_; loseAnim = BOTH_K1_S1_T_; @@ -10013,17 +10262,17 @@ int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int break } break; case BOTH_BF1LOCK: - if ( breakType == SABERLOCK_SUPERBREAK ) + if (breakType == SABERLOCK_SUPERBREAK) { loseAnim = BOTH_LK_S_S_T_SB_1_L; } - else if ( result == LOCK_DRAW ) + else if (result == LOCK_DRAW) { loseAnim = BOTH_KNOCKDOWN4; } else { - if ( result == LOCK_STALEMATE ) + if (result == LOCK_STALEMATE) {//no-one won genemy->client->ps.saberMove = LS_A_T2B; loseAnim = BOTH_A3_T__B_; @@ -10035,11 +10284,11 @@ int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int break } break; case BOTH_CWCIRCLELOCK: - if ( breakType == SABERLOCK_SUPERBREAK ) + if (breakType == SABERLOCK_SUPERBREAK) { loseAnim = BOTH_LK_S_S_S_SB_1_L; } - else if ( result == LOCK_DRAW ) + else if (result == LOCK_DRAW) { genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BL; genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN; @@ -10047,7 +10296,7 @@ int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int break } else { - if ( result == LOCK_STALEMATE ) + if (result == LOCK_STALEMATE) {//no-one won loseAnim = BOTH_CCWCIRCLEBREAK; } @@ -10065,11 +10314,11 @@ int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int break } break; case BOTH_CCWCIRCLELOCK: - if ( breakType == SABERLOCK_SUPERBREAK ) + if (breakType == SABERLOCK_SUPERBREAK) { loseAnim = BOTH_LK_S_S_S_SB_1_L; } - else if ( result == LOCK_DRAW ) + else if (result == LOCK_DRAW) { genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_V1_BR; genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN; @@ -10077,7 +10326,7 @@ int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int break } else { - if ( result == LOCK_STALEMATE ) + if (result == LOCK_STALEMATE) {//no-one won loseAnim = BOTH_CWCIRCLEBREAK; } @@ -10095,9 +10344,9 @@ int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int break } break; } - if ( loseAnim != -1 ) + if (loseAnim != -1) { - NPC_SetAnim( genemy, SETANIM_BOTH, loseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + NPC_SetAnim(genemy, SETANIM_BOTH, loseAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); genemy->client->ps.weaponTime = genemy->client->ps.torsoAnimTimer;// + 250; genemy->client->ps.saberBlocked = BLOCKED_NONE; genemy->client->ps.weaponstate = WEAPON_READY; @@ -10105,10 +10354,10 @@ int PM_SaberLockLoseAnim( gentity_t *genemy, saberLockResult_t result, int break return loseAnim; } -int PM_SaberLockResultAnim( gentity_t *duelist, int lockOrBreakOrSuperBreak, int winOrLose ) +int PM_SaberLockResultAnim(gentity_t *duelist, int lockOrBreakOrSuperBreak, int winOrLose) { int baseAnim = duelist->client->ps.torsoAnim; - switch ( baseAnim ) + switch (baseAnim) { case BOTH_LK_S_S_S_L_2: //lock if I'm using single vs. a single and other intitiated baseAnim = BOTH_LK_S_S_S_L_1; @@ -10130,11 +10379,11 @@ int PM_SaberLockResultAnim( gentity_t *duelist, int lockOrBreakOrSuperBreak, int break; } //what kind of break? - if ( lockOrBreakOrSuperBreak == SABERLOCK_BREAK ) + if (lockOrBreakOrSuperBreak == SABERLOCK_BREAK) { baseAnim -= 2; } - else if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK ) + else if (lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK) { baseAnim += 1; } @@ -10143,19 +10392,19 @@ int PM_SaberLockResultAnim( gentity_t *duelist, int lockOrBreakOrSuperBreak, int return -1; } //win or lose? - if ( winOrLose == SABERLOCK_WIN ) + if (winOrLose == SABERLOCK_WIN) { baseAnim += 1; } //play the anim and hold it - NPC_SetAnim( duelist, SETANIM_BOTH, baseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + NPC_SetAnim(duelist, SETANIM_BOTH, baseAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); - if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK - && winOrLose == SABERLOCK_LOSE ) + if (lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK + && winOrLose == SABERLOCK_LOSE) {//if you lose a superbreak, you're defenseless //make saberent not block gentity_t *saberent = &g_entities[duelist->client->ps.saberEntityNum]; - if ( saberent ) + if (saberent) { VectorClear(saberent->mins); VectorClear(saberent->maxs); @@ -10170,29 +10419,29 @@ int PM_SaberLockResultAnim( gentity_t *duelist, int lockOrBreakOrSuperBreak, int //no attacking during this anim duelist->client->ps.weaponTime = duelist->client->ps.torsoAnimTimer; duelist->client->ps.saberBlocked = BLOCKED_NONE; - if ( lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK + if (lockOrBreakOrSuperBreak == SABERLOCK_SUPERBREAK && winOrLose == SABERLOCK_WIN - && baseAnim != BOTH_LK_ST_DL_T_SB_1_W ) + && baseAnim != BOTH_LK_ST_DL_T_SB_1_W) {//going to attack with saber, do a saber trail - duelist->client->ps.SaberActivateTrail( 200 ); + duelist->client->ps.SaberActivateTrail(200); } return baseAnim; } -void PM_SaberLockBreak( gentity_t *gent, gentity_t *genemy, saberLockResult_t result, int victoryStrength ) +void PM_SaberLockBreak(gentity_t *gent, gentity_t *genemy, saberLockResult_t result, int victoryStrength) { int winAnim = -1, loseAnim = -1; int breakType = SABERLOCK_BREAK; qboolean singleVsSingle = qtrue; - if ( result == LOCK_VICTORY - && Q_irand(0,7) < victoryStrength ) + if (result == LOCK_VICTORY + && Q_irand(0, 7) < victoryStrength) { - if ( genemy + if (genemy && genemy->NPC && ((genemy->NPC->aiFlags&NPCAI_BOSS_CHARACTER) - ||(genemy->NPC->aiFlags&NPCAI_SUBBOSS_CHARACTER) - ||(genemy->client&&genemy->client->NPC_class == CLASS_SHADOWTROOPER)) + || (genemy->NPC->aiFlags&NPCAI_SUBBOSS_CHARACTER) + || (genemy->client&&genemy->client->NPC_class == CLASS_SHADOWTROOPER)) && Q_irand(0, 4) ) {//less of a chance of getting a superbreak against a boss @@ -10200,92 +10449,97 @@ void PM_SaberLockBreak( gentity_t *gent, gentity_t *genemy, saberLockResult_t re } else { - breakType = SABERLOCK_SUPERBREAK; + if (g_saberLockSuperBreaks->integer) { + breakType = SABERLOCK_SUPERBREAK; + } + else { + breakType = SABERLOCK_BREAK; + } } } else { breakType = SABERLOCK_BREAK; } - winAnim = PM_SaberLockWinAnim( result, breakType ); - if ( winAnim != -1 ) + winAnim = PM_SaberLockWinAnim(result, breakType); + if (winAnim != -1) {//a single vs. single break - if ( genemy && genemy->client ) + if (genemy && genemy->client) { - loseAnim = PM_SaberLockLoseAnim( genemy, result, breakType ); + loseAnim = PM_SaberLockLoseAnim(genemy, result, breakType); } } else {//must be a saberlock that's not between single and single... singleVsSingle = qfalse; - winAnim = PM_SaberLockResultAnim( gent, breakType, SABERLOCK_WIN ); + winAnim = PM_SaberLockResultAnim(gent, breakType, SABERLOCK_WIN); pm->ps->weaponstate = WEAPON_FIRING; - if ( genemy && genemy->client ) + if (genemy && genemy->client) { - loseAnim = PM_SaberLockResultAnim( genemy, breakType, SABERLOCK_LOSE ); + loseAnim = PM_SaberLockResultAnim(genemy, breakType, SABERLOCK_LOSE); genemy->client->ps.weaponstate = WEAPON_READY; } } - if ( d_saberCombat->integer ) + if (d_saberCombat->integer) { - Com_Printf( "%s won saber lock, anim = %s!\n", gent->NPC_type, animTable[winAnim].name ); - Com_Printf( "%s lost saber lock, anim = %s!\n", genemy->NPC_type, animTable[loseAnim].name ); + Com_Printf("%s won saber lock, anim = %s!\n", gent->NPC_type, animTable[winAnim].name); + Com_Printf("%s lost saber lock, anim = %s!\n", genemy->NPC_type, animTable[loseAnim].name); } pm->ps->saberLockTime = genemy->client->ps.saberLockTime = 0; pm->ps->saberLockEnemy = genemy->client->ps.saberLockEnemy = ENTITYNUM_NONE; pm->ps->saberMoveNext = LS_NONE; - if ( genemy && genemy->client ) + if (genemy && genemy->client) { genemy->client->ps.saberMoveNext = LS_NONE; } - PM_AddEvent( EV_JUMP ); - if ( result == LOCK_STALEMATE ) + PM_AddEvent(EV_JUMP); + if (result == LOCK_STALEMATE) {//no-one won - G_AddEvent( genemy, EV_JUMP, 0 ); + G_AddEvent(genemy, EV_JUMP, 0); } else { - if ( pm->ps->clientNum ) + if (pm->ps->clientNum) {//an NPC pm->ps->saberEventFlags |= SEF_LOCK_WON;//tell the winner to press the advantage } //painDebounceTime will stop them from doing anything genemy->painDebounceTime = level.time + genemy->client->ps.torsoAnimTimer + 500; - if ( Q_irand( 0, 1 ) ) + if (Q_irand(0, 1)) { - G_AddEvent( genemy, EV_PAIN, Q_irand( 0, 75 ) ); + G_AddEvent(genemy, EV_PAIN, Q_irand(0, 75)); } else { - if ( genemy->NPC ) + if (genemy->NPC) { genemy->NPC->blockedSpeechDebounceTime = 0; } - G_AddVoiceEvent( genemy, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 500 ); + G_AddVoiceEvent(genemy, Q_irand(EV_PUSHED1, EV_PUSHED3), 500); } - if ( result == LOCK_VICTORY ) + if (result == LOCK_VICTORY) {//one person won - if ( Q_irand( FORCE_LEVEL_1, FORCE_LEVEL_2 ) < pm->ps->forcePowerLevel[FP_SABER_OFFENSE] ) + if (Q_irand(FORCE_LEVEL_1, FORCE_LEVEL_2) < pm->ps->forcePowerLevel[FP_SABER_OFFENSE]) { - vec3_t throwDir = {0,0,350}; + vec3_t throwDir = { 0, 0, 350 }; int winMove = pm->ps->saberMove; - if ( !singleVsSingle ) + if (!singleVsSingle) {//all others have their own super breaks //so it doesn't try to set some other anim below winAnim = -1; } - else if ( winAnim == BOTH_LK_S_S_S_SB_1_W - || winAnim == BOTH_LK_S_S_T_SB_1_W ) + else if (winAnim == BOTH_LK_S_S_S_SB_1_W + || winAnim == BOTH_LK_S_S_T_SB_1_W) {//doing a superbreak on single-vs-single, don't do the old superbreaks this time //so it doesn't try to set some other anim below winAnim = -1; } else {//JK2-style - switch ( winAnim ) + switch (winAnim) { case BOTH_A3_T__B_: winAnim = BOTH_D1_TL___; @@ -10304,21 +10558,21 @@ void PM_SaberLockBreak( gentity_t *gent, gentity_t *genemy, saberLockResult_t re //FIXME: mod throwDir? break; } - if ( winAnim != BOTH_CCWCIRCLEBREAK ) + if (winAnim != BOTH_CCWCIRCLEBREAK) { - if ( (!genemy->s.number&&genemy->health<=25)//player low on health - ||(genemy->s.number&&genemy->client->NPC_class!=CLASS_KYLE&&genemy->client->NPC_class!=CLASS_LUKE&&genemy->client->NPC_class!=CLASS_TAVION&&genemy->client->NPC_class!=CLASS_ALORA&&genemy->client->NPC_class!=CLASS_DESANN)//any NPC that's not a boss character - ||(genemy->s.number&&genemy->health<=50) )//boss character with less than 50 health left + if ((!genemy->s.number&&genemy->health <= 25)//player low on health + || (genemy->s.number&&genemy->client->NPC_class != CLASS_KYLE&&genemy->client->NPC_class != CLASS_LUKE&&genemy->client->NPC_class != CLASS_TAVION&&genemy->client->NPC_class != CLASS_ALORA&&genemy->client->NPC_class != CLASS_DESANN)//any NPC that's not a boss character + || (genemy->s.number&&genemy->health <= 50))//boss character with less than 50 health left {//possibly knock saber out of hand OR cut hand off! - if ( Q_irand( 0, 25 ) < victoryStrength - && ((!genemy->s.number&&genemy->health<=10)||genemy->s.number) ) + if (Q_irand(0, 25) < victoryStrength + && ((!genemy->s.number&&genemy->health <= 10) || genemy->s.number)) { - NPC_SetAnim( genemy, SETANIM_BOTH, BOTH_RIGHTHANDCHOPPEDOFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );//force this + NPC_SetAnim(genemy, SETANIM_BOTH, BOTH_RIGHTHANDCHOPPEDOFF, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//force this genemy->client->dismembered = false; - G_DoDismemberment( genemy, genemy->client->renderInfo.handRPoint, MOD_SABER, 1000, HL_HAND_RT, qtrue ); - G_Damage( genemy, gent, gent, throwDir, genemy->client->renderInfo.handRPoint, genemy->health+10, DAMAGE_NO_PROTECTION|DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_SABER, HL_NONE ); + G_DoDismemberment(genemy, genemy->client->renderInfo.handRPoint, MOD_SABER, 1000, HL_HAND_RT, qtrue); + G_Damage(genemy, gent, gent, throwDir, genemy->client->renderInfo.handRPoint, genemy->health + 10, DAMAGE_NO_PROTECTION | DAMAGE_NO_ARMOR | DAMAGE_NO_KNOCKBACK | DAMAGE_NO_HIT_LOC, MOD_SABER, HL_NONE); - PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); pm->ps->weaponTime = pm->ps->torsoAnimTimer + 500; pm->ps->saberMove = winMove; pm->ps->saberBlocked = BLOCKED_NONE; @@ -10330,36 +10584,36 @@ void PM_SaberLockBreak( gentity_t *gent, gentity_t *genemy, saberLockResult_t re } //else see if we can knock the saber out of their hand //FIXME: for now, always disarm the right-hand saber - if ( !(genemy->client->ps.saber[0].saberFlags&SFL_NOT_DISARMABLE) ) + if (!(genemy->client->ps.saber[0].saberFlags&SFL_NOT_DISARMABLE)) { //add disarmBonus into this check - victoryStrength += pm->ps->SaberDisarmBonus( 0 )*2; - if ( (genemy->client->ps.saber[0].saberFlags&SFL_TWO_HANDED) - || (genemy->client->ps.dualSabers && genemy->client->ps.saber[1].Active()) ) + victoryStrength += pm->ps->SaberDisarmBonus(0) * 2; + if ((genemy->client->ps.saber[0].saberFlags&SFL_TWO_HANDED) + || (genemy->client->ps.dualSabers && genemy->client->ps.saber[1].Active())) {//defender gets a bonus for using a 2-handed saber or 2 sabers victoryStrength -= 2; } - if ( pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1 << FP_RAGE)) { victoryStrength += gent->client->ps.forcePowerLevel[FP_RAGE]; } - else if ( pm->ps->forceRageRecoveryTime > pm->cmd.serverTime ) + else if (pm->ps->forceRageRecoveryTime > pm->cmd.serverTime) { victoryStrength--; } - if ( genemy->client->ps.forceRageRecoveryTime > pm->cmd.serverTime ) + if (genemy->client->ps.forceRageRecoveryTime > pm->cmd.serverTime) { victoryStrength++; } - if ( Q_irand( 0, 10 ) < victoryStrength ) + if (Q_irand(0, 10) < victoryStrength) { - if ( !(genemy->client->ps.saber[0].saberFlags&SFL_TWO_HANDED) - || !Q_irand( 0, 1 ) ) + if (!(genemy->client->ps.saber[0].saberFlags&SFL_TWO_HANDED) + || !Q_irand(0, 1)) {//if it's a two-handed saber, it has a 50% chance of resisting a disarming - WP_SaberLose( genemy, throwDir ); - if ( winAnim != -1 ) + WP_SaberLose(genemy, throwDir); + if (winAnim != -1) { - PM_SetAnim( pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); pm->ps->weaponTime = pm->ps->torsoAnimTimer; pm->ps->saberMove = winMove; pm->ps->saberBlocked = BLOCKED_NONE; @@ -10373,59 +10627,59 @@ void PM_SaberLockBreak( gentity_t *gent, gentity_t *genemy, saberLockResult_t re } } -int G_SaberLockStrength( gentity_t *gent ) +int G_SaberLockStrength(gentity_t *gent) { int strength = gent->client->ps.saber[0].lockBonus; - if ( (gent->client->ps.saber[0].saberFlags&SFL_TWO_HANDED) ) + if ((gent->client->ps.saber[0].saberFlags&SFL_TWO_HANDED)) { strength += 1; } - if ( gent->client->ps.dualSabers && gent->client->ps.saber[1].Active() ) + if (gent->client->ps.dualSabers && gent->client->ps.saber[1].Active()) { strength += 1 + gent->client->ps.saber[1].lockBonus; } - if ( gent->client->ps.forcePowersActive&(1<client->ps.forcePowersActive&(1 << FP_RAGE)) { strength += gent->client->ps.forcePowerLevel[FP_RAGE]; } - else if ( gent->client->ps.forceRageRecoveryTime > pm->cmd.serverTime ) + else if (gent->client->ps.forceRageRecoveryTime > pm->cmd.serverTime) { strength--; } - if ( gent->s.number >= MAX_CLIENTS ) + if (gent->s.number >= MAX_CLIENTS) { - if ( gent->client->NPC_class == CLASS_DESANN || gent->client->NPC_class == CLASS_LUKE ) + if (gent->client->NPC_class == CLASS_DESANN || gent->client->NPC_class == CLASS_LUKE) { - strength += 5+Q_irand(0,g_spskill->integer); + strength += 5 + Q_irand(0, g_spskill->integer); } else { - strength += gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]+Q_irand(0,g_spskill->integer); - if ( gent->NPC ) + strength += gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE] + Q_irand(0, g_spskill->integer); + if (gent->NPC) { - if ( (gent->NPC->aiFlags&NPCAI_BOSS_CHARACTER) + if ((gent->NPC->aiFlags&NPCAI_BOSS_CHARACTER) || (gent->NPC->aiFlags&NPCAI_ROSH) - || gent->client->NPC_class == CLASS_SHADOWTROOPER ) + || gent->client->NPC_class == CLASS_SHADOWTROOPER) { - strength += Q_irand(0,2); + strength += Q_irand(0, 2); } - else if ( (gent->NPC->aiFlags&NPCAI_SUBBOSS_CHARACTER) ) + else if ((gent->NPC->aiFlags&NPCAI_SUBBOSS_CHARACTER)) { - strength += Q_irand(-1,1); + strength += Q_irand(-1, 1); } } } } else {//player - strength += gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]+Q_irand(0,g_spskill->integer)+Q_irand(0,1); + strength += gent->client->ps.forcePowerLevel[FP_SABER_OFFENSE] + Q_irand(0, g_spskill->integer) + Q_irand(0, 1); } return strength; } -qboolean PM_InSaberLockOld( int anim ) +qboolean PM_InSaberLockOld(int anim) { - switch ( anim ) + switch (anim) { case BOTH_BF2LOCK: case BOTH_BF1LOCK: @@ -10436,9 +10690,9 @@ qboolean PM_InSaberLockOld( int anim ) return qfalse; } -qboolean PM_InSaberLock( int anim ) +qboolean PM_InSaberLock(int anim) { - switch ( anim ) + switch (anim) { case BOTH_LK_S_DL_S_L_1: //lock if I'm using single vs. a dual case BOTH_LK_S_DL_T_L_1: //lock if I'm using single vs. a dual @@ -10467,70 +10721,70 @@ qboolean PM_InSaberLock( int anim ) return qtrue; break; default: - return PM_InSaberLockOld( anim ); + return PM_InSaberLockOld(anim); break; } //return qfalse; } -extern qboolean ValidAnimFileIndex ( int index ); -extern qboolean G_CheckIncrementLockAnim( int anim, int winOrLose ); -qboolean PM_SaberLocked( void ) +extern qboolean ValidAnimFileIndex(int index); +extern qboolean G_CheckIncrementLockAnim(int anim, int winOrLose); +qboolean PM_SaberLocked(void) { //FIXME: maybe kick out of saberlock? - if ( pm->ps->saberLockEnemy == ENTITYNUM_NONE ) + if (pm->ps->saberLockEnemy == ENTITYNUM_NONE) { - if ( PM_InSaberLock( pm->ps->torsoAnim ) ) + if (PM_InSaberLock(pm->ps->torsoAnim)) {//wtf? Maybe enemy died? - PM_SaberLockWinAnim( LOCK_STALEMATE, SABERLOCK_BREAK ); + PM_SaberLockWinAnim(LOCK_STALEMATE, SABERLOCK_BREAK); } return qfalse; } gentity_t *gent = pm->gent; - if ( !gent ) + if (!gent) { return qfalse; } gentity_t *genemy = &g_entities[pm->ps->saberLockEnemy]; - if ( !genemy ) + if (!genemy) { return qfalse; } - if ( PM_InSaberLock( pm->ps->torsoAnim ) && PM_InSaberLock( genemy->client->ps.torsoAnim ) ) + if (PM_InSaberLock(pm->ps->torsoAnim) && PM_InSaberLock(genemy->client->ps.torsoAnim)) { - if ( pm->ps->saberLockTime <= level.time + 500 - && pm->ps->saberLockEnemy != ENTITYNUM_NONE ) + if (pm->ps->saberLockTime <= level.time + 500 + && pm->ps->saberLockEnemy != ENTITYNUM_NONE) {//lock just ended - int strength = G_SaberLockStrength( gent ); - int eStrength = G_SaberLockStrength( genemy ); - if ( strength > 1 && eStrength > 1 && !Q_irand( 0, abs(strength-eStrength)+1 ) ) + int strength = G_SaberLockStrength(gent); + int eStrength = G_SaberLockStrength(genemy); + if (strength > 1 && eStrength > 1 && !Q_irand(0, abs(strength - eStrength) + 1)) {//both knock each other down! - PM_SaberLockBreak( gent, genemy, LOCK_DRAW, 0 ); + PM_SaberLockBreak(gent, genemy, LOCK_DRAW, 0); } else {//both "win" - PM_SaberLockBreak( gent, genemy, LOCK_STALEMATE, 0 ); + PM_SaberLockBreak(gent, genemy, LOCK_STALEMATE, 0); } return qtrue; } - else if ( pm->ps->saberLockTime < level.time ) + else if (pm->ps->saberLockTime < level.time) {//done... tie breaker above should have handled this, but...? - if ( PM_InSaberLock( pm->ps->torsoAnim ) && pm->ps->torsoAnimTimer > 0 ) + if (PM_InSaberLock(pm->ps->torsoAnim) && pm->ps->torsoAnimTimer > 0) { pm->ps->torsoAnimTimer = 0; } - if ( PM_InSaberLock( pm->ps->legsAnim ) && pm->ps->legsAnimTimer > 0 ) + if (PM_InSaberLock(pm->ps->legsAnim) && pm->ps->legsAnimTimer > 0) { pm->ps->legsAnimTimer = 0; } return qfalse; } - else if ( pm->cmd.buttons & BUTTON_ATTACK ) + else if (pm->cmd.buttons & BUTTON_ATTACK) {//holding attack - if ( !(pm->ps->pm_flags&PMF_ATTACK_HELD) ) + if (!(pm->ps->pm_flags&PMF_ATTACK_HELD)) {//tapping int remaining = 0; - if( ValidAnimFileIndex( gent->client->clientInfo.animFileIndex ) ) + if (ValidAnimFileIndex(gent->client->clientInfo.animFileIndex)) { animation_t *anim; float currentFrame, junk2; @@ -10541,126 +10795,126 @@ qboolean PM_SaberLocked( void ) #ifdef _DEBUG qboolean ret = #endif - gi.G2API_GetBoneAnimIndex( &gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, - (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &junk2, NULL ); - assert( ret ); // this would be pretty bad, the below code seems to assume the call succeeds. -gil + gi.G2API_GetBoneAnimIndex(&gent->ghoul2[gent->playerModel], gent->lowerLumbarBone, + (cg.time ? cg.time : level.time), ¤tFrame, &junk, &junk, &junk, &junk2, NULL); + assert(ret); // this would be pretty bad, the below code seems to assume the call succeeds. -gil - strength = G_SaberLockStrength( gent ); - if ( PM_InSaberLockOld( pm->ps->torsoAnim ) ) + strength = G_SaberLockStrength(gent); + if (PM_InSaberLockOld(pm->ps->torsoAnim)) {//old locks - if ( pm->ps->torsoAnim == BOTH_CCWCIRCLELOCK || - pm->ps->torsoAnim == BOTH_BF2LOCK ) + if (pm->ps->torsoAnim == BOTH_CCWCIRCLELOCK || + pm->ps->torsoAnim == BOTH_BF2LOCK) { - curFrame = floor( currentFrame )-strength; + curFrame = floor(currentFrame) - strength; //drop my frame one - if ( curFrame <= anim->firstFrame ) + if (curFrame <= anim->firstFrame) {//I won! Break out - PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength ); + PM_SaberLockBreak(gent, genemy, LOCK_VICTORY, strength); return qtrue; } else { - PM_SetAnimFrame( gent, curFrame, qtrue, qtrue ); - remaining = curFrame-anim->firstFrame; - if ( d_saberCombat->integer ) + PM_SetAnimFrame(gent, curFrame, qtrue, qtrue); + remaining = curFrame - anim->firstFrame; + if (d_saberCombat->integer) { - Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining ); + Com_Printf("%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining); } } } else { - curFrame = ceil( currentFrame )+strength; + curFrame = ceil(currentFrame) + strength; //advance my frame one - if ( curFrame >= anim->firstFrame+anim->numFrames ) + if (curFrame >= anim->firstFrame + anim->numFrames) {//I won! Break out - PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength ); + PM_SaberLockBreak(gent, genemy, LOCK_VICTORY, strength); return qtrue; } else { - PM_SetAnimFrame( gent, curFrame, qtrue, qtrue ); - remaining = anim->firstFrame+anim->numFrames-curFrame; - if ( d_saberCombat->integer ) + PM_SetAnimFrame(gent, curFrame, qtrue, qtrue); + remaining = anim->firstFrame + anim->numFrames - curFrame; + if (d_saberCombat->integer) { - Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining ); + Com_Printf("%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining); } } } } else {//new locks - if ( G_CheckIncrementLockAnim( pm->ps->torsoAnim, SABERLOCK_WIN ) ) + if (G_CheckIncrementLockAnim(pm->ps->torsoAnim, SABERLOCK_WIN)) { - curFrame = ceil( currentFrame )+strength; + curFrame = ceil(currentFrame) + strength; //advance my frame one - if ( curFrame >= anim->firstFrame+anim->numFrames ) + if (curFrame >= anim->firstFrame + anim->numFrames) {//I won! Break out - PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength ); + PM_SaberLockBreak(gent, genemy, LOCK_VICTORY, strength); return qtrue; } else { - PM_SetAnimFrame( gent, curFrame, qtrue, qtrue ); - remaining = anim->firstFrame+anim->numFrames-curFrame; - if ( d_saberCombat->integer ) + PM_SetAnimFrame(gent, curFrame, qtrue, qtrue); + remaining = anim->firstFrame + anim->numFrames - curFrame; + if (d_saberCombat->integer) { - Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining ); + Com_Printf("%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining); } } } else { - curFrame = floor( currentFrame )-strength; + curFrame = floor(currentFrame) - strength; //drop my frame one - if ( curFrame <= anim->firstFrame ) + if (curFrame <= anim->firstFrame) {//I won! Break out - PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, strength ); + PM_SaberLockBreak(gent, genemy, LOCK_VICTORY, strength); return qtrue; } else { - PM_SetAnimFrame( gent, curFrame, qtrue, qtrue ); - remaining = curFrame-anim->firstFrame; - if ( d_saberCombat->integer ) + PM_SetAnimFrame(gent, curFrame, qtrue, qtrue); + remaining = curFrame - anim->firstFrame; + if (d_saberCombat->integer) { - Com_Printf( "%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining ); + Com_Printf("%s pushing in saber lock, %d frames to go!\n", gent->NPC_type, remaining); } } } } - if ( !Q_irand( 0, 2 ) ) + if (!Q_irand(0, 2)) { - if ( pm->ps->clientNum < MAX_CLIENTS ) + if (pm->ps->clientNum < MAX_CLIENTS) { - if ( !Q_irand( 0, 3 ) ) + if (!Q_irand(0, 3)) { - PM_AddEvent( EV_JUMP ); + PM_AddEvent(EV_JUMP); } else { - PM_AddEvent( Q_irand( EV_PUSHED1, EV_PUSHED3 ) ); + PM_AddEvent(Q_irand(EV_PUSHED1, EV_PUSHED3)); } } else { - if ( gent->NPC && gent->NPC->blockedSpeechDebounceTime < level.time ) + if (gent->NPC && gent->NPC->blockedSpeechDebounceTime < level.time) { - switch ( Q_irand( 0, 3 ) ) + switch (Q_irand(0, 3)) { case 0: - PM_AddEvent( EV_JUMP ); + PM_AddEvent(EV_JUMP); break; case 1: - PM_AddEvent( Q_irand( EV_ANGER1, EV_ANGER3 ) ); + PM_AddEvent(Q_irand(EV_ANGER1, EV_ANGER3)); gent->NPC->blockedSpeechDebounceTime = level.time + 3000; break; case 2: - PM_AddEvent( Q_irand( EV_TAUNT1, EV_TAUNT3 ) ); + PM_AddEvent(Q_irand(EV_TAUNT1, EV_TAUNT3)); gent->NPC->blockedSpeechDebounceTime = level.time + 3000; break; case 3: - PM_AddEvent( Q_irand( EV_GLOAT1, EV_GLOAT3 ) ); + PM_AddEvent(Q_irand(EV_GLOAT1, EV_GLOAT3)); gent->NPC->blockedSpeechDebounceTime = level.time + 3000; break; } @@ -10673,7 +10927,7 @@ qboolean PM_SaberLocked( void ) return qfalse; } - if( ValidAnimFileIndex( genemy->client->clientInfo.animFileIndex ) ) + if (ValidAnimFileIndex(genemy->client->clientInfo.animFileIndex)) { animation_t *anim; anim = &level.knownAnimFileSets[genemy->client->clientInfo.animFileIndex].animations[genemy->client->ps.torsoAnim]; @@ -10684,47 +10938,47 @@ qboolean PM_SaberLocked( void ) gi.G2API_GetBoneAnimIndex( &genemy->ghoul2[genemy->playerModel], genemy->lowerLumbarBone, (cg.time?cg.time:level.time), ¤tFrame, &junk, &junk, &junk, &junk2, NULL ); */ - if ( !Q_irand( 0, 2 ) ) + if (!Q_irand(0, 2)) { - switch ( Q_irand( 0, 3 ) ) + switch (Q_irand(0, 3)) { case 0: - G_AddEvent( genemy, EV_PAIN, floor((float)genemy->health/genemy->max_health*100.0f) ); + G_AddEvent(genemy, EV_PAIN, floor((float)genemy->health / genemy->max_health*100.0f)); break; case 1: - G_AddVoiceEvent( genemy, Q_irand( EV_PUSHED1, EV_PUSHED3 ), 500 ); + G_AddVoiceEvent(genemy, Q_irand(EV_PUSHED1, EV_PUSHED3), 500); break; case 2: - G_AddVoiceEvent( genemy, Q_irand( EV_CHOKE1, EV_CHOKE3 ), 500 ); + G_AddVoiceEvent(genemy, Q_irand(EV_CHOKE1, EV_CHOKE3), 500); break; case 3: - G_AddVoiceEvent( genemy, EV_PUSHFAIL, 2000 ); + G_AddVoiceEvent(genemy, EV_PUSHFAIL, 2000); break; } } - if ( PM_InSaberLockOld( genemy->client->ps.torsoAnim ) ) + if (PM_InSaberLockOld(genemy->client->ps.torsoAnim)) { - if ( genemy->client->ps.torsoAnim == BOTH_CCWCIRCLELOCK || - genemy->client->ps.torsoAnim == BOTH_BF2LOCK ) + if (genemy->client->ps.torsoAnim == BOTH_CCWCIRCLELOCK || + genemy->client->ps.torsoAnim == BOTH_BF2LOCK) { - PM_SetAnimFrame( genemy, anim->firstFrame+anim->numFrames-remaining, qtrue, qtrue ); + PM_SetAnimFrame(genemy, anim->firstFrame + anim->numFrames - remaining, qtrue, qtrue); } else { - PM_SetAnimFrame( genemy, anim->firstFrame+remaining, qtrue, qtrue ); + PM_SetAnimFrame(genemy, anim->firstFrame + remaining, qtrue, qtrue); } } else {//new locks //??? - if ( G_CheckIncrementLockAnim( genemy->client->ps.torsoAnim, SABERLOCK_LOSE ) ) + if (G_CheckIncrementLockAnim(genemy->client->ps.torsoAnim, SABERLOCK_LOSE)) { - PM_SetAnimFrame( genemy, anim->firstFrame+anim->numFrames-remaining, qtrue, qtrue ); + PM_SetAnimFrame(genemy, anim->firstFrame + anim->numFrames - remaining, qtrue, qtrue); } else { - PM_SetAnimFrame( genemy, anim->firstFrame+remaining, qtrue, qtrue ); + PM_SetAnimFrame(genemy, anim->firstFrame + remaining, qtrue, qtrue); } } } @@ -10740,35 +10994,35 @@ qboolean PM_SaberLocked( void ) } else {//something broke us out of it - if ( gent->painDebounceTime > level.time && genemy->painDebounceTime > level.time ) + if (gent->painDebounceTime > level.time && genemy->painDebounceTime > level.time) { - PM_SaberLockBreak( gent, genemy, LOCK_DRAW, 0 ); + PM_SaberLockBreak(gent, genemy, LOCK_DRAW, 0); } - else if ( gent->painDebounceTime > level.time ) + else if (gent->painDebounceTime > level.time) { - PM_SaberLockBreak( genemy, gent, LOCK_VICTORY, 0 ); + PM_SaberLockBreak(genemy, gent, LOCK_VICTORY, 0); } - else if ( genemy->painDebounceTime > level.time ) + else if (genemy->painDebounceTime > level.time) { - PM_SaberLockBreak( gent, genemy, LOCK_VICTORY, 0 ); + PM_SaberLockBreak(gent, genemy, LOCK_VICTORY, 0); } else { - PM_SaberLockBreak( gent, genemy, LOCK_STALEMATE, 0 ); + PM_SaberLockBreak(gent, genemy, LOCK_STALEMATE, 0); } } return qtrue; } -qboolean G_EnemyInKickRange( gentity_t *self, gentity_t *enemy ) +qboolean G_EnemyInKickRange(gentity_t *self, gentity_t *enemy) { - if ( !self || !enemy ) + if (!self || !enemy) { return qfalse; } - if ( fabs(self->currentOrigin[2]-enemy->currentOrigin[2]) < 32 ) + if (fabs(self->currentOrigin[2] - enemy->currentOrigin[2]) < 32) {//generally at same height - if ( DistanceHorizontal( self->currentOrigin, enemy->currentOrigin ) <= (STAFF_KICK_RANGE+8.0f+(self->maxs[0]*1.5f)+(enemy->maxs[0]*1.5f)) ) + if (DistanceHorizontal(self->currentOrigin, enemy->currentOrigin) <= (STAFF_KICK_RANGE + 8.0f + (self->maxs[0] * 1.5f) + (enemy->maxs[0] * 1.5f))) {//within kicking range! return qtrue; } @@ -10776,11 +11030,11 @@ qboolean G_EnemyInKickRange( gentity_t *self, gentity_t *enemy ) return qfalse; } -qboolean G_CanKickEntity( gentity_t *self, gentity_t *target ) +qboolean G_CanKickEntity(gentity_t *self, gentity_t *target) { - if ( target && target->client - && !PM_InKnockDown( &target->client->ps ) - && G_EnemyInKickRange( self, target ) ) + if (target && target->client + && !PM_InKnockDown(&target->client->ps) + && G_EnemyInKickRange(self, target)) { return qtrue; } @@ -10805,7 +11059,7 @@ float PM_GroundDistance(void) float G_GroundDistance(gentity_t *self) { - if ( !self ) + if (!self) {//wtf?!! return Q3_INFINITE; } @@ -10823,26 +11077,26 @@ float G_GroundDistance(gentity_t *self) return VectorLength(down); } -saberMoveName_t G_PickAutoKick( gentity_t *self, gentity_t *enemy, qboolean storeMove ) +saberMoveName_t G_PickAutoKick(gentity_t *self, gentity_t *enemy, qboolean storeMove) { saberMoveName_t kickMove = LS_NONE; - if ( !self || !self->client ) + if (!self || !self->client) { return LS_NONE; } - if ( !enemy ) + if (!enemy) { return LS_NONE; } - vec3_t v_fwd, v_rt, enemyDir, fwdAngs = {0,self->client->ps.viewangles[YAW],0}; - VectorSubtract( enemy->currentOrigin, self->currentOrigin, enemyDir ); - VectorNormalize( enemyDir );//not necessary, I guess, but doesn't happen often - AngleVectors( fwdAngs, v_fwd, v_rt, NULL ); - float fDot = DotProduct( enemyDir, v_fwd ); - float rDot = DotProduct( enemyDir, v_rt ); - if ( fabs( rDot ) > 0.5f && fabs( fDot ) < 0.5f ) + vec3_t v_fwd, v_rt, enemyDir, fwdAngs = { 0, self->client->ps.viewangles[YAW], 0 }; + VectorSubtract(enemy->currentOrigin, self->currentOrigin, enemyDir); + VectorNormalize(enemyDir);//not necessary, I guess, but doesn't happen often + AngleVectors(fwdAngs, v_fwd, v_rt, NULL); + float fDot = DotProduct(enemyDir, v_fwd); + float rDot = DotProduct(enemyDir, v_rt); + if (fabs(rDot) > 0.5f && fabs(fDot) < 0.5f) {//generally to one side - if ( rDot > 0 ) + if (rDot > 0) {//kick right kickMove = LS_KICK_R; } @@ -10851,9 +11105,9 @@ saberMoveName_t G_PickAutoKick( gentity_t *self, gentity_t *enemy, qboolean stor kickMove = LS_KICK_L; } } - else if ( fabs( fDot ) > 0.5f && fabs( rDot ) < 0.5f ) + else if (fabs(fDot) > 0.5f && fabs(rDot) < 0.5f) {//generally in front or behind us - if ( fDot > 0 ) + if (fDot > 0) {//kick fwd kickMove = LS_KICK_F; } @@ -10865,20 +11119,20 @@ saberMoveName_t G_PickAutoKick( gentity_t *self, gentity_t *enemy, qboolean stor else {//diagonal to us, kick would miss } - if ( kickMove != LS_NONE ) + if (kickMove != LS_NONE) {//have a valid one to do - if ( self->client->ps.groundEntityNum == ENTITYNUM_NONE ) + if (self->client->ps.groundEntityNum == ENTITYNUM_NONE) {//if in air, convert kick to an in-air kick - float gDist = G_GroundDistance( self ); + float gDist = G_GroundDistance(self); //let's only allow air kicks if a certain distance from the ground //it's silly to be able to do them right as you land. //also looks wrong to transition from a non-complete flip anim... - if ((!PM_FlippingAnim( self->client->ps.legsAnim ) || self->client->ps.legsAnimTimer <= 0) && + if ((!PM_FlippingAnim(self->client->ps.legsAnim) || self->client->ps.legsAnimTimer <= 0) && gDist > 64.0f && //strict minimum - gDist > (-self->client->ps.velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well + gDist > (-self->client->ps.velocity[2]) - 64.0f //make sure we are high to ground relative to downward velocity as well ) { - switch ( kickMove ) + switch (kickMove) { case LS_KICK_F: kickMove = LS_KICK_F_AIR; @@ -10899,13 +11153,13 @@ saberMoveName_t G_PickAutoKick( gentity_t *self, gentity_t *enemy, qboolean stor } else {//leave it as a normal kick unless we're too high up - if ( gDist > 128.0f || self->client->ps.velocity[2] >= 0 ) + if (gDist > 128.0f || self->client->ps.velocity[2] >= 0) { //off ground, but too close to ground kickMove = LS_NONE; } } } - if ( storeMove ) + if (storeMove) { self->client->ps.saberMoveNext = kickMove; } @@ -10913,18 +11167,18 @@ saberMoveName_t G_PickAutoKick( gentity_t *self, gentity_t *enemy, qboolean stor return kickMove; } -saberMoveName_t PM_PickAutoKick( gentity_t *enemy ) +saberMoveName_t PM_PickAutoKick(gentity_t *enemy) { - return G_PickAutoKick( pm->gent, enemy, qfalse ); + return G_PickAutoKick(pm->gent, enemy, qfalse); } -saberMoveName_t G_PickAutoMultiKick( gentity_t *self, qboolean allowSingles, qboolean storeMove ) +saberMoveName_t G_PickAutoMultiKick(gentity_t *self, qboolean allowSingles, qboolean storeMove) { gentity_t *ent; gentity_t *entityList[MAX_GENTITIES]; vec3_t mins, maxs; int i, e; - int radius = ((self->maxs[0]*1.5f)+(self->maxs[0]*1.5f)+STAFF_KICK_RANGE+24.0f);//a little wide on purpose + int radius = ((self->maxs[0] * 1.5f) + (self->maxs[0] * 1.5f) + STAFF_KICK_RANGE + 24.0f);//a little wide on purpose vec3_t center; saberMoveName_t kickMove, bestKick = LS_NONE; float distToEnt, bestDistToEnt = Q3_INFINITE; @@ -10935,54 +11189,54 @@ saberMoveName_t G_PickAutoMultiKick( gentity_t *self, qboolean allowSingles, qbo int enemiesLeft = 0; int enemiesSpin = 0; - if ( !self || !self->client ) + if (!self || !self->client) { return LS_NONE; } - VectorCopy( self->currentOrigin, center ); + VectorCopy(self->currentOrigin, center); - for ( i = 0 ; i < 3 ; i++ ) + for (i = 0; i < 3; i++) { mins[i] = center[i] - radius; maxs[i] = center[i] + radius; } - int numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); + int numListedEntities = gi.EntitiesInBox(mins, maxs, entityList, MAX_GENTITIES); - for ( e = 0 ; e < numListedEntities ; e++ ) + for (e = 0; e < numListedEntities; e++) { - ent = entityList[ e ]; + ent = entityList[e]; if (ent == self) continue; if (ent->owner == self) continue; - if ( !(ent->inuse) ) + if (!(ent->inuse)) continue; //not a client? - if ( !ent->client ) + if (!ent->client) continue; //ally? - if ( ent->client->playerTeam == self->client->playerTeam ) + if (ent->client->playerTeam == self->client->playerTeam) continue; //dead? - if ( ent->health <= 0 ) + if (ent->health <= 0) continue; //too far? - distToEnt = DistanceSquared( ent->currentOrigin, center ); - if ( distToEnt > (radius*radius) ) + distToEnt = DistanceSquared(ent->currentOrigin, center); + if (distToEnt > (radius*radius)) continue; - kickMove = G_PickAutoKick( self, ent, qfalse ); - if ( kickMove == LS_KICK_F_AIR + kickMove = G_PickAutoKick(self, ent, qfalse); + if (kickMove == LS_KICK_F_AIR && kickMove == LS_KICK_B_AIR && kickMove == LS_KICK_R_AIR - && kickMove == LS_KICK_L_AIR ) + && kickMove == LS_KICK_L_AIR) {//in air? Can't do multikicks } else { - switch ( kickMove ) + switch (kickMove) { case LS_KICK_F: enemiesFront++; @@ -11001,10 +11255,10 @@ saberMoveName_t G_PickAutoMultiKick( gentity_t *self, qboolean allowSingles, qbo break; } } - if ( allowSingles ) + if (allowSingles) { - if ( kickMove != LS_NONE - && distToEnt < bestDistToEnt ) + if (kickMove != LS_NONE + && distToEnt < bestDistToEnt) { bestKick = kickMove; bestEnt = ent; @@ -11012,41 +11266,48 @@ saberMoveName_t G_PickAutoMultiKick( gentity_t *self, qboolean allowSingles, qbo } } kickMove = LS_NONE; - if ( self->client->ps.groundEntityNum != ENTITYNUM_NONE ) + if (self->client->ps.groundEntityNum != ENTITYNUM_NONE) {//can't do the multikicks in air - if ( enemiesFront && enemiesBack - && (enemiesFront+enemiesBack)-(enemiesRight+enemiesLeft)>1 ) + if (enemiesFront && enemiesBack + && (enemiesFront + enemiesBack) - (enemiesRight + enemiesLeft)>1 + && self->client->ps.forcePowerLevel[FP_SABER_OFFENSE] > 2) {//more enemies in front/back than left/right kickMove = LS_KICK_BF; } - else if ( enemiesRight && enemiesLeft - && (enemiesRight+enemiesLeft)-(enemiesFront+enemiesBack)>1 ) + else if ((enemiesRight && enemiesLeft + && (enemiesRight + enemiesLeft) - (enemiesFront + enemiesBack)>1) + && self->client->ps.forcePowerLevel[FP_SABER_OFFENSE] > 2) {//more enemies on left & right than front/back kickMove = LS_KICK_RL; } - else if ( (enemiesFront || enemiesBack) && (enemiesRight || enemiesLeft) ) - {//at least 2 enemies around us, not aligned + else if ((((enemiesFront || enemiesBack) && (enemiesRight || enemiesLeft)) + || pm->cmd.buttons&BUTTON_USE) + && self->client->ps.forcePowerLevel[FP_SABER_OFFENSE] > 2) + {//at least 2 enemies around us, not aligned, or doing a special + G_DrainPowerForSpecialMove(pm->gent, FP_SABER_OFFENSE, g_saberForceDrainAmount->integer); kickMove = LS_KICK_S; } - else if ( enemiesSpin > 1 ) - {//at least 2 enemies around us, not aligned + else if ((enemiesSpin > 1 || pm->cmd.buttons&BUTTON_USE) + && self->client->ps.forcePowerLevel[FP_SABER_OFFENSE] > 2) + {//at least 2 enemies around us, not aligned, or doing a special + G_DrainPowerForSpecialMove(pm->gent, FP_SABER_OFFENSE, g_saberForceDrainAmount->integer); kickMove = LS_KICK_S; } } - if ( kickMove == LS_NONE - && bestKick != LS_NONE ) + if (kickMove == LS_NONE + && bestKick != LS_NONE) {//no good multi-kick move, but we do have a nice single-kick we found kickMove = bestKick; //get mad at him so he knows he's being targetted - if ( (self->s.number < MAX_CLIENTS||G_ControlledByPlayer(self)) - && bestEnt != NULL ) + if ((self->s.number < MAX_CLIENTS || G_ControlledByPlayer(self)) + && bestEnt != NULL) {//player - G_SetEnemy( self, bestEnt ); + G_SetEnemy(self, bestEnt); } } - if ( kickMove != LS_NONE ) + if (kickMove != LS_NONE) { - if ( storeMove ) + if (storeMove) { self->client->ps.saberMoveNext = kickMove; } @@ -11054,44 +11315,44 @@ saberMoveName_t G_PickAutoMultiKick( gentity_t *self, qboolean allowSingles, qbo return kickMove; } -qboolean PM_PickAutoMultiKick( qboolean allowSingles ) +qboolean PM_PickAutoMultiKick(qboolean allowSingles) { - saberMoveName_t kickMove = G_PickAutoMultiKick( pm->gent, allowSingles, qfalse ); - if ( kickMove != LS_NONE ) + saberMoveName_t kickMove = G_PickAutoMultiKick(pm->gent, allowSingles, qfalse); + if (kickMove != LS_NONE) { - PM_SetSaberMove( kickMove ); + PM_SetSaberMove(kickMove); return qtrue; } return qfalse; } -qboolean PM_SaberThrowable( void ) +qboolean PM_SaberThrowable(void) { - //ugh, hard-coding this is bad... - if ( pm->ps->saberAnimLevel == SS_STAFF ) + //can't throw staff unless we have ST 3 + if (pm->ps->saberAnimLevel == SS_STAFF && pm->ps->forcePowerLevel[FP_SABERTHROW] < 3) { return qfalse; } - if ( !(pm->ps->saber[0].saberFlags&SFL_NOT_THROWABLE) ) - {//yes, this saber is always throwable + if (!(pm->ps->saber[0].saberFlags&SFL_NOT_THROWABLE)) + {//yes, this saber is throwable return qtrue; } //saber is not normally throwable - if ( (pm->ps->saber[0].saberFlags&SFL_SINGLE_BLADE_THROWABLE) ) + if ((pm->ps->saber[0].saberFlags&SFL_SINGLE_BLADE_THROWABLE)) {//it is throwable if only one blade is on - if ( pm->ps->saber[0].numBlades > 1 ) + if (pm->ps->saber[0].numBlades > 1) {//it has more than one blade int numBladesActive = 0; - for ( int i = 0; i < pm->ps->saber[0].numBlades; i++ ) + for (int i = 0; i < pm->ps->saber[0].numBlades; i++) { - if ( pm->ps->saber[0].blade[i].active ) + if (pm->ps->saber[0].blade[i].active) { numBladesActive++; } } - if ( numBladesActive == 1 ) + if (numBladesActive == 1) {//only 1 blade is on return qtrue; } @@ -11101,15 +11362,18 @@ qboolean PM_SaberThrowable( void ) return qfalse; } -qboolean PM_CheckAltKickAttack( void ) +qboolean PM_CheckAltKickAttack(void) { - if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK) - && (!(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) ||PM_SaberInReturn(pm->ps->saberMove)) - && (!PM_FlippingAnim(pm->ps->legsAnim)||pm->ps->legsAnimTimer<=250) - && (!PM_SaberThrowable()) + if (pm->cmd.buttons&BUTTON_ALT_ATTACK + && (!(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) || PM_SaberInReturn(pm->ps->saberMove)) + && (!PM_FlippingAnim(pm->ps->legsAnim) || pm->ps->legsAnimTimer <= 250) && pm->ps->SaberActive() && !(pm->ps->saber[0].saberFlags&SFL_NO_KICKS)//okay to do kicks with this saber - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_KICKS) )//okay to do kicks with this saber + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_KICKS))//okay to do kicks with the 2nd saber + && ((!(pm->cmd.buttons&BUTTON_FORCE_FOCUS) && pm->ps->clientNum < MAX_CLIENTS) || pm->ps->clientNum >= MAX_CLIENTS) + //if we are the player we have to press Force Focus to saber throw + //&& !PM_SaberThrowable() + //&& (!WP_ForcePowerUsable(pm->gent, FP_SABERTHROW, 20)) //make sure saber throw is disabled before trying to kick) ) { return qtrue; @@ -11117,33 +11381,42 @@ qboolean PM_CheckAltKickAttack( void ) return qfalse; } -qboolean PM_CheckUpsideDownAttack( void ) +qboolean PM_CheckAltKickAttackMelee(void) +{//only used by a melee npc for now + if (!PM_FlippingAnim(pm->ps->legsAnim) || pm->ps->legsAnimTimer <= 250) + { + return qtrue; + } + return qfalse; +} + +qboolean PM_CheckUpsideDownAttack(void) { - if ( pm->ps->saberMove != LS_READY ) + if (pm->ps->saberMove != LS_READY) { return qfalse; } - if ( !(pm->cmd.buttons&BUTTON_ATTACK) ) + if (!(pm->cmd.buttons&BUTTON_ATTACK)) { return qfalse; } - if ( pm->ps->saberAnimLevel < SS_FAST - || pm->ps->saberAnimLevel > SS_STRONG ) + if (pm->ps->saberAnimLevel < SS_FAST + || pm->ps->saberAnimLevel > SS_STRONG) { return qfalse; } - if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())) {//FIXME: check ranks? return qfalse; } //FIXME: enemy below //FIXME: more than 64 off ground - if ( !g_debugMelee->integer ) + if (!g_debugMelee->integer) {//hmm, can't get this to work quite the way we wanted... secret move, then! return qfalse; } - switch( pm->ps->legsAnim ) + switch (pm->ps->legsAnim) { case BOTH_WALL_RUN_RIGHT_FLIP: case BOTH_WALL_RUN_LEFT_FLIP: @@ -11154,34 +11427,34 @@ qboolean PM_CheckUpsideDownAttack( void ) case BOTH_FLIP_BACK3: case BOTH_WALL_FLIP_BACK1: case BOTH_ALORA_FLIP_B: - //JKA + //JKA case BOTH_FORCEWALLRUNFLIP_END: - { - float animLength = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, (animNumber_t)pm->ps->legsAnim ); - float elapsedTime = (float)(animLength-pm->ps->legsAnimTimer); - float midPoint = animLength/2.0f; - if ( elapsedTime < midPoint-100.0f - || elapsedTime > midPoint+100.0f ) - {//only a 200ms window (in middle of anim) of opportunity to do this move in these anims - return qfalse; - } - } + { + float animLength = PM_AnimLength(pm->gent->client->clientInfo.animFileIndex, (animNumber_t)pm->ps->legsAnim); + float elapsedTime = (float)(animLength - pm->ps->legsAnimTimer); + float midPoint = animLength / 2.0f; + if (elapsedTime < midPoint - 100.0f + || elapsedTime > midPoint + 100.0f) + {//only a 200ms window (in middle of anim) of opportunity to do this move in these anims + return qfalse; + } + } //NOTE: falls through on purpose case BOTH_FLIP_HOLD7: pm->ps->pm_flags |= PMF_SLOW_MO_FALL; - PM_SetSaberMove( LS_UPSIDE_DOWN_ATTACK ); + PM_SetSaberMove(LS_UPSIDE_DOWN_ATTACK); return qtrue; break; } return qfalse; } -qboolean PM_SaberMoveOkayForKata( void ) +qboolean PM_SaberMoveOkayForKata(void) { - if ( g_saberNewControlScheme->integer ) + if (g_saberNewControlScheme->integer) { - if ( pm->ps->saberMove == LS_READY //not doing anything - || PM_SaberInReflect( pm->ps->saberMove ) )//interrupt a projectile blocking move + if (pm->ps->saberMove == LS_READY //not doing anything + || PM_SaberInReflect(pm->ps->saberMove))//interrupt a projectile blocking move { return qtrue; } @@ -11192,9 +11465,9 @@ qboolean PM_SaberMoveOkayForKata( void ) } else {//old control scheme, allow it to interrupt a start or ready - if ( pm->ps->saberMove == LS_READY - || PM_SaberInReflect( pm->ps->saberMove )//interrupt a projectile blocking move - || PM_SaberInStart( pm->ps->saberMove ) ) + if (pm->ps->saberMove == LS_READY + || PM_SaberInReflect(pm->ps->saberMove)//interrupt a projectile blocking move + || PM_SaberInStart(pm->ps->saberMove)) { return qtrue; } @@ -11205,13 +11478,13 @@ qboolean PM_SaberMoveOkayForKata( void ) } } -qboolean PM_CanDoKata( void ) +qboolean PM_CanDoKata(void) { - if ( PM_InSecondaryStyle() ) + if (PM_InSecondaryStyle()) { return qfalse; } - if ( !pm->ps->saberInFlight//not throwing saber + if (!pm->ps->saberInFlight//not throwing saber && PM_SaberMoveOkayForKata() /* && pm->ps->saberAnimLevel >= SS_FAST//fast, med or strong style @@ -11219,66 +11492,66 @@ qboolean PM_CanDoKata( void ) */ && pm->ps->groundEntityNum != ENTITYNUM_NONE//not in the air && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack - && pm->cmd.forwardmove >=0 //not moving back (used to be !pm->cmd.forwardmove) + && pm->cmd.forwardmove >= 0 //not moving back (used to be !pm->cmd.forwardmove) && !pm->cmd.rightmove//not moving r/l && pm->cmd.upmove <= 0//not jumping...? - && G_TryingKataAttack(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*///holding focus - && G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue )/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER*/ )//SINGLE_SPECIAL_POWER )// have enough power + && G_TryingKataAttack(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*///holding focus + && G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER, qtrue)/*pm->ps->forcePower >= SABER_ALT_ATTACK_POWER*/)//SINGLE_SPECIAL_POWER )// have enough power {//FIXME: check rage, etc... return qtrue; } return qfalse; } -void PM_SaberDroidWeapon( void ) +void PM_SaberDroidWeapon(void) { // make weapon function - if ( pm->ps->weaponTime > 0 ) { + if (pm->ps->weaponTime > 0) { pm->ps->weaponTime -= pml.msec; - if ( pm->ps->weaponTime <= 0 ) + if (pm->ps->weaponTime <= 0) { pm->ps->weaponTime = 0; } } // Now we react to a block action by the player's lightsaber. - if ( pm->ps->saberBlocked ) + if (pm->ps->saberBlocked) { - switch ( pm->ps->saberBlocked ) + switch (pm->ps->saberBlocked) { - case BLOCKED_PARRY_BROKEN: - PM_SetAnim( pm, SETANIM_BOTH, Q_irand(BOTH_PAIN1,BOTH_PAIN3), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - pm->ps->weaponTime = pm->ps->legsAnimTimer; - break; - case BLOCKED_ATK_BOUNCE: - PM_SetAnim( pm, SETANIM_BOTH, Q_irand(BOTH_PAIN1,BOTH_PAIN3), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - pm->ps->weaponTime = pm->ps->legsAnimTimer; - break; - case BLOCKED_UPPER_RIGHT: - case BLOCKED_UPPER_RIGHT_PROJ: - case BLOCKED_LOWER_RIGHT: - case BLOCKED_LOWER_RIGHT_PROJ: - PM_SetAnim( pm, SETANIM_BOTH, BOTH_P1_S1_TR, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - pm->ps->legsAnimTimer += Q_irand( 200, 1000 ); - pm->ps->weaponTime = pm->ps->legsAnimTimer; - break; - case BLOCKED_UPPER_LEFT: - case BLOCKED_UPPER_LEFT_PROJ: - case BLOCKED_LOWER_LEFT: - case BLOCKED_LOWER_LEFT_PROJ: - PM_SetAnim( pm, SETANIM_BOTH, BOTH_P1_S1_TL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - pm->ps->legsAnimTimer += Q_irand( 200, 1000 ); - pm->ps->weaponTime = pm->ps->legsAnimTimer; - break; - case BLOCKED_TOP: - case BLOCKED_TOP_PROJ: - PM_SetAnim( pm, SETANIM_BOTH, BOTH_P1_S1_T_, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); - pm->ps->legsAnimTimer += Q_irand( 200, 1000 ); - pm->ps->weaponTime = pm->ps->legsAnimTimer; - break; - default: - pm->ps->saberBlocked = BLOCKED_NONE; - break; + case BLOCKED_PARRY_BROKEN: + PM_SetAnim(pm, SETANIM_BOTH, Q_irand(BOTH_PAIN1, BOTH_PAIN3), SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); + pm->ps->weaponTime = pm->ps->legsAnimTimer; + break; + case BLOCKED_ATK_BOUNCE: + PM_SetAnim(pm, SETANIM_BOTH, Q_irand(BOTH_PAIN1, BOTH_PAIN3), SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); + pm->ps->weaponTime = pm->ps->legsAnimTimer; + break; + case BLOCKED_UPPER_RIGHT: + case BLOCKED_UPPER_RIGHT_PROJ: + case BLOCKED_LOWER_RIGHT: + case BLOCKED_LOWER_RIGHT_PROJ: + PM_SetAnim(pm, SETANIM_BOTH, BOTH_P1_S1_TR, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); + pm->ps->legsAnimTimer += Q_irand(200, 1000); + pm->ps->weaponTime = pm->ps->legsAnimTimer; + break; + case BLOCKED_UPPER_LEFT: + case BLOCKED_UPPER_LEFT_PROJ: + case BLOCKED_LOWER_LEFT: + case BLOCKED_LOWER_LEFT_PROJ: + PM_SetAnim(pm, SETANIM_BOTH, BOTH_P1_S1_TL, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); + pm->ps->legsAnimTimer += Q_irand(200, 1000); + pm->ps->weaponTime = pm->ps->legsAnimTimer; + break; + case BLOCKED_TOP: + case BLOCKED_TOP_PROJ: + PM_SetAnim(pm, SETANIM_BOTH, BOTH_P1_S1_T_, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); + pm->ps->legsAnimTimer += Q_irand(200, 1000); + pm->ps->weaponTime = pm->ps->legsAnimTimer; + break; + default: + pm->ps->saberBlocked = BLOCKED_NONE; + break; } pm->ps->saberBlocked = BLOCKED_NONE; @@ -11290,20 +11563,20 @@ void PM_SaberDroidWeapon( void ) } } -void PM_TryGrab( void ) +void PM_TryGrab(void) { - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE + if (pm->ps->groundEntityNum != ENTITYNUM_NONE //&& !pm->ps->saberInFlight - && pm->ps->weaponTime <= 0 )//< 200 ) + && pm->ps->weaponTime <= 0)//< 200 ) { - PM_SetAnim( pm, SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); pm->ps->torsoAnimTimer += 200; pm->ps->weaponTime = pm->ps->torsoAnimTimer; pm->ps->saberMove = pm->ps->saberMoveNext = LS_READY; - VectorClear( pm->ps->velocity ); - VectorClear( pm->ps->moveDir ); + VectorClear(pm->ps->velocity); + VectorClear(pm->ps->moveDir); pm->cmd.rightmove = pm->cmd.forwardmove = pm->cmd.upmove = 0; - if ( pm->gent ) + if (pm->gent) { pm->gent->painDebounceTime = level.time + pm->ps->torsoAnimTimer; } @@ -11311,11 +11584,11 @@ void PM_TryGrab( void ) } } -void PM_TryAirKick( saberMoveName_t kickMove ) +void PM_TryAirKick(saberMoveName_t kickMove) { - if ( pm->ps->groundEntityNum < ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum < ENTITYNUM_NONE) {//just do it - PM_SetSaberMove( kickMove ); + PM_SetSaberMove(kickMove); } else { @@ -11323,33 +11596,33 @@ void PM_TryAirKick( saberMoveName_t kickMove ) //let's only allow air kicks if a certain distance from the ground //it's silly to be able to do them right as you land. //also looks wrong to transition from a non-complete flip anim... - if ((!PM_FlippingAnim( pm->ps->legsAnim ) || pm->ps->legsAnimTimer <= 0) && + if ((!PM_FlippingAnim(pm->ps->legsAnim) || pm->ps->legsAnimTimer <= 0) && gDist > 64.0f && //strict minimum - gDist > (-pm->ps->velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well + gDist > (-pm->ps->velocity[2]) - 64.0f //make sure we are high to ground relative to downward velocity as well ) { - PM_SetSaberMove( kickMove ); + PM_SetSaberMove(kickMove); } else {//leave it as a normal kick unless we're too high up - if ( gDist > 128.0f || pm->ps->velocity[2] >= 0 ) + if (gDist > 128.0f || pm->ps->velocity[2] >= 0) { //off ground, but too close to ground } else {//high close enough to ground to do a normal kick, convert it - switch ( kickMove ) + switch (kickMove) { case LS_KICK_F_AIR: - PM_SetSaberMove( LS_KICK_F ); + PM_SetSaberMove(LS_KICK_F); break; case LS_KICK_B_AIR: - PM_SetSaberMove( LS_KICK_B ); + PM_SetSaberMove(LS_KICK_B); break; case LS_KICK_R_AIR: - PM_SetSaberMove( LS_KICK_R ); + PM_SetSaberMove(LS_KICK_R); break; case LS_KICK_L_AIR: - PM_SetSaberMove( LS_KICK_L ); + PM_SetSaberMove(LS_KICK_L); break; default: break; @@ -11359,101 +11632,101 @@ void PM_TryAirKick( saberMoveName_t kickMove ) } } -void PM_CheckKick( void ) +void PM_CheckKick(void) { - if ( !PM_KickMove( pm->ps->saberMove )//not already in a kick + if (!PM_KickMove(pm->ps->saberMove)//not already in a kick && !(pm->ps->pm_flags&PMF_DUCKED)//not ducked - && (pm->cmd.upmove >= 0 ) )//not trying to duck + && (pm->cmd.upmove >= 0))//not trying to duck {//player kicks //FIXME: only if FP_SABER_OFFENSE >= 3 - if ( pm->cmd.rightmove ) + if (pm->cmd.rightmove) {//kick to side - if ( pm->cmd.rightmove > 0 ) + if (pm->cmd.rightmove > 0) {//kick right - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE - || pm->cmd.upmove > 0 ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE + || pm->cmd.upmove > 0) { - PM_TryAirKick( LS_KICK_R_AIR ); + PM_TryAirKick(LS_KICK_R_AIR); } else { - PM_SetSaberMove( LS_KICK_R ); + PM_SetSaberMove(LS_KICK_R); } } else {//kick left - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE - || pm->cmd.upmove > 0 ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE + || pm->cmd.upmove > 0) { - PM_TryAirKick( LS_KICK_L_AIR ); + PM_TryAirKick(LS_KICK_L_AIR); } else { - PM_SetSaberMove( LS_KICK_L ); + PM_SetSaberMove(LS_KICK_L); } } pm->cmd.rightmove = 0; } - else if ( pm->cmd.forwardmove ) + else if (pm->cmd.forwardmove) {//kick front/back - if ( pm->cmd.forwardmove > 0 ) + if (pm->cmd.forwardmove > 0) {//kick fwd - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE - || pm->cmd.upmove > 0 ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE + || pm->cmd.upmove > 0) { - PM_TryAirKick( LS_KICK_F_AIR ); + PM_TryAirKick(LS_KICK_F_AIR); } /* else if ( pm->ps->weapon == WP_SABER - && pm->ps->saberAnimLevel == SS_STAFF - && pm->gent - && G_CheckEnemyPresence( pm->gent, DIR_FRONT, 64, 0.8f ) ) + && pm->ps->saberAnimLevel == SS_STAFF + && pm->gent + && G_CheckEnemyPresence( pm->gent, DIR_FRONT, 64, 0.8f ) ) {//FIXME: don't jump while doing this move and don't do this move if in air - PM_SetSaberMove( LS_HILT_BASH ); + PM_SetSaberMove( LS_HILT_BASH ); } */ else { - PM_SetSaberMove( LS_KICK_F ); + PM_SetSaberMove(LS_KICK_F); } } - else if ( pm->ps->groundEntityNum == ENTITYNUM_NONE - || pm->cmd.upmove > 0 ) + else if (pm->ps->groundEntityNum == ENTITYNUM_NONE + || pm->cmd.upmove > 0) { - PM_TryAirKick( LS_KICK_B_AIR ); + PM_TryAirKick(LS_KICK_B_AIR); } else {//kick back - PM_SetSaberMove( LS_KICK_B ); + PM_SetSaberMove(LS_KICK_B); } pm->cmd.forwardmove = 0; } - else if ( pm->gent + else if (pm->gent && pm->gent->enemy - && G_CanKickEntity( pm->gent, pm->gent->enemy ) ) + && G_CanKickEntity(pm->gent, pm->gent->enemy)) {//auto-pick? if ( /*(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) - && (pm->cmd.buttons&BUTTON_ATTACK) - &&*/ PM_PickAutoMultiKick( qfalse ) ) + && (pm->cmd.buttons&BUTTON_ATTACK) + &&*/ PM_PickAutoMultiKick(qfalse)) {//kicked! - if ( pm->ps->saberMove == LS_KICK_RL ) + if (pm->ps->saberMove == LS_KICK_RL) {//just pull back - if ( d_slowmodeath->integer > 3 ) + if (d_slowmodeath->integer > 3) { - G_StartMatrixEffect( pm->gent, MEF_NO_SPIN, pm->ps->legsAnimTimer+500 ); + G_StartMatrixEffect(pm->gent, MEF_NO_SPIN, pm->ps->legsAnimTimer + 500); } } else {//normal spin - if ( d_slowmodeath->integer > 3 ) + if (d_slowmodeath->integer > 3) { - G_StartMatrixEffect( pm->gent, 0, pm->ps->legsAnimTimer+500 ); + G_StartMatrixEffect(pm->gent, 0, pm->ps->legsAnimTimer + 500); } } - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE - &&( pm->ps->saberMove == LS_KICK_S - ||pm->ps->saberMove == LS_KICK_BF - ||pm->ps->saberMove == LS_KICK_RL ) ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE + && (pm->ps->saberMove == LS_KICK_S + || pm->ps->saberMove == LS_KICK_BF + || pm->ps->saberMove == LS_KICK_RL)) {//in the air and doing a jump-kick, which is a ground anim, so.... //cut z velocity...? pm->ps->velocity[2] = 0; @@ -11462,12 +11735,12 @@ void PM_CheckKick( void ) } else { - saberMoveName_t kickMove = PM_PickAutoKick( pm->gent->enemy ); - if ( kickMove != LS_NONE ) + saberMoveName_t kickMove = PM_PickAutoKick(pm->gent->enemy); + if (kickMove != LS_NONE) {//Matrix? - PM_SetSaberMove( kickMove ); + PM_SetSaberMove(kickMove); int meFlags = 0; - switch ( kickMove ) + switch (kickMove) { case LS_KICK_B://just pull back case LS_KICK_B_AIR://just pull back @@ -11480,19 +11753,19 @@ void PM_CheckKick( void ) default: break; } - if ( d_slowmodeath->integer > 3 ) + if (d_slowmodeath->integer > 3) { - G_StartMatrixEffect( pm->gent, meFlags, pm->ps->legsAnimTimer+500 ); + G_StartMatrixEffect(pm->gent, meFlags, pm->ps->legsAnimTimer + 500); } } } } else { - if ( PM_PickAutoMultiKick( qtrue ) ) + if (PM_PickAutoMultiKick(qtrue)) { int meFlags = 0; - switch ( pm->ps->saberMove ) + switch (pm->ps->saberMove) { case LS_KICK_RL://just pull back case LS_KICK_B://just pull back @@ -11506,14 +11779,14 @@ void PM_CheckKick( void ) default: break; } - if ( d_slowmodeath->integer > 3 ) + if (d_slowmodeath->integer > 3) { - G_StartMatrixEffect( pm->gent, meFlags, pm->ps->legsAnimTimer+500 ); + G_StartMatrixEffect(pm->gent, meFlags, pm->ps->legsAnimTimer + 500); } - if ( pm->ps->groundEntityNum == ENTITYNUM_NONE - &&( pm->ps->saberMove == LS_KICK_S - ||pm->ps->saberMove == LS_KICK_BF - ||pm->ps->saberMove == LS_KICK_RL ) ) + if (pm->ps->groundEntityNum == ENTITYNUM_NONE + && (pm->ps->saberMove == LS_KICK_S + || pm->ps->saberMove == LS_KICK_BF + || pm->ps->saberMove == LS_KICK_RL)) {//in the air and doing a jump-kick, which is a ground anim, so.... //cut z velocity...? pm->ps->velocity[2] = 0; @@ -11524,27 +11797,27 @@ void PM_CheckKick( void ) } } -void PM_CheckClearSaberBlock( void ) +void PM_CheckClearSaberBlock(void) { - if ( pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer() ) + if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) {//player - if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ ) + if (pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ) {//blocking a projectile - if ( pm->ps->forcePowerDebounce[FP_SABER_DEFENSE] < level.time ) + if (pm->ps->forcePowerDebounce[FP_SABER_DEFENSE] < level.time) {//block is done or breaking out of it with an attack pm->ps->weaponTime = 0; pm->ps->saberBlocked = BLOCKED_NONE; } - else if ( (pm->cmd.buttons&BUTTON_ATTACK) ) + else if ((pm->cmd.buttons&BUTTON_ATTACK)) {//block is done or breaking out of it with an attack pm->ps->weaponTime = 0; pm->ps->saberBlocked = BLOCKED_NONE; } } - else if ( pm->ps->saberBlocked == BLOCKED_UPPER_LEFT - && pm->ps->powerups[PW_SHOCKED] > level.time ) + else if (pm->ps->saberBlocked == BLOCKED_UPPER_LEFT + && pm->ps->powerups[PW_SHOCKED] > level.time) {//probably blocking lightning - if ( (pm->cmd.buttons&BUTTON_ATTACK) ) + if ((pm->cmd.buttons&BUTTON_ATTACK)) {//trying to attack //allow the attack pm->ps->weaponTime = 0; @@ -11554,31 +11827,31 @@ void PM_CheckClearSaberBlock( void ) } } -qboolean PM_SaberBlocking( void ) +qboolean PM_SaberBlocking(void) { // Now we react to a block action by the player's lightsaber. - if ( pm->ps->saberBlocked ) + if (pm->ps->saberBlocked) { - if ( pm->ps->saberMove > LS_PUTAWAY && pm->ps->saberMove <= LS_A_BL2TR && pm->ps->saberBlocked != BLOCKED_PARRY_BROKEN && + if (pm->ps->saberMove > LS_PUTAWAY && pm->ps->saberMove <= LS_A_BL2TR && pm->ps->saberBlocked != BLOCKED_PARRY_BROKEN && (pm->ps->saberBlocked < BLOCKED_UPPER_RIGHT_PROJ || pm->ps->saberBlocked > BLOCKED_TOP_PROJ))//&& Q_irand( 0, 2 ) {//we parried another lightsaber while attacking, so treat it as a bounce pm->ps->saberBlocked = BLOCKED_ATK_BOUNCE; } - else if ( pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer() )//player + else if (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())//player { - if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ - && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ ) + if (pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT_PROJ + && pm->ps->saberBlocked <= BLOCKED_TOP_PROJ) {//blocking a projectile - if ( (pm->cmd.buttons&BUTTON_ATTACK) ) + if ((pm->cmd.buttons&BUTTON_ATTACK)) {//trying to attack - if ( pm->ps->saberMove == LS_READY - || PM_SaberInReflect( pm->ps->saberMove ) ) + if (pm->ps->saberMove == LS_READY + || PM_SaberInReflect(pm->ps->saberMove)) {//not already busy or already in projectile deflection //trying to attack during projectile blocking, so do the attack instead pm->ps->saberBlocked = BLOCKED_NONE; pm->ps->saberBounceMove = LS_NONE; pm->ps->weaponstate = WEAPON_READY; - if ( PM_SaberInReflect( pm->ps->saberMove ) && pm->ps->weaponTime > 0 ) + if (PM_SaberInReflect(pm->ps->saberMove) && pm->ps->weaponTime > 0) {//interrupt the current deflection move pm->ps->weaponTime = 0; } @@ -11589,23 +11862,23 @@ qboolean PM_SaberBlocking( void ) } /* else if ( (pm->cmd.buttons&BUTTON_ATTACK) - && pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT - && pm->ps->saberBlocked <= BLOCKED_TOP - && !PM_SaberInKnockaway( pm->ps->saberBounceMove ) ) + && pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT + && pm->ps->saberBlocked <= BLOCKED_TOP + && !PM_SaberInKnockaway( pm->ps->saberBounceMove ) ) {//if hitting attack during a parry (not already a knockaway) - if ( pm->ps->forcePowerLevel[FP_SABER_DEFENSE] > FORCE_LEVEL_2 ) - {//have high saber defense, turn the parry into a knockaway? - //FIXME: this could actually be bad for us...? Leaves us open - pm->ps->saberBounceMove = PM_KnockawayForParry( pm->ps->saberBlocked ); - } + if ( pm->ps->forcePowerLevel[FP_SABER_DEFENSE] > FORCE_LEVEL_2 ) + {//have high saber defense, turn the parry into a knockaway? + //FIXME: this could actually be bad for us...? Leaves us open + pm->ps->saberBounceMove = PM_KnockawayForParry( pm->ps->saberBlocked ); + } } */ - if ( pm->ps->saberBlocked != BLOCKED_ATK_BOUNCE ) + if (pm->ps->saberBlocked != BLOCKED_ATK_BOUNCE) {//can't attack for twice whatever your skill level's parry debounce time is - if ( pm->ps->clientNum == 0 || PM_ControlledByPlayer() ) + if (pm->ps->clientNum == 0 || PM_ControlledByPlayer()) {//player - if ( pm->ps->forcePowerLevel[FP_SABER_DEFENSE] <= FORCE_LEVEL_1 ) + if (pm->ps->forcePowerLevel[FP_SABER_DEFENSE] <= FORCE_LEVEL_1) { pm->ps->weaponTime = parryDebounce[pm->ps->forcePowerLevel[FP_SABER_DEFENSE]]; } @@ -11613,9 +11886,9 @@ qboolean PM_SaberBlocking( void ) else {//NPC //pm->ps->weaponTime = parryDebounce[pm->ps->forcePowerLevel[FP_SABER_DEFENSE]] * 2; - if ( pm->gent ) + if (pm->gent) { - pm->ps->weaponTime = Jedi_ReCalcParryTime( pm->gent, EVASION_PARRY ); + pm->ps->weaponTime = Jedi_ReCalcParryTime(pm->gent, EVASION_PARRY); } else {//WTF??? @@ -11623,235 +11896,236 @@ qboolean PM_SaberBlocking( void ) } } } - switch ( pm->ps->saberBlocked ) + switch (pm->ps->saberBlocked) { - case BLOCKED_PARRY_BROKEN: - //whatever parry we were is in now broken, play the appropriate knocked-away anim - { - saberMoveName_t nextMove; - if ( PM_SaberInBrokenParry( pm->ps->saberBounceMove ) ) - {//already have one...? - nextMove = (saberMoveName_t)pm->ps->saberBounceMove; + case BLOCKED_PARRY_BROKEN: + //whatever parry we were is in now broken, play the appropriate knocked-away anim + { + saberMoveName_t nextMove; + if (PM_SaberInBrokenParry(pm->ps->saberBounceMove)) + {//already have one...? + nextMove = (saberMoveName_t)pm->ps->saberBounceMove; + } + else + { + nextMove = PM_BrokenParryForParry((saberMoveName_t)pm->ps->saberMove); + } + if (nextMove != LS_NONE) + { + PM_SetSaberMove(nextMove); + pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + else + {//Maybe in a knockaway? + } + //pm->ps->saberBounceMove = LS_NONE; + } + break; + case BLOCKED_ATK_BOUNCE: + // If there is absolutely no blocked move in the chart, don't even mess with the animation. + // OR if we are already in a block or parry. + if (pm->ps->saberMove >= LS_T1_BR__R/*LS_BOUNCE_TOP*/)//|| saberMoveData[pm->ps->saberMove].bounceMove == LS_NONE ) + {//an actual bounce? Other bounces before this are actually transitions? + pm->ps->saberBlocked = BLOCKED_NONE; + } + else if (PM_SaberInBounce(pm->ps->saberMove) || !PM_SaberInAttack(pm->ps->saberMove)) + {//already in the bounce, go into an attack or transition to ready.. should never get here since can't be blocked in a bounce! + int nextMove; + + if (pm->cmd.buttons & BUTTON_ATTACK) + {//transition to a new attack + if (pm->ps->clientNum && !PM_ControlledByPlayer()) + {//NPC + nextMove = saberMoveData[pm->ps->saberMove].chain_attack; } else - { - nextMove = PM_BrokenParryForParry( (saberMoveName_t)pm->ps->saberMove ); + {//player + int newQuad = PM_SaberMoveQuadrantForMovement(&pm->cmd); + while (newQuad == saberMoveData[pm->ps->saberMove].startQuad) + {//player is still in same attack quad, don't repeat that attack because it looks bad, + //FIXME: try to pick one that might look cool? + newQuad = Q_irand(Q_BR, Q_BL); + //FIXME: sanity check, just in case? + }//else player is switching up anyway, take the new attack dir + nextMove = transitionMove[saberMoveData[pm->ps->saberMove].startQuad][newQuad]; } - if ( nextMove != LS_NONE ) - { - PM_SetSaberMove( nextMove ); - pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + else + {//return to ready + if (pm->ps->clientNum && !PM_ControlledByPlayer()) + {//NPC + nextMove = saberMoveData[pm->ps->saberMove].chain_idle; } else - {//Maybe in a knockaway? - } - //pm->ps->saberBounceMove = LS_NONE; - } - break; - case BLOCKED_ATK_BOUNCE: - // If there is absolutely no blocked move in the chart, don't even mess with the animation. - // OR if we are already in a block or parry. - if ( pm->ps->saberMove >= LS_T1_BR__R/*LS_BOUNCE_TOP*/ )//|| saberMoveData[pm->ps->saberMove].bounceMove == LS_NONE ) - {//an actual bounce? Other bounces before this are actually transitions? - pm->ps->saberBlocked = BLOCKED_NONE; - } - else if ( PM_SaberInBounce( pm->ps->saberMove ) || !PM_SaberInAttack( pm->ps->saberMove ) ) - {//already in the bounce, go into an attack or transition to ready.. should never get here since can't be blocked in a bounce! - int nextMove; - - if ( pm->cmd.buttons & BUTTON_ATTACK ) - {//transition to a new attack - if ( pm->ps->clientNum && !PM_ControlledByPlayer() ) - {//NPC - nextMove = saberMoveData[pm->ps->saberMove].chain_attack; - } - else - {//player - int newQuad = PM_SaberMoveQuadrantForMovement( &pm->cmd ); - while ( newQuad == saberMoveData[pm->ps->saberMove].startQuad ) - {//player is still in same attack quad, don't repeat that attack because it looks bad, - //FIXME: try to pick one that might look cool? - newQuad = Q_irand( Q_BR, Q_BL ); - //FIXME: sanity check, just in case? - }//else player is switching up anyway, take the new attack dir - nextMove = transitionMove[saberMoveData[pm->ps->saberMove].startQuad][newQuad]; + {//player + //forces the player to have a cooldown between deflections + if (saberMoveData[pm->ps->saberMove].startQuad == Q_T) + { + nextMove = LS_R_BL2TR; } - } - else - {//return to ready - if ( pm->ps->clientNum && !PM_ControlledByPlayer() ) - {//NPC - nextMove = saberMoveData[pm->ps->saberMove].chain_idle; + else if (saberMoveData[pm->ps->saberMove].startQuad < Q_T) + { + nextMove = LS_R_TL2BR + (saberMoveName_t)(saberMoveData[pm->ps->saberMove].startQuad - Q_BR); } - else - {//player - if ( saberMoveData[pm->ps->saberMove].startQuad == Q_T ) - { - nextMove = LS_R_BL2TR; - } - else if ( saberMoveData[pm->ps->saberMove].startQuad < Q_T ) - { - nextMove = LS_R_TL2BR+(saberMoveName_t)(saberMoveData[pm->ps->saberMove].startQuad-Q_BR); - } - else// if ( saberMoveData[pm->ps->saberMove].startQuad > Q_T ) - { - nextMove = LS_R_BR2TL+(saberMoveName_t)(saberMoveData[pm->ps->saberMove].startQuad-Q_TL); - } + else// if ( saberMoveData[pm->ps->saberMove].startQuad > Q_T ) + { + nextMove = LS_R_BR2TL + (saberMoveName_t)(saberMoveData[pm->ps->saberMove].startQuad - Q_TL); } } - PM_SetSaberMove( (saberMoveName_t)nextMove ); - pm->ps->weaponTime = pm->ps->torsoAnimTimer; - } - else - {//start the bounce move - saberMoveName_t bounceMove; - if ( pm->ps->saberBounceMove != LS_NONE ) - { - bounceMove = (saberMoveName_t)pm->ps->saberBounceMove; - } - else - { - bounceMove = PM_SaberBounceForAttack( (saberMoveName_t)pm->ps->saberMove ); - } - PM_SetSaberMove( bounceMove ); - pm->ps->weaponTime = pm->ps->torsoAnimTimer; - } - //clear the saberBounceMove - //pm->ps->saberBounceMove = LS_NONE; - - if (cg_debugSaber.integer>=2) - { - Com_Printf("Saber Block: Bounce\n"); } - break; - case BLOCKED_UPPER_RIGHT: - if ( pm->ps->saberBounceMove != LS_NONE ) + PM_SetSaberMove((saberMoveName_t)nextMove); + pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + else + {//start the bounce move + saberMoveName_t bounceMove; + if (pm->ps->saberBounceMove != LS_NONE) { - PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove ); - //pm->ps->saberBounceMove = LS_NONE; - pm->ps->weaponTime = pm->ps->torsoAnimTimer; + bounceMove = (saberMoveName_t)pm->ps->saberBounceMove; } else { - PM_SetSaberMove( LS_PARRY_UR ); + bounceMove = PM_SaberBounceForAttack((saberMoveName_t)pm->ps->saberMove); } + PM_SetSaberMove(bounceMove); + pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + //clear the saberBounceMove + //pm->ps->saberBounceMove = LS_NONE; - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf( "Saber Block: Parry UR\n" ); - } - break; - case BLOCKED_UPPER_RIGHT_PROJ: - PM_SetSaberMove( LS_REFLECT_UR ); + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Bounce\n"); + } + break; + case BLOCKED_UPPER_RIGHT: + if (pm->ps->saberBounceMove != LS_NONE) + { + PM_SetSaberMove((saberMoveName_t)pm->ps->saberBounceMove); + //pm->ps->saberBounceMove = LS_NONE; + pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + else + { + PM_SetSaberMove(LS_PARRY_UR); + } - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf("Saber Block: Deflect UR\n"); - } - break; - case BLOCKED_UPPER_LEFT: - if ( pm->ps->saberBounceMove != LS_NONE ) - { - PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove ); - //pm->ps->saberBounceMove = LS_NONE; - pm->ps->weaponTime = pm->ps->torsoAnimTimer; - } - else - { - PM_SetSaberMove( LS_PARRY_UL ); - } + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Parry UR\n"); + } + break; + case BLOCKED_UPPER_RIGHT_PROJ: + PM_SetSaberMove(LS_REFLECT_UR); - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf( "Saber Block: Parry UL\n" ); - } - break; - case BLOCKED_UPPER_LEFT_PROJ: - PM_SetSaberMove( LS_REFLECT_UL ); + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Deflect UR\n"); + } + break; + case BLOCKED_UPPER_LEFT: + if (pm->ps->saberBounceMove != LS_NONE) + { + PM_SetSaberMove((saberMoveName_t)pm->ps->saberBounceMove); + //pm->ps->saberBounceMove = LS_NONE; + pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + else + { + PM_SetSaberMove(LS_PARRY_UL); + } - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf("Saber Block: Deflect UL\n"); - } - break; - case BLOCKED_LOWER_RIGHT: - if ( pm->ps->saberBounceMove != LS_NONE ) - { - PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove ); - //pm->ps->saberBounceMove = LS_NONE; - pm->ps->weaponTime = pm->ps->torsoAnimTimer; - } - else - { - PM_SetSaberMove( LS_PARRY_LR ); - } + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Parry UL\n"); + } + break; + case BLOCKED_UPPER_LEFT_PROJ: + PM_SetSaberMove(LS_REFLECT_UL); - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf("Saber Block: Parry LR\n"); - } - break; - case BLOCKED_LOWER_RIGHT_PROJ: - PM_SetSaberMove( LS_REFLECT_LR ); + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Deflect UL\n"); + } + break; + case BLOCKED_LOWER_RIGHT: + if (pm->ps->saberBounceMove != LS_NONE) + { + PM_SetSaberMove((saberMoveName_t)pm->ps->saberBounceMove); + //pm->ps->saberBounceMove = LS_NONE; + pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + else + { + PM_SetSaberMove(LS_PARRY_LR); + } - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf("Saber Block: Deflect LR\n"); - } - break; - case BLOCKED_LOWER_LEFT: - if ( pm->ps->saberBounceMove != LS_NONE ) - { - PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove ); - //pm->ps->saberBounceMove = LS_NONE; - pm->ps->weaponTime = pm->ps->torsoAnimTimer; - } - else - { - PM_SetSaberMove( LS_PARRY_LL ); - } + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Parry LR\n"); + } + break; + case BLOCKED_LOWER_RIGHT_PROJ: + PM_SetSaberMove(LS_REFLECT_LR); - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf("Saber Block: Parry LL\n"); - } - break; - case BLOCKED_LOWER_LEFT_PROJ: - PM_SetSaberMove( LS_REFLECT_LL); + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Deflect LR\n"); + } + break; + case BLOCKED_LOWER_LEFT: + if (pm->ps->saberBounceMove != LS_NONE) + { + PM_SetSaberMove((saberMoveName_t)pm->ps->saberBounceMove); + //pm->ps->saberBounceMove = LS_NONE; + pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + else + { + PM_SetSaberMove(LS_PARRY_LL); + } - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf("Saber Block: Deflect LL\n"); - } - break; - case BLOCKED_TOP: - if ( pm->ps->saberBounceMove != LS_NONE ) - { - PM_SetSaberMove( (saberMoveName_t)pm->ps->saberBounceMove ); - //pm->ps->saberBounceMove = LS_NONE; - pm->ps->weaponTime = pm->ps->torsoAnimTimer; - } - else - { - PM_SetSaberMove( LS_PARRY_UP ); - } + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Parry LL\n"); + } + break; + case BLOCKED_LOWER_LEFT_PROJ: + PM_SetSaberMove(LS_REFLECT_LL); - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf("Saber Block: Parry Top\n"); - } - break; - case BLOCKED_TOP_PROJ: - PM_SetSaberMove( LS_REFLECT_UP ); + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Deflect LL\n"); + } + break; + case BLOCKED_TOP: + if (pm->ps->saberBounceMove != LS_NONE) + { + PM_SetSaberMove((saberMoveName_t)pm->ps->saberBounceMove); + //pm->ps->saberBounceMove = LS_NONE; + pm->ps->weaponTime = pm->ps->torsoAnimTimer; + } + else + { + PM_SetSaberMove(LS_PARRY_UP); + } - if ( cg_debugSaber.integer >= 2 ) - { - Com_Printf("Saber Block: Deflect Top\n"); - } - break; - default: - pm->ps->saberBlocked = BLOCKED_NONE; - break; + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Parry Top\n"); + } + break; + case BLOCKED_TOP_PROJ: + PM_SetSaberMove(LS_REFLECT_UP); + + if (cg_debugSaber.integer >= 2) + { + Com_Printf("Saber Block: Deflect Top\n"); + } + break; + default: + pm->ps->saberBlocked = BLOCKED_NONE; + break; } // Charging is like a lead-up before attacking again. This is an appropriate use, or we can create a new weaponstate for blocking @@ -11864,17 +12138,17 @@ qboolean PM_SaberBlocking( void ) return qfalse; } -qboolean PM_NPCCheckAttackRoll( void ) +qboolean PM_NPCCheckAttackRoll(void) { - if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()//NPC + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()//NPC && pm->gent && pm->gent->NPC //&& Q_irand(-3,pm->gent->NPC->rank)>RANK_CREWMAN) - && ( pm->gent->NPC->rank > RANK_CREWMAN && !Q_irand(0,3-g_spskill->integer) ) + && (pm->gent->NPC->rank > RANK_CREWMAN && !Q_irand(0, 3 - g_spskill->integer)) && pm->gent->enemy - && fabs(pm->gent->enemy->currentOrigin[2]-pm->ps->origin[2])<32 - && DistanceHorizontalSquared(pm->gent->enemy->currentOrigin, pm->ps->origin ) < (128.0f*128.0f) - && InFOV( pm->gent->enemy->currentOrigin, pm->ps->origin, pm->ps->viewangles, 30, 90 ) ) + && fabs(pm->gent->enemy->currentOrigin[2] - pm->ps->origin[2])<32 + && DistanceHorizontalSquared(pm->gent->enemy->currentOrigin, pm->ps->origin) < (128.0f*128.0f) + && InFOV(pm->gent->enemy->currentOrigin, pm->ps->origin, pm->ps->viewangles, 30, 90)) {//stab! return qtrue; } @@ -11894,26 +12168,26 @@ void PM_WeaponLightsaber(void) { int addTime; qboolean delayed_fire = qfalse, animLevelOverridden = qfalse; - int anim=-1; - int curmove, newmove=LS_NONE; + int anim = -1; + int curmove, newmove = LS_NONE; - if ( pm->gent + if (pm->gent && pm->gent->client - && pm->gent->client->NPC_class == CLASS_SABER_DROID ) + && pm->gent->client->NPC_class == CLASS_SABER_DROID) {//Saber droid does it's own attack logic PM_SaberDroidWeapon(); return; } // don't allow attack until all buttons are up - if ( pm->ps->pm_flags & PMF_RESPAWNED ) { + if (pm->ps->pm_flags & PMF_RESPAWNED) { return; } // check for dead player - if ( pm->ps->stats[STAT_HEALTH] <= 0 ) + if (pm->ps->stats[STAT_HEALTH] <= 0) { - if ( pm->gent ) + if (pm->gent) { pm->gent->s.loopSound = 0; } @@ -11921,149 +12195,149 @@ void PM_WeaponLightsaber(void) } // make weapon function - if ( pm->ps->weaponTime > 0 ) { + if (pm->ps->weaponTime > 0) { pm->ps->weaponTime -= pml.msec; - if ( pm->ps->weaponTime <= 0 ) + if (pm->ps->weaponTime <= 0) { pm->ps->weaponTime = 0; } } - if ( pm->ps->stats[STAT_WEAPONS]&(1<ps->weapons[WP_SCEPTER] && !pm->ps->dualSabers && pm->gent - && pm->gent->weaponModel[1] ) + && pm->gent->weaponModel[1]) {//holding scepter in left hand, use dual style pm->ps->saberAnimLevel = SS_DUAL; animLevelOverridden = qtrue; } - else if ( pm->ps->saber[0].singleBladeStyle != SS_NONE//SaberStaff() + else if (pm->ps->saber[0].singleBladeStyle != SS_NONE//SaberStaff() && !pm->ps->dualSabers && pm->ps->saber[0].blade[0].active - && !pm->ps->saber[0].blade[1].active ) + && !pm->ps->saber[0].blade[1].active) {//using a staff, but only with first blade turned on, so use is as a normal saber...? //override so that single-blade staff must be used with specified style pm->ps->saberAnimLevel = pm->ps->saber[0].singleBladeStyle;//SS_STRONG; animLevelOverridden = qtrue; } - else if ( pm->gent + else if (pm->gent && cg.saberAnimLevelPending != pm->ps->saberAnimLevel - && WP_SaberStyleValidForSaber( pm->gent, cg.saberAnimLevelPending ) ) + && WP_SaberStyleValidForSaber(pm->gent, cg.saberAnimLevelPending)) {//go ahead and use the cg.saberAnimLevelPending below animLevelOverridden = qfalse; } - else if ( pm->gent - && ( WP_SaberStyleValidForSaber( pm->gent, pm->ps->saberAnimLevel ) - || WP_UseFirstValidSaberStyle( pm->gent, &pm->ps->saberAnimLevel ) ) ) + else if (pm->gent + && (WP_SaberStyleValidForSaber(pm->gent, pm->ps->saberAnimLevel) + || WP_UseFirstValidSaberStyle(pm->gent, &pm->ps->saberAnimLevel))) {//style we are using is not valid, switched us to a valid one animLevelOverridden = qtrue; } /* else if ( pm->ps->saber[0].Active() - && pm->ps->saber[0].stylesAllowed ) + && pm->ps->saber[0].stylesAllowed ) {//one of the sabers I'm using forces me to use one of a set of styles - if ( !(pm->ps->saber[0].stylesAllowed&(1<ps->saberAnimLevel)) ) - {//I'm not currently using a valid one - for ( int styleNum = SS_NONE+1; styleNum < SS_NUM_SABER_STYLES; styleNum++ ) - {//loop through and use the first valid one - if ( (pm->ps->saber[0].stylesAllowed&(1<ps->saberAnimLevel = styleNum; - animLevelOverridden = qtrue; - } - } - } + if ( !(pm->ps->saber[0].stylesAllowed&(1<ps->saberAnimLevel)) ) + {//I'm not currently using a valid one + for ( int styleNum = SS_NONE+1; styleNum < SS_NUM_SABER_STYLES; styleNum++ ) + {//loop through and use the first valid one + if ( (pm->ps->saber[0].stylesAllowed&(1<ps->saberAnimLevel = styleNum; + animLevelOverridden = qtrue; + } + } + } } */ - else if ( pm->ps->dualSabers ) + else if (pm->ps->dualSabers) { /* if ( pm->ps->saber[1].Active() - && pm->ps->saber[1].stylesAllowed ) + && pm->ps->saber[1].stylesAllowed ) {//one of the sabers I'm using forces me to use one of a set of styles - if ( !(pm->ps->saber[1].stylesAllowed&(1<ps->saberAnimLevel)) ) - {//I'm not currently using a valid one - for ( int styleNum = SS_NONE+1; styleNum < SS_NUM_SABER_STYLES; styleNum++ ) - {//loop through and use the first valid one - if ( (pm->ps->saber[1].stylesAllowed&(1<ps->saberAnimLevel = styleNum; - animLevelOverridden = qtrue; - } - } - } + if ( !(pm->ps->saber[1].stylesAllowed&(1<ps->saberAnimLevel)) ) + {//I'm not currently using a valid one + for ( int styleNum = SS_NONE+1; styleNum < SS_NUM_SABER_STYLES; styleNum++ ) + {//loop through and use the first valid one + if ( (pm->ps->saber[1].stylesAllowed&(1<ps->saberAnimLevel = styleNum; + animLevelOverridden = qtrue; + } + } + } } - else*/ if ( pm->ps->saber[1].Active() ) + else*/ if (pm->ps->saber[1].Active()) {//if second saber is on, must use dual style pm->ps->saberAnimLevel = SS_DUAL; animLevelOverridden = qtrue; } - else if ( pm->ps->saber[0].Active() ) + else if (pm->ps->saber[0].Active()) {//with only one saber on, use fast style pm->ps->saberAnimLevel = SS_FAST; animLevelOverridden = qtrue; } } - if ( !animLevelOverridden ) + if (!animLevelOverridden) { - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) - && cg.saberAnimLevelPending > SS_NONE - && cg.saberAnimLevelPending != pm->ps->saberAnimLevel ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + && cg.saberAnimLevelPending > SS_NONE + && cg.saberAnimLevelPending != pm->ps->saberAnimLevel) { - if ( !PM_SaberInStart( pm->ps->saberMove ) - && !PM_SaberInTransition( pm->ps->saberMove ) - && !PM_SaberInAttack( pm->ps->saberMove ) ) + if (!PM_SaberInStart(pm->ps->saberMove) + && !PM_SaberInTransition(pm->ps->saberMove) + && !PM_SaberInAttack(pm->ps->saberMove)) {//don't allow changes when in the middle of an attack set...(or delay the change until it's done) pm->ps->saberAnimLevel = cg.saberAnimLevelPending; } } } - else if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + else if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) {//if overrid the player's saberAnimLevel, let the cgame know cg.saberAnimLevelPending = pm->ps->saberAnimLevel; } /* if ( PM_InForceGetUp( pm->ps ) ) {//if mostly up, can start attack - if ( pm->ps->torsoAnimTimer > 800 ) - {//not up enough yet - // make weapon function - if ( pm->ps->weaponTime > 0 ) { - pm->ps->weaponTime -= pml.msec; - if ( pm->ps->weaponTime <= 0 ) - { - pm->ps->weaponTime = 0; - } - } - return; - } - else - { - if ( pm->cmd.buttons & BUTTON_ATTACK ) - {//let an attack interrupt the torso part of this force getup - pm->ps->weaponTime = 0; - } - } + if ( pm->ps->torsoAnimTimer > 800 ) + {//not up enough yet + // make weapon function + if ( pm->ps->weaponTime > 0 ) { + pm->ps->weaponTime -= pml.msec; + if ( pm->ps->weaponTime <= 0 ) + { + pm->ps->weaponTime = 0; + } + } + return; + } + else + { + if ( pm->cmd.buttons & BUTTON_ATTACK ) + {//let an attack interrupt the torso part of this force getup + pm->ps->weaponTime = 0; + } + } } else */ - if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps )) + if (PM_InKnockDown(pm->ps) || PM_InRoll(pm->ps)) {//in knockdown - if ( pm->ps->legsAnim == BOTH_ROLL_F - && pm->ps->legsAnimTimer <= 250 ) + if (pm->ps->legsAnim == BOTH_ROLL_F + && pm->ps->legsAnimTimer <= 250) { - if ( (pm->cmd.buttons&BUTTON_ATTACK) - || PM_NPCCheckAttackRoll() ) + if ((pm->cmd.buttons&BUTTON_ATTACK) + || PM_NPCCheckAttackRoll()) { - if ( G_EnoughPowerForSpecialMove( pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB ) ) + if (G_EnoughPowerForSpecialMove(pm->ps->forcePower, SABER_ALT_ATTACK_POWER_FB)) { - if ( !(pm->ps->saber[0].saberFlags&SFL_NO_ROLL_STAB) - && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_ROLL_STAB)) ) + if (!(pm->ps->saber[0].saberFlags&SFL_NO_ROLL_STAB) + && (!pm->ps->dualSabers || !(pm->ps->saber[1].saberFlags&SFL_NO_ROLL_STAB))) {//okay to do roll-stab - PM_SetSaberMove( LS_ROLL_STAB ); - if ( pm->gent ) + PM_SetSaberMove(LS_ROLL_STAB); + if (pm->gent) { - G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER_FB ); + G_DrainPowerForSpecialMove(pm->gent, FP_SABER_OFFENSE, g_saberForceDrainAmount->integer);//SABER_ALT_ATTACK_POWER_FB); } } } @@ -12072,30 +12346,30 @@ void PM_WeaponLightsaber(void) return; } - if ( PM_SaberLocked() ) + if (PM_SaberLocked()) { pm->ps->saberMove = LS_NONE; return; } - if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_LAND - || (pm->ps->torsoAnim == BOTH_FORCELONGLEAP_START && !(pm->cmd.buttons&BUTTON_ATTACK)) ) + if (pm->ps->torsoAnim == BOTH_FORCELONGLEAP_LAND + || (pm->ps->torsoAnim == BOTH_FORCELONGLEAP_START && !(pm->cmd.buttons&BUTTON_ATTACK))) {//if you're in the long-jump and you're not attacking (or are landing), you're not doing anything - if ( pm->ps->torsoAnimTimer ) + if (pm->ps->torsoAnimTimer) { return; } } - if ( pm->ps->legsAnim == BOTH_FLIP_HOLD7 - && !(pm->cmd.buttons&BUTTON_ATTACK) ) + if (pm->ps->legsAnim == BOTH_FLIP_HOLD7 + && !(pm->cmd.buttons&BUTTON_ATTACK)) {//if you're in the upside-down attack hold, don't do anything unless you're attacking return; } - if ( PM_KickingAnim( pm->ps->legsAnim ) ) + if (PM_KickingAnim(pm->ps->legsAnim)) { - if ( pm->ps->legsAnimTimer ) + if (pm->ps->legsAnimTimer) {//you're kicking, no interruptions return; } @@ -12104,51 +12378,51 @@ void PM_WeaponLightsaber(void) pm->ps->weaponTime = 0; } - if ( pm->ps->saberMoveNext != LS_NONE - && (pm->ps->saberMove == LS_READY||pm->ps->saberMove == LS_NONE))//ready for another one + if (pm->ps->saberMoveNext != LS_NONE + && (pm->ps->saberMove == LS_READY || pm->ps->saberMove == LS_NONE))//ready for another one {//something is forcing us to set a specific next saberMove //FIXME: if this is a NPC kick, re-verify it before executing it! - PM_SetSaberMove( (saberMoveName_t)pm->ps->saberMoveNext ); + PM_SetSaberMove((saberMoveName_t)pm->ps->saberMoveNext); pm->ps->saberMoveNext = LS_NONE;//clear it now that we played it return; } - if ( pm->ps->saberEventFlags&SEF_INWATER )//saber in water + if (pm->ps->saberEventFlags&SEF_INWATER)//saber in water { - pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS); + pm->cmd.buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS|BUTTON_SABERTHROW); } qboolean saberInAir = qtrue; - if ( !PM_SaberInBrokenParry( pm->ps->saberMove ) && pm->ps->saberBlocked != BLOCKED_PARRY_BROKEN && !PM_DodgeAnim( pm->ps->torsoAnim ) && + if (!PM_SaberInBrokenParry(pm->ps->saberMove) && pm->ps->saberBlocked != BLOCKED_PARRY_BROKEN && !PM_DodgeAnim(pm->ps->torsoAnim) && pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING) {//we're not stuck in a broken parry - if ( pm->ps->saberInFlight ) + if (pm->ps->saberInFlight) {//guiding saber - if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + if (pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0)//player is 0 {// - if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + if (&g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY) {//fell to the ground and we're not trying to pull it back saberInAir = qfalse; } } - if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) + if (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) { - if ( pm->ps->weapon != pm->cmd.weapon ) + if (pm->ps->weapon != pm->cmd.weapon) { - PM_BeginWeaponChange( pm->cmd.weapon ); + PM_BeginWeaponChange(pm->cmd.weapon); } } - else if ( pm->ps->weapon == WP_SABER - && (!pm->ps->dualSabers || !pm->ps->saber[1].Active()) ) + else if (pm->ps->weapon == WP_SABER + && (!pm->ps->dualSabers || !pm->ps->saber[1].Active())) {//guiding saber - if ( saberInAir ) + if (saberInAir) { - if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 ) + if (!PM_ForceAnim(pm->ps->torsoAnim) || pm->ps->torsoAnimTimer < 300) {//don't interrupt a force power anim - if ( pm->ps->torsoAnim != BOTH_LOSE_SABER - || !pm->ps->torsoAnimTimer ) + if (pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer) { - PM_SetAnim( pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } } } @@ -12157,10 +12431,10 @@ void PM_WeaponLightsaber(void) } } - if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 ) + if (pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0) {//FIXME: this is going to fire off one frame before you expect, actually pm->gent->client->fireDelay -= pml.msec; - if ( pm->gent->client->fireDelay <= 0 ) + if (pm->gent->client->fireDelay <= 0) {//just finished delay timer pm->gent->client->fireDelay = 0; delayed_fire = qtrue; @@ -12169,50 +12443,50 @@ void PM_WeaponLightsaber(void) PM_CheckClearSaberBlock(); - if ( PM_LockedAnim( pm->ps->torsoAnim ) - && pm->ps->torsoAnimTimer ) + if (PM_LockedAnim(pm->ps->torsoAnim) + && pm->ps->torsoAnimTimer) {//can't interrupt these anims ever return; } - if ( PM_SuperBreakLoseAnim( pm->ps->torsoAnim ) - && pm->ps->torsoAnimTimer ) + if (PM_SuperBreakLoseAnim(pm->ps->torsoAnim) + && pm->ps->torsoAnimTimer) {//don't interrupt these anims return; } - if ( PM_SuperBreakWinAnim( pm->ps->torsoAnim ) - && pm->ps->torsoAnimTimer ) + if (PM_SuperBreakWinAnim(pm->ps->torsoAnim) + && pm->ps->torsoAnimTimer) {//don't interrupt these anims return; } - if ( PM_SaberBlocking() ) + if (PM_SaberBlocking()) {//busy blocking, don't do attacks return; - } + } // check for weapon change // can't change if weapon is firing, but can change again if lowering or raising - if ( (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) && pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING) { - if ( pm->ps->weapon != pm->cmd.weapon ) { - PM_BeginWeaponChange( pm->cmd.weapon ); + if ((pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) && pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING) { + if (pm->ps->weapon != pm->cmd.weapon) { + PM_BeginWeaponChange(pm->cmd.weapon); } } - if ( pm->ps->weaponTime > 0 ) + if (pm->ps->weaponTime > 0) { //FIXME: allow some window of opportunity to change your attack // if it just started and your directional input is different // than it was before... but only 100 milliseconds at most? //OR: Make it so that attacks don't start until 100ms after you // press the attack button...??? - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) //player - && PM_SaberInReturn( pm->ps->saberMove )//in a saber return move - FIXME: what about transitions? + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) //player + && PM_SaberInReturn(pm->ps->saberMove)//in a saber return move - FIXME: what about transitions? //&& pm->ps->torsoAnimTimer<=250//towards the end of a saber return anim && pm->ps->saberBlocked == BLOCKED_NONE//not interacting with any other saber && !(pm->cmd.buttons&BUTTON_ATTACK)//not trying to swing the saber - && (pm->cmd.forwardmove||pm->cmd.rightmove) )//trying to kick in a specific direction + && (pm->cmd.forwardmove || pm->cmd.rightmove))//trying to kick in a specific direction { - if ( PM_CheckAltKickAttack() )//trying to do a kick + if (PM_CheckAltKickAttack())//trying to do a kick {//allow them to do the kick now! pm->ps->weaponTime = 0; PM_CheckKick(); @@ -12221,25 +12495,25 @@ void PM_WeaponLightsaber(void) } else { - if ( !pm->cmd.rightmove + if (!pm->cmd.rightmove &&!pm->cmd.forwardmove - &&(pm->cmd.buttons&BUTTON_ATTACK) ) + && (pm->cmd.buttons&BUTTON_ATTACK)) { /* if ( PM_CheckDualSpinProtect() ) {//check to see if we're going to do the special dual push protect move - PM_SetSaberMove( LS_DUAL_SPIN_PROTECT ); - pm->ps->weaponstate = WEAPON_FIRING; - return; + PM_SetSaberMove( LS_DUAL_SPIN_PROTECT ); + pm->ps->weaponstate = WEAPON_FIRING; + return; } else */ - if ( !g_saberNewControlScheme->integer ) + if (!g_saberNewControlScheme->integer) { saberMoveName_t pullAtk = PM_CheckPullAttack(); - if ( pullAtk != LS_NONE ) + if (pullAtk != LS_NONE) { - PM_SetSaberMove( pullAtk ); + PM_SetSaberMove(pullAtk); pm->ps->weaponstate = WEAPON_FIRING; return; } @@ -12257,7 +12531,7 @@ void PM_WeaponLightsaber(void) // ********************************************************* // change weapon if time - if ( pm->ps->weaponstate == WEAPON_DROPPING ) { + if (pm->ps->weaponstate == WEAPON_DROPPING) { PM_FinishWeaponChange(); return; } @@ -12266,12 +12540,12 @@ void PM_WeaponLightsaber(void) // WEAPON_RAISING // ********************************************************* - if ( pm->ps->weaponstate == WEAPON_RAISING ) + if (pm->ps->weaponstate == WEAPON_RAISING) {//Just selected the weapon pm->ps->weaponstate = WEAPON_IDLE; - if(pm->gent && (pm->gent->s.numbergent))) + if (pm->gent && (pm->gent->s.numbergent))) { - switch ( pm->ps->legsAnim ) + switch (pm->ps->legsAnim) { case BOTH_WALK1: case BOTH_WALK2: @@ -12288,13 +12562,13 @@ void PM_WeaponLightsaber(void) case BOTH_RUNBACK1: case BOTH_RUNBACK2: case BOTH_RUNBACK_STAFF: - PM_SetAnim(pm,SETANIM_TORSO,pm->ps->legsAnim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, pm->ps->legsAnim, SETANIM_FLAG_NORMAL); break; default: int anim = PM_ReadyPoseForSaberAnimLevel(); - if (anim!=-1) + if (anim != -1) { - PM_SetAnim(pm,SETANIM_TORSO,anim,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, anim, SETANIM_FLAG_NORMAL); } break; } @@ -12302,31 +12576,31 @@ void PM_WeaponLightsaber(void) else { qboolean saberInAir = qtrue; - if ( pm->ps->saberInFlight ) + if (pm->ps->saberInFlight) {//guiding saber - if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + if (PM_SaberInBrokenParry(pm->ps->saberMove) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim(pm->ps->torsoAnim)) {//we're stuck in a broken parry saberInAir = qfalse; } - if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + if (pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0)//player is 0 {// - if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + if (&g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY) {//fell to the ground and we're not trying to pull it back saberInAir = qfalse; } } } - if ( pm->ps->weapon == WP_SABER + if (pm->ps->weapon == WP_SABER && pm->ps->saberInFlight && saberInAir && (!pm->ps->dualSabers || !pm->ps->saber[1].Active())) {//guiding saber - if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 ) + if (!PM_ForceAnim(pm->ps->torsoAnim) || pm->ps->torsoAnimTimer < 300) {//don't interrupt a force power anim - if ( pm->ps->torsoAnim != BOTH_LOSE_SABER - || !pm->ps->torsoAnimTimer ) + if (pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer) { - PM_SetAnim( pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } } } @@ -12344,24 +12618,24 @@ void PM_WeaponLightsaber(void) // Check for WEAPON ATTACK // ********************************************************* - if ( PM_CanDoKata() ) + if (PM_CanDoKata()) { saberMoveName_t overrideMove = LS_INVALID; //see if we have an overridden (or cancelled) kata move - if ( pm->ps->saber[0].kataMove != LS_INVALID ) + if (pm->ps->saber[0].kataMove != LS_INVALID) { - if ( pm->ps->saber[0].kataMove != LS_NONE ) + if (pm->ps->saber[0].kataMove != LS_NONE) { overrideMove = (saberMoveName_t)pm->ps->saber[0].kataMove; } } - if ( overrideMove == LS_INVALID ) + if (overrideMove == LS_INVALID) {//not overridden by first saber, check second - if ( pm->ps->dualSabers ) + if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].kataMove != LS_INVALID ) + if (pm->ps->saber[1].kataMove != LS_INVALID) { - if ( pm->ps->saber[1].kataMove != LS_NONE ) + if (pm->ps->saber[1].kataMove != LS_NONE) { overrideMove = (saberMoveName_t)pm->ps->saber[1].kataMove; } @@ -12369,74 +12643,75 @@ void PM_WeaponLightsaber(void) } } //no overrides, cancelled? - if ( overrideMove == LS_INVALID ) + if (overrideMove == LS_INVALID) { - if ( pm->ps->saber[0].kataMove == LS_NONE ) + if (pm->ps->saber[0].kataMove == LS_NONE) { overrideMove = LS_NONE; } - else if ( pm->ps->dualSabers ) + else if (pm->ps->dualSabers) { - if ( pm->ps->saber[1].kataMove == LS_NONE ) + if (pm->ps->saber[1].kataMove == LS_NONE) { overrideMove = LS_NONE; } } } - if ( overrideMove == LS_INVALID ) + if (overrideMove == LS_INVALID) {//not overridden //FIXME: make sure to turn on saber(s)! - switch ( pm->ps->saberAnimLevel ) + switch (pm->ps->saberAnimLevel) { case SS_FAST: case SS_TAVION: + case SS_KATARN: PM_SetSaberMove( LS_A1_SPECIAL ); break; case SS_MEDIUM: - PM_SetSaberMove( LS_A2_SPECIAL ); + PM_SetSaberMove(LS_A2_SPECIAL); break; case SS_STRONG: case SS_DESANN: - PM_SetSaberMove( LS_A3_SPECIAL ); + PM_SetSaberMove(LS_A3_SPECIAL); break; case SS_DUAL: - PM_SetSaberMove( LS_DUAL_SPIN_PROTECT );//PM_CheckDualSpinProtect(); + PM_SetSaberMove(LS_DUAL_SPIN_PROTECT);//PM_CheckDualSpinProtect(); break; case SS_STAFF: - PM_SetSaberMove( LS_STAFF_SOULCAL ); + PM_SetSaberMove(LS_STAFF_SOULCAL); break; } pm->ps->weaponstate = WEAPON_FIRING; - if ( pm->gent ) + if (pm->gent) { - G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER, qtrue );//FP_SPEED, SINGLE_SPECIAL_POWER ); + G_DrainPowerForSpecialMove(pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER, qtrue);//FP_SPEED, SINGLE_SPECIAL_POWER ); //G_StartMatrixEffect( pm->gent, MEF_REVERSE_SPIN, pm->ps->torsoAnimTimer ); } } - else if ( overrideMove != LS_NONE ) + else if (overrideMove != LS_NONE) { - PM_SetSaberMove( overrideMove ); + PM_SetSaberMove(overrideMove); pm->ps->weaponstate = WEAPON_FIRING; - if ( pm->gent ) + if (pm->gent) { - G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER, qtrue );//FP_SPEED, SINGLE_SPECIAL_POWER ); + G_DrainPowerForSpecialMove(pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER, qtrue);//FP_SPEED, SINGLE_SPECIAL_POWER ); //G_StartMatrixEffect( pm->gent, MEF_REVERSE_SPIN, pm->ps->torsoAnimTimer ); } } - if ( overrideMove != LS_NONE ) + if (overrideMove != LS_NONE) {//not cancelled return; } } - if ( PM_CheckAltKickAttack() ) + if (PM_CheckAltKickAttack()) {//trying to do a kick //FIXME: in-air kicks? - if ( pm->ps->saberAnimLevel == SS_STAFF - && (pm->ps->clientNum >= MAX_CLIENTS||PM_ControlledByPlayer()) ) + if (pm->ps->saberAnimLevel == SS_STAFF + && (pm->ps->clientNum >= MAX_CLIENTS || PM_ControlledByPlayer())) {//NPCs spin the staff //NOTE: only NPCs can do it the easy way... they kick directly, not through ucmds... - PM_SetSaberMove( LS_SPINATTACK ); + PM_SetSaberMove(LS_SPINATTACK); return; } else @@ -12448,59 +12723,65 @@ void PM_WeaponLightsaber(void) //this is never a valid regular saber attack button //pm->cmd.buttons &= ~BUTTON_FORCE_FOCUS; - if ( PM_CheckUpsideDownAttack() ) + if (PM_CheckUpsideDownAttack()) { return; } - if(!delayed_fire) + if (!delayed_fire) { // Start with the current move, and cross index it with the current control states. - if ( pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX ) + if (pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX) { - curmove = (saberMoveName_t)pm->ps->saberMove; + curmove = (saberMoveName_t)pm->ps->saberMove; //something here - Dusty } else { curmove = LS_READY; } - if ( curmove == LS_A_JUMP_T__B_ || pm->ps->torsoAnim == BOTH_FORCELEAP2_T__B_ ) + if (curmove == LS_A_JUMP_T__B_ || pm->ps->torsoAnim == BOTH_FORCELEAP2_T__B_) {//must transition back to ready from this anim newmove = LS_R_T2B; } // check for fire - else if ( !(pm->cmd.buttons & BUTTON_ATTACK) )//(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS)) ) + else if (!(pm->cmd.buttons & BUTTON_ATTACK))//(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS)) ) {//not attacking pm->ps->weaponTime = 0; - if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 ) + if (pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0) {//Still firing pm->ps->weaponstate = WEAPON_FIRING; } - else if ( pm->ps->weaponstate != WEAPON_READY ) + else if (pm->ps->weaponstate != WEAPON_READY) { pm->ps->weaponstate = WEAPON_IDLE; } //Check for finishing an anim if necc. - if ( curmove >= LS_S_TL2BR && curmove <= LS_S_T2B ) + if (curmove >= LS_S_TL2BR && curmove <= LS_S_T2B) {//started a swing, must continue from here - newmove = LS_A_TL2BR + (curmove-LS_S_TL2BR); + newmove = LS_A_TL2BR + (curmove - LS_S_TL2BR); + + if (!(pm->ps->clientNum)) + { + pm->ps->saberEventFlags &= ~SEF_EVENTS; + } } - else if ( curmove >= LS_A_TL2BR && curmove <= LS_A_T2B ) + else if (curmove >= LS_A_TL2BR && curmove <= LS_A_T2B) {//finished an attack, must continue from here - newmove = LS_R_TL2BR + (curmove-LS_A_TL2BR); + newmove = LS_R_TL2BR + (curmove - LS_A_TL2BR); } - else if ( PM_SaberInTransition( curmove ) ) + else if (PM_SaberInTransition(curmove)) {//in a transition, must play sequential attack newmove = saberMoveData[curmove].chain_attack; } - else if ( PM_SaberInBounce( curmove ) ) + else if (PM_SaberInBounce(curmove)) {//in a bounce - if ( pm->ps->clientNum && !PM_ControlledByPlayer() ) + if (pm->ps->clientNum && !PM_ControlledByPlayer()) {//NPCs must play sequential attack //going into another attack... //allow endless chaining in level 1 attacks, several in level 2 and only one or a few in level 3 - if ( PM_SaberKataDone( LS_NONE, LS_NONE ) ) + if ((!g_saberNewCombat->integer && PM_SaberKataDone(LS_NONE, LS_NONE)) + || (g_saberNewCombat->integer && PM_SaberKataDoneNew(LS_NONE, LS_NONE))) {//done with this kata, must return to ready before attack again newmove = saberMoveData[curmove].chain_idle; } @@ -12518,9 +12799,9 @@ void PM_WeaponLightsaber(void) else {//FIXME: what about returning from a parry? //PM_SetSaberMove( LS_READY ); - if ( pm->ps->saberBlockingTime > cg.time ) + if (pm->ps->saberBlockingTime > cg.time) { - PM_SetSaberMove( LS_READY ); + PM_SetSaberMove(LS_READY); } return; } @@ -12529,36 +12810,36 @@ void PM_WeaponLightsaber(void) // *************************************************** // Pressing attack, so we must look up the proper attack move. qboolean saberInAir = qtrue; - if ( pm->ps->saberInFlight ) + if (pm->ps->saberInFlight) {//guiding saber - if ( PM_SaberInBrokenParry( pm->ps->saberMove ) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim( pm->ps->torsoAnim ) ) + if (PM_SaberInBrokenParry(pm->ps->saberMove) || pm->ps->saberBlocked == BLOCKED_PARRY_BROKEN || PM_DodgeAnim(pm->ps->torsoAnim)) {//we're stuck in a broken parry saberInAir = qfalse; } - if ( pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0 )//player is 0 + if (pm->ps->saberEntityNum < ENTITYNUM_NONE && pm->ps->saberEntityNum > 0)//player is 0 {// - if ( &g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY ) + if (&g_entities[pm->ps->saberEntityNum] != NULL && g_entities[pm->ps->saberEntityNum].s.pos.trType == TR_STATIONARY) {//fell to the ground and we're not trying to pull it back saberInAir = qfalse; } } } - if ( pm->ps->weapon == WP_SABER + if (pm->ps->weapon == WP_SABER && pm->ps->saberInFlight && saberInAir && (!pm->ps->dualSabers || !pm->ps->saber[1].Active())) {//guiding saber - if ( !PM_ForceAnim( pm->ps->torsoAnim ) || pm->ps->torsoAnimTimer < 300 ) + if (!PM_ForceAnim(pm->ps->torsoAnim) || pm->ps->torsoAnimTimer < 300) {//don't interrupt a force power anim - if ( pm->ps->torsoAnim != BOTH_LOSE_SABER - || !pm->ps->torsoAnimTimer ) + if (pm->ps->torsoAnim != BOTH_LOSE_SABER + || !pm->ps->torsoAnimTimer) { - PM_SetAnim( pm, SETANIM_TORSO,BOTH_SABERPULL,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } } } - else if ( pm->ps->weaponTime > 0 ) + else if (pm->ps->weaponTime > 0) { // Last attack is not yet complete. pm->ps->weaponstate = WEAPON_FIRING; return; @@ -12566,52 +12847,52 @@ void PM_WeaponLightsaber(void) else { int both = qfalse; - if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_ATTACK - || pm->ps->torsoAnim == BOTH_FORCELONGLEAP_LAND ) + if (pm->ps->torsoAnim == BOTH_FORCELONGLEAP_ATTACK + || pm->ps->torsoAnim == BOTH_FORCELONGLEAP_LAND) {//can't attack in these anims return; } - else if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_START ) + else if (pm->ps->torsoAnim == BOTH_FORCELONGLEAP_START) {//only 1 attack you can do from this anim - if ( pm->ps->torsoAnimTimer >= 200 ) + if (pm->ps->torsoAnimTimer >= 200) {//hit it early enough to do the attack - PM_SetSaberMove( LS_LEAP_ATTACK ); + PM_SetSaberMove(LS_LEAP_ATTACK); } return; } - if ( curmove >= LS_PARRY_UP && curmove <= LS_REFLECT_LL ) + if (curmove >= LS_PARRY_UP && curmove <= LS_REFLECT_LL) {//from a parry or reflection, can go directly into an attack - if ( pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() ) + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer()) {//NPCs - newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + newmove = PM_NPCSaberAttackFromQuad(saberMoveData[curmove].endQuad); } else { - newmove = PM_SaberAttackForMovement( pm->cmd.forwardmove, pm->cmd.rightmove, curmove ); + newmove = PM_SaberAttackForMovement(pm->cmd.forwardmove, pm->cmd.rightmove, curmove); } } - if ( newmove != LS_NONE ) + if (newmove != LS_NONE) {//have a valid, final LS_ move picked, so skip findingt he transition move and just get the anim - if (PM_HasAnimation( pm->gent, saberMoveData[newmove].animToUse)) + if (PM_HasAnimation(pm->gent, saberMoveData[newmove].animToUse)) { anim = saberMoveData[newmove].animToUse; } } //FIXME: diagonal dirs use the figure-eight attacks from ready pose? - if ( anim == -1 ) + if (anim == -1) { //FIXME: take FP_SABER_OFFENSE into account here somehow? - if ( PM_SaberInTransition( curmove ) ) + if (PM_SaberInTransition(curmove)) {//in a transition, must play sequential attack newmove = saberMoveData[curmove].chain_attack; } - else if ( curmove >= LS_S_TL2BR && curmove <= LS_S_T2B ) + else if (curmove >= LS_S_TL2BR && curmove <= LS_S_T2B) {//started a swing, must continue from here - newmove = LS_A_TL2BR + (curmove-LS_S_TL2BR); + newmove = LS_A_TL2BR + (curmove - LS_S_TL2BR); } - else if ( PM_SaberInBrokenParry( curmove ) ) + else if (PM_SaberInBrokenParry(curmove)) {//broken parries must always return to ready newmove = LS_READY; } @@ -12620,27 +12901,28 @@ void PM_WeaponLightsaber(void) /* if ( PM_SaberKataDone() ) {//we came from a bounce and cannot chain to another attack because our kata is done - newmove = saberMoveData[curmove].chain_idle; + newmove = saberMoveData[curmove].chain_idle; } else */ - if ( pm->ps->clientNum >= MAX_CLIENTS + if (pm->ps->clientNum >= MAX_CLIENTS && !PM_ControlledByPlayer() - && (Q_irand( 0, pm->ps->forcePowerLevel[FP_SABER_OFFENSE]-1 ) - || (pm->gent&&pm->gent->enemy&&pm->gent->enemy->client&&PM_InKnockDownOnGround(&pm->gent->enemy->client->ps))//enemy knocked down, use some logic - || ( pm->ps->saberAnimLevel == SS_FAST && pm->gent && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand( 0, 1 ) ) ) )//minor change to make fast-attack users use the special attacks more + && (Q_irand(0, pm->ps->forcePowerLevel[FP_SABER_OFFENSE] - 1) + || (pm->gent&&pm->gent->enemy&&pm->gent->enemy->client&&PM_InKnockDownOnGround(&pm->gent->enemy->client->ps))//enemy knocked down, use some logic + || (pm->ps->saberAnimLevel == SS_FAST && pm->gent && pm->gent->NPC && pm->gent->NPC->rank >= RANK_LT_JG && Q_irand(0, 1))))//minor change to make fast-attack users use the special attacks more {//NPCs use more randomized attacks the more skilled they are - newmove = PM_NPCSaberAttackFromQuad( saberMoveData[curmove].endQuad ); + newmove = PM_NPCSaberAttackFromQuad(saberMoveData[curmove].endQuad); } else { - newmove = PM_SaberAttackForMovement( pm->cmd.forwardmove, pm->cmd.rightmove, curmove ); - if ( (PM_SaberInBounce( curmove )||PM_SaberInBrokenParry( curmove )) - && saberMoveData[newmove].startQuad == saberMoveData[curmove].endQuad ) + newmove = PM_SaberAttackForMovement(pm->cmd.forwardmove, pm->cmd.rightmove, curmove); //Dusty + if ((PM_SaberInBounce(curmove) || PM_SaberInBrokenParry(curmove)) + && saberMoveData[newmove].startQuad == saberMoveData[curmove].endQuad) {//this attack would be a repeat of the last (which was blocked), so don't actually use it, use the default chain attack for this bounce newmove = saberMoveData[curmove].chain_attack; } } - if ( PM_SaberKataDone( curmove, newmove ) ) + if ((!g_saberNewCombat->integer && PM_SaberKataDone(curmove, newmove)) + || (g_saberNewCombat->integer && PM_SaberKataDoneNew(curmove, newmove))) {//cannot chain this time newmove = saberMoveData[curmove].chain_idle; } @@ -12648,33 +12930,41 @@ void PM_WeaponLightsaber(void) /* if ( newmove == LS_NONE ) {//FIXME: should we allow this? Are there some anims that you should never be able to chain into an attack? - //only curmove that might get in here is LS_NONE, LS_DRAW, LS_PUTAWAY and the LS_R_ returns... all of which are in Q_R - newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad ); + //only curmove that might get in here is LS_NONE, LS_DRAW, LS_PUTAWAY and the LS_R_ returns... all of which are in Q_R + newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad ); } */ - if ( newmove != LS_NONE ) + if (newmove != LS_NONE) { - if ( !PM_InCartwheel( pm->ps->legsAnim ) ) + if (!PM_InCartwheel(pm->ps->legsAnim)) {//don't do transitions when cartwheeling - could make you spin! //Now get the proper transition move - newmove = PM_SaberAnimTransitionMove( (saberMoveName_t)curmove, (saberMoveName_t)newmove ); - if ( PM_HasAnimation( pm->gent, saberMoveData[newmove].animToUse ) ) + newmove = PM_SaberAnimTransitionMove((saberMoveName_t)curmove, (saberMoveName_t)newmove); + if (PM_HasAnimation(pm->gent, saberMoveData[newmove].animToUse)) { anim = saberMoveData[newmove].animToUse; } + + if (!(pm->ps->clientNum)) + { + if (PM_SaberInAttack(curmove)) + {//clear parry status from player here + pm->ps->saberEventFlags &= ~SEF_EVENTS; + } + } } } } if (anim == -1) {//not side-stepping, pick neutral anim - if ( !G_TryingSpecial(pm->gent,&pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/ ) + if (!G_TryingSpecial(pm->gent, &pm->cmd)/*(pm->cmd.buttons&BUTTON_FORCE_FOCUS)*/) {//but only if not trying one of the special attacks! - if ( PM_InCartwheel( pm->ps->legsAnim ) - && pm->ps->legsAnimTimer > 100 ) + if (PM_InCartwheel(pm->ps->legsAnim) + && pm->ps->legsAnimTimer > 100) {//if in the middle of a cartwheel, the chain attack is just a normal attack //NOTE: this should match the switch in PM_InCartwheel! - switch( pm->ps->legsAnim ) + switch (pm->ps->legsAnim) { case BOTH_ARIAL_LEFT://swing from l to r case BOTH_CARTWHEEL_LEFT: @@ -12685,7 +12975,7 @@ void PM_WeaponLightsaber(void) newmove = LS_A_R2L; break; case BOTH_ARIAL_F1://random l/r attack - if ( Q_irand( 0, 1 ) ) + if (Q_irand(0, 1)) { newmove = LS_A_L2R; } @@ -12699,27 +12989,27 @@ void PM_WeaponLightsaber(void) else { // Add randomness for prototype? - newmove = saberMoveData[curmove].chain_attack; + newmove = saberMoveData[curmove].chain_attack; //Dusty } - if ( newmove != LS_NONE ) + if (newmove != LS_NONE) { - if (PM_HasAnimation( pm->gent, saberMoveData[newmove].animToUse)) + if (PM_HasAnimation(pm->gent, saberMoveData[newmove].animToUse)) { - anim= saberMoveData[newmove].animToUse; + anim = saberMoveData[newmove].animToUse; //Dusty } } } - if ( !pm->cmd.forwardmove && !pm->cmd.rightmove && pm->cmd.upmove >= 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE ) + if (!pm->cmd.forwardmove && !pm->cmd.rightmove && pm->cmd.upmove >= 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE) {//not moving at all, so set the anim on entire body both = qtrue; } } - if ( anim == -1) + if (anim == -1) { - switch ( pm->ps->legsAnim ) + switch (pm->ps->legsAnim) { case BOTH_WALK1: case BOTH_WALK2: @@ -12745,23 +13035,23 @@ void PM_WeaponLightsaber(void) newmove = LS_READY; } - if ( !pm->ps->SaberActive() ) + if (!pm->ps->SaberActive()) {//turn on the saber if it's not on - pm->ps->SaberActivate(); + pm->ps->SaberActivate(); //Dusty } - PM_SetSaberMove( (saberMoveName_t)newmove ); + PM_SetSaberMove((saberMoveName_t)newmove); //Dusty - if ( both && pm->ps->legsAnim != pm->ps->torsoAnim ) + if (both && pm->ps->legsAnim != pm->ps->torsoAnim) { - PM_SetAnim( pm,SETANIM_LEGS,pm->ps->torsoAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_LEGS, pm->ps->torsoAnim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { -// pm->gent->client->saberTrail.inAction = qtrue; -// pm->gent->client->saberTrail.duration = 75; // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter + // pm->gent->client->saberTrail.inAction = qtrue; + // pm->gent->client->saberTrail.duration = 75; // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter } - if ( !PM_InCartwheel( pm->ps->torsoAnim ) ) + if (!PM_InCartwheel(pm->ps->torsoAnim)) {//can still attack during a cartwheel/arial //don't fire again until anim is done pm->ps->weaponTime = pm->ps->torsoAnimTimer; @@ -12770,7 +13060,7 @@ void PM_WeaponLightsaber(void) //FIXME: this may be making it so sometimes you can't swing again right away... if ( newmove == LS_READY ) { - pm->ps->weaponTime = 500; + pm->ps->weaponTime = 500; } */ } @@ -12782,7 +13072,7 @@ void PM_WeaponLightsaber(void) pm->ps->weaponstate = WEAPON_FIRING; - if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 ) + if (pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0) {//FIXME: this is going to fire off one frame before you expect, actually // Clear these out since we're not actually firing yet pm->ps->eFlags &= ~EF_FIRING; @@ -12792,46 +13082,46 @@ void PM_WeaponLightsaber(void) addTime = pm->ps->weaponTime; /*if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) { - PM_AddEvent( EV_ALT_FIRE ); - if ( !addTime ) - { - addTime = weaponData[pm->ps->weapon].altFireTime; - if ( g_timescale != NULL ) - { - if ( g_timescale->value < 1.0f ) - { - if ( !MatrixMode ) - {//Special test for Matrix Mode (tm) - if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<value; - } - else if ( g_entities[pm->ps->clientNum].client && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<value; - } - } - } - } - } + PM_AddEvent( EV_ALT_FIRE ); + if ( !addTime ) + { + addTime = weaponData[pm->ps->weapon].altFireTime; + if ( g_timescale != NULL ) + { + if ( g_timescale->value < 1.0f ) + { + if ( !MatrixMode ) + {//Special test for Matrix Mode (tm) + if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<value; + } + else if ( g_entities[pm->ps->clientNum].client && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<value; + } + } + } + } + } } else */{ - PM_AddEvent( EV_FIRE_WEAPON ); - if ( !addTime ) + PM_AddEvent(EV_FIRE_WEAPON); + if (!addTime) { addTime = weaponData[pm->ps->weapon].fireTime; - if ( g_timescale != NULL ) + if (g_timescale != NULL) { - if ( g_timescale->value < 1.0f ) + if (g_timescale->value < 1.0f) { - if ( !MatrixMode ) + if (!MatrixMode) {//Special test for Matrix Mode (tm) - if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<ps->clientNum == 0 && !player_locked && !PlayerAffectedByStasis() && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<value; } - else if ( g_entities[pm->ps->clientNum].client - && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<ps->clientNum].client + && (pm->ps->forcePowersActive&(1 << FP_SPEED) || pm->ps->forcePowersActive&(1 << FP_RAGE))) { addTime *= g_timescale->value; } @@ -12851,65 +13141,65 @@ void PM_WeaponLightsaber(void) } //If the phaser has been fired, delay the next recharge time - if ( !PM_ControlledByPlayer() ) + if (!PM_ControlledByPlayer()) { - if( pm->gent && pm->gent->NPC != NULL ) + if (pm->gent && pm->gent->NPC != NULL) {//NPCs have their own refire logic //FIXME: this really should be universal... return; } } - if ( !PM_InCartwheel( pm->ps->torsoAnim ) ) + if (!PM_InCartwheel(pm->ps->torsoAnim)) {//can still attack during a cartwheel/arial pm->ps->weaponTime = addTime; } } //--------------------------------------- -static bool PM_DoChargedWeapons( void ) +static bool PM_DoChargedWeapons(void) //--------------------------------------- { qboolean charging = qfalse, - altFire = qfalse; + altFire = qfalse; //FIXME: make jedi aware they're being aimed at with a charged-up weapon (strafe and be evasive?) // If you want your weapon to be a charging weapon, just set this bit up - switch( pm->ps->weapon ) + switch (pm->ps->weapon) { - //------------------ + //------------------ case WP_BRYAR_PISTOL: case WP_BLASTER_PISTOL: // alt-fire charges the weapon - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { charging = qtrue; altFire = qtrue; } break; - //------------------ + //------------------ case WP_DISRUPTOR: // alt-fire charges the weapon...but due to zooming being controlled by the alt-button, the main button actually charges...but only when zoomed. // lovely, eh? - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) { - if ( cg.zoomMode == 2 ) + if (cg.zoomMode == 2) { - if ( pm->cmd.buttons & BUTTON_ATTACK ) + if (pm->cmd.buttons & BUTTON_ATTACK) { charging = qtrue; altFire = qtrue; // believe it or not, it really is an alt-fire in this case! } } } - else if ( pm->gent && pm->gent->NPC ) + else if (pm->gent && pm->gent->NPC) { - if ( (pm->gent->NPC->scriptFlags&SCF_ALT_FIRE) ) + if ((pm->gent->NPC->scriptFlags&SCF_ALT_FIRE)) { - if ( pm->gent->fly_sound_debounce_time > level.time ) + if (pm->gent->fly_sound_debounce_time > level.time) { charging = qtrue; altFire = qtrue; @@ -12918,50 +13208,50 @@ static bool PM_DoChargedWeapons( void ) } break; - //------------------ + //------------------ case WP_BOWCASTER: // main-fire charges the weapon - if ( pm->cmd.buttons & BUTTON_ATTACK ) + if (pm->cmd.buttons & BUTTON_ATTACK) { charging = qtrue; } break; - //------------------ + //------------------ case WP_DEMP2: // alt-fire charges the weapon - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { charging = qtrue; altFire = qtrue; } break; - //------------------ + //------------------ case WP_ROCKET_LAUNCHER: // Not really a charge weapon, but we still want to delay fire until the button comes up so that we can // implement our alt-fire locking stuff - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { charging = qtrue; altFire = qtrue; } break; - //------------------ + //------------------ case WP_THERMAL: // FIXME: Really should have a wind-up anim for player // as he holds down the fire button to throw, then play // the actual throw when he lets go... - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { altFire = qtrue; // override default of not being an alt-fire charging = qtrue; } - else if ( pm->cmd.buttons & BUTTON_ATTACK ) + else if (pm->cmd.buttons & BUTTON_ATTACK) { charging = qtrue; } @@ -12971,17 +13261,17 @@ static bool PM_DoChargedWeapons( void ) // set up the appropriate weapon state based on the button that's down. // Note that we ALWAYS return if charging is set ( meaning the buttons are still down ) - if ( charging ) + if (charging) { - if ( altFire ) + if (altFire) { - if ( pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_DROPPING ) + if (pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_DROPPING) { - if ( pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0) + if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0) { - PM_AddEvent( EV_NOAMMO ); + PM_AddEvent(EV_NOAMMO); pm->ps->weaponTime += 500; return true; } @@ -12990,20 +13280,20 @@ static bool PM_DoChargedWeapons( void ) pm->ps->weaponstate = WEAPON_CHARGING_ALT; pm->ps->weaponChargeTime = level.time; - if ( cg_weapons[pm->ps->weapon].altChargeSound ) + if (cg_weapons[pm->ps->weapon].altChargeSound) { - G_SoundOnEnt( pm->gent, CHAN_WEAPON, weaponData[pm->ps->weapon].altChargeSnd ); + G_SoundOnEnt(pm->gent, CHAN_WEAPON, weaponData[pm->ps->weapon].altChargeSnd); } } } else { - if ( pm->ps->weaponstate != WEAPON_CHARGING && pm->ps->weaponstate != WEAPON_DROPPING ) + if (pm->ps->weaponstate != WEAPON_CHARGING && pm->ps->weaponstate != WEAPON_DROPPING) { - if ( pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0) + if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0) { - PM_AddEvent( EV_NOAMMO ); + PM_AddEvent(EV_NOAMMO); pm->ps->weaponTime += 500; return true; } @@ -13012,9 +13302,9 @@ static bool PM_DoChargedWeapons( void ) pm->ps->weaponstate = WEAPON_CHARGING; pm->ps->weaponChargeTime = level.time; - if ( cg_weapons[pm->ps->weapon].chargeSound && pm->gent && !pm->gent->NPC ) // HACK: !NPC mostly for bowcaster and weequay + if (cg_weapons[pm->ps->weapon].chargeSound && pm->gent && !pm->gent->NPC) // HACK: !NPC mostly for bowcaster and weequay { - G_SoundOnEnt( pm->gent, CHAN_WEAPON, weaponData[pm->ps->weapon].chargeSnd ); + G_SoundOnEnt(pm->gent, CHAN_WEAPON, weaponData[pm->ps->weapon].chargeSnd); } } } @@ -13024,19 +13314,19 @@ static bool PM_DoChargedWeapons( void ) // Only charging weapons should be able to set these states...so.... // let's see which fire mode we need to set up now that the buttons are up - if ( pm->ps->weaponstate == WEAPON_CHARGING ) + if (pm->ps->weaponstate == WEAPON_CHARGING) { // weapon has a charge, so let us do an attack // dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now pm->cmd.buttons |= BUTTON_ATTACK; pm->ps->eFlags |= EF_FIRING; } - else if ( pm->ps->weaponstate == WEAPON_CHARGING_ALT ) + else if (pm->ps->weaponstate == WEAPON_CHARGING_ALT) { // weapon has a charge, so let us do an alt-attack // dumb, but since we shoot a charged weapon on button-up, we need to repress this button for now pm->cmd.buttons |= BUTTON_ALT_ATTACK; - pm->ps->eFlags |= (EF_FIRING|EF_ALT_FIRING); + pm->ps->eFlags |= (EF_FIRING | EF_ALT_FIRING); } return false; // continue with the rest of the weapon code @@ -13051,126 +13341,126 @@ static bool PM_DoChargedWeapons( void ) // Specific weapons can opt to modify the ammo usage based on charges, otherwise if no special case code // is handled below, regular ammo usage will happen //--------------------------------------- -static int PM_DoChargingAmmoUsage( int *amount ) +static int PM_DoChargingAmmoUsage(int *amount) //--------------------------------------- { int count = 0; - if ( pm->ps->weapon == WP_BOWCASTER && !( pm->cmd.buttons & BUTTON_ALT_ATTACK )) + if (pm->ps->weapon == WP_BOWCASTER && !(pm->cmd.buttons & BUTTON_ALT_ATTACK)) { // this code is duplicated ( I know, I know ) in G_weapon.cpp for the bowcaster alt-fire - count = ( level.time - pm->ps->weaponChargeTime ) / BOWCASTER_CHARGE_UNIT; + count = (level.time - pm->ps->weaponChargeTime) / BOWCASTER_CHARGE_UNIT; - if ( count < 1 ) + if (count < 1) { count = 1; } - else if ( count > 5 ) + else if (count > 5) { count = 5; } - if ( !(count & 1 )) + if (!(count & 1)) { // if we aren't odd, knock us down a level count--; } // Only bother with these checks if we don't have infinite ammo - if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 ) + if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] != -1) { int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count; // If we have enough ammo to do the full charged shot, we are ok - if ( dif < 0 ) + if (dif < 0) { // we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative count += floor(dif / (float)*amount); - if ( count < 1 ) + if (count < 1) { count = 1; } // now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked - pm->ps->weaponChargeTime = level.time - ( count * BOWCASTER_CHARGE_UNIT ); + pm->ps->weaponChargeTime = level.time - (count * BOWCASTER_CHARGE_UNIT); } } // now that count is cool, get the real ammo usage *amount *= count; } - else if( ( pm->ps->weapon == WP_BRYAR_PISTOL && pm->cmd.buttons & BUTTON_ALT_ATTACK ) - || ( pm->ps->weapon == WP_BLASTER_PISTOL && pm->cmd.buttons & BUTTON_ALT_ATTACK ) ) + else if ((pm->ps->weapon == WP_BRYAR_PISTOL && pm->cmd.buttons & BUTTON_ALT_ATTACK) + || (pm->ps->weapon == WP_BLASTER_PISTOL && pm->cmd.buttons & BUTTON_ALT_ATTACK)) { // this code is duplicated ( I know, I know ) in G_weapon.cpp for the bryar alt-fire - count = ( level.time - pm->ps->weaponChargeTime ) / BRYAR_CHARGE_UNIT; + count = (level.time - pm->ps->weaponChargeTime) / BRYAR_CHARGE_UNIT; - if ( count < 1 ) + if (count < 1) { count = 1; } - else if ( count > 5 ) + else if (count > 5) { count = 5; } // Only bother with these checks if we don't have infinite ammo - if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 ) + if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] != -1) { int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count; // If we have enough ammo to do the full charged shot, we are ok - if ( dif < 0 ) + if (dif < 0) { // we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative count += floor(dif / (float)*amount); - if ( count < 1 ) + if (count < 1) { count = 1; } // now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked - pm->ps->weaponChargeTime = level.time - ( count * BRYAR_CHARGE_UNIT ); + pm->ps->weaponChargeTime = level.time - (count * BRYAR_CHARGE_UNIT); } } // now that count is cool, get the real ammo usage *amount *= count; } - else if ( pm->ps->weapon == WP_DEMP2 && pm->cmd.buttons & BUTTON_ALT_ATTACK ) + else if (pm->ps->weapon == WP_DEMP2 && pm->cmd.buttons & BUTTON_ALT_ATTACK) { // this code is duplicated ( I know, I know ) in G_weapon.cpp for the demp2 alt-fire - count = ( level.time - pm->ps->weaponChargeTime ) / DEMP2_CHARGE_UNIT; + count = (level.time - pm->ps->weaponChargeTime) / DEMP2_CHARGE_UNIT; - if ( count < 1 ) + if (count < 1) { count = 1; } - else if ( count > 3 ) + else if (count > 3) { count = 3; } // Only bother with these checks if we don't have infinite ammo - if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 ) + if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] != -1) { int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count; // If we have enough ammo to do the full charged shot, we are ok - if ( dif < 0 ) + if (dif < 0) { // we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative count += floor(dif / (float)*amount); - if ( count < 1 ) + if (count < 1) { count = 1; } // now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked - pm->ps->weaponChargeTime = level.time - ( count * DEMP2_CHARGE_UNIT ); + pm->ps->weaponChargeTime = level.time - (count * DEMP2_CHARGE_UNIT); } } @@ -13178,43 +13468,43 @@ static int PM_DoChargingAmmoUsage( int *amount ) *amount *= count; // this is an after-thought. should probably re-write the function to do this naturally. - if ( *amount > pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] ) + if (*amount > pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex]) { *amount = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex]; } } - else if ( pm->ps->weapon == WP_DISRUPTOR && pm->cmd.buttons & BUTTON_ALT_ATTACK ) // BUTTON_ATTACK will have been mapped to BUTTON_ALT_ATTACK if we are zoomed + else if (pm->ps->weapon == WP_DISRUPTOR && pm->cmd.buttons & BUTTON_ALT_ATTACK) // BUTTON_ATTACK will have been mapped to BUTTON_ALT_ATTACK if we are zoomed { // this code is duplicated ( I know, I know ) in G_weapon.cpp for the disruptor alt-fire - count = ( level.time - pm->ps->weaponChargeTime ) / DISRUPTOR_CHARGE_UNIT; + count = (level.time - pm->ps->weaponChargeTime) / DISRUPTOR_CHARGE_UNIT; - if ( count < 1 ) + if (count < 1) { count = 1; } - else if ( count > 10 ) + else if (count > 10) { count = 10; } // Only bother with these checks if we don't have infinite ammo - if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 ) + if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] != -1) { int dif = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - *amount * count; // If we have enough ammo to do the full charged shot, we are ok - if ( dif < 0 ) + if (dif < 0) { // we are not ok, so hack our chargetime and ammo usage, note that DIF is going to be negative count += floor(dif / (float)*amount); - if ( count < 1 ) + if (count < 1) { count = 1; } // now get a real chargeTime so the duplicated code in g_weapon doesn't get freaked - pm->ps->weaponChargeTime = level.time - ( count * DISRUPTOR_CHARGE_UNIT ); + pm->ps->weaponChargeTime = level.time - (count * DISRUPTOR_CHARGE_UNIT); } } @@ -13222,7 +13512,7 @@ static int PM_DoChargingAmmoUsage( int *amount ) *amount *= count; // this is an after-thought. should probably re-write the function to do this naturally. - if ( *amount > pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] ) + if (*amount > pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex]) { *amount = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex]; } @@ -13231,25 +13521,25 @@ static int PM_DoChargingAmmoUsage( int *amount ) return count; } -qboolean PM_DroidMelee( int npc_class ) +qboolean PM_DroidMelee(int npc_class) { - if ( npc_class == CLASS_PROBE + if (npc_class == CLASS_PROBE || npc_class == CLASS_SEEKER || npc_class == CLASS_INTERROGATOR || npc_class == CLASS_SENTRY - || npc_class == CLASS_REMOTE ) + || npc_class == CLASS_REMOTE) { return qtrue; } return qfalse; } -void PM_WeaponWampa( void ) +void PM_WeaponWampa(void) { // make weapon function - if ( pm->ps->weaponTime > 0 ) { + if (pm->ps->weaponTime > 0) { pm->ps->weaponTime -= pml.msec; - if ( pm->ps->weaponTime <= 0 ) + if (pm->ps->weaponTime <= 0) { pm->ps->weaponTime = 0; } @@ -13257,38 +13547,38 @@ void PM_WeaponWampa( void ) // check for weapon change // can't change if weapon is firing, but can change again if lowering or raising - if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) { - if ( pm->ps->weapon != pm->cmd.weapon ) { - PM_BeginWeaponChange( pm->cmd.weapon ); + if (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) { + if (pm->ps->weapon != pm->cmd.weapon) { + PM_BeginWeaponChange(pm->cmd.weapon); } } - if ( pm->ps->weaponTime > 0 ) + if (pm->ps->weaponTime > 0) { return; } // change weapon if time - if ( pm->ps->weaponstate == WEAPON_DROPPING ) { + if (pm->ps->weaponstate == WEAPON_DROPPING) { PM_FinishWeaponChange(); return; } - if ( pm->ps->weapon == WP_SABER + if (pm->ps->weapon == WP_SABER && (pm->cmd.buttons&BUTTON_ATTACK) - && pm->ps->torsoAnim == BOTH_HANG_IDLE ) + && pm->ps->torsoAnim == BOTH_HANG_IDLE) { pm->ps->SaberActivate(); - pm->ps->SaberActivateTrail( 150 ); - PM_SetAnim( pm, SETANIM_BOTH, BOTH_HANG_ATTACK, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + pm->ps->SaberActivateTrail(150); + PM_SetAnim(pm, SETANIM_BOTH, BOTH_HANG_ATTACK, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); pm->ps->weaponstate = WEAPON_FIRING; pm->ps->saberBlocked = BLOCKED_NONE; pm->ps->saberMove = LS_READY; pm->ps->saberMoveNext = LS_NONE; } - else if ( pm->ps->torsoAnim == BOTH_HANG_IDLE ) + else if (pm->ps->torsoAnim == BOTH_HANG_IDLE) { - pm->ps->SaberDeactivateTrail( 0 ); + pm->ps->SaberDeactivateTrail(0); pm->ps->weaponstate = WEAPON_READY; pm->ps->saberMove = LS_READY; pm->ps->saberMoveNext = LS_NONE; @@ -13301,47 +13591,48 @@ PM_Weapon Generates weapon events and modifes the weapon counter ============== */ -static void PM_Weapon( void ) +static void PM_Weapon(void) { int addTime, amount, trueCount = 1; + static int punch = 0; qboolean delayed_fire = qfalse; - if ( (pm->ps->eFlags&EF_HELD_BY_WAMPA) ) + if ((pm->ps->eFlags&EF_HELD_BY_WAMPA)) { PM_WeaponWampa(); return; } - if ( pm->ps->eFlags&EF_FORCE_DRAINED ) + if (pm->ps->eFlags&EF_FORCE_DRAINED) {//being drained return; } - if ( (pm->ps->forcePowersActive&(1<ps->forceDrainEntityNum < ENTITYNUM_WORLD ) + if ((pm->ps->forcePowersActive&(1 << FP_DRAIN)) + && pm->ps->forceDrainEntityNum < ENTITYNUM_WORLD) {//draining return; } - if (pm->ps->weapon == WP_SABER && (cg.zoomMode==3||!cg.zoomMode||pm->ps->clientNum) ) // WP_LIGHTSABER + if (pm->ps->weapon == WP_SABER && (cg.zoomMode == 3 || !cg.zoomMode || pm->ps->clientNum)) // WP_LIGHTSABER { // Separate logic for lightsaber, but not for player when zoomed PM_WeaponLightsaber(); - if ( pm->gent && pm->gent->client && pm->ps->saber[0].Active() && pm->ps->saberInFlight ) + if (pm->gent && pm->gent->client && pm->ps->saber[0].Active() && pm->ps->saberInFlight) {//FIXME: put saberTrail in playerState - if ( pm->gent->client->ps.saberEntityState == SES_RETURNING ) + if (pm->gent->client->ps.saberEntityState == SES_RETURNING) {//turn off the saber trail - pm->gent->client->ps.SaberDeactivateTrail( 75 ); + pm->gent->client->ps.SaberDeactivateTrail(75); } else {//turn on the saber trail - pm->gent->client->ps.SaberActivateTrail( 150 ); + pm->gent->client->ps.SaberActivateTrail(150); } } return; } - if ( PM_InKnockDown( pm->ps ) || PM_InRoll( pm->ps )) + if (PM_InKnockDown(pm->ps) || PM_InRoll(pm->ps)) {//in knockdown - if ( pm->ps->weaponTime > 0 ) { + if (pm->ps->weaponTime > 0) { pm->ps->weaponTime -= pml.msec; - if ( pm->ps->weaponTime <= 0 ) + if (pm->ps->weaponTime <= 0) { pm->ps->weaponTime = 0; } @@ -13349,56 +13640,51 @@ static void PM_Weapon( void ) return; } - if( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { - if ( pm->gent->client->fireDelay > 0 ) + if (pm->gent->client->fireDelay > 0) {//FIXME: this is going to fire off one frame before you expect, actually pm->gent->client->fireDelay -= pml.msec; - if(pm->gent->client->fireDelay <= 0) + if (pm->gent->client->fireDelay <= 0) {//just finished delay timer - if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER ) + if (pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER) { - G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/lock.wav" ); + G_SoundOnEnt(pm->gent, CHAN_WEAPON, "sound/weapons/rocket/lock.wav"); pm->cmd.buttons |= BUTTON_ALT_ATTACK; } pm->gent->client->fireDelay = 0; delayed_fire = qtrue; - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && pm->ps->weapon == WP_THERMAL - && pm->gent->alt_fire ) + && pm->gent->alt_fire) { pm->cmd.buttons |= BUTTON_ALT_ATTACK; } } else { - if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER && Q_irand( 0, 1 ) ) + if (pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER && Q_irand(0, 1)) { - G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/tick.wav" ); + G_SoundOnEnt(pm->gent, CHAN_WEAPON, "sound/weapons/rocket/tick.wav"); } } } } - // don't allow attack until all buttons are up - if ( pm->ps->pm_flags & PMF_RESPAWNED ) { + // don't allow attack until all buttons are up + if (pm->ps->pm_flags & PMF_RESPAWNED) { return; } // check for dead player - if ( pm->ps->stats[STAT_HEALTH] <= 0 ) + if (pm->ps->stats[STAT_HEALTH] <= 0) { - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { - // borg no longer exist, use NPC_class to check for any npc's that don't drop their weapons (if there are any) - // Sigh..borg shouldn't drop their weapon attachments when they die. Also, never drop a lightsaber! - // if ( pm->gent->client->playerTeam != TEAM_BORG) - { - pm->ps->weapon = WP_NONE; - } + pm->ps->weapon = WP_NONE; } - if ( pm->gent ) + if (pm->gent) { pm->gent->s.loopSound = 0; } @@ -13406,9 +13692,9 @@ static void PM_Weapon( void ) } // make weapon function - if ( pm->ps->weaponTime > 0 ) { + if (pm->ps->weaponTime > 0) { pm->ps->weaponTime -= pml.msec; - if ( pm->ps->weaponTime <= 0 ) + if (pm->ps->weaponTime <= 0) { pm->ps->weaponTime = 0; } @@ -13416,102 +13702,102 @@ static void PM_Weapon( void ) // check for weapon change // can't change if weapon is firing, but can change again if lowering or raising - if ( (pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) && pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING) { - if ( pm->ps->weapon != pm->cmd.weapon && (!pm->ps->viewEntity || pm->ps->viewEntity >= ENTITYNUM_WORLD) && !PM_DoChargedWeapons()) { - PM_BeginWeaponChange( pm->cmd.weapon ); + if ((pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING) && pm->ps->weaponstate != WEAPON_CHARGING_ALT && pm->ps->weaponstate != WEAPON_CHARGING) { + if (pm->ps->weapon != pm->cmd.weapon && (!pm->ps->viewEntity || pm->ps->viewEntity >= ENTITYNUM_WORLD) && !PM_DoChargedWeapons()) { + PM_BeginWeaponChange(pm->cmd.weapon); } } - if ( pm->ps->weaponTime > 0 ) + if (pm->ps->weaponTime > 0) { return; } // change weapon if time - if ( pm->ps->weaponstate == WEAPON_DROPPING ) { + if (pm->ps->weaponstate == WEAPON_DROPPING) { PM_FinishWeaponChange(); return; } - if ( pm->ps->weapon == WP_NONE ) + if (pm->ps->weapon == WP_NONE) { return; } - if ( pm->ps->weaponstate == WEAPON_RAISING ) + if (pm->ps->weaponstate == WEAPON_RAISING) { //Just selected the weapon pm->ps->weaponstate = WEAPON_IDLE; - if(pm->gent && (pm->gent->s.numbergent))) + if (pm->gent && (pm->gent->s.numbergent))) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } else { - switch(pm->ps->weapon) + switch (pm->ps->weapon) { case WP_BRYAR_PISTOL: case WP_BLASTER_PISTOL: - if ( pm->gent - && pm->gent->weaponModel[1] > 0 ) + if (pm->gent + && pm->gent->weaponModel[1] > 0) {//dual pistols //FIXME: should be a better way of detecting a dual-pistols user so it's not hardcoded to the saboteurcommando... - PM_SetAnim(pm,SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_STAND1, SETANIM_FLAG_NORMAL); } else {//single pistol - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE2,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE2, SETANIM_FLAG_NORMAL); } break; default: - PM_SetAnim(pm,SETANIM_TORSO,TORSO_WEAPONIDLE3,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_TORSO, TORSO_WEAPONIDLE3, SETANIM_FLAG_NORMAL); break; } } return; } - if ( pm->gent ) + if (pm->gent) {//ready to throw thermal again, add it - if ( pm->ps->weapon == WP_THERMAL - && pm->gent->weaponModel[0] == -1 ) + if (pm->ps->weapon == WP_THERMAL + && pm->gent->weaponModel[0] == -1) {//add the thermal model back in our hand // remove anything if we have anything already G_RemoveWeaponModels( pm->gent ); - if (weaponData[pm->ps->weapon].weaponMdl[0]) { //might be NONE, so check if it has a model - G_CreateG2AttachedWeaponModel( pm->gent, weaponData[pm->ps->weapon].weaponMdl, pm->gent->handRBolt, 0 ); + if (weaponData[pm->ps->weapon].worldModel[0]) { //might be NONE, so check if it has a model + G_CreateG2AttachedWeaponModel( pm->gent, weaponData[pm->ps->weapon].worldModel, pm->gent->handRBolt, 0 ); //make it sound like we took another one out from... uh.. somewhere... - if ( cg.time > 0 ) + if (cg.time > 0) {//this way we don't get that annoying change weapon sound every time a map starts - PM_AddEvent( EV_CHANGE_WEAPON ); + PM_AddEvent(EV_CHANGE_WEAPON); } } } } - if ( !delayed_fire ) + if (!delayed_fire) {//didn't just finish a fire delay - if ( PM_DoChargedWeapons()) + if (PM_DoChargedWeapons()) { // In some cases the charged weapon code may want us to short circuit the rest of the firing code return; } else { - if ( !pm->gent->client->fireDelay//not already waiting to fire - && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//player + if (!pm->gent->client->fireDelay//not already waiting to fire + && (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())//player && pm->ps->weapon == WP_THERMAL//holding thermal && pm->gent//gent && pm->gent->client//client - && (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) )//holding fire + && (pm->cmd.buttons&(BUTTON_ATTACK | BUTTON_ALT_ATTACK)))//holding fire {//delay the actual firing of the missile until the anim has played some - if ( PM_StandingAnim( pm->ps->legsAnim ) - || pm->ps->legsAnim == BOTH_THERMAL_READY ) + if (PM_StandingAnim(pm->ps->legsAnim) + || pm->ps->legsAnim == BOTH_THERMAL_READY) { - PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } - PM_SetAnim(pm,SETANIM_TORSO,BOTH_THERMAL_THROW,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); pm->gent->client->fireDelay = 300; pm->ps->weaponstate = WEAPON_FIRING; pm->gent->alt_fire = (qboolean)(pm->cmd.buttons&BUTTON_ALT_ATTACK); @@ -13520,18 +13806,18 @@ static void PM_Weapon( void ) } } - if(!delayed_fire) + if (!delayed_fire) { - if ( pm->ps->weapon == WP_MELEE && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + if (pm->ps->weapon == WP_MELEE && (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) {//melee - if ( (pm->cmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK)) != (BUTTON_ATTACK|BUTTON_ALT_ATTACK) ) + if ((pm->cmd.buttons&(BUTTON_ATTACK | BUTTON_ALT_ATTACK)) != (BUTTON_ATTACK | BUTTON_ALT_ATTACK)) {//not holding both buttons - if ( (pm->cmd.buttons&BUTTON_ATTACK)&&(pm->ps->pm_flags&PMF_ATTACK_HELD) ) + if ((pm->cmd.buttons&BUTTON_ATTACK) && (pm->ps->pm_flags&PMF_ATTACK_HELD)) {//held button //clear it pm->cmd.buttons &= ~BUTTON_ATTACK; } - if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK)&&(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) ) + if ((pm->cmd.buttons&BUTTON_ALT_ATTACK) && (pm->ps->pm_flags&PMF_ALT_ATTACK_HELD)) {//held button //clear it pm->cmd.buttons &= ~BUTTON_ALT_ATTACK; @@ -13539,155 +13825,195 @@ static void PM_Weapon( void ) } } // check for fire - if ( !(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) ) + if (!(pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_ALT_ATTACK))) { pm->ps->weaponTime = 0; - if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 ) + if (pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0) {//Still firing pm->ps->weaponstate = WEAPON_FIRING; } - else if ( pm->ps->weaponstate != WEAPON_READY ) + else if (pm->ps->weaponstate != WEAPON_READY) { - if ( !pm->gent || !pm->gent->NPC || pm->gent->attackDebounceTime < level.time ) + if (!pm->gent || !pm->gent->NPC || pm->gent->attackDebounceTime < level.time) { pm->ps->weaponstate = WEAPON_IDLE; } } - if ( pm->ps->weapon == WP_MELEE - && (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) - && PM_KickMove( pm->ps->saberMove ) ) + if (pm->ps->weapon == WP_MELEE + && (pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + && PM_KickMove(pm->ps->saberMove)) {//melee, not attacking, clear move pm->ps->saberMove = LS_NONE; } return; } - if (pm->gent->s.m_iVehicleNum!=0) + if (pm->gent->s.m_iVehicleNum != 0) { // No Anims if on Veh } // start the animation even if out of ammo - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ROCKETTROOPER ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ROCKETTROOPER) { - if ( pm->gent->client->moveType == MT_FLYSWIM ) + if (pm->gent->client->moveType == MT_FLYSWIM) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } } #ifndef BASE_SAVE_COMPAT - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_HAZARD_TROOPER ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_HAZARD_TROOPER) { // Kneel attack //-------------- - if( pm->cmd.upmove == -127 ) + if (pm->cmd.upmove == -127) { - PM_SetAnim(pm,SETANIM_TORSO, BOTH_KNEELATTACK, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_KNEELATTACK, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } // Standing attack //----------------- } #endif - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ASSASSIN_DROID ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_ASSASSIN_DROID) { // Crouched Attack - if (PM_CrouchAnim(pm->gent->client->ps.legsAnim)) + if (PM_CrouchAnim(pm->gent->client->ps.legsAnim)) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLDLESS); } // Standing Attack //----------------- else { - // if (PM_StandingAnim(pm->gent->client->ps.legsAnim)) - // { - // PM_SetAnim(pm,SETANIM_BOTH,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS); - // } - // else + // if (PM_StandingAnim(pm->gent->client->ps.legsAnim)) + // { + // PM_SetAnim(pm,SETANIM_BOTH,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS); + // } + // else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLDLESS); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLDLESS); } } } else { - switch(pm->ps->weapon) + switch (pm->ps->weapon) { /* - case WP_SABER://1 - handed + case WP_SABER://1 - handed PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); break; - */ + */ case WP_BRYAR_PISTOL://1-handed case WP_BLASTER_PISTOL://1-handed - if ( pm->gent && pm->gent->weaponModel[1] > 0 ) + if (pm->gent && pm->gent->weaponModel[1] > 0) {//dual pistols - PM_SetAnim(pm,SETANIM_TORSO,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_GUNSIT1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } else {//single pistol - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } break; case WP_MELEE: // since there's no RACE_BOTS, I listed all the droids that have might have melee attacks - dmv - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { - if ( PM_DroidMelee( pm->gent->client->NPC_class ) ) + if (PM_DroidMelee(pm->gent->client->NPC_class)) { - if ( rand() & 1 ) - PM_SetAnim(pm,SETANIM_BOTH,BOTH_MELEE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + if (punch == 0) + { + PM_SetAnim(pm, SETANIM_BOTH, BOTH_MELEE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); + punch = 1; + } else - PM_SetAnim(pm,SETANIM_BOTH,BOTH_MELEE2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); + { + PM_SetAnim(pm, SETANIM_BOTH, BOTH_MELEE2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); + punch = 0; + } + } else { int anim = -1; - if ( (pm->ps->clientNum < MAX_CLIENTS ||PM_ControlledByPlayer()) - && g_debugMelee->integer ) - { - if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK) ) + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) + && !(g_debugMelee->integer < 0) ) + {//player melee usage + if ((pm->cmd.buttons&BUTTON_ALT_ATTACK)) { - if ( (pm->cmd.buttons&BUTTON_ATTACK) ) + if ((pm->cmd.buttons&BUTTON_ATTACK) && pm->ps->forcePowerLevel[FP_SABER_OFFENSE] > 1 + /*&& pm->gent->flags&FL_MELEEKATAS*/) { PM_TryGrab(); } - else if ( !(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) ) + else if (!(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD) + //&& pm->gent->flags&FL_MELEEKICKS + /*&& pm->ps->forcePowerLevel[FP_SABER_OFFENSE] > 0*/) { PM_CheckKick(); } } - else if ( !(pm->ps->pm_flags&PMF_ATTACK_HELD) ) + else if (!(pm->ps->pm_flags&PMF_ATTACK_HELD)) { - anim = PM_PickAnim( pm->gent, BOTH_MELEE1, BOTH_MELEE2 ); + //anim = PM_PickAnim(pm->gent, BOTH_MELEE1, BOTH_MELEE2); + if (punch == 0) + { + anim = BOTH_MELEE1; + punch = 1; + } + else + { + anim = BOTH_MELEE2; + punch = 0; + } } } else - { - anim = PM_PickAnim( pm->gent, BOTH_MELEE1, BOTH_MELEE2 ); + {//NPC trying to punch or kick + if (pm->ps->saberMoveNext) //must be kicking... or doing something related to a kata??? + { + //if (!(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD)) + // { + PM_WeaponLightsaber(); //kind of an ugly hack, but hey, technically kicks are saber moves... + // } + } + else + { + anim = PM_PickAnim(pm->gent, BOTH_MELEE1, BOTH_MELEE2); //is this even used? + } } - if ( anim != -1 ) + if (anim != -1) { - if ( VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 ) + int extraAnimFlag = 0; + if (pm->gent->NPC && pm->gent->NPC->aiFlags&NPCAI_HEAVY_MELEE) + {//heavy punchers hold it out there + extraAnimFlag = SETANIM_FLAG_HOLD; + } + else + {//fast punchers pull it back after finishing anim + extraAnimFlag = SETANIM_FLAG_HOLDLESS; + } + + if (VectorCompare(pm->ps->velocity, vec3_origin) && pm->cmd.upmove >= 0) { - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | extraAnimFlag | SETANIM_FLAG_RESTART); } else { - PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + PM_SetAnim(pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE | extraAnimFlag | SETANIM_FLAG_RESTART); } } } @@ -13695,36 +14021,36 @@ static void PM_Weapon( void ) break; case WP_TUSKEN_RIFLE: - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) {//shoot //in alt-fire, sniper mode - PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } else {//melee - int anim = PM_PickAnim( pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3 ); // Rifle - if ( VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 ) + int anim = PM_PickAnim(pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3); // Rifle + if (VectorCompare(pm->ps->velocity, vec3_origin) && pm->cmd.upmove >= 0) { - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); } else { - PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + PM_SetAnim(pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); } } break; case WP_TUSKEN_STAFF: - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { int anim; - int flags = (SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); - if ( (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) ) + int flags = (SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); + if ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer())) {//player - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { - if ( pm->cmd.buttons & BUTTON_ATTACK ) + if (pm->cmd.buttons & BUTTON_ATTACK) { anim = BOTH_TUSKENATTACK3; } @@ -13740,7 +14066,7 @@ static void PM_Weapon( void ) } else {// npc - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { anim = BOTH_TUSKENLUNGE1; if (pm->ps->torsoAnimTimer>0) @@ -13750,87 +14076,107 @@ static void PM_Weapon( void ) } else { - anim = PM_PickAnim( pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3 ); + anim = PM_PickAnim(pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3); } } - if ( VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 ) + if (VectorCompare(pm->ps->velocity, vec3_origin) && pm->cmd.upmove >= 0) { - PM_SetAnim( pm, SETANIM_BOTH, anim, flags, 0); + PM_SetAnim(pm, SETANIM_BOTH, anim, flags, 0); } else { - PM_SetAnim( pm, SETANIM_TORSO, anim, flags, 0); + PM_SetAnim(pm, SETANIM_TORSO, anim, flags, 0); } } break; case WP_NOGHRI_STICK: - if ( pm->gent && pm->gent->client ) + if (pm->gent && pm->gent->client) { int anim; - if ( pm->cmd.buttons & BUTTON_ATTACK ) + if (pm->cmd.buttons & BUTTON_ATTACK) { anim = BOTH_ATTACK3; } else { - anim = PM_PickAnim( pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3 ); + anim = PM_PickAnim(pm->gent, BOTH_TUSKENATTACK1, BOTH_TUSKENATTACK3); } - if ( anim != BOTH_ATTACK3 && VectorCompare( pm->ps->velocity, vec3_origin ) && pm->cmd.upmove >= 0 ) + if (anim != BOTH_ATTACK3 && VectorCompare(pm->ps->velocity, vec3_origin) && pm->cmd.upmove >= 0) { - PM_SetAnim( pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + PM_SetAnim(pm, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); } else { - PM_SetAnim( pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + PM_SetAnim(pm, SETANIM_TORSO, anim, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); } } break; - case WP_BLASTER: + case WP_E5_CARBINE: + PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); + break; + + case WP_DC15S_CARBINE: + PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); + break; + + case WP_Z6_ROTARY: + PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); + break; + + case WP_DC15A_RIFLE: + PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); + break; + + case WP_SONIC_BLASTER: PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); break; + case WP_BLASTER: + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); + break; + case WP_DISRUPTOR: - if ( ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())&& pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags&SCF_ALT_FIRE)) || - ((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) && cg.zoomMode == 2 ) ) + if (((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) && pm->gent && pm->gent->NPC && (pm->gent->NPC->scriptFlags&SCF_ALT_FIRE)) || + ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) && cg.zoomMode == 2)) {//NPC or player in alt-fire, sniper mode - PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK4, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } else {//in primary fire mode - PM_SetAnim( pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD | SETANIM_FLAG_RESTART); } break; case WP_BOT_LASER: - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); break; case WP_THERMAL: - if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())) { - if ( PM_StandingAnim( pm->ps->legsAnim ) ) + if (PM_StandingAnim(pm->ps->legsAnim)) { - PM_SetAnim( pm, SETANIM_LEGS, BOTH_ATTACK10, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_ATTACK10, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK10,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK10, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } else { - if ( cg.renderingThirdPerson ) + if ( BG_AllowThirdPersonSpecialMove( pm->ps ) ) { - if ( PM_StandingAnim( pm->ps->legsAnim ) - || pm->ps->legsAnim == BOTH_THERMAL_READY ) + if (PM_StandingAnim(pm->ps->legsAnim) + || pm->ps->legsAnim == BOTH_THERMAL_READY) { - PM_SetAnim( pm, SETANIM_LEGS, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); } - PM_SetAnim(pm,SETANIM_TORSO,BOTH_THERMAL_THROW,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//|SETANIM_FLAG_RESTART + PM_SetAnim(pm, SETANIM_TORSO, BOTH_THERMAL_THROW, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//|SETANIM_FLAG_RESTART } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } } break; @@ -13844,36 +14190,36 @@ static void PM_Weapon( void ) break; case WP_REPEATER: - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_GALAKMECH) {// - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } } else { - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); } break; case WP_TRIP_MINE: case WP_DET_PACK: - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK11,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK11, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); break; default://2-handed heavy weapon - PM_SetAnim(pm,SETANIM_TORSO,BOTH_ATTACK3,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_RESTART|SETANIM_FLAG_HOLD); + PM_SetAnim(pm, SETANIM_TORSO, BOTH_ATTACK3, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_RESTART | SETANIM_FLAG_HOLD); break; } } } - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { amount = weaponData[pm->ps->weapon].altEnergyPerShot; } @@ -13882,16 +14228,16 @@ static void PM_Weapon( void ) amount = weaponData[pm->ps->weapon].energyPerShot; } - if ( (pm->ps->weaponstate == WEAPON_CHARGING) || (pm->ps->weaponstate == WEAPON_CHARGING_ALT) ) + if ((pm->ps->weaponstate == WEAPON_CHARGING) || (pm->ps->weaponstate == WEAPON_CHARGING_ALT)) { // charging weapons may want to do their own ammo logic. - trueCount = PM_DoChargingAmmoUsage( &amount ); + trueCount = PM_DoChargingAmmoUsage(&amount); } pm->ps->weaponstate = WEAPON_FIRING; // take an ammo away if not infinite - if ( pm->ps->ammo[ weaponData[pm->ps->weapon].ammoIndex ] != -1 ) + if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] != -1) { // enough energy to fire this weapon? if ((pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - amount) >= 0) @@ -13900,17 +14246,17 @@ static void PM_Weapon( void ) } else // Not enough energy { - if ( !( pm->ps->eFlags & EF_LOCKED_TO_WEAPON )) + if (!(pm->ps->eFlags & EF_LOCKED_TO_WEAPON)) { // Switch weapons - PM_AddEvent( EV_NOAMMO ); + PM_AddEvent(EV_NOAMMO); pm->ps->weaponTime += 500; } return; } } - if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 ) + if (pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0) {//FIXME: this is going to fire off one frame before you expect, actually // Clear these out since we're not actually firing yet pm->ps->eFlags &= ~EF_FIRING; @@ -13918,99 +14264,113 @@ static void PM_Weapon( void ) return; } - if ( pm->ps->weapon == WP_EMPLACED_GUN ) + if (pm->ps->weapon == WP_EMPLACED_GUN) { - if ( pm->gent + if (pm->gent && pm->gent->owner - && pm->gent->owner->e_UseFunc == useF_eweb_use ) + && pm->gent->owner->e_UseFunc == useF_eweb_use) {//eweb always shoots alt-fire, for proper effects and sounds - PM_AddEvent( EV_ALT_FIRE ); + PM_AddEvent(EV_ALT_FIRE); addTime = weaponData[pm->ps->weapon].altFireTime; } else {//emplaced gun always shoots normal fire - PM_AddEvent( EV_FIRE_WEAPON ); + PM_AddEvent(EV_FIRE_WEAPON); addTime = weaponData[pm->ps->weapon].fireTime; } } - else if ( (pm->ps->weapon == WP_MELEE && (pm->ps->clientNum>=MAX_CLIENTS||!g_debugMelee->integer) ) - || pm->ps->weapon == WP_TUSKEN_STAFF - || (pm->ps->weapon == WP_TUSKEN_RIFLE&&!(pm->cmd.buttons&BUTTON_ALT_ATTACK)) ) + else if ( /*(pm->ps->weapon == WP_MELEE && (pm->ps->clientNum>=MAX_CLIENTS||!g_debugMelee->integer) ) //will commenting this fix firetime discrepancy for melee with kungfu? + ||*/ pm->ps->weapon == WP_TUSKEN_STAFF + || (pm->ps->weapon == WP_TUSKEN_RIFLE&&!(pm->cmd.buttons&BUTTON_ALT_ATTACK))) { - PM_AddEvent( EV_FIRE_WEAPON ); + PM_AddEvent(EV_FIRE_WEAPON); addTime = pm->ps->torsoAnimTimer; } - else if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + else if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { - PM_AddEvent( EV_ALT_FIRE ); + PM_AddEvent(EV_ALT_FIRE); addTime = weaponData[pm->ps->weapon].altFireTime; - if ( pm->ps->weapon == WP_THERMAL ) + if (pm->ps->weapon == WP_THERMAL) {//threw our thermal - if ( pm->gent ) + if (pm->gent) {// remove the thermal model if we had it. - G_RemoveWeaponModels( pm->gent ); - if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + G_RemoveWeaponModels(pm->gent); + if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())) {//NPCs need to know when to put the thermal back in their hand - pm->ps->weaponTime = pm->ps->torsoAnimTimer-500; + pm->ps->weaponTime = pm->ps->torsoAnimTimer - 500; } } } } else { - if ( pm->ps->clientNum //NPC + if (pm->ps->clientNum //NPC && !PM_ControlledByPlayer() //not under player control && pm->ps->weapon == WP_THERMAL //using thermals - && pm->ps->torsoAnim != BOTH_ATTACK10 )//not in the throw anim + && pm->ps->torsoAnim != BOTH_ATTACK10)//not in the throw anim {//oops, got knocked out of the anim, don't throw the thermal return; } - PM_AddEvent( EV_FIRE_WEAPON ); + PM_AddEvent(EV_FIRE_WEAPON); addTime = weaponData[pm->ps->weapon].fireTime; - switch( pm->ps->weapon) + switch (pm->ps->weapon) { + case WP_MELEE: + //melee with g_debugmelee on + addTime = pm->ps->torsoAnimTimer; + break; case WP_REPEATER: // repeater is supposed to do smoke after sustained bursts pm->ps->weaponShotCount++; break; case WP_BOWCASTER: - addTime *= (( trueCount < 3 ) ? 0.35f : 1.0f );// if you only did a small charge shot with the bowcaster, use less time between shots + addTime *= ((trueCount < 3) ? 0.35f : 1.0f);// if you only did a small charge shot with the bowcaster, use less time between shots break; case WP_THERMAL: - if ( pm->gent ) + if (pm->gent) {// remove the thermal model if we had it. - G_RemoveWeaponModels( pm->gent ); - if ( (pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer()) ) + G_RemoveWeaponModels(pm->gent); + if ((pm->ps->clientNum >= MAX_CLIENTS&&!PM_ControlledByPlayer())) {//NPCs need to know when to put the thermal back in their hand - pm->ps->weaponTime = pm->ps->torsoAnimTimer-500; + pm->ps->weaponTime = pm->ps->torsoAnimTimer - 500; } } break; + case WP_MELEE: + //addTime *= ((trueCount < 3) ? 0.35f : 1.0f);//slightly faster if high offense level, speed punching + //if (pm->ps->forcePowerLevel[FP_SABER_OFFENSE] <= 1) addTime *= 1.2; + //else if (pm->ps->forcePowerLevel[FP_SABER_OFFENSE] == 3) addTime *= 0.85; + if (pm->ps->clientNum >= MAX_CLIENTS && !(pm->gent->NPC->aiFlags&NPCAI_HEAVY_MELEE)) + {//so non-heavy melee NPCs punch faster + addTime *= 0.75; + } + if (pm->ps->forcePowersActive&(1 << FP_SPEED) && pm->ps->forcePowerLevel[FP_SPEED] > 1) addTime *= 0.5; //speed punching bonus + break; } } - if(!PM_ControlledByPlayer()) + if (!PM_ControlledByPlayer()) { - if(pm->gent && pm->gent->NPC != NULL ) + if (pm->gent && pm->gent->NPC != NULL) {//NPCs have their own refire logic return; } } - if ( g_timescale != NULL ) + if (g_timescale != NULL) { - if ( g_timescale->value < 1.0f ) + if (g_timescale->value < 1.0f) { - if ( !MatrixMode ) + if (!MatrixMode) {//Special test for Matrix Mode (tm) - if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<ps->clientNum == 0 && !player_locked && !PlayerAffectedByStasis() && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<value; } - else if ( g_entities[pm->ps->clientNum].client - && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<ps->clientNum].client + && (pm->ps->forcePowersActive&(1 << FP_SPEED) || pm->ps->forcePowersActive&(1 << FP_RAGE))) { addTime *= g_timescale->value; } @@ -14022,14 +14382,14 @@ static void PM_Weapon( void ) pm->ps->lastShotTime = level.time;//so we know when the last time we fired our gun is // HACK!!!!! - if ( pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0 ) + if (pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] <= 0) { - if ( pm->ps->weapon == WP_THERMAL || pm->ps->weapon == WP_TRIP_MINE ) + if (pm->ps->weapon == WP_THERMAL || pm->ps->weapon == WP_TRIP_MINE) { // because these weapons have the ammo attached to the hand, we should switch weapons when the last one is thrown, otherwise it will look silly // NOTE: could also switch to an empty had version, but was told we aren't getting any new models at this point CG_OutOfAmmoChange(); - PM_SetAnim(pm,SETANIM_TORSO,TORSO_DROPWEAP1 + 2,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD); // hack weapon down! + PM_SetAnim(pm, SETANIM_TORSO, TORSO_DROPWEAP1 + 2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD); // hack weapon down! pm->ps->weaponTime = 50; } } @@ -14042,24 +14402,24 @@ PM_VehicleWeapon Generates weapon events and modifes the weapon counter ============== */ -static void PM_VehicleWeapon( void ) +static void PM_VehicleWeapon(void) { int addTime = 0; qboolean delayed_fire = qfalse; - if ( pm->ps->weapon == WP_NONE ) + if (pm->ps->weapon == WP_NONE) { return; } - if(pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0) + if (pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0) {//FIXME: this is going to fire off one frame before you expect, actually pm->gent->client->fireDelay -= pml.msec; - if(pm->gent->client->fireDelay <= 0) + if (pm->gent->client->fireDelay <= 0) {//just finished delay timer - if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER ) + if (pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER) { - G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/lock.wav" ); + G_SoundOnEnt(pm->gent, CHAN_WEAPON, "sound/weapons/rocket/lock.wav"); pm->cmd.buttons |= BUTTON_ALT_ATTACK; } pm->gent->client->fireDelay = 0; @@ -14067,31 +14427,21 @@ static void PM_VehicleWeapon( void ) } else { - if ( pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER && Q_irand( 0, 1 ) ) + if (pm->ps->clientNum && pm->ps->weapon == WP_ROCKET_LAUNCHER && Q_irand(0, 1)) { - G_SoundOnEnt( pm->gent, CHAN_WEAPON, "sound/weapons/rocket/tick.wav" ); + G_SoundOnEnt(pm->gent, CHAN_WEAPON, "sound/weapons/rocket/tick.wav"); } } } - // don't allow attack until all buttons are up - if ( pm->ps->pm_flags & PMF_RESPAWNED ) { + // don't allow attack until all buttons are up + if (pm->ps->pm_flags & PMF_RESPAWNED) { return; } // check for dead player - if ( pm->ps->stats[STAT_HEALTH] <= 0 ) + if (pm->ps->stats[STAT_HEALTH] <= 0) { - if ( pm->gent && pm->gent->client ) - { - // borg no longer exist, use NPC_class to check for any npc's that don't drop their weapons (if there are any) - // Sigh..borg shouldn't drop their weapon attachments when they die. Also, never drop a lightsaber! - // if ( pm->gent->client->playerTeam != TEAM_BORG) - { - // pm->ps->weapon = WP_NONE; - } - } - if ( pm->gent ) { pm->gent->s.loopSound = 0; @@ -14100,45 +14450,45 @@ static void PM_VehicleWeapon( void ) } // make weapon function - if ( pm->ps->weaponTime > 0 ) { + if (pm->ps->weaponTime > 0) { pm->ps->weaponTime -= pml.msec; - if ( pm->ps->weaponTime <= 0 ) + if (pm->ps->weaponTime <= 0) { pm->ps->weaponTime = 0; } } - if ( pm->ps->weaponTime > 0 ) + if (pm->ps->weaponTime > 0) { return; } // change weapon if time - if ( pm->ps->weaponstate == WEAPON_DROPPING ) { + if (pm->ps->weaponstate == WEAPON_DROPPING) { PM_FinishWeaponChange(); return; } - if ( PM_DoChargedWeapons()) + if (PM_DoChargedWeapons()) { // In some cases the charged weapon code may want us to short circuit the rest of the firing code return; } - if(!delayed_fire) + if (!delayed_fire) { // check for fire - if ( !(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) ) + if (!(pm->cmd.buttons & (BUTTON_ATTACK | BUTTON_ALT_ATTACK))) { pm->ps->weaponTime = 0; - if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 ) + if (pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0) {//Still firing pm->ps->weaponstate = WEAPON_FIRING; } - else if ( pm->ps->weaponstate != WEAPON_READY ) + else if (pm->ps->weaponstate != WEAPON_READY) { - if ( !pm->gent || !pm->gent->NPC || pm->gent->attackDebounceTime < level.time ) + if (!pm->gent || !pm->gent->NPC || pm->gent->attackDebounceTime < level.time) { pm->ps->weaponstate = WEAPON_IDLE; } @@ -14150,7 +14500,7 @@ static void PM_VehicleWeapon( void ) pm->ps->weaponstate = WEAPON_FIRING; - if ( pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0 ) + if (pm->gent && pm->gent->client && pm->gent->client->fireDelay > 0) {//FIXME: this is going to fire off one frame before you expect, actually // Clear these out since we're not actually firing yet pm->ps->eFlags &= ~EF_FIRING; @@ -14158,35 +14508,35 @@ static void PM_VehicleWeapon( void ) return; } - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { - PM_AddEvent( EV_ALT_FIRE ); + PM_AddEvent(EV_ALT_FIRE); //addTime = weaponData[pm->ps->weapon].altFireTime; } else { - PM_AddEvent( EV_FIRE_WEAPON ); + PM_AddEvent(EV_FIRE_WEAPON); // TODO: Use the real weapon fire time from the vehicle cfg file. //addTime = weaponData[pm->ps->weapon].fireTime; } -/* if(pm->gent && pm->gent->NPC != NULL ) + /* if(pm->gent && pm->gent->NPC != NULL ) {//NPCs have their own refire logic - return; + return; }*/ - if ( g_timescale != NULL ) + if (g_timescale != NULL) { - if ( g_timescale->value < 1.0f ) + if (g_timescale->value < 1.0f) { - if ( !MatrixMode ) + if (!MatrixMode) {//Special test for Matrix Mode (tm) - if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<ps->clientNum == 0 && !player_locked && !PlayerAffectedByStasis() && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<value; } - else if ( g_entities[pm->ps->clientNum].client - && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<ps->clientNum].client + && (pm->ps->forcePowersActive&(1 << FP_SPEED) || pm->ps->forcePowersActive&(1 << FP_RAGE))) { addTime *= g_timescale->value; } @@ -14205,52 +14555,78 @@ extern void ForceRage( gentity_t *self ); extern void ForceProtect( gentity_t *self ); extern void ForceAbsorb( gentity_t *self ); extern void ForceSeeing( gentity_t *self ); +extern void ForceDestruction( gentity_t *self ); +extern void ForceInsanity( gentity_t *self ); +extern void ForceStasis( gentity_t *self ); +extern void ForceBlinding( gentity_t *self ); +extern void ForceDeadlySight( gentity_t *self ); +extern void ForceRepulse( gentity_t *self ); +extern void ForceInvulnerability( gentity_t *self ); + void PM_CheckForceUseButton( gentity_t *ent, usercmd_t *ucmd ) { - if ( !ent ) + if (!ent) { return; } - if ( ucmd->buttons & BUTTON_USE_FORCE ) + if (ucmd->buttons & BUTTON_USE_FORCE) { if (!(ent->client->ps.pm_flags & PMF_USEFORCE_HELD)) { //impulse one shot - switch ( showPowers[cg.forcepowerSelect] ) + switch (showPowers[cg.forcepowerSelect]) { case FP_HEAL: - ForceHeal( ent ); + ForceHeal(ent); break; case FP_SPEED: - ForceSpeed( ent ); + ForceSpeed(ent); break; case FP_PUSH: - ForceThrow( ent, qfalse ); + ForceThrow(ent, qfalse); break; case FP_PULL: - ForceThrow( ent, qtrue ); + ForceThrow(ent, qtrue); break; case FP_TELEPATHY: - ForceTelepathy( ent ); + ForceTelepathy(ent); break; // Added 01/20/03 by AReis. // New Jedi Academy powers. case FP_RAGE: //duration - speed, invincibility and extra damage for short period, drains your health and leaves you weak and slow afterwards. - ForceRage( ent ); + ForceRage(ent); break; case FP_PROTECT: //duration - protect against physical/energy (level 1 stops blaster/energy bolts, level 2 stops projectiles, level 3 protects against explosions) - ForceProtect( ent ); + ForceProtect(ent); break; case FP_ABSORB: //duration - protect against dark force powers (grip, lightning, drain - maybe push/pull, too?) - ForceAbsorb( ent ); + ForceAbsorb(ent); break; case FP_SEE: //duration - detect/see hidden enemies - ForceSeeing( ent ); + ForceSeeing(ent); + break; + case FP_DESTRUCTION: + ForceDestruction( ent ); + break; + case FP_INSANITY: + ForceInsanity( ent ); + break; + case FP_STASIS: + ForceStasis( ent ); + break; + case FP_BLINDING: + ForceBlinding( ent ); + break; + case FP_DEADLYSIGHT: + ForceDeadlySight( ent ); + break; + case FP_INVULNERABILITY: + ForceInvulnerability( ent ); break; } } //these stay are okay to call every frame button is down - switch ( showPowers[cg.forcepowerSelect] ) + switch (showPowers[cg.forcepowerSelect]) { case FP_LEVITATION: ucmd->upmove = 127; @@ -14265,6 +14641,12 @@ void PM_CheckForceUseButton( gentity_t *ent, usercmd_t *ucmd ) // FIXME! Failing at WP_ForcePowerUsable(). -AReis ucmd->buttons |= BUTTON_FORCE_DRAIN; break; + case FP_SABERTHROW: + ucmd->buttons |= BUTTON_SABERTHROW; + break; + case FP_REPULSE: + ucmd->buttons |= BUTTON_REPULSE; + break; // default: // Com_Printf( "Use Force: Unhandled force: %d\n", showPowers[cg.forcepowerSelect]); // break; @@ -14287,20 +14669,20 @@ sends event to client for client side fx, not used /* static void PM_ForcePower(void) { - // check for item using - if ( pm->cmd.buttons & BUTTON_USE_FORCE ) - { - if ( ! ( pm->ps->pm_flags & PMF_USE_FORCE ) ) - { - pm->ps->pm_flags |= PMF_USE_FORCE; - PM_AddEvent( EV_USE_FORCE); - return; - } - } - else - { - pm->ps->pm_flags &= ~PMF_USE_FORCE; - } +// check for item using +if ( pm->cmd.buttons & BUTTON_USE_FORCE ) +{ +if ( ! ( pm->ps->pm_flags & PMF_USE_FORCE ) ) +{ +pm->ps->pm_flags |= PMF_USE_FORCE; +PM_AddEvent( EV_USE_FORCE); +return; +} +} +else +{ +pm->ps->pm_flags &= ~PMF_USE_FORCE; +} } */ @@ -14309,12 +14691,12 @@ static void PM_ForcePower(void) PM_DropTimers ================ */ -static void PM_DropTimers( void ) +static void PM_DropTimers(void) { // drop misc timing counter - if ( pm->ps->pm_time ) + if (pm->ps->pm_time) { - if ( pml.msec >= pm->ps->pm_time ) + if (pml.msec >= pm->ps->pm_time) { pm->ps->pm_flags &= ~PMF_ALL_TIMES; pm->ps->pm_time = 0; @@ -14326,68 +14708,68 @@ static void PM_DropTimers( void ) } // drop legs animation counter - if ( pm->ps->legsAnimTimer > 0 ) + if (pm->ps->legsAnimTimer > 0) { int newTime = pm->ps->legsAnimTimer - pml.msec; - if ( newTime < 0 ) + if (newTime < 0) { newTime = 0; } - PM_SetLegsAnimTimer( pm->gent, &pm->ps->legsAnimTimer, newTime ); + PM_SetLegsAnimTimer(pm->gent, &pm->ps->legsAnimTimer, newTime); } // drop torso animation counter - if ( pm->ps->torsoAnimTimer > 0 ) + if (pm->ps->torsoAnimTimer > 0) { int newTime = pm->ps->torsoAnimTimer - pml.msec; - if ( newTime < 0 ) + if (newTime < 0) { newTime = 0; } - PM_SetTorsoAnimTimer( pm->gent, &pm->ps->torsoAnimTimer, newTime ); + PM_SetTorsoAnimTimer(pm->gent, &pm->ps->torsoAnimTimer, newTime); } } -void PM_SetSpecialMoveValues (void ) +void PM_SetSpecialMoveValues(void) { Flying = 0; - if ( pm->gent ) + if (pm->gent) { - if ( pm->gent->client && pm->gent->client->moveType == MT_FLYSWIM ) + if (pm->gent->client && pm->gent->client->moveType == MT_FLYSWIM) { Flying = FLY_NORMAL; } - else if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) + else if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) { - if ( pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER ) + if (pm->gent->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER) { Flying = FLY_VEHICLE; } - else if ( pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0 ) + else if (pm->gent->m_pVehicle->m_pVehicleInfo->hoverHeight > 0) {//FIXME: or just check for hoverHeight? Flying = FLY_HOVER; } } } - if ( g_timescale != NULL ) + if (g_timescale != NULL) { - if ( g_timescale->value < 1.0f ) + if (g_timescale->value < 1.0f) { - if ( !MatrixMode ) + if (!MatrixMode) { - if ( pm->ps->clientNum == 0 && !player_locked && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<ps->clientNum == 0 && !player_locked && !PlayerAffectedByStasis() && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<value); + pml.frametime *= (1.0f / g_timescale->value); } - else if ( g_entities[pm->ps->clientNum].client - && (pm->ps->forcePowersActive&(1<ps->forcePowersActive&(1<ps->clientNum].client + && (pm->ps->forcePowersActive&(1 << FP_SPEED) || pm->ps->forcePowersActive&(1 << FP_RAGE))) { - pml.frametime *= (1.0f/g_timescale->value); + pml.frametime *= (1.0f / g_timescale->value); } } } @@ -14397,99 +14779,104 @@ void PM_SetSpecialMoveValues (void ) extern float cg_zoomFov; //from cg_view.cpp //------------------------------------------- -void PM_AdjustAttackStates( pmove_t *pm ) +void PM_AdjustAttackStates(pmove_t *pm) //------------------------------------------- { int amount; - if ( !g_saberAutoBlocking->integer + if (!g_saberAutoBlocking->integer && !g_saberNewControlScheme->integer - && (pm->cmd.buttons&BUTTON_FORCE_FOCUS) ) + && (pm->cmd.buttons&BUTTON_FORCE_FOCUS)) { pm->ps->saberBlockingTime = pm->cmd.serverTime + 100; pm->cmd.buttons &= ~BUTTON_ATTACK; pm->cmd.buttons &= ~BUTTON_ALT_ATTACK; } // get ammo usage - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { - amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].altEnergyPerShot; + amount = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - weaponData[pm->ps->weapon].altEnergyPerShot; } else { - amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - weaponData[pm->ps->weapon].energyPerShot; + amount = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - weaponData[pm->ps->weapon].energyPerShot; } - if ( pm->ps->weapon == WP_SABER && (!cg.zoomMode||pm->ps->clientNum) ) + if (pm->ps->weapon == WP_SABER && (!cg.zoomMode || pm->ps->clientNum)) {//don't let the alt-attack be interpreted as an actual attack command if ( pm->ps->saberInFlight ) { - pm->cmd.buttons &= ~BUTTON_ALT_ATTACK; + if (pm->ps->saberAnimLevel != SS_KATARN) + { + pm->cmd.buttons &= ~BUTTON_ALT_ATTACK; + } + pm->cmd.buttons &= ~BUTTON_SABERTHROW; //FIXME: what about alt-attack modifier button? - if ( (!pm->ps->dualSabers || !pm->ps->saber[1].Active()) ) + if ((!pm->ps->dualSabers || !pm->ps->saber[1].Active())) {//saber not in hand, can't swing it pm->cmd.buttons &= ~BUTTON_ATTACK; } } //saber staff alt-attack does a special attack anim, non-throwable sabers do kicks - if ( pm->ps->saberAnimLevel != SS_STAFF - && !(pm->ps->saber[0].saberFlags&SFL_NOT_THROWABLE) ) + /* ack let me kick with single + if (pm->ps->saberAnimLevel != SS_STAFF + && !(pm->ps->saber[0].saberFlags&SFL_NOT_THROWABLE)) {//using a throwable saber, so remove the saber throw button - if ( !g_saberNewControlScheme->integer - && PM_CanDoKata() ) + if (!g_saberNewControlScheme->integer + && PM_CanDoKata()) {//old control scheme - alt-attack + attack does kata } else {//new control scheme - alt-attack doesn't have anything to do with katas, safe to clear it here pm->cmd.buttons &= ~BUTTON_ALT_ATTACK; } - } + }*/ } // disruptor alt-fire should toggle the zoom mode, but only bother doing this for the player? - if ( pm->ps->weapon == WP_DISRUPTOR && pm->gent && (pm->gent->s.numbergent)) && pm->ps->weaponstate != WEAPON_DROPPING ) + if (pm->ps->weapon == WP_DISRUPTOR && pm->gent && (pm->gent->s.numbergent)) && pm->ps->weaponstate != WEAPON_DROPPING) { // we are not alt-firing yet, but the alt-attack button was just pressed and // we either are ducking ( in which case we don't care if they are moving )...or they are not ducking...and also not moving right/forward. - if ( !(pm->ps->eFlags & EF_ALT_FIRING) && (pm->cmd.buttons & BUTTON_ALT_ATTACK) - && ( pm->cmd.upmove < 0 || ( !pm->cmd.forwardmove && !pm->cmd.rightmove ))) + if (!(pm->ps->eFlags & EF_ALT_FIRING) && (pm->cmd.buttons & BUTTON_ALT_ATTACK) + && (pm->cmd.upmove < 0 || (!pm->cmd.forwardmove && !pm->cmd.rightmove))) { // We just pressed the alt-fire key - if ( cg.zoomMode == 0 || cg.zoomMode == 3 ) + if (cg.zoomMode == 0 || cg.zoomMode == 3) { - G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomstart.wav" ); + G_SoundOnEnt(pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomstart.wav"); // not already zooming, so do it now cg.zoomMode = 2; cg.zoomLocked = qfalse; cg_zoomFov = 80.0f;//(cg.overrides.active&CG_OVERRIDE_FOV) ? cg.overrides.fov : cg_fov.value; } - else if ( cg.zoomMode == 2 ) + else if (cg.zoomMode == 2) { - G_SoundOnEnt( pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomend.wav" ); + G_SoundOnEnt(pm->gent, CHAN_AUTO, "sound/weapons/disruptor/zoomend.wav"); // already zooming, so must be wanting to turn it off cg.zoomMode = 0; cg.zoomTime = cg.time; cg.zoomLocked = qfalse; } } - else if ( !(pm->cmd.buttons & BUTTON_ALT_ATTACK )) + else if (!(pm->cmd.buttons & BUTTON_ALT_ATTACK)) { // Not pressing zoom any more - if ( cg.zoomMode == 2 ) + if (cg.zoomMode == 2) { // were zooming in, so now lock the zoom cg.zoomLocked = qtrue; } } - if ( pm->cmd.buttons & BUTTON_ATTACK ) + if (pm->cmd.buttons & BUTTON_ATTACK) { // If we are zoomed, we should switch the ammo usage to the alt-fire, otherwise, we'll // just use whatever ammo was selected from above - if ( cg.zoomMode == 2 ) + if (cg.zoomMode == 2) { - amount = pm->ps->ammo[weaponData[ pm->ps->weapon ].ammoIndex] - - weaponData[pm->ps->weapon].altEnergyPerShot; + amount = pm->ps->ammo[weaponData[pm->ps->weapon].ammoIndex] - + weaponData[pm->ps->weapon].altEnergyPerShot; } } else @@ -14501,15 +14888,15 @@ void PM_AdjustAttackStates( pmove_t *pm ) } // Check for binocular specific mode - if ( cg.zoomMode == 1 && pm->gent && (pm->gent->s.numbergent)) ) // + if (cg.zoomMode == 1 && pm->gent && (pm->gent->s.numbergent))) // { - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK && pm->ps->batteryCharge ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK && pm->ps->batteryCharge) { // zooming out cg.zoomLocked = qfalse; cg.zoomDir = 1; } - else if ( pm->cmd.buttons & BUTTON_ATTACK && pm->ps->batteryCharge ) + else if (pm->cmd.buttons & BUTTON_ATTACK && pm->ps->batteryCharge) { // zooming in cg.zoomLocked = qfalse; @@ -14524,16 +14911,16 @@ void PM_AdjustAttackStates( pmove_t *pm ) // kill buttons and associated firing flags so we can't fire pm->ps->eFlags &= ~EF_FIRING; pm->ps->eFlags &= ~EF_ALT_FIRING; - pm->cmd.buttons &= ~(BUTTON_ALT_ATTACK|BUTTON_ATTACK); + pm->cmd.buttons &= ~(BUTTON_ALT_ATTACK | BUTTON_ATTACK); } // set the firing flag for continuous beam weapons, phaser will fire even if out of ammo - if ( (( pm->cmd.buttons & BUTTON_ATTACK || pm->cmd.buttons & BUTTON_ALT_ATTACK ) && ( amount >= 0 || pm->ps->weapon == WP_SABER )) ) + if (((pm->cmd.buttons & BUTTON_ATTACK || pm->cmd.buttons & BUTTON_ALT_ATTACK) && (amount >= 0 || pm->ps->weapon == WP_SABER))) { - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { pm->ps->eFlags |= EF_ALT_FIRING; - if ( pm->ps->clientNum < MAX_CLIENTS && pm->gent && (pm->ps->eFlags&EF_IN_ATST) ) + if (pm->ps->clientNum < MAX_CLIENTS && pm->gent && (pm->ps->eFlags&EF_IN_ATST)) {//switch ATST barrels pm->gent->alt_fire = qtrue; } @@ -14541,7 +14928,7 @@ void PM_AdjustAttackStates( pmove_t *pm ) else { pm->ps->eFlags &= ~EF_ALT_FIRING; - if ( pm->ps->clientNum < MAX_CLIENTS && pm->gent && (pm->ps->eFlags&EF_IN_ATST) ) + if (pm->ps->clientNum < MAX_CLIENTS && pm->gent && (pm->ps->eFlags&EF_IN_ATST)) {//switch ATST barrels pm->gent->alt_fire = qfalse; } @@ -14552,7 +14939,7 @@ void PM_AdjustAttackStates( pmove_t *pm ) } else { -// int iFlags = pm->ps->eFlags; + // int iFlags = pm->ps->eFlags; // Clear 'em out pm->ps->eFlags &= ~EF_FIRING; @@ -14562,18 +14949,18 @@ void PM_AdjustAttackStates( pmove_t *pm ) // the stronger FFFX so you can hardly feel them. However, if you only do iton these flags then the // repeat-fire weapons like tetrion and dreadnought don't switch off quick enough. So... // -/* // Might need this for beam type weapons + /* // Might need this for beam type weapons if ( pm->ps->weapon == WP_DREADNOUGHT || (iFlags & (EF_FIRING|EF_ALT_FIRING) ) { - cgi_FF_StopAllFX(); + cgi_FF_StopAllFX(); } */ } // disruptor should convert a main fire to an alt-fire if the gun is currently zoomed - if ( pm->ps->weapon == WP_DISRUPTOR && pm->gent && (pm->gent->s.numbergent)) ) + if (pm->ps->weapon == WP_DISRUPTOR && pm->gent && (pm->gent->s.numbergent))) { - if ( pm->cmd.buttons & BUTTON_ATTACK && cg.zoomMode == 2 ) + if (pm->cmd.buttons & BUTTON_ATTACK && cg.zoomMode == 2) { // converting the main fire to an alt-fire pm->cmd.buttons |= BUTTON_ALT_ATTACK; @@ -14587,10 +14974,10 @@ void PM_AdjustAttackStates( pmove_t *pm ) } } -qboolean PM_WeaponOkOnVehicle( int weapon ) +qboolean PM_WeaponOkOnVehicle(int weapon) { //FIXME: check g_vehicleInfo for our vehicle? - switch ( weapon ) + switch (weapon) { case WP_NONE: case WP_SABER: @@ -14602,25 +14989,25 @@ qboolean PM_WeaponOkOnVehicle( int weapon ) return qfalse; } -void PM_CheckInVehicleSaberAttackAnim( void ) +void PM_CheckInVehicleSaberAttackAnim(void) {//A bit of a hack, but makes the vehicle saber attacks act like any other saber attack... // make weapon function - if ( pm->ps->weaponTime > 0 ) { + if (pm->ps->weaponTime > 0) { pm->ps->weaponTime -= pml.msec; - if ( pm->ps->weaponTime <= 0 ) + if (pm->ps->weaponTime <= 0) { pm->ps->weaponTime = 0; } } PM_CheckClearSaberBlock(); -/* if ( PM_SaberBlocking() ) + /* if ( PM_SaberBlocking() ) {//busy blocking, don't do attacks - return; + return; } -*/ + */ saberMoveName_t saberMove = LS_INVALID; - switch ( pm->ps->torsoAnim ) + switch (pm->ps->torsoAnim) { case BOTH_VS_ATR_S: saberMove = LS_SWOOP_ATTACK_RIGHT; @@ -14635,33 +15022,33 @@ void PM_CheckInVehicleSaberAttackAnim( void ) saberMove = LS_TAUNTAUN_ATTACK_LEFT; break; } - if ( saberMove != LS_INVALID ) + if (saberMove != LS_INVALID) { - if ( pm->ps->saberMove == saberMove ) + if (pm->ps->saberMove == saberMove) {//already playing it - if ( !pm->ps->torsoAnimTimer ) + if (!pm->ps->torsoAnimTimer) {//anim was done, set it back to ready - PM_SetSaberMove( LS_READY ); + PM_SetSaberMove(LS_READY); pm->ps->saberMove = LS_READY; pm->ps->weaponstate = WEAPON_IDLE; if (pm->cmd.buttons&BUTTON_ATTACK) { - if ( !pm->ps->weaponTime ) + if (!pm->ps->weaponTime) { - PM_SetSaberMove( saberMove ); + PM_SetSaberMove(saberMove); pm->ps->weaponstate = WEAPON_FIRING; pm->ps->weaponTime = pm->ps->torsoAnimTimer; } } } } - else if ( pm->ps->torsoAnimTimer - && !pm->ps->weaponTime ) + else if (pm->ps->torsoAnimTimer + && !pm->ps->weaponTime) { - PM_SetSaberMove( LS_READY ); + PM_SetSaberMove(LS_READY); pm->ps->saberMove = LS_READY; pm->ps->weaponstate = WEAPON_IDLE; - PM_SetSaberMove( saberMove ); + PM_SetSaberMove(saberMove); pm->ps->weaponstate = WEAPON_FIRING; pm->ps->weaponTime = pm->ps->torsoAnimTimer; } @@ -14670,9 +15057,9 @@ void PM_CheckInVehicleSaberAttackAnim( void ) } //force the vehicle to turn and travel to its forced destination point -void PM_VehForcedTurning( gentity_t *veh ) +void PM_VehForcedTurning(gentity_t *veh) { - gentity_t *dst = &g_entities[pm->ps->vehTurnaroundIndex]; + gentity_t *dst = &g_entities[veh->client->ps.vehTurnaroundIndex]; float pitchD, yawD; vec3_t dir; @@ -14705,6 +15092,81 @@ void PM_VehForcedTurning( gentity_t *veh ) //PM_SetPMViewAngle(pm->ps, pm->ps->viewangles, &pm->cmd); SetClientViewAngle(pm->gent, pm->ps->viewangles); } + +void PM_VehFaceHyperspacePoint(gentity_t *veh) +{ + + if (!veh || !veh->m_pVehicle) + { + return; + } + else + { + float timeFrac = ((float)(pm->cmd.serverTime-veh->client->ps.hyperSpaceTime))/HYPERSPACE_TIME; + float turnRate, aDelta; + int i, matchedAxes = 0; + + pm->cmd.upmove = veh->m_pVehicle->m_ucmd.upmove = 127; + pm->cmd.forwardmove = veh->m_pVehicle->m_ucmd.forwardmove = 0; + pm->cmd.rightmove = veh->m_pVehicle->m_ucmd.rightmove = 0; + + turnRate = (90.0f*pml.frametime); + for ( i = 0; i < 3; i++ ) + { + aDelta = AngleSubtract(veh->client->ps.hyperSpaceAngles[i], veh->m_pVehicle->m_vOrientation[i]); + if ( fabs( aDelta ) < turnRate ) + {//all is good + pm->ps->viewangles[i] = veh->client->ps.hyperSpaceAngles[i]; + matchedAxes++; + } + else + { + aDelta = AngleSubtract(veh->client->ps.hyperSpaceAngles[i], pm->ps->viewangles[i]); + if ( fabs( aDelta ) < turnRate ) + { + pm->ps->viewangles[i] = veh->client->ps.hyperSpaceAngles[i]; + } + else if ( aDelta > 0 ) + { + if ( i == YAW ) + { + pm->ps->viewangles[i] = AngleNormalize360( pm->ps->viewangles[i]+turnRate ); + } + else + { + pm->ps->viewangles[i] = AngleNormalize180( pm->ps->viewangles[i]+turnRate ); + } + } + else + { + if ( i == YAW ) + { + pm->ps->viewangles[i] = AngleNormalize360( pm->ps->viewangles[i]-turnRate ); + } + else + { + pm->ps->viewangles[i] = AngleNormalize180( pm->ps->viewangles[i]-turnRate ); + } + } + } + } + + SetClientViewAngle(pm->gent, pm->ps->viewangles); + + if ( timeFrac < HYPERSPACE_TELEPORT_FRAC ) + {//haven't gone through yet + if ( matchedAxes < 3 ) + {//not facing the right dir yet + //keep hyperspace time up to date + veh->client->ps.hyperSpaceTime += pml.msec; + } + else if ( !(veh->client->ps.eFlags2&EF2_HYPERSPACE)) + {//flag us as ready to hyperspace! + veh->client->ps.eFlags2 |= EF2_HYPERSPACE; + } + } + } +} /* ================ Pmove @@ -14712,7 +15174,7 @@ Pmove Can be called by either the server or the client ================ */ -void Pmove( pmove_t *pmove ) +void Pmove(pmove_t *pmove) { Vehicle_t *pVeh = NULL; @@ -14732,53 +15194,60 @@ void Pmove( pmove_t *pmove ) // In certain situations, we may want to control which attack buttons are pressed and what kind of functionality // is attached to them - PM_AdjustAttackStates( pm ); + PM_AdjustAttackStates(pm); // clear the respawned flag if attack and use are cleared - if ( pm->ps->stats[STAT_HEALTH] > 0 && - !( pm->cmd.buttons & BUTTON_ATTACK ) ) + if (pm->ps->stats[STAT_HEALTH] > 0 && + !(pm->cmd.buttons & BUTTON_ATTACK)) { pm->ps->pm_flags &= ~PMF_RESPAWNED; } // clear all pmove local vars - memset (&pml, 0, sizeof(pml)); + memset(&pml, 0, sizeof(pml)); // determine the time pml.msec = pmove->cmd.serverTime - pm->ps->commandTime; - if ( pml.msec < 1 ) { + if (pml.msec < 1) { pml.msec = 1; - } else if ( pml.msec > 200 ) { + } + else if (pml.msec > 200) { pml.msec = 200; } pm->ps->commandTime = pmove->cmd.serverTime; // save old org in case we get stuck - VectorCopy (pm->ps->origin, pml.previous_origin); + VectorCopy(pm->ps->origin, pml.previous_origin); // save old velocity for crashlanding - VectorCopy (pm->ps->velocity, pml.previous_velocity); + VectorCopy(pm->ps->velocity, pml.previous_velocity); pml.frametime = pml.msec * 0.001; - if ( pm->ps->clientNum >= MAX_CLIENTS && + if (pm->ps->clientNum >= MAX_CLIENTS && pm->gent && pm->gent->client && - pm->gent->client->NPC_class == CLASS_VEHICLE ) + pm->gent->client->NPC_class == CLASS_VEHICLE) { //we are a vehicle pVeh = pm->gent->m_pVehicle; - assert( pVeh ); - if ( pVeh ) + assert(pVeh); + if (pVeh) { pVeh->m_fTimeModifier = (pml.frametime*60.0f);//at 16.67ms (60fps), should be 1.0f } } - else if ( pm->gent && PM_RidingVehicle() ) + else if (pm->gent && PM_RidingVehicle()) { - if ( pm->ps->vehTurnaroundIndex - && pm->ps->vehTurnaroundTime > pm->cmd.serverTime ) + if ( (&g_entities[pm->gent->s.m_iVehicleNum])->client && + (pm->cmd.serverTime-(&g_entities[pm->gent->s.m_iVehicleNum])->client->ps.hyperSpaceTime) < HYPERSPACE_TIME) + { //going into hyperspace, turn to face the right angles + PM_VehFaceHyperspacePoint( &g_entities[pm->gent->s.m_iVehicleNum] ); + } + else if ( (&g_entities[pm->gent->s.m_iVehicleNum])->client && (&g_entities[pm->gent->s.m_iVehicleNum])->client->ps.vehTurnaroundIndex + && (&g_entities[pm->gent->s.m_iVehicleNum])->client->ps.vehTurnaroundTime > pm->cmd.serverTime ) { //riding this vehicle, turn my view too + Com_Printf("forced turning!\n"); PM_VehForcedTurning( &g_entities[pm->gent->s.m_iVehicleNum] ); } } @@ -14786,74 +15255,75 @@ void Pmove( pmove_t *pmove ) PM_SetSpecialMoveValues(); // update the viewangles - PM_UpdateViewAngles( pm->ps, &pm->cmd, pm->gent); + PM_UpdateViewAngles(pm->ps, &pm->cmd, pm->gent); - AngleVectors ( pm->ps->viewangles, pml.forward, pml.right, pml.up ); + AngleVectors(pm->ps->viewangles, pml.forward, pml.right, pml.up); - if ( pm->cmd.upmove < 10 ) { + if (pm->cmd.upmove < 10) { // not holding jump pm->ps->pm_flags &= ~PMF_JUMP_HELD; } // decide if backpedaling animations should be used - if ( pm->cmd.forwardmove < 0 ) { + if (pm->cmd.forwardmove < 0) { pm->ps->pm_flags |= PMF_BACKWARDS_RUN; - } else if ( pm->cmd.forwardmove > 0 || ( pm->cmd.forwardmove == 0 && pm->cmd.rightmove ) ) { + } + else if (pm->cmd.forwardmove > 0 || (pm->cmd.forwardmove == 0 && pm->cmd.rightmove)) { pm->ps->pm_flags &= ~PMF_BACKWARDS_RUN; } - if ( pm->ps->pm_type >= PM_DEAD ) { + if (pm->ps->pm_type >= PM_DEAD) { pm->cmd.forwardmove = 0; pm->cmd.rightmove = 0; pm->cmd.upmove = 0; - if ( pm->ps->viewheight > -12 ) + if (pm->ps->viewheight > -12) {//slowly sink view to ground pm->ps->viewheight -= 1; } } - if ( pm->ps->pm_type == PM_SPECTATOR ) { - PM_CheckDuck (); - PM_FlyMove (); - PM_DropTimers (); + if (pm->ps->pm_type == PM_SPECTATOR) { + PM_CheckDuck(); + PM_FlyMove(); + PM_DropTimers(); return; } - if ( pm->ps->pm_type == PM_NOCLIP ) { - PM_NoclipMove (); - PM_DropTimers (); + if (pm->ps->pm_type == PM_NOCLIP) { + PM_NoclipMove(); + PM_DropTimers(); return; } if (pm->ps->pm_type == PM_FREEZE) { return; // no movement at all } - + if ( pm->ps->pm_type == PM_INTERMISSION ) { return; // no movement at all } - if ( pm->ps->pm_flags & PMF_SLOW_MO_FALL ) + if (pm->ps->pm_flags & PMF_SLOW_MO_FALL) {//half grav pm->ps->gravity *= 0.5; } // set watertype, and waterlevel - PM_SetWaterLevelAtPoint( pm->ps->origin, &pm->waterlevel, &pm->watertype ); + PM_SetWaterLevelAtPoint(pm->ps->origin, &pm->waterlevel, &pm->watertype); PM_SetWaterHeight(); - if ( !(pm->watertype & CONTENTS_LADDER) ) + if (!(pm->watertype & CONTENTS_LADDER)) {//Don't want to remember this for ladders, is only for waterlevel change events (sounds) pml.previous_waterlevel = pmove->waterlevel; } waterForceJump = qfalse; - if ( pmove->waterlevel && pm->ps->clientNum ) + if (pmove->waterlevel && pm->ps->clientNum) { - if ( pm->ps->forceJumpZStart//force jumping - ||(pm->gent&&pm->gent->NPC && level.timegent->NPC->jumpTime)) //TIMER_Done(pm->gent, "forceJumpChasing" )) )//force-jumping + if (pm->ps->forceJumpZStart//force jumping + || (pm->gent&&pm->gent->NPC && level.timegent->NPC->jumpTime)) //TIMER_Done(pm->gent, "forceJumpChasing" )) )//force-jumping { waterForceJump = qtrue; } @@ -14862,20 +15332,20 @@ void Pmove( pmove_t *pmove ) // set mins, maxs, and viewheight PM_SetBounds(); - if ( !Flying && !(pm->watertype & CONTENTS_LADDER) && pm->ps->pm_type != PM_DEAD ) + if (!Flying && !(pm->watertype & CONTENTS_LADDER) && pm->ps->pm_type != PM_DEAD) {//NOTE: noclippers shouldn't jump or duck either, no? PM_CheckDuck(); } // set groundentity PM_GroundTrace(); - if ( Flying == FLY_HOVER ) + if (Flying == FLY_HOVER) {//never stick to the ground PM_HoverTrace(); } - if ( pm->ps->pm_type == PM_DEAD ) { - PM_DeadMove (); + if (pm->ps->pm_type == PM_DEAD) { + PM_DeadMove(); } PM_DropTimers(); @@ -14883,35 +15353,35 @@ void Pmove( pmove_t *pmove ) /* if ( PM_RidingVehicle() ) { - PM_NoclipMove(); + PM_NoclipMove(); } - else */if ( pm->ps && ( (pm->ps->eFlags&EF_LOCKED_TO_WEAPON) - || (pm->ps->eFlags&EF_HELD_BY_RANCOR) - || (pm->ps->eFlags&EF_HELD_BY_WAMPA) - || (pm->ps->eFlags&EF_HELD_BY_SAND_CREATURE) ) ) + else */if (pm->ps && ((pm->ps->eFlags&EF_LOCKED_TO_WEAPON) + || (pm->ps->eFlags&EF_HELD_BY_RANCOR) + || (pm->ps->eFlags&EF_HELD_BY_WAMPA) + || (pm->ps->eFlags&EF_HELD_BY_SAND_CREATURE))) {//in an emplaced gun PM_NoclipMove(); } - else if ( Flying == FLY_NORMAL )//|| pm->ps->gravity <= 0 ) + else if (Flying == FLY_NORMAL)//|| pm->ps->gravity <= 0 ) { // flight powerup doesn't allow jump and has different friction PM_FlyMove(); } - else if ( Flying == FLY_VEHICLE ) + else if (Flying == FLY_VEHICLE) { PM_FlyVehicleMove(); } - else if ( pm->ps->pm_flags & PMF_TIME_WATERJUMP ) + else if (pm->ps->pm_flags & PMF_TIME_WATERJUMP) { PM_WaterJumpMove(); } - else if ( pm->waterlevel > 1 //in water - &&((pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer()) || !waterForceJump) )//player or NPC not force jumping + else if (pm->waterlevel > 1 //in water + && ((pm->ps->clientNum < MAX_CLIENTS || PM_ControlledByPlayer()) || !waterForceJump))//player or NPC not force jumping {//force-jumping NPCs should // swimming or in ladder PM_WaterMove(); } - else if (pm->gent && pm->gent->NPC && pm->gent->NPC->jumpTime!=0) + else if (pm->gent && pm->gent->NPC && pm->gent->NPC->jumpTime != 0) { ucmd.forwardmove = 0; ucmd.rightmove = 0; @@ -14921,29 +15391,29 @@ void Pmove( pmove_t *pmove ) PM_AirMove(); } - else if ( pml.walking ) + else if (pml.walking) {// walking on ground vec3_t oldOrg; - VectorCopy( pm->ps->origin, oldOrg ); + VectorCopy(pm->ps->origin, oldOrg); PM_WalkMove(); - float threshHold = 0.001f, movedDist = DistanceSquared( oldOrg, pm->ps->origin ); - if ( PM_StandingAnim( pm->ps->legsAnim ) || pm->ps->legsAnim == BOTH_CROUCH1 ) + float threshHold = 0.001f, movedDist = DistanceSquared(oldOrg, pm->ps->origin); + if (PM_StandingAnim(pm->ps->legsAnim) || pm->ps->legsAnim == BOTH_CROUCH1) { threshHold = 0.005f; } - if ( movedDist < threshHold ) + if (movedDist < threshHold) {//didn't move, play no legs anim - // pm->cmd.forwardmove = pm->cmd.rightmove = 0; + // pm->cmd.forwardmove = pm->cmd.rightmove = 0; } } else { - if ( pm->ps->gravity <= 0 ) + if (pm->ps->gravity <= 0) { PM_FlyMove(); } @@ -14957,16 +15427,16 @@ void Pmove( pmove_t *pmove ) //PM_Animate(); // If we didn't move at all, then why bother doing this again -MW. - if(!(VectorCompare(pm->ps->origin,pml.previous_origin))) + if (!(VectorCompare(pm->ps->origin, pml.previous_origin))) { PM_GroundTrace(); - if ( Flying == FLY_HOVER ) + if (Flying == FLY_HOVER) {//never stick to the ground PM_HoverTrace(); } } - if ( pm->ps->groundEntityNum != ENTITYNUM_NONE ) + if (pm->ps->groundEntityNum != ENTITYNUM_NONE) {//on ground pm->ps->forceJumpZStart = 0; pm->ps->jumpZStart = 0; @@ -14977,16 +15447,16 @@ void Pmove( pmove_t *pmove ) // If we didn't move at all, then why bother doing this again -MW. // Note: ok, so long as we don't have water levels that change. - if(!(VectorCompare(pm->ps->origin,pml.previous_origin))) + if (!(VectorCompare(pm->ps->origin, pml.previous_origin))) { - PM_SetWaterLevelAtPoint( pm->ps->origin, &pm->waterlevel, &pm->watertype ); + PM_SetWaterLevelAtPoint(pm->ps->origin, &pm->waterlevel, &pm->watertype); PM_SetWaterHeight(); } -// PM_ForcePower(); sends event to client for client side fx, not used + // PM_ForcePower(); sends event to client for client side fx, not used // If we're a vehicle, do our special weapon function. - if ( pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE ) + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE) { pVeh = pm->gent->m_pVehicle; @@ -14999,19 +15469,19 @@ void Pmove( pmove_t *pmove ) } } // If we are riding a vehicle... - else if ( PM_RidingVehicle() ) + else if (PM_RidingVehicle()) { - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) {// alt attack always does other stuff when riding a vehicle (turbo) } - else if ( (pm->ps->eFlags&EF_NODRAW) ) + else if ((pm->ps->eFlags&EF_NODRAW)) {//inside a vehicle? don't do any weapon stuff } - else if ( pm->ps->weapon == WP_BLASTER//using blaster - || pm->ps->weapon == WP_THERMAL//using thermal - || pm->ps->weaponstate == WEAPON_DROPPING//changing weapon - dropping - || pm->ps->weaponstate == WEAPON_RAISING//changing weapon - raising - || (pm->cmd.weapon != pm->ps->weapon && PM_WeaponOkOnVehicle( pm->cmd.weapon )) )//FIXME: make this a vehicle call to see if this new weapon is valid for this vehicle + else if (pm->ps->weapon == WP_BLASTER//using blaster + || pm->ps->weapon == WP_THERMAL//using thermal + || pm->ps->weaponstate == WEAPON_DROPPING//changing weapon - dropping + || pm->ps->weaponstate == WEAPON_RAISING//changing weapon - raising + || (pm->cmd.weapon != pm->ps->weapon && PM_WeaponOkOnVehicle(pm->cmd.weapon)))//FIXME: make this a vehicle call to see if this new weapon is valid for this vehicle {//either weilding a weapon we can fire with normal weapon logic, or trying to change to a valid weapon // call normal weapons code... should we override the normal fire anims with vehicle fire anims in here or in a subsequent call to VehicleWeapons or something? //Maybe break PM_Weapon into PM_Weapon and PM_WeaponAnimate (then call our own PM_VehicleWeaponAnimate)? @@ -15025,7 +15495,7 @@ void Pmove( pmove_t *pmove ) // weapons PM_Weapon(); } - if ( pm->cmd.buttons & BUTTON_ATTACK ) + if (pm->cmd.buttons & BUTTON_ATTACK) { pm->ps->pm_flags |= PMF_ATTACK_HELD; } @@ -15033,7 +15503,7 @@ void Pmove( pmove_t *pmove ) { pm->ps->pm_flags &= ~PMF_ATTACK_HELD; } - if ( pm->cmd.buttons & BUTTON_ALT_ATTACK ) + if (pm->cmd.buttons & BUTTON_ALT_ATTACK) { pm->ps->pm_flags |= PMF_ALT_ATTACK_HELD; } @@ -15041,7 +15511,7 @@ void Pmove( pmove_t *pmove ) { pm->ps->pm_flags &= ~PMF_ALT_ATTACK_HELD; } - if ( pm->cmd.buttons & BUTTON_FORCE_FOCUS ) + if (pm->cmd.buttons & BUTTON_FORCE_FOCUS) { pm->ps->pm_flags |= PMF_FORCE_FOCUS_HELD; } @@ -15049,8 +15519,17 @@ void Pmove( pmove_t *pmove ) { pm->ps->pm_flags &= ~PMF_FORCE_FOCUS_HELD; } + + if ( pm->cmd.buttons & BUTTON_SABERTHROW ) + { + pm->ps->pm_flags |= PMF_SABERTHROW_HELD; + } + else + { + pm->ps->pm_flags &= ~PMF_SABERTHROW_HELD; + } - if ( pm->gent )//&& pm->gent->s.number == 0 )//player only? + if (pm->gent)//&& pm->gent->s.number == 0 )//player only? { // Use PM_Use(); @@ -15058,7 +15537,7 @@ void Pmove( pmove_t *pmove ) // Calculate the resulting speed of the last pmove //------------------------------------------------- - if ( pm->gent ) + if (pm->gent) { pm->gent->resultspeed = ((Distance(pm->ps->origin, pm->gent->currentOrigin) / pml.msec) * 1000); if (pm->gent->resultspeed>5.0f) @@ -15074,45 +15553,48 @@ void Pmove( pmove_t *pmove ) } } - // ANIMATION //================================ // TEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMP - if ( pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON ) + if (pm->gent && pm->ps && pm->ps->eFlags & EF_LOCKED_TO_WEAPON) { - if ( pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use )//ugly way to tell, but... + if (pm->gent->owner && pm->gent->owner->e_UseFunc == useF_emplaced_gun_use)//ugly way to tell, but... {//full body - PM_SetAnim(pm,SETANIM_BOTH,BOTH_GUNSIT1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + PM_SetAnim(pm, SETANIM_BOTH, BOTH_GUNSIT1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL } else {//stand (or could be overridden by strafe anims) - PM_SetAnim(pm,SETANIM_LEGS,BOTH_STAND1,SETANIM_FLAG_NORMAL); + PM_SetAnim(pm, SETANIM_LEGS, BOTH_STAND1, SETANIM_FLAG_NORMAL); } } - else if ( pm->gent && pm->ps && (pm->ps->eFlags&EF_HELD_BY_RANCOR) ) + else if (pm->gent && pm->ps && (pm->ps->eFlags&EF_HELD_BY_RANCOR)) { - PM_SetAnim(pm,SETANIM_LEGS,BOTH_SWIM_IDLE1,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL + PM_SetAnim(pm, SETANIM_LEGS, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD);//SETANIM_FLAG_NORMAL } // If we are a vehicle, animate... - else if ( pVeh ) + else if (pVeh) { - pVeh->m_pVehicleInfo->Animate( pVeh ); + pVeh->m_pVehicleInfo->Animate(pVeh); } // If we're riding a vehicle, don't do anything!. - else if ( ( pVeh = PM_RidingVehicle() ) != 0 ) + else if ((pVeh = PM_RidingVehicle()) != 0) { PM_CheckInVehicleSaberAttackAnim(); } else // TEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMPTEMP { // footstep events / legs animations - PM_Footsteps(); + if (pm->ps->stasisTime < level.time) { + PM_Footsteps(); + } } // torso animation - if ( !pVeh ) + if (!pVeh) {//not riding a vehicle - PM_TorsoAnimation(); + if (pm->ps->stasisTime < level.time) { + PM_TorsoAnimation(); + } } // entering / leaving water splashes @@ -15121,15 +15603,15 @@ void Pmove( pmove_t *pmove ) // snap some parts of playerstate to save network bandwidth // SnapVector( pm->ps->velocity ); - if ( !pm->cmd.rightmove && !pm->cmd.forwardmove && pm->cmd.upmove <= 0 ) + if (!pm->cmd.rightmove && !pm->cmd.forwardmove && pm->cmd.upmove <= 0) { - if ( VectorCompare( pm->ps->velocity, vec3_origin ) ) + if (VectorCompare(pm->ps->velocity, vec3_origin)) { pm->ps->lastStationary = level.time; } } - if ( pm->ps->pm_flags & PMF_SLOW_MO_FALL ) + if (pm->ps->pm_flags & PMF_SLOW_MO_FALL) {//half grav pm->ps->gravity *= 2; } diff --git a/code/game/bg_public.h b/code/game/bg_public.h index becb74b25a..2469a95d10 100644 --- a/code/game/bg_public.h +++ b/code/game/bg_public.h @@ -123,6 +123,7 @@ typedef enum { #define PMF_BUMPED (1<<17)//131072 // Bumped into something #define PMF_FORCE_FOCUS_HELD (1<<18)//262144 // Holding down the saberthrow/kick button #define PMF_FIX_MINS (1<<19)//524288 // Mins raised for dual forward jump, fix them +#define PMF_SABERTHROW_HELD (1<<20) #define PMF_ALL_TIMES (PMF_TIME_WATERJUMP|PMF_TIME_LAND|PMF_TIME_KNOCKBACK|PMF_TIME_NOFRICTION) #define MAXTOUCH 32 @@ -241,6 +242,10 @@ typedef enum { #define EF_FORCE_DRAINED 0x40000000 // Force drained effect #define EF_BLOCKED_MOVER 0x80000000 // for movers that are blocked - shared with previous +//These new EF2_??? flags were added for NPCs, they really should not be used often. Taken from MP. +#define EF2_RADAROBJECT (1<<0) // Show entity in radar / minimap +#define EF2_HYPERSPACE (1<<5) // Used to both start the hyperspace effect on the predicted client and to let the vehicle know it can now jump into hyperspace (after turning to face the proper angle) + typedef enum { PW_NONE, PW_QUAD,// This can go away @@ -258,6 +263,9 @@ typedef enum { PW_INVINCIBLE, PW_FORCE_PUSH, PW_FORCE_PUSH_RHAND, + PW_REFRACT_MUZZLE, + PW_FORCE_REPULSE, + PW_FORCE_PROJECTILE, PW_NUM_POWERUPS } powerup_t; @@ -454,16 +462,37 @@ typedef enum { } entity_event_t; -#pragma pack(push, 1) -typedef struct animation_s { +class animation_t +{ +public: unsigned short firstFrame; unsigned short numFrames; short frameLerp; // msec between frames //initial lerp is abs(frameLerp) signed char loopFrames; // 0 to numFrames, -1 = no loop unsigned char glaIndex; -} animation_t; -#pragma pack(pop) + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(firstFrame); + saved_game.write(numFrames); + saved_game.write(frameLerp); + saved_game.write(loopFrames); + saved_game.write(glaIndex); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(firstFrame); + saved_game.read(numFrames); + saved_game.read(frameLerp); + saved_game.read(loopFrames); + saved_game.read(glaIndex); + } +}; // animation_t #define MAX_ANIM_FILES 16 #define MAX_ANIM_EVENTS 300 @@ -524,6 +553,29 @@ typedef struct animevent_s unsigned short keyFrame; //Frame to play event on signed short eventData[AED_ARRAY_SIZE]; //Unique IDs, can be soundIndex of sound file to play OR effect index or footstep type, etc. char *stringData; //we allow storage of one string, temporarily (in case we have to look up an index later, then make sure to set stringData to NULL so we only do the look-up once) + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(eventType); + saved_game.write(modelOnly); + saved_game.write(glaIndex); + saved_game.write(keyFrame); + saved_game.write(eventData); + saved_game.write(stringData); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(eventType); + saved_game.read(modelOnly); + saved_game.read(glaIndex); + saved_game.read(keyFrame); + saved_game.read(eventData); + saved_game.read(stringData); + } } animevent_t; typedef enum @@ -590,6 +642,8 @@ typedef enum { MOD_SUICIDE, MOD_TRIGGER_HURT, MOD_GAS, + + MOD_DESTRUCTION, NUM_MODS, @@ -718,4 +772,9 @@ void PlayerStateToEntityState( playerState_t *ps, entityState_t *s ); qboolean BG_PlayerTouchesItem( playerState_t *ps, entityState_t *item, int atTime ); +#define HYPERSPACE_TIME 4000 //For hyperspace triggers +#define HYPERSPACE_TELEPORT_FRAC 0.75f +#define HYPERSPACE_SPEED 10000.0f//was 30000 +#define HYPERSPACE_TURN_RATE 45.0f + #endif//#ifndef __BG_PUBLIC_H__ diff --git a/code/game/bg_slidemove.cpp b/code/game/bg_slidemove.cpp index 5beeac7179..f962e9b240 100644 --- a/code/game/bg_slidemove.cpp +++ b/code/game/bg_slidemove.cpp @@ -199,7 +199,7 @@ qboolean PM_SlideMove( float gravMod ) { continue; } - if (pm->gent->client && + if (pm->gent && pm->gent->client && pm->gent->client->NPC_class == CLASS_VEHICLE && trace.plane.normal[2]gent->m_pVehicle->m_pVehicleInfo->maxSlope ) @@ -326,7 +326,7 @@ qboolean PM_SlideMove( float gravMod ) { VectorCopy( primal_velocity, pm->ps->velocity ); } - return ( bumpcount != 0 ); + return (qboolean)( bumpcount != 0 ); } /* diff --git a/code/game/characters.h b/code/game/characters.h deleted file mode 100644 index 53ff022550..0000000000 --- a/code/game/characters.h +++ /dev/null @@ -1,52 +0,0 @@ -typedef enum //# characters_e -{ - //HazTeam Alpha - //CHARACTER_MUNRO = 0, - CHARACTER_FOSTER = 0, - CHARACTER_TELSIA, - CHARACTER_BIESSMAN, - CHARACTER_CHANG, - CHARACTER_CHELL, - CHARACTER_JUROT, - //HazTeam Beta - CHARACTER_LAIRD, - CHARACTER_KENN, - CHARACTER_OVIEDO, - CHARACTER_ODELL, - CHARACTER_NELSON, - CHARACTER_JAWORSKI, - CHARACTER_CSATLOS, - //Senior Crew - CHARACTER_JANEWAY, - CHARACTER_CHAKOTAY, - CHARACTER_TUVOK, - CHARACTER_TUVOKHAZ, - CHARACTER_TORRES, - CHARACTER_PARIS, - CHARACTER_KIM, - CHARACTER_DOCTOR, - CHARACTER_SEVEN, - CHARACTER_SEVENHAZ, - CHARACTER_NEELIX, - //Other Crew - CHARACTER_PELLETIER, - //Generic Crew - CHARACTER_CREWMAN, - //CHARACTER_ENSIGN, - CHARACTER_LT, - CHARACTER_COMM, - CHARACTER_CAPT, - CHARACTER_GENERIC1, - CHARACTER_GENERIC2, - CHARACTER_GENERIC3, - CHARACTER_GENERIC4, - //# #eol - CHARACTER_NUM_CHARS -} characters_t; - -typedef struct -{ - char *name; - char *sound; - sfxHandle_t soundIndex; -} characterName_t; diff --git a/code/game/fp_repulse.cpp b/code/game/fp_repulse.cpp new file mode 100644 index 0000000000..1cd34260e6 --- /dev/null +++ b/code/game/fp_repulse.cpp @@ -0,0 +1,715 @@ +/* +=========================================================================== +Copyright (C) 2000 - 2013, Raven Software, Inc. +Copyright (C) 2001 - 2013, Activision, Inc. +Copyright (C) 2013 - 2015, OpenJK contributors + +This file is part of the OpenJK source code. + +OpenJK is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 as +published by the Free Software Foundation. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, see . +=========================================================================== +*/ + +#include "g_local.h" +#include "anims.h" +#include "b_local.h" +#include "bg_local.h" +#include "g_functions.h" +#include "wp_saber.h" +#include "g_vehicles.h" +#include "../qcommon/tri_coll_test.h" +#include "../cgame/cg_local.h" + +extern qboolean WP_ForcePowerUsable( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); +extern qboolean WP_ForceThrowable( gentity_t *ent, gentity_t *forwardEnt, gentity_t *self, qboolean pull, float cone, float radius, vec3_t forward ); +extern void WP_ForcePowerStop( gentity_t *self, forcePowers_t forcePower ); +extern void WP_ForceThrowHazardTrooper( gentity_t *self, gentity_t *trooper, qboolean pull ); +extern void WP_ResistForcePush( gentity_t *self, gentity_t *pusher, qboolean noPenalty ); +extern int WP_AbsorbConversion(gentity_t *attacked, int atdAbsLevel, gentity_t *attacker, int atPower, int atPowerLevel, int atForceSpent); +extern qboolean ShouldPlayerResistForceThrow( gentity_t *player, gentity_t *attacker, qboolean pull ); +extern qboolean Rosh_BeingHealed( gentity_t *self ); +extern void G_KnockOffVehicle( gentity_t *pRider, gentity_t *self, qboolean bPull ); +extern void WP_ForceKnockdown( gentity_t *self, gentity_t *pusher, qboolean pull, qboolean strongKnockdown, qboolean breakSaberLock ); +extern qboolean Jedi_WaitingAmbush( gentity_t *self ); +extern void G_ReflectMissile( gentity_t *ent, gentity_t *missile, vec3_t forward ); +extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); +extern void WP_KnockdownTurret( gentity_t *self, gentity_t *pas ); +extern void WP_SaberDrop( gentity_t *self, gentity_t *saber ); +extern void WP_SaberReturn( gentity_t *self, gentity_t *saber ); +extern qboolean PM_InKnockDown( playerState_t *ps ); +extern void WP_ForcePowerDrain( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); + +extern cvar_t *g_timescale; + +extern int forcePowerNeeded[NUM_FORCE_POWERS]; + +void RepulseDamage( gentity_t *self, gentity_t *enemy, vec3_t location, int damageLevel ) +{ + switch (damageLevel) { + case FORCE_LEVEL_1: + G_Damage( enemy, self, self, NULL, location, 10, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); + break; + case FORCE_LEVEL_2: + G_Damage( enemy, self, self, NULL, location, 25, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); + break; + case FORCE_LEVEL_3: + G_Damage( enemy, self, self, NULL, location, 50, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); + break; + default: + break; + } +} + +void ForceRepulseThrow( gentity_t *self, int chargeTime ) +{ + //shove things around you away + qboolean fake = qfalse; + float dist; + gentity_t *ent, *forwardEnt = NULL; + gentity_t *entityList[MAX_GENTITIES]; + gentity_t *push_list[MAX_GENTITIES]; + int numListedEntities = 0; + vec3_t mins, maxs; + vec3_t v; + int i, e; + int ent_count = 0; + int radius; + vec3_t center, ent_org, size, forward, right, end, dir, fwdangles = {0}; + trace_t tr; + int anim, hold, soundIndex, cost; + int damageLevel = FORCE_LEVEL_0; + + if ( self->health <= 0 ) + { + return; + } + if ( self->client->ps.leanofs ) + {//can't force-throw while leaning + return; + } + if ( self->client->ps.forcePowerDebounce[FP_REPULSE] > level.time ) + {//already pushing- now you can't haul someone across the room, sorry + return; + } + if ( !self->s.number && (cg.zoomMode || in_camera) ) + {//can't force throw/pull when zoomed in or in cinematic + return; + } + if ( self->client->ps.saberLockTime > level.time ) + { + if ( self->client->ps.forcePowerLevel[FP_REPULSE] < FORCE_LEVEL_3 ) + {//this can be a way to break out + return; + } + //else, I'm breaking my half of the saberlock + self->client->ps.saberLockTime = 0; + self->client->ps.saberLockEnemy = ENTITYNUM_NONE; + } + + if ( self->client->ps.legsAnim == BOTH_KNOCKDOWN3 + || (self->client->ps.torsoAnim == BOTH_FORCE_GETUP_F1 && self->client->ps.torsoAnimTimer > 400) + || (self->client->ps.torsoAnim == BOTH_FORCE_GETUP_F2 && self->client->ps.torsoAnimTimer > 900) + || (self->client->ps.torsoAnim == BOTH_GETUP3 && self->client->ps.torsoAnimTimer > 500) + || (self->client->ps.torsoAnim == BOTH_GETUP4 && self->client->ps.torsoAnimTimer > 300) + || (self->client->ps.torsoAnim == BOTH_GETUP5 && self->client->ps.torsoAnimTimer > 500) ) + {//we're face-down, so we'd only be force-push/pulling the floor + return; + } + + radius = forcePushPullRadius[self->client->ps.forcePowerLevel[FP_REPULSE]]; + + if ( !radius ) + {//no ability to do this yet + return; + } + + if ( chargeTime > 2500.0f ) + { + damageLevel = FORCE_LEVEL_3; + } + else if ( chargeTime > 1250.0f ) + { + damageLevel = FORCE_LEVEL_2; + } + else if ( chargeTime > 500.0f ) + { + damageLevel = FORCE_LEVEL_1; + } + + cost = forcePowerNeeded[FP_REPULSE]; + if ( !WP_ForcePowerUsable( self, FP_REPULSE, cost ) ) + { + return; + } + //make sure this plays and that you cannot press fire for about 1 second after this + anim = BOTH_FORCE_2HANDEDLIGHTNING_HOLD; + soundIndex = G_SoundIndex( "sound/weapons/force/repulse.wav" ); + hold = 650; + + int parts = SETANIM_TORSO; + if ( !PM_InKnockDown( &self->client->ps ) ) + { + if ( self->client->ps.saberLockTime > level.time ) + { + self->client->ps.saberLockTime = 0; + self->painDebounceTime = level.time + 2000; + hold += 1000; + parts = SETANIM_BOTH; + } + } + NPC_SetAnim( self, parts, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); + self->client->ps.saberMove = self->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in + self->client->ps.saberBlocked = BLOCKED_NONE; + if ( self->client->ps.forcePowersActive&(1<value ); + } + self->client->ps.weaponTime = hold;//was 1000, but want to swing sooner + //do effect... FIXME: build-up or delay this until in proper part of anim + self->client->ps.powerups[PW_FORCE_REPULSE] = level.time + self->client->ps.torsoAnimTimer + 500; + //reset to 0 in case it's still > 0 from a previous push + self->client->pushEffectFadeTime = 0; + + G_Sound( self, soundIndex ); + + VectorCopy( self->client->ps.viewangles, fwdangles ); + //fwdangles[1] = self->client->ps.viewangles[1]; + AngleVectors( fwdangles, forward, right, NULL ); + VectorCopy( self->currentOrigin, center ); + + if ( !numListedEntities ) + { + for ( i = 0 ; i < 3 ; i++ ) + { + mins[i] = center[i] - radius; + maxs[i] = center[i] + radius; + } + + numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); + + for ( e = 0 ; e < numListedEntities ; e++ ) + { + ent = entityList[ e ]; + + if ( !WP_ForceThrowable( ent, forwardEnt, self, qfalse, 0.0f, radius, forward ) ) + { + continue; + } + + //this is all to see if we need to start a saber attack, if it's in flight, this doesn't matter + // find the distance from the edge of the bounding box + for ( i = 0 ; i < 3 ; i++ ) + { + if ( center[i] < ent->absmin[i] ) + { + v[i] = ent->absmin[i] - center[i]; + } else if ( center[i] > ent->absmax[i] ) + { + v[i] = center[i] - ent->absmax[i]; + } else + { + v[i] = 0; + } + } + + VectorSubtract( ent->absmax, ent->absmin, size ); + VectorMA( ent->absmin, 0.5, size, ent_org ); + + //see if they're in front of me + VectorSubtract( ent_org, center, dir ); + VectorNormalize( dir ); + + dist = VectorLength( v ); + + if ( dist >= radius ) + { + continue; + } + + //in PVS? + if ( !ent->bmodel && !gi.inPVS( ent_org, self->client->renderInfo.eyePoint ) ) + {//must be in PVS + continue; + } + + if ( ent != forwardEnt ) + {//don't need to trace against forwardEnt again + //really should have a clear LOS to this thing... + gi.trace( &tr, self->client->renderInfo.eyePoint, vec3_origin, vec3_origin, ent_org, self->s.number, MASK_FORCE_PUSH, (EG2_Collision)0, 0 );//was MASK_SHOT, but changed to match above trace and crosshair trace + if ( tr.fraction < 1.0f && tr.entityNum != ent->s.number ) + {//must have clear LOS + continue; + } + } + + // ok, we are within the radius, add us to the incoming list + push_list[ent_count] = ent; + ent_count++; + } + } + + for ( int x = 0; x < ent_count; x++ ) + { + if ( push_list[x]->client ) + { + vec3_t pushDir; + float knockback = 200; + + //SIGH band-aid... + if ( push_list[x]->s.number >= MAX_CLIENTS + && self->s.number < MAX_CLIENTS ) + { + if ( (push_list[x]->client->ps.forcePowersActive&(1<client->ps.forcePowerDebounce[FP_GRIP] < level.time + && push_list[x]->client->ps.forceGripEntityNum == self->s.number ) + { + WP_ForcePowerStop( push_list[x], FP_GRIP ); + } + if ( (push_list[x]->client->ps.forcePowersActive&(1<client->ps.forcePowerDebounce[FP_DRAIN] < level.time + && push_list[x]->client->ps.forceDrainEntityNum == self->s.number ) + { + WP_ForcePowerStop( push_list[x], FP_DRAIN ); + } + } + + if ( Rosh_BeingHealed( push_list[x] ) ) + { + continue; + } + if ( push_list[x]->client->NPC_class == CLASS_HAZARD_TROOPER + && push_list[x]->health > 0 ) + {//living hazard troopers resist push/pull + WP_ForceThrowHazardTrooper( self, push_list[x], qfalse ); + continue; + } + if ( fake ) + {//always resist + WP_ResistForcePush( push_list[x], self, qfalse ); + continue; + } + + int powerLevel, powerUse; + powerLevel = self->client->ps.forcePowerLevel[FP_REPULSE]; + powerUse = FP_REPULSE; + + int modPowerLevel = WP_AbsorbConversion( push_list[x], push_list[x]->client->ps.forcePowerLevel[FP_ABSORB], self, powerUse, powerLevel, forcePowerNeeded[self->client->ps.forcePowerLevel[powerUse]] ); + if (push_list[x]->client->NPC_class==CLASS_ASSASSIN_DROID || + push_list[x]->client->NPC_class==CLASS_HAZARD_TROOPER) + { + modPowerLevel = 0; // devides throw by 10 + } + + //First, if this is the player we're push/pulling, see if he can counter it + if ( modPowerLevel != -1 + && InFront( self->currentOrigin, push_list[x]->client->renderInfo.eyePoint, push_list[x]->client->ps.viewangles, 0.3f ) ) + {//absorbed and I'm in front of them + //counter it + if ( push_list[x]->client->ps.forcePowerLevel[FP_ABSORB] > FORCE_LEVEL_2 ) + {//no reaction at all + } + else + { + WP_ResistForcePush( push_list[x], self, qfalse ); + push_list[x]->client->ps.saberMove = push_list[x]->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in + push_list[x]->client->ps.saberBlocked = BLOCKED_NONE; + } + continue; + } + else if ( !push_list[x]->s.number ) + {//player + if ( ShouldPlayerResistForceThrow(push_list[x], self, qfalse) ) + { + WP_ResistForcePush( push_list[x], self, qfalse ); + push_list[x]->client->ps.saberMove = push_list[x]->client->ps.saberBounceMove = LS_READY;//don't finish whatever saber anim you may have been in + push_list[x]->client->ps.saberBlocked = BLOCKED_NONE; + continue; + } + } + else if ( push_list[x]->client && Jedi_WaitingAmbush( push_list[x] ) ) + { + WP_ForceKnockdown( push_list[x], self, qfalse, qtrue, qfalse ); + RepulseDamage( self, push_list[x], tr.endpos, damageLevel ); + continue; + } + + G_KnockOffVehicle( push_list[x], self, qfalse ); + + if ( push_list[x]->client->ps.forceDrainEntityNum == self->s.number + && (self->s.eFlags&EF_FORCE_DRAINED) ) + {//stop them from draining me now, dammit! + WP_ForcePowerStop( push_list[x], FP_DRAIN ); + } + + //okay, everyone else (or player who couldn't resist it)... + if ( ((self->s.number == 0 && Q_irand( 0, 2 ) ) || Q_irand( 0, 2 ) ) && push_list[x]->client && push_list[x]->health > 0 //a living client + && push_list[x]->client->ps.weapon == WP_SABER //Jedi + && push_list[x]->health > 0 //alive + && push_list[x]->client->ps.forceRageRecoveryTime < level.time //not recobering from rage + && ((self->client->NPC_class != CLASS_DESANN&&Q_stricmp("Yoda",self->NPC_type)) || !Q_irand( 0, 2 ) )//only 30% chance of resisting a Desann push + && push_list[x]->client->ps.groundEntityNum != ENTITYNUM_NONE //on the ground + && InFront( self->currentOrigin, push_list[x]->currentOrigin, push_list[x]->client->ps.viewangles, 0.3f ) //I'm in front of him + && ( push_list[x]->client->ps.powerups[PW_FORCE_PUSH] > level.time ||//he's pushing too + (push_list[x]->s.number != 0 && push_list[x]->client->ps.weaponTime < level.time)//not the player and not attacking (NPC jedi auto-defend against pushes) + ) + ) + {//Jedi don't get pushed, they resist as long as they aren't already attacking and are on the ground + if ( push_list[x]->client->ps.saberLockTime > level.time ) + {//they're in a lock + if ( push_list[x]->client->ps.saberLockEnemy != self->s.number ) + {//they're not in a lock with me + continue; + } + else if ( self->client->ps.forcePowerLevel[FP_REPULSE] < FORCE_LEVEL_3 || + push_list[x]->client->ps.forcePowerLevel[FP_PUSH] > FORCE_LEVEL_2 ) + {//they're in a lock with me, but my push is too weak + continue; + } + else + {//we will knock them down + self->painDebounceTime = 0; + self->client->ps.weaponTime = 500; + if ( self->client->ps.forcePowersActive&(1<client->ps.weaponTime = floor( self->client->ps.weaponTime * g_timescale->value ); + } + } + } + int resistChance = Q_irand(0, 2); + if ( push_list[x]->s.number >= MAX_CLIENTS ) + {//NPC + if ( g_spskill->integer == 1 ) + {//stupid tweak for graham + resistChance = Q_irand(0, 3); + } + } + if ( modPowerLevel == -1 + && self->client->ps.forcePowerLevel[FP_REPULSE] > FORCE_LEVEL_2 + && !resistChance + && push_list[x]->client->ps.forcePowerLevel[FP_PUSH] < FORCE_LEVEL_3 ) + {//a level 3 push can even knock down a jedi + if ( PM_InKnockDown( &push_list[x]->client->ps ) ) + {//can't knock them down again + continue; + } + WP_ForceKnockdown( push_list[x], self, qfalse, qfalse, qtrue ); + RepulseDamage( self, push_list[x], tr.endpos, damageLevel ); + } + else + { + WP_ResistForcePush( push_list[x], self, qfalse ); + } + } + else + { + //UGH: FIXME: for enemy jedi, they should probably always do force pull 3, and not your weapon (if player?)! + //shove them + if ( push_list[x]->NPC + && push_list[x]->NPC->jumpState == JS_JUMPING ) + {//don't interrupt a scripted jump + //WP_ResistForcePush( push_list[x], self, qfalse ); + push_list[x]->forcePushTime = level.time + 600; // let the push effect last for 600 ms + continue; + } + + if ( push_list[x]->s.number + && (push_list[x]->message || (push_list[x]->flags&FL_NO_KNOCKBACK)) ) + {//an NPC who has a key + //don't push me... FIXME: maybe can pull the key off me? + WP_ForceKnockdown( push_list[x], self, qfalse, qfalse, qfalse ); + RepulseDamage( self, push_list[x], tr.endpos, damageLevel ); + continue; + } + { + VectorSubtract( push_list[x]->currentOrigin, self->currentOrigin, pushDir ); + knockback -= VectorNormalize( pushDir ); + if ( knockback < 100 ) + { + knockback = 100; + } + //scale for push level + if ( self->client->ps.forcePowerLevel[FP_REPULSE] < FORCE_LEVEL_2 ) + {//maybe just knock them down + knockback /= 3; + } + else if ( self->client->ps.forcePowerLevel[FP_REPULSE] > FORCE_LEVEL_2 ) + {//super-hard push + //Hmm, maybe in this case can even nudge/knockdown a jedi? Especially if close? + //knockback *= 5; + } + } + + if ( modPowerLevel != -1 ) + { + if ( !modPowerLevel ) + { + knockback /= 10.0f; + } + else if ( modPowerLevel == 1 ) + { + knockback /= 6.0f; + } + else// if ( modPowerLevel == 2 ) + { + knockback /= 2.0f; + } + } + //actually push/pull the enemy + G_Throw( push_list[x], pushDir, knockback ); + //make it so they don't actually hurt me when pulled at me... + push_list[x]->forcePuller = self->s.number; + + if ( push_list[x]->client->ps.groundEntityNum != ENTITYNUM_NONE ) + {//if on the ground, make sure they get shoved up some + if ( push_list[x]->client->ps.velocity[2] < knockback ) + { + push_list[x]->client->ps.velocity[2] = knockback; + } + } + + if ( push_list[x]->health > 0 ) + {//target is still alive + if ( (push_list[x]->s.number||(cg.renderingThirdPerson&&!cg.zoomMode)) //NPC or 3rd person player + && ((self->client->ps.forcePowerLevel[FP_REPULSE] < FORCE_LEVEL_2 && push_list[x]->client->ps.forcePowerLevel[FP_PUSH] < FORCE_LEVEL_1)) ) + {//NPC or third person player (without force push/pull skill), and force push/pull level is at 1 + WP_ForceKnockdown( push_list[x], self, qfalse, (qboolean)(knockback>150), qfalse ); + RepulseDamage( self, push_list[x], tr.endpos, damageLevel ); + } + else if ( !push_list[x]->s.number ) + {//player, have to force an anim on him + WP_ForceKnockdown( push_list[x], self, qfalse, (qboolean)(knockback>150), qfalse ); + RepulseDamage( self, push_list[x], tr.endpos, damageLevel ); + } + else + {//NPC and force-push/pull at level 2 or higher + WP_ForceKnockdown( push_list[x], self, qfalse, (qboolean)(knockback>100), qfalse ); + RepulseDamage( self, push_list[x], tr.endpos, damageLevel ); + } + } + push_list[x]->forcePushTime = level.time + 600; // let the push effect last for 600 ms + } + } + else if ( !fake ) + {//not a fake push/pull + if ( push_list[x]->s.weapon == WP_SABER && (push_list[x]->contents&CONTENTS_LIGHTSABER) ) + {//a thrown saber, just send it back + /* + if ( pull ) + {//steal it? + } + else */if ( push_list[x]->owner && push_list[x]->owner->client && push_list[x]->owner->client->ps.SaberActive() && push_list[x]->s.pos.trType == TR_LINEAR && push_list[x]->owner->client->ps.saberEntityState != SES_RETURNING ) + {//it's on and being controlled + //FIXME: prevent it from damaging me? + if ( self->s.number == 0 || Q_irand( 0, 2 ) ) + {//certain chance of throwing it aside and turning it off? + //give it some velocity away from me + //FIXME: maybe actually push or pull it? + if ( Q_irand( 0, 1 ) ) + { + VectorScale( right, -1, right ); + } + G_ReflectMissile( self, push_list[x], right ); + //FIXME: isn't turning off!!! + WP_SaberDrop( push_list[x]->owner, push_list[x] ); + } + else + { + WP_SaberReturn( push_list[x]->owner, push_list[x] ); + } + //different effect? + } + } + else if ( push_list[x]->s.eType == ET_MISSILE + && push_list[x]->s.pos.trType != TR_STATIONARY + && (push_list[x]->s.pos.trType != TR_INTERPOLATE||push_list[x]->s.weapon != WP_THERMAL) )//rolling and stationary thermal detonators are dealt with below + { + vec3_t dir2Me; + VectorSubtract( self->currentOrigin, push_list[x]->currentOrigin, dir2Me ); + float dot = DotProduct( push_list[x]->s.pos.trDelta, dir2Me ); + + if ( push_list[x]->s.eFlags&EF_MISSILE_STICK ) + {//caught a sticky in-air + push_list[x]->s.eType = ET_MISSILE; + push_list[x]->s.eFlags &= ~EF_MISSILE_STICK; + push_list[x]->s.eFlags |= EF_BOUNCE_HALF; + push_list[x]->splashDamage /= 3; + push_list[x]->splashRadius /= 3; + push_list[x]->e_ThinkFunc = thinkF_WP_Explode; + push_list[x]->nextthink = level.time + Q_irand( 500, 3000 ); + } + if ( dot >= 0 ) + {//it's heading towards me + G_ReflectMissile( self, push_list[x], forward ); + } + else + { + VectorScale( push_list[x]->s.pos.trDelta, 1.25f, push_list[x]->s.pos.trDelta ); + } + //deflect sound + //G_Sound( push_list[x], G_SoundIndex( va("sound/weapons/blaster/reflect%d.wav", Q_irand( 1, 3 ) ) ) ); + //push_list[x]->forcePushTime = level.time + 600; // let the push effect last for 600 ms + + if ( push_list[x]->s.eType == ET_MISSILE + && push_list[x]->s.weapon == WP_ROCKET_LAUNCHER + && push_list[x]->damage < 60 ) + {//pushing away a rocket raises it's damage to the max for NPCs + push_list[x]->damage = 60; + } + } + else if ( push_list[x]->svFlags & SVF_GLASS_BRUSH ) + {//break the glass + trace_t tr; + vec3_t pushDir; + float damage = 800; + + AngleVectors( self->client->ps.viewangles, forward, NULL, NULL ); + VectorNormalize( forward ); + VectorMA( self->client->renderInfo.eyePoint, radius, forward, end ); + gi.trace( &tr, self->client->renderInfo.eyePoint, vec3_origin, vec3_origin, end, self->s.number, MASK_SHOT, (EG2_Collision)0, 0 ); + if ( tr.entityNum != push_list[x]->s.number || tr.fraction == 1.0 || tr.allsolid || tr.startsolid ) + {//must be pointing right at it + continue; + } + + + VectorSubtract( tr.endpos, self->client->renderInfo.eyePoint, pushDir ); + + damage -= VectorNormalize( pushDir ); + if ( damage < 200 ) + { + damage = 200; + } + VectorScale( pushDir, damage, pushDir ); + + G_Damage( push_list[x], self, self, pushDir, tr.endpos, damage, 0, MOD_UNKNOWN ); + } + else if ( push_list[x]->s.eType == ET_MISSILE/*thermal resting on ground*/ + || push_list[x]->s.eType == ET_ITEM + || push_list[x]->e_ThinkFunc == thinkF_G_RunObject || Q_stricmp( "limb", push_list[x]->classname ) == 0 ) + {//general object, toss it + vec3_t pushDir, kvel; + float knockback = 200; + float mass = 200; + + if ( self->enemy //I have an enemy + && push_list[x]->s.eType != ET_ITEM //not an item + && self->client->ps.forcePowerLevel[FP_REPULSE] > FORCE_LEVEL_2 //have push 3 or greater + && InFront(push_list[x]->currentOrigin, self->currentOrigin, self->currentAngles, 0.25f)//object is generally in front of me + && InFront(self->enemy->currentOrigin, self->currentOrigin, self->currentAngles, 0.75f)//enemy is pretty much right in front of me + && InFront(push_list[x]->currentOrigin, self->enemy->currentOrigin, self->enemy->currentAngles, 0.25f)//object is generally in front of enemy + //FIXME: check dist to enemy and clear LOS to enemy and clear Path between object and enemy? + && ( (self->NPC&&(Q_irand(0,RANK_CAPTAIN)NPC->rank) )//NPC with enough skill + ||( self->s.numbermass > TARGETED_OBJECT_PUSH_MASS_MAX ) + {//already pushed too many things + //FIXME: pick closest? + continue; + } + targetedObjectMassTotal += push_list[x]->mass; + */ + VectorSubtract( self->enemy->currentOrigin, push_list[x]->currentOrigin, pushDir ); + } + else + { + VectorSubtract( push_list[x]->currentOrigin, self->currentOrigin, pushDir ); + } + knockback -= VectorNormalize( pushDir ); + if ( knockback < 100 ) + { + knockback = 100; + } + + //FIXME: if pull a FL_FORCE_PULLABLE_ONLY, clear the flag, assuming it's no longer in solid? or check? + VectorCopy( push_list[x]->currentOrigin, push_list[x]->s.pos.trBase ); + push_list[x]->s.pos.trTime = level.time; // move a bit on the very first frame + if ( push_list[x]->s.pos.trType != TR_INTERPOLATE ) + {//don't do this to rolling missiles + push_list[x]->s.pos.trType = TR_GRAVITY; + } + + if ( push_list[x]->e_ThinkFunc == thinkF_G_RunObject && push_list[x]->physicsBounce ) + {//it's a pushable misc_model_breakable, use it's mass instead of our one-size-fits-all mass + mass = push_list[x]->physicsBounce;//same as push_list[x]->mass, right? + } + if ( mass < 50 ) + {//??? + mass = 50; + } + if ( g_gravity->value > 0 ) + { + VectorScale( pushDir, g_knockback->value * knockback / mass * 0.8, kvel ); + kvel[2] = pushDir[2] * g_knockback->value * knockback / mass * 1.5; + } + else + { + VectorScale( pushDir, g_knockback->value * knockback / mass, kvel ); + } + VectorAdd( push_list[x]->s.pos.trDelta, kvel, push_list[x]->s.pos.trDelta ); + if ( g_gravity->value > 0 ) + { + if ( push_list[x]->s.pos.trDelta[2] < knockback ) + { + push_list[x]->s.pos.trDelta[2] = knockback; + } + } + //no trDuration? + if ( push_list[x]->e_ThinkFunc != thinkF_G_RunObject ) + {//objects spin themselves? + //spin it + //FIXME: messing with roll ruins the rotational center??? + push_list[x]->s.apos.trTime = level.time; + push_list[x]->s.apos.trType = TR_LINEAR; + VectorClear( push_list[x]->s.apos.trDelta ); + push_list[x]->s.apos.trDelta[1] = Q_irand( -800, 800 ); + } + + if ( Q_stricmp( "limb", push_list[x]->classname ) == 0 ) + {//make sure it runs it's physics + push_list[x]->e_ThinkFunc = thinkF_LimbThink; + push_list[x]->nextthink = level.time + FRAMETIME; + } + push_list[x]->forcePushTime = level.time + 600; // let the push effect last for 600 ms + push_list[x]->forcePuller = self->s.number;//remember this regardless + if ( push_list[x]->item && push_list[x]->item->giTag == INV_SECURITY_KEY ) + { + AddSightEvent( player, push_list[x]->currentOrigin, 128, AEL_DISCOVERED );//security keys are more important + } + else + { + AddSightEvent( player, push_list[x]->currentOrigin, 128, AEL_SUSPICIOUS );//hmm... or should this always be discovered? + } + } + else if ( push_list[x]->s.weapon == WP_TURRET + && !Q_stricmp( "PAS", push_list[x]->classname ) + && push_list[x]->s.apos.trType == TR_STATIONARY ) + {//a portable turret + WP_KnockdownTurret( self, push_list[x] ); + } + } + } + + WP_ForcePowerDrain(self, FP_REPULSE, cost); + + if ( self->NPC ) + {//NPCs can push more often + //FIXME: vary by rank and game skill? + self->client->ps.forcePowerDebounce[FP_REPULSE] = level.time + 200; + } + else + { + self->client->ps.forcePowerDebounce[FP_REPULSE] = level.time + self->client->ps.torsoAnimTimer + 500; + } +} diff --git a/code/game/g_active.cpp b/code/game/g_active.cpp index 63fc0e77e2..c8164af5f6 100644 --- a/code/game/g_active.cpp +++ b/code/game/g_active.cpp @@ -48,6 +48,11 @@ extern void WP_SaberReflectCheck( gentity_t *self, usercmd_t *ucmd ); extern void WP_SaberUpdate( gentity_t *self, usercmd_t *ucmd ); extern void WP_SaberStartMissileBlockCheck( gentity_t *self, usercmd_t *ucmd ); extern void WP_ForcePowersUpdate( gentity_t *self, usercmd_t *ucmd ); +extern void WP_SaberBlockPointsRegenerate(gentity_t * self); +extern void Jedi_MeleeEvasionDefense(gentity_t *self, usercmd_t *ucmd); +extern void WP_CheckPlayerSaberEvents(gentity_t *self); +extern qboolean NPC_JediClass(int className); + extern gentity_t *SeekerAcquiresTarget ( gentity_t *ent, vec3_t pos ); extern void FireSeeker( gentity_t *owner, gentity_t *target, vec3_t origin, vec3_t dir ); extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); @@ -107,6 +112,8 @@ extern qboolean PM_LockedAnim( int anim ); extern qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLockMode_t lockMode ); extern qboolean G_JediInNormalAI( gentity_t *ent ); +extern qboolean PlayerAffectedByStasis( void ); + extern bool in_camera; extern qboolean player_locked; extern qboolean stop_icarus; @@ -121,6 +128,7 @@ extern cvar_t *d_slowmodeath; extern cvar_t *g_debugMelee; extern vmCvar_t cg_thirdPersonAlpha; extern vmCvar_t cg_thirdPersonAutoAlpha; +extern cvar_t *g_saberNewCombat; void ClientEndPowerUps( gentity_t *ent ); @@ -420,7 +428,8 @@ void G_ChooseLookEnemy( gentity_t *self, usercmd_t *ucmd ) { if ( (ucmd->buttons&BUTTON_ATTACK) || (ucmd->buttons&BUTTON_ALT_ATTACK) - || (ucmd->buttons&BUTTON_FORCE_FOCUS) ) + || (ucmd->buttons&BUTTON_FORCE_FOCUS) + || (ucmd->buttons&BUTTON_SABERTHROW) ) {//if attacking, don't consider dead enemies continue; } @@ -1239,7 +1248,7 @@ void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf, trace_t * || self->client->NPC_class == CLASS_VEHICLE || ( magnitude >= 700 ) )//health here is used to simulate structural integrity { - if ( (self->s.weapon == WP_SABER || self->s.numberclient&&(self->client->NPC_class==CLASS_BOBAFETT||self->client->NPC_class==CLASS_ROCKETTROOPER))) && self->client && self->client->ps.groundEntityNum < ENTITYNUM_NONE && magnitude < 1000 ) + if ((self->s.weapon == WP_SABER || self->s.numberclient && (self->client->NPC_class == CLASS_BOBAFETT || self->client->NPC_class == CLASS_ROCKETTROOPER || self->client->NPC_class == CLASS_MANDA || self->client->NPC_class == CLASS_COMMANDO))) && self->client && self->client->ps.groundEntityNum < ENTITYNUM_NONE && magnitude < 1000) {//players and jedi take less impact damage //allow for some lenience on high falls magnitude /= 2; @@ -1295,7 +1304,25 @@ void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf, trace_t * magnitude = 0; } - G_Damage( self, NULL, NULL, NULL, self->currentOrigin, magnitude/2, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT + if( (!Q_stricmp(self->NPC_type, "rosh_penin") || + !Q_stricmp(self->NPC_type, "rosh_penin_noforce")) && + !Q_stricmp(level.mapname, "yavin1b") ) + { + // This is a small little fix I implemented over a matter of 3 commits due to bugs/etc. + // There is an EXTREMELY frustrating bug on yavin1b where Rosh can take enough damage from howlers to the point + // where, during one section where he jumps over a stream, he can suffer falling damage and die. So the player + // would be forced to repeat the level over and over again. + // Despite it being clearly a jump, the scripters for the level somehow forgot to add a cushion brush on + // the landing zone where Rosh would be, (fucking woglodytes that Raven outsourced the levels to, I swear...) + // resulting in a very weird and unnatural death. So it didn't make any sense. So I did the nasty thing and did + // it through code. + // This could also probably explain why Rosh suddenly dies for NO reason whatsoever in rare occasions on Jedi + // Master/Jedi Knight mode at the start of the level. --eezstreet + } + else + { + G_Damage( self, NULL, NULL, NULL, self->currentOrigin, magnitude/2, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT + } } } } @@ -1634,7 +1661,11 @@ void G_MatchPlayerWeapon( gentity_t *ent ) if ( newWeap != WP_NONE && ent->client->ps.weapon != newWeap ) { G_RemoveWeaponModels( ent ); - ent->client->ps.stats[STAT_WEAPONS] = ( 1 << newWeap ); + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + ent->client->ps.weapons[i] = 0; + } + ent->client->ps.weapons[newWeap] = 1; ent->client->ps.ammo[weaponData[newWeap].ammoIndex] = 999; ChangeWeapon( ent, newWeap ); ent->client->ps.weapon = newWeap; @@ -1644,6 +1675,7 @@ void G_MatchPlayerWeapon( gentity_t *ent ) //FIXME: AddSound/Sight Event int numSabers = WP_SaberInitBladeData( ent ); WP_SaberAddG2SaberModels( ent ); + G_RemoveHolsterModels( ent ); for ( int saberNum = 0; saberNum < numSabers; saberNum++ ) { //G_CreateG2AttachedWeaponModel( ent, ent->client->ps.saber[saberNum].model, ent->handRBolt, 0 ); @@ -1659,7 +1691,8 @@ void G_MatchPlayerWeapon( gentity_t *ent ) } else { - G_CreateG2AttachedWeaponModel( ent, weaponData[newWeap].weaponMdl, ent->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel( ent, weaponData[newWeap].worldModel, ent->handRBolt, 0 ); + WP_SaberAddHolsteredG2SaberModels( ent ); } } } @@ -2099,11 +2132,13 @@ gentity_t *G_KickTrace( gentity_t *ent, vec3_t kickDir, float kickDist, vec3_t k { G_Throw( hitEnt, kickDir, kickPush ); } - if ( kickPush >= 150.0f/*75.0f*/ && !Q_irand( 0, 2 ) ) - { + if ( kickPush >= 150.0f/*75.0f*/ + && !Q_irand( 0, 2 ) + && ent->client->ps.forcePowerLevel[FP_SABER_OFFENSE] >= hitEnt->client->ps.forcePowerLevel[FP_SABER_DEFENSE]) + { G_Knockdown( hitEnt, ent, kickDir, 300, qtrue ); } - else + else if (ent->client->ps.forcePowerLevel[FP_SABER_OFFENSE] >= hitEnt->client->ps.forcePowerLevel[FP_SABER_DEFENSE]) { G_Knockdown( hitEnt, ent, kickDir, kickPush, qtrue ); } @@ -2233,7 +2268,8 @@ qboolean G_GrabClient( gentity_t *ent, usercmd_t *ucmd ) int numEnts; const float radius = 100.0f; const float radiusSquared = (radius*radius); - float bestDistSq = (radiusSquared+1.0f), distSq; + float bestDistSq = (radiusSquared + 1.0f), distSq; + int i; vec3_t boltOrg; @@ -2283,21 +2319,55 @@ qboolean G_GrabClient( gentity_t *ent, usercmd_t *ucmd ) continue; } - if ( fabs(radiusEnts[i]->currentOrigin[2]-ent->currentOrigin[2])>8.0f ) - {//have to be close in Z - continue; + if (ent->NPC || (g_debugMelee->integer && ent->s.number < MAX_CLIENTS)) + {//either I'm an kyle_boss (an NPC) or player and using cheats = less precise aim necessary + if (fabs(radiusEnts[i]->currentOrigin[2] - ent->currentOrigin[2]) > 8.0f) + {//have to be close in Z + continue; + } } + else {//more precise aim needed if not cheating or a boss + if (fabs(radiusEnts[i]->currentOrigin[2] - ent->currentOrigin[2]) > 1.0f) + {//have to be close in Z + continue; + } + } + if ( !PM_HasAnimation( radiusEnts[i], BOTH_PLAYER_PA_1 ) ) {//doesn't have matching anims continue; } + if (((radiusEnts[i]->client->ps.weapon == WP_SABER + && radiusEnts[i]->client->ps.forcePowersKnown&(1 << FP_SABER_OFFENSE) + && radiusEnts[i]->client->ps.forcePowersKnown&(1 << FP_SABER_DEFENSE)) //not on saber-wielders who have skillz + || radiusEnts[i]->client->NPC_class == CLASS_REBORN //melee force users? Should we allow kataing? + || radiusEnts[i]->client->NPC_class == CLASS_BOBAFETT //he's too leet + || radiusEnts[i]->NPC->aiFlags&NPCAI_BOSS_CHARACTER) + || ((!Q_stricmp("chewie", radiusEnts[i]->NPC_type) || radiusEnts[i]->client->NPC_class == CLASS_WOOKIEE) + && ent->client->NPC_class != CLASS_WOOKIEE) //if you are a wook you can kata other wooks? + && !(ent->NPC || (g_debugMelee->integer && ent->s.number < MAX_CLIENTS))) + { //FIXME: Some kind of kata resist animation? + continue; + } + distSq = DistanceSquared( radiusEnts[i]->currentOrigin, boltOrg ); - if ( distSq < bestDistSq ) - { - bestDistSq = distSq; - bestEnt = radiusEnts[i]; + if (ent->NPC || (g_debugMelee->integer && ent->s.number < MAX_CLIENTS)) + {//either I'm an kyle_boss (an NPC) or player and using cheats = longer range for grabs + if (distSq < bestDistSq) + { + bestDistSq = distSq; + bestEnt = radiusEnts[i]; + } + } + else { //player without cheats = 0.5x range + if (distSq < (bestDistSq * 0.5)) + { + bestDistSq *= 0.5; + bestDistSq = distSq; + bestEnt = radiusEnts[i]; + } } } @@ -2312,6 +2382,7 @@ qboolean G_GrabClient( gentity_t *ent, usercmd_t *ucmd ) { lockType = LOCK_KYLE_GRAB2; } + WP_SabersCheckLock2( ent, bestEnt, (sabersLockMode_t)lockType ); return qtrue; } @@ -2536,7 +2607,7 @@ qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) if ( (ent->client->ps.forcePowersActive&(1<forwardmove = ucmd->rightmove = ucmd->upmove = 0; - ucmd->buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS); + ucmd->buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS|BUTTON_SABERTHROW); if ( ent->NPC ) { VectorClear( ent->client->ps.moveDir ); @@ -2546,7 +2617,7 @@ qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) if ( ent->client->ps.saberMove == LS_A_LUNGE ) {//can't move during lunge ucmd->rightmove = ucmd->upmove = 0; - if ( ent->client->ps.legsAnimTimer > 500 && (ent->s.number || !player_locked) ) + if ( ent->client->ps.legsAnimTimer > 500 && (ent->s.number || (!player_locked && !PlayerAffectedByStasis())) ) { ucmd->forwardmove = 127; } @@ -2577,7 +2648,7 @@ qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) if ( ent->client->ps.saberMove == LS_A_JUMP_T__B_ ) {//can't move during leap - if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE || (!ent->s.number && player_locked) ) + if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE || (!ent->s.number && (player_locked || PlayerAffectedByStasis())) ) {//hit the ground ucmd->forwardmove = 0; } @@ -2882,7 +2953,7 @@ qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) VectorClear( ent->client->ps.moveDir ); } overridAngles = PM_AdjustAnglesForGrapple( ent, ucmd )?qtrue:overridAngles; - //if ( g_debugMelee->integer ) + if ( g_debugMelee->integer > -1 ) {//actually do some damage during sequence int damage = 0; int dflags = (DAMAGE_NO_KNOCKBACK|DAMAGE_NO_ARMOR|DAMAGE_NO_KILL); @@ -3165,8 +3236,39 @@ qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) float remainingTime = (animLength-elapsedTime); float kickDist = (ent->maxs[0]*1.5f)+STAFF_KICK_RANGE+8.0f;//fudge factor of 8 float kickDist2 = kickDist; - int kickDamage = Q_irand( 3, 8 ); - int kickDamage2 = Q_irand( 3, 8 ); + int kickDamage; //= Q_irand( 3, 8 ); + int kickDamage2; //= Q_irand( 3, 8 ); + + int kickDmgFlux = weaponData[WP_MELEE].velocity; + kickDamage = weaponData[WP_MELEE].splashDamage + Q_irand(-kickDmgFlux, kickDmgFlux); + kickDamage2 = weaponData[WP_MELEE].splashDamage + Q_irand(-kickDmgFlux, kickDmgFlux); + + /* + switch (ent->client->ps.forcePowerLevel[FP_SABER_OFFENSE]) + { + case FORCE_LEVEL_0: + case FORCE_LEVEL_1: + case FORCE_LEVEL_2: + case FORCE_LEVEL_3: + kickDamage = Q_irand(5, 6); + kickDamage2 = Q_irand(5, 6); + } + */ + + switch (ent->client->ps.legsAnim) + { + case BOTH_A7_KICK_S: + case BOTH_A7_KICK_BF: + case BOTH_A7_KICK_RL: + case BOTH_A7_KICK_F_AIR: + case BOTH_A7_KICK_R_AIR: + case BOTH_A7_KICK_L_AIR: + case BOTH_A7_KICK_B_AIR: + kickDamage = weaponData[WP_MELEE].altSplashDamage + Q_irand(-kickDmgFlux, kickDmgFlux); + kickDamage2 = weaponData[WP_MELEE].altSplashDamage + Q_irand(-kickDmgFlux, kickDmgFlux); + } + + int kickPush = Q_flrand( 100.0f, 200.0f );//Q_flrand( 50.0f, 100.0f ); int kickPush2 = Q_flrand( 100.0f, 200.0f );//Q_flrand( 50.0f, 100.0f ); qboolean doKick = qfalse; @@ -3887,6 +3989,8 @@ qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) || (ucmd->buttons&BUTTON_FORCEGRIP) || (ucmd->buttons&BUTTON_FORCE_LIGHTNING) || (ucmd->buttons&BUTTON_FORCE_DRAIN) + || (ucmd->buttons&BUTTON_REPULSE) + || (ucmd->buttons&BUTTON_SABERTHROW) || ucmd->upmove ) {//stop the anim if ( ent->client->ps.legsAnim == BOTH_MEDITATE @@ -3963,7 +4067,7 @@ qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) if ( PM_InRoll( &ent->client->ps ) ) { - if ( ent->s.number >= MAX_CLIENTS || !player_locked ) + if ( ent->s.number >= MAX_CLIENTS || (!player_locked && !PlayerAffectedByStasis()) ) { //FIXME: NPCs should try to face us during this roll, so they roll around us...? PM_CmdForRoll( &ent->client->ps, ucmd ); @@ -3991,7 +4095,7 @@ qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) {//invalid now VectorClear( ent->client->ps.moveDir ); } - if ( ent->s.number || !player_locked ) + if ( ent->s.number || (!player_locked && !PlayerAffectedByStasis()) ) { switch ( ent->client->ps.legsAnim ) { @@ -4023,7 +4127,7 @@ qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd ) {//invalid now VectorClear( ent->client->ps.moveDir ); } - if ( ent->s.number || !player_locked ) + if ( ent->s.number || (!player_locked && !PlayerAffectedByStasis()) ) { if ( ent->client->ps.legsAnimTimer > 450 ) { @@ -4182,33 +4286,7 @@ static int NPC_GetRunSpeed( gentity_t *ent ) if ( ( ent->client == NULL ) || ( ent->NPC == NULL ) ) return 0; -/* - switch ( ent->client->playerTeam ) - { - case TEAM_BORG: - runSpeed = ent->NPC->stats.runSpeed; - runSpeed += BORG_RUN_INCR * (g_spskill->integer%3); - break; - case TEAM_8472: - runSpeed = ent->NPC->stats.runSpeed; - runSpeed += SPECIES_RUN_INCR * (g_spskill->integer%3); - break; - - case TEAM_STASIS: - runSpeed = ent->NPC->stats.runSpeed; - runSpeed += STASIS_RUN_INCR * (g_spskill->integer%3); - break; - - case TEAM_BOTS: - runSpeed = ent->NPC->stats.runSpeed; - break; - - default: - runSpeed = ent->NPC->stats.runSpeed; - break; - } -*/ // team no longer indicates species/race. Use NPC_class to adjust speed for specific npc types switch( ent->client->NPC_class) { @@ -4312,7 +4390,11 @@ void G_CheckClientIdle( gentity_t *ent, usercmd_t *ucmd ) { return; } - if ( !ent->s.number && ( !cg.renderingThirdPerson || cg.zoomMode ) ) + if ( !ent->s.number && (cg_trueguns.integer || (!cg.renderingThirdPerson && (ent->client->ps.weapon == WP_SABER || ent->client->ps.weapon == WP_MELEE)) ) ) + { + return; + } + if ( !ent->s.number && ( /*!cg.renderingThirdPerson ||*/ cg.zoomMode ) ) { if ( ent->client->idleTime < level.time ) { @@ -4455,8 +4537,8 @@ void ClientAlterSpeed(gentity_t *ent, usercmd_t *ucmd, qboolean controlledByPlay { if ( !(ucmd->buttons & BUTTON_USE) ) {//Not leaning - qboolean Flying = (ucmd->upmove && ent->client->moveType == MT_FLYSWIM); - qboolean Climbing = (ucmd->upmove && ent->watertype&CONTENTS_LADDER ); + qboolean Flying = (qboolean)(ucmd->upmove && ent->client->moveType == MT_FLYSWIM); + qboolean Climbing = (qboolean)(ucmd->upmove && ent->watertype&CONTENTS_LADDER ); client->ps.friction = 6; @@ -4476,7 +4558,6 @@ void ClientAlterSpeed(gentity_t *ent, usercmd_t *ucmd, qboolean controlledByPlay if ( ent->NPC->currentSpeed >= 80 && !controlledByPlayer ) {//At higher speeds, need to slow down close to stuff //Slow down as you approach your goal - // if ( ent->NPC->distToGoal < SLOWDOWN_DIST && client->race != RACE_BORG && !(ent->NPC->aiFlags&NPCAI_NO_SLOWDOWN) )//128 if ( ent->NPC->distToGoal < SLOWDOWN_DIST && !(ent->NPC->aiFlags&NPCAI_NO_SLOWDOWN) )//128 { if ( ent->NPC->desiredSpeed > MIN_NPC_SPEED ) @@ -4694,6 +4775,15 @@ void ClientAlterSpeed(gentity_t *ent, usercmd_t *ucmd, qboolean controlledByPlay { client->ps.speed *= 0.75; } + + if ( client->ps.weapon == WP_EMPLACED_GUN && !(client->ps.eFlags & EF_LOCKED_TO_WEAPON) ) + { + if (!(ucmd->buttons & BUTTON_WALKING)) + { + ucmd->buttons |= BUTTON_WALKING; + client->ps.speed *= 0.5; + } + } if ( client->ps.weapon == WP_SABER ) { @@ -4721,6 +4811,14 @@ extern void ForceSeeing(gentity_t *ent); extern void ForceTelepathy(gentity_t *ent); extern void ForceAbsorb(gentity_t *ent); extern void ForceHeal(gentity_t *ent); +extern void ForceDestruction( gentity_t *ent ); +extern void ForceInsanity( gentity_t *ent ); +extern void ForceStasis( gentity_t *ent ); +extern void ForceBlinding( gentity_t *ent ); +extern void ForceDeadlySight( gentity_t *ent ); +extern void ForceRepulse( gentity_t *ent ); +extern void ForceInvulnerability( gentity_t *ent ); + static void ProcessGenericCmd(gentity_t *ent, byte cmd) { switch(cmd) { @@ -4762,6 +4860,27 @@ static void ProcessGenericCmd(gentity_t *ent, byte cmd) case GENCMD_FORCE_SEEING: ForceSeeing(ent); break; + case GENCMD_FORCE_DESTRUCTION: + ForceDestruction(ent); + break; + case GENCMD_FORCE_INSANITY: + ForceInsanity(ent); + break; + case GENCMD_FORCE_STASIS: + ForceStasis(ent); + break; + case GENCMD_FORCE_BLINDING: + ForceBlinding(ent); + break; + case GENCMD_FORCE_DEADLYSIGHT: + ForceDeadlySight(ent); + break; + case GENCMD_FORCE_REPULSE: + ForceRepulse(ent); + break; + case GENCMD_FORCE_INVULNERABILITY: + ForceInvulnerability(ent); + break; } } @@ -4918,6 +5037,7 @@ extern cvar_t *g_skippingcin; } if ( (player_locked + || PlayerAffectedByStasis() || (ent->client->ps.eFlags&EF_FORCE_GRIPPED) || (ent->client->ps.eFlags&EF_FORCE_DRAINED) || (ent->client->ps.legsAnim==BOTH_PLAYER_PA_1) @@ -4925,9 +5045,9 @@ extern cvar_t *g_skippingcin; || (ent->client->ps.legsAnim==BOTH_PLAYER_PA_3)) && ent->client->ps.pm_type < PM_DEAD ) // unless dead {//lock out player control - if ( !player_locked ) + if ( !player_locked && !PlayerAffectedByStasis() ) { - VectorClear( ucmd->angles ); + VectorClearM( ucmd->angles ); } ucmd->forwardmove = 0; ucmd->rightmove = 0; @@ -5241,7 +5361,7 @@ extern cvar_t *g_skippingcin; //FIXME: if global gravity changes this should update everyone's personal gravity... if ( !(ent->svFlags & SVF_CUSTOM_GRAVITY) ) { - if (ent->client->inSpaceIndex) + if (ent->client->inSpaceIndex && ent->client->inSpaceIndex != ENTITYNUM_NONE) { //in space, so no gravity... client->ps.gravity = 0.0f; } @@ -5265,10 +5385,17 @@ extern cvar_t *g_skippingcin; WP_ForcePowersUpdate( ent, ucmd ); //if we have the saber in hand, check for starting a block to reflect shots - if ( ent->s.number < MAX_CLIENTS//player - || ( ent->NPC && G_JediInNormalAI( ent ) ) )//NPC jedi not in a special AI mode + if ((ent->s.number < MAX_CLIENTS//player + || (ent->NPC && G_JediInNormalAI(ent)))) //NPC jedi not in a special AI mode { - WP_SaberStartMissileBlockCheck( ent, ucmd ); + if (ent->client->ps.weapon == WP_SABER) + { + WP_SaberStartMissileBlockCheck(ent, ucmd); + } + else if (NPC_JediClass(ent->client->NPC_class) || !ent->s.number) + {//we may be a non-saber force user + Jedi_MeleeEvasionDefense(ent, ucmd); + } } // Update the position of the saber, and check to see if we're throwing it @@ -5421,7 +5548,7 @@ extern cvar_t *g_skippingcin; pm.trace = gi.trace; pm.pointcontents = gi.pointcontents; pm.debugLevel = g_debugMove->integer; - pm.noFootsteps = 0;//( g_dmflags->integer & DF_NO_FOOTSTEPS ) > 0; + pm.noFootsteps = qfalse;//( g_dmflags->integer & DF_NO_FOOTSTEPS ) > 0; if ( ent->client && ent->NPC ) { @@ -5475,7 +5602,7 @@ extern cvar_t *g_skippingcin; ent->waterlevel = pm.waterlevel; ent->watertype = pm.watertype; - _VectorCopy( ucmd->angles, client->pers.cmd_angles ); + VectorCopyM( ucmd->angles, client->pers.cmd_angles ); // execute client events ClientEvents( ent, oldEventSequence ); diff --git a/code/game/g_breakable.cpp b/code/game/g_breakable.cpp index fccf0f524a..ae7c860e44 100644 --- a/code/game/g_breakable.cpp +++ b/code/game/g_breakable.cpp @@ -114,7 +114,7 @@ void funcBBrushDieGo (gentity_t *self) VectorSubtract( self->absmax, self->absmin, org );// size - numChunks = random() * 6 + 18; + numChunks = Q_flrand(0.0f, 1.0f) * 6 + 18; // This formula really has no logical basis other than the fact that it seemed to be the closest to yielding the results that I wanted. // Volume is length * width * height...then break that volume down based on how many chunks we have @@ -502,7 +502,7 @@ void misc_model_breakable_die( gentity_t *self, gentity_t *inflictor, gentity_t AngleVectors( self->s.apos.trBase, dir, NULL, NULL ); VectorNormalize( dir ); - numChunks = random() * 6 + 20; + numChunks = Q_flrand(0.0f, 1.0f) * 6 + 20; VectorSubtract( self->absmax, self->absmin, dis ); diff --git a/code/game/g_client.cpp b/code/game/g_client.cpp index 4fb143297b..e487317d48 100644 --- a/code/game/g_client.cpp +++ b/code/game/g_client.cpp @@ -49,6 +49,25 @@ extern cvar_t *g_saber2; extern cvar_t *g_saber_color; extern cvar_t *g_saber2_color; extern cvar_t *g_saberDarkSideSaberColor; +extern cvar_t *g_playerCheatPowers; + +extern cvar_t *g_char_head_model; +extern cvar_t *g_char_head_skin; + +extern cvar_t *g_char_color_2_red; +extern cvar_t *g_char_color_2_green; +extern cvar_t *g_char_color_2_blue; + +extern cvar_t *g_hilt_color_red; +extern cvar_t *g_hilt_color_green; +extern cvar_t *g_hilt_color_blue; + +extern cvar_t *g_hilt2_color_red; +extern cvar_t *g_hilt2_color_green; +extern cvar_t *g_hilt2_color_blue; + +extern cvar_t *g_saber_skin[MAX_SABER_PARTS]; +extern cvar_t *g_saber2_skin[MAX_SABER_PARTS]; // g_client.c -- client functions that don't happen every frame @@ -454,7 +473,7 @@ if desired. void ClientUserinfoChanged( int clientNum ) { gentity_t *ent = g_entities + clientNum; gclient_t *client = ent->client; - int health=100, maxHealth=100; + int health = 100; const char *s=NULL; char userinfo[MAX_INFO_STRING]={0}, buf[MAX_INFO_STRING]={0}, sound[MAX_STRING_CHARS]={0}, oldname[34]={0}; @@ -472,12 +491,11 @@ void ClientUserinfoChanged( int clientNum ) { ClientCleanName( s, client->pers.netname, sizeof( client->pers.netname ) ); // set max health - maxHealth = 100; - health = Com_Clampi( 1, 100, atoi( Info_ValueForKey( userinfo, "handicap" ) ) ); + int handicapLimit = 200; //the health AND armor limit for handicap cvar + int handicap = atoi(Info_ValueForKey(userinfo, "handicap")); + + health = Com_Clampi(1, handicapLimit, handicap); //forces handicap value limit client->pers.maxHealth = health; - if ( client->pers.maxHealth < 1 || client->pers.maxHealth > maxHealth ) - client->pers.maxHealth = 100; - client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // sounds Q_strncpyz( sound, Info_ValueForKey (userinfo, "snd"), sizeof( sound ) ); @@ -604,7 +622,7 @@ void ClientBegin( int clientNum, usercmd_t *cmd, SavedGameJustLoaded_e eSavedGam client->pers.connected = CON_CONNECTED; client->pers.teamState.state = TEAM_BEGIN; - _VectorCopy( cmd->angles, client->pers.cmd_angles ); + VectorCopyM( cmd->angles, client->pers.cmd_angles ); memset( &client->ps, 0, sizeof( client->ps ) ); if( gi.Cvar_VariableIntegerValue( "g_clearstats" ) ) @@ -636,29 +654,21 @@ Player_CacheFromPrevLevel void Player_CacheFromPrevLevel(void) { char s[MAX_STRING_CHARS]; + const char *var; int i; gi.Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) ); if (s[0]) // actually this would be safe anyway because of the way sscanf() works, but this is clearer { - int iDummy, bits, ibits; + int iDummy, ibits; - sscanf( s, "%i %i %i %i", + sscanf( s, "%i %i %i", &iDummy,//client->ps.stats[STAT_HEALTH], &iDummy,//client->ps.stats[STAT_ARMOR], - &bits, //client->ps.stats[STAT_WEAPONS] &ibits //client->ps.stats[STAT_ITEMS] ); - for ( i = 1 ; i < 16 ; i++ ) - { - if ( bits & ( 1 << i ) ) - { - RegisterItem( FindItemForWeapon( (weapon_t)i ) ); - } - } - extern gitem_t *FindItemForInventory( int inv ); for ( i = 1 ; i < 16 ; i++ ) @@ -669,6 +679,31 @@ extern gitem_t *FindItemForInventory( int inv ); } } } + + gi.Cvar_VariableStringBuffer( "playerweaps", s, sizeof(s) ); + i=0; + if (s[0]) + { + var = strtok( s, " " ); + while( var != NULL ) + { + /* While there are tokens in "s" */ + if ( atoi(var) > 0 ) + { + if (i == WP_NONE) //don't register! + { + i++; + } + else + { + RegisterItem( FindItemForWeapon( (weapon_t)i++ ) ); + } + } + /* Get next token: */ + var = strtok( NULL, " " ); + } + assert (i==WP_NUM_WEAPONS); + } } /* @@ -696,13 +731,16 @@ static void Player_RestoreFromPrevLevel(gentity_t *ent, SavedGameJustLoaded_e eS if (strlen(s)) // actually this would be safe anyway because of the way sscanf() works, but this is clearer {// |general info |-force powers |-saber 1 |-saber 2 |-general saber + int saber1BladeActive[8]; + int saber2BladeActive[8]; unsigned int saber1BladeColor[8]; unsigned int saber2BladeColor[8]; + int saber1Crystals; + int saber2Crystals; - sscanf( s, "%i %i %i %i %i %i %i %f %f %f %i %i %i %i %i %s %i %i %i %i %i %i %i %i %u %u %u %u %u %u %u %u %s %i %i %i %i %i %i %i %i %u %u %u %u %u %u %u %u %i %i %i %i", + sscanf( s, "%i %i %i %i %i %i %f %f %f %i %i %i %i %i %s %i %i %i %i %i %i %i %i %u %u %u %u %u %u %u %u %i %s %i %i %i %i %i %i %i %i %u %u %u %u %u %u %u %u %i %i %i %i %i", &client->ps.stats[STAT_HEALTH], &client->ps.stats[STAT_ARMOR], - &client->ps.stats[STAT_WEAPONS], &client->ps.stats[STAT_ITEMS], &client->ps.weapon, &client->ps.weaponstate, @@ -718,14 +756,14 @@ static void Player_RestoreFromPrevLevel(gentity_t *ent, SavedGameJustLoaded_e eS &client->ps.forcePowerRegenAmount, //saber 1 data saber0Name, - &client->ps.saber[0].blade[0].active, - &client->ps.saber[0].blade[1].active, - &client->ps.saber[0].blade[2].active, - &client->ps.saber[0].blade[3].active, - &client->ps.saber[0].blade[4].active, - &client->ps.saber[0].blade[5].active, - &client->ps.saber[0].blade[6].active, - &client->ps.saber[0].blade[7].active, + &saber1BladeActive[0], + &saber1BladeActive[1], + &saber1BladeActive[2], + &saber1BladeActive[3], + &saber1BladeActive[4], + &saber1BladeActive[5], + &saber1BladeActive[6], + &saber1BladeActive[7], &saber1BladeColor[0], &saber1BladeColor[1], &saber1BladeColor[2], @@ -734,16 +772,17 @@ static void Player_RestoreFromPrevLevel(gentity_t *ent, SavedGameJustLoaded_e eS &saber1BladeColor[5], &saber1BladeColor[6], &saber1BladeColor[7], + &saber1Crystals, //saber 2 data saber1Name, - &client->ps.saber[1].blade[0].active, - &client->ps.saber[1].blade[1].active, - &client->ps.saber[1].blade[2].active, - &client->ps.saber[1].blade[3].active, - &client->ps.saber[1].blade[4].active, - &client->ps.saber[1].blade[5].active, - &client->ps.saber[1].blade[6].active, - &client->ps.saber[1].blade[7].active, + &saber2BladeActive[0], + &saber2BladeActive[1], + &saber2BladeActive[2], + &saber2BladeActive[3], + &saber2BladeActive[4], + &saber2BladeActive[5], + &saber2BladeActive[6], + &saber2BladeActive[7], &saber2BladeColor[0], &saber2BladeColor[1], &saber2BladeColor[2], @@ -752,6 +791,7 @@ static void Player_RestoreFromPrevLevel(gentity_t *ent, SavedGameJustLoaded_e eS &saber2BladeColor[5], &saber2BladeColor[6], &saber2BladeColor[7], + &saber2Crystals, //general saber data &client->ps.saberStylesKnown, &client->ps.saberAnimLevel, @@ -760,9 +800,14 @@ static void Player_RestoreFromPrevLevel(gentity_t *ent, SavedGameJustLoaded_e eS ); for (int j = 0; j < 8; j++) { + client->ps.saber[0].blade[j].active = saber1BladeActive[j] ? qtrue : qfalse; client->ps.saber[0].blade[j].color = (saber_colors_t)saber1BladeColor[j]; + client->ps.saber[1].blade[j].active = saber2BladeActive[j] ? qtrue : qfalse; client->ps.saber[1].blade[j].color = (saber_colors_t)saber2BladeColor[j]; } + + client->ps.saber[0].crystals = (saber_crystals_t)saber1Crystals; + client->ps.saber[1].crystals = (saber_crystals_t)saber2Crystals; ent->health = client->ps.stats[STAT_HEALTH]; @@ -797,6 +842,19 @@ static void Player_RestoreFromPrevLevel(gentity_t *ent, SavedGameJustLoaded_e eS // // SetClientViewAngle( ent, ent->client->ps.viewangles); + //weapons + gi.Cvar_VariableStringBuffer( "playerweaps", s, sizeof(s) ); + i=0; + var = strtok( s, " " ); + while( var != NULL ) + { + /* While there are tokens in "s" */ + client->ps.weapons[i++] = atoi(var); + /* Get next token: */ + var = strtok( NULL, " " ); + } + assert (i==WP_NUM_WEAPONS); + //ammo gi.Cvar_VariableStringBuffer( "playerammo", s, sizeof(s) ); i=0; @@ -886,6 +944,36 @@ static void G_SetSkin( gentity_t *ent ) ent->client->renderInfo.customRGBA[2] = g_char_color_blue->integer; ent->client->renderInfo.customRGBA[3] = 255; } + + if ( g_char_color_2_red->integer + || g_char_color_2_green->integer + || g_char_color_2_blue->integer ) + { + ent->client->renderInfo.newCustomRGBA[TINT_NEW_ENT][0] = g_char_color_2_red->integer; + ent->client->renderInfo.newCustomRGBA[TINT_NEW_ENT][1] = g_char_color_2_green->integer; + ent->client->renderInfo.newCustomRGBA[TINT_NEW_ENT][2] = g_char_color_2_blue->integer; + ent->client->renderInfo.newCustomRGBA[TINT_NEW_ENT][3] = 255; + } + + if ( g_hilt_color_red->integer + || g_hilt_color_green->integer + || g_hilt_color_blue->integer ) + { + ent->client->renderInfo.newCustomRGBA[TINT_HILT1][0] = g_hilt_color_red->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT1][1] = g_hilt_color_green->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT1][2] = g_hilt_color_blue->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT1][3] = 255; + } + + if ( g_hilt2_color_red->integer + || g_hilt2_color_green->integer + || g_hilt2_color_blue->integer ) + { + ent->client->renderInfo.newCustomRGBA[TINT_HILT2][0] = g_hilt2_color_red->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT2][1] = g_hilt2_color_green->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT2][2] = g_hilt2_color_blue->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT2][3] = 255; + } } qboolean G_StandardHumanoid( gentity_t *self ) @@ -1187,12 +1275,14 @@ qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const ch ent->footLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*l_leg_foot"); ent->footRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*r_leg_foot"); if ( ent->client->NPC_class == CLASS_BOBAFETT - || ent->client->NPC_class == CLASS_ROCKETTROOPER ) + || ent->client->NPC_class == CLASS_ROCKETTROOPER + || ent->client->NPC_class == CLASS_MANDA) {//get jet bolts ent->genericBolt1 = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*jet1" ); ent->genericBolt2 = gi.G2API_AddBolt( &ent->ghoul2[ent->playerModel], "*jet2" ); } - if ( ent->client->NPC_class == CLASS_BOBAFETT ) + if ( ent->client->NPC_class == CLASS_BOBAFETT + || ent->client->NPC_class == CLASS_MANDA) {//get the flamethrower bolt ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flamethrower"); } @@ -1286,6 +1376,16 @@ qboolean G_SetG2PlayerModelInfo( gentity_t *ent, const char *modelName, const ch { ent->handRBolt = ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_f1"); } + else if ( !Q_stricmp( "galak_mech", modelName )) + { + ent->genericBolt1 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*antenna_effect"); + ent->headBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*head_eyes"); + ent->torsoBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "torso"); + ent->crotchBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "hips"); + ent->handRBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flasha"); + ent->genericBolt3 = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flashb"); + ent->handLBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->playerModel], "*flashc"); + } else if ( !Q_stricmp( "rancor", modelName ) || !Q_stricmp( "mutant_rancor", modelName )) { @@ -1795,6 +1895,15 @@ void G_SetG2PlayerModel( gentity_t * const ent, const char *modelName, const cha Ghoul2 Insert End */ +void G_RemoveHeadModel( gentity_t *ent ) +{ + if ( ent->headModel >= 0 && ent->headModel != ent->playerModel && ent->ghoul2.size() ) + { + gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->headModel ); + ent->headModel = -1; + } +} + void G_RemovePlayerModel( gentity_t *ent ) { if ( ent->playerModel >= 0 && ent->ghoul2.size() ) @@ -1821,6 +1930,23 @@ void G_RemoveWeaponModels( gentity_t *ent ) } } +void G_RemoveHolsterModels( gentity_t *ent ) +{ + if ( ent->ghoul2.size() ) + { + if ( ent->holsterModel[0] > 0 ) + { + gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->holsterModel[0] ); + ent->holsterModel[0] = -1; + } + if ( ent->holsterModel[1] > 0 ) + { + gi.G2API_RemoveGhoul2Model( ent->ghoul2, ent->holsterModel[1] ); + ent->holsterModel[1] = -1; + } + } +} + void G_AddWeaponModels( gentity_t *ent ) { if ( !ent || !ent->client ) @@ -1832,10 +1958,19 @@ void G_AddWeaponModels( gentity_t *ent ) if ( ent->client->ps.weapon == WP_SABER ) { WP_SaberAddG2SaberModels( ent ); + G_RemoveHolsterModels( ent ); } else if ( ent->client->ps.weapon != WP_NONE ) { - G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 ); + if ( ent->client->ps.weapon == WP_EMPLACED_GUN && !(ent->client->ps.eFlags & EF_LOCKED_TO_WEAPON) ) + { + G_CreateG2AttachedWeaponModel( ent, "models/map_objects/hoth/eweb_model.glm", ent->handRBolt, 0 ); + } + else + { + G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].worldModel, ent->handRBolt, 0 ); + } + WP_SaberAddHolsteredG2SaberModels( ent ); } } } @@ -1843,6 +1978,7 @@ void G_AddWeaponModels( gentity_t *ent ) extern saber_colors_t TranslateSaberColor( const char *name ); extern void WP_RemoveSaber( gentity_t *ent, int saberNum ); void G_ChangePlayerModel( gentity_t *ent, const char *newModel ); +void G_ChangeHeadModel( gentity_t *ent, const char *newModel ); void G_SetSabersFromCVars( gentity_t *ent ) { if ( g_saber->string @@ -1859,6 +1995,39 @@ void G_SetSabersFromCVars( gentity_t *ent ) { ent->client->ps.saberStylesKnown |= ent->client->ps.saber[0].singleBladeStyle; } + //Custom saber stuff! + if (ent->client->ps.saber[0].name && ent->client->ps.saber[0].name[0] && Q_stristr(ent->client->ps.saber[0].name, "saberbuilder") && ent->client->ps.saber[0].model) + { + char skinRoot[MAX_QPATH] = {0}; + Q_strncpyz(skinRoot, ent->client->ps.saber[0].model, MAX_QPATH); + int l = strlen(skinRoot); + while (l > 0 && skinRoot[l] != '/') + { //parse back to first / + l--; + } + + if (skinRoot[l] == '/') + { + l++; + skinRoot[l] = 0; + + Q_strcat(skinRoot, MAX_QPATH, "|_"); + + for (int j = 0; j < MAX_SABER_PARTS; j++) + { + Q_strcat(skinRoot, MAX_QPATH, "|"); + if (g_saber_skin[j] && g_saber_skin[j]->string && g_saber_skin[j]->string[0]) + { + Q_strcat(skinRoot, MAX_QPATH, g_saber_skin[j]->string); + } + } + + if(ent->client->ps.saber[0].skin && gi.bIsFromZone(ent->client->ps.saber[0].skin, TAG_G_ALLOC) ) { + gi.Free(ent->client->ps.saber[0].skin); + } + ent->client->ps.saber[0].skin = G_NewString(skinRoot); + } + } } if ( player @@ -1896,6 +2065,41 @@ void G_SetSabersFromCVars( gentity_t *ent ) { ent->client->ps.saberStylesKnown |= ent->client->ps.saber[1].singleBladeStyle; } + + //Custom saber stuff! + if (ent->client->ps.saber[1].name && ent->client->ps.saber[1].name[0] && Q_stristr(ent->client->ps.saber[1].name, "saberbuilder") && ent->client->ps.saber[1].model) + { + char skinRoot[MAX_QPATH] = {0}; + Q_strncpyz(skinRoot, ent->client->ps.saber[1].model, MAX_QPATH); + int l = strlen(skinRoot); + while (l > 0 && skinRoot[l] != '/') + { //parse back to first / + l--; + } + + if (skinRoot[l] == '/') + { + l++; + skinRoot[l] = 0; + + Q_strcat(skinRoot, MAX_QPATH, "|_"); + + for (int j = 0; j < MAX_SABER_PARTS; j++) + { + Q_strcat(skinRoot, MAX_QPATH, "|"); + if (g_saber2_skin[j] && g_saber2_skin[j]->string && g_saber2_skin[j]->string[0]) + { + Q_strcat(skinRoot, MAX_QPATH, g_saber2_skin[j]->string); + } + } + + if(ent->client->ps.saber[1].skin && gi.bIsFromZone(ent->client->ps.saber[1].skin, TAG_G_ALLOC) ) { + gi.Free(ent->client->ps.saber[1].skin); + } + ent->client->ps.saber[1].skin = G_NewString(skinRoot); + } + } + if ( (ent->client->ps.saber[1].saberFlags&SFL_TWO_HANDED) ) {//tsk tsk, can't use a twoHanded saber as second saber WP_RemoveSaber( ent, 1 ); @@ -1935,6 +2139,11 @@ void G_InitPlayerFromCvars( gentity_t *ent ) else G_ChangePlayerModel( ent, va("%s|%s|%s|%s", g_char_model->string, g_char_skin_head->string, g_char_skin_torso->string, g_char_skin_legs->string) ); + if (g_char_head_model->string && g_char_head_model->string[0]) + { + G_ChangeHeadModel( ent, va("%s|%s", g_char_head_model->string, g_char_head_skin->string) ); + } + //FIXME: parse these 2 from some cvar or require playermodel to be in a *.npc? if( ent->NPC_type && gi.bIsFromZone(ent->NPC_type, TAG_G_ALLOC) ) { gi.Free(ent->NPC_type); @@ -1975,6 +2184,198 @@ void G_InitPlayerFromCvars( gentity_t *ent ) ent->client->renderInfo.customRGBA[2] = g_char_color_blue->integer; ent->client->renderInfo.customRGBA[3] = 255; } + + if ( g_char_color_2_red->integer + || g_char_color_2_green->integer + || g_char_color_2_blue->integer ) + { + ent->client->renderInfo.newCustomRGBA[TINT_NEW_ENT][0] = g_char_color_2_red->integer; + ent->client->renderInfo.newCustomRGBA[TINT_NEW_ENT][1] = g_char_color_2_green->integer; + ent->client->renderInfo.newCustomRGBA[TINT_NEW_ENT][2] = g_char_color_2_blue->integer; + ent->client->renderInfo.newCustomRGBA[TINT_NEW_ENT][3] = 255; + } + + if ( g_hilt_color_red->integer + || g_hilt_color_green->integer + || g_hilt_color_blue->integer ) + { + ent->client->renderInfo.newCustomRGBA[TINT_HILT1][0] = g_hilt_color_red->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT1][1] = g_hilt_color_green->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT1][2] = g_hilt_color_blue->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT1][3] = 255; + } + + if ( g_hilt2_color_red->integer + || g_hilt2_color_green->integer + || g_hilt2_color_blue->integer ) + { + ent->client->renderInfo.newCustomRGBA[TINT_HILT2][0] = g_hilt2_color_red->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT2][1] = g_hilt2_color_green->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT2][2] = g_hilt2_color_blue->integer; + ent->client->renderInfo.newCustomRGBA[TINT_HILT2][3] = 255; + } +} + +void G_SetHeadSkin( gentity_t *ent ) +{ + + if (g_char_head_model->string && g_char_head_model->string[0]) + { + if (g_char_head_skin->string && g_char_head_skin->string[0]) + { + G_ChangeHeadModel( ent, va("%s|%s", g_char_head_model->string, g_char_head_skin->string) ); + } + else + { + G_ChangeHeadModel( ent, va("%s|default", g_char_head_model->string) ); + } + } + +/* char skinName[MAX_QPATH]; + if (g_char_head_model->string && g_char_head_model->string[0]) + { + if (g_char_head_skin->string && g_char_head_skin->string[0]) + { + Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_%s.skin", g_char_head_model->string, g_char_head_skin->string ); + } + else + { + Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_default.skin", g_char_head_model->string ); + } + + } + + // lets see if it's out there + int skin = gi.RE_RegisterSkin( skinName ); + + //What if it's a 3-parter or one part of a 3-parter? + if (!skin && g_char_head_skin->string && g_char_head_skin->string[0]) + { + if (strchr(g_char_head_skin->string, '|')) + { + Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/|%s", g_char_head_model->string, g_char_head_skin->string ); + } + else + { + Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/%s.skin", g_char_head_model->string, g_char_head_skin->string ); + } + skin = gi.RE_RegisterSkin( skinName ); + } + + if ( skin ) + {//what if this returns 0 because *one* part of a multi-skin didn't load? + // put it in the config strings + // and set the ghoul2 model to use it + gi.G2API_SetSkin( &ent->ghoul2[ent->headModel], G_SkinIndex( skinName ), skin ); + }*/ +} + +void G_SetHeadSurfaceOnOff( gentity_t *ent ) +{ + if (g_char_head_model->string && g_char_head_model->string[0]) + { + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "head", G2SURFACEFLAG_NODESCENDANTS); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->playerModel], "torso_cap_head", 0x00); + } +} + +void G_ChangeHeadModel( gentity_t *ent, const char *newModel ) +{ + if ( !ent || !ent->client || !newModel ) + { + return; + } + + G_RemoveHeadModel( ent ); + + char name[MAX_QPATH]; + strcpy(name, newModel); + char *p = strchr(name, '|'); + + if ( p ) + { + *p = 0; + p++; + } + + char skinName[MAX_QPATH]; + vec3_t angles = {0,0,0}; + + if ( p && p[0] ) + { + Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_%s.skin", name, p ); + } + else + { + Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/model_default.skin", name ); + } + + int skin = gi.RE_RegisterSkin( skinName ); + + //What if it's a 3-parter or one part of a 3-parter? + if (!skin && p && p[0]) + { + if (strchr(p, '|')) + { + Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/|%s", name, p ); + } + else + { + Com_sprintf( skinName, sizeof( skinName ), "models/players/%s/%s.skin", name, p ); + } + skin = gi.RE_RegisterSkin( skinName ); + } + + if (!skin) { + return; + } + + //now generate the ghoul2 model this client should be. + //NOTE: it still loads the default skin's tga's because they're referenced in the .glm. + ent->headModel = gi.G2API_InitGhoul2Model( ent->ghoul2, va("models/players/%s/model.glm", name), G_ModelIndex( va("models/players/%s/model.glm", name) ), G_SkinIndex( skinName ), NULL_HANDLE, 0, 0 ); + + if (ent->headModel == -1) + { + return; + } + + gi.G2API_SetSkin( &ent->ghoul2[ent->headModel], G_SkinIndex( skinName ), skin );//this is going to set the surfs on/off matching the skin file + +// int getBolt = gi.G2API_AddBolt(&ent->ghoul2[ent->headModel], "cranium"); + gi.G2API_SetRootSurface(ent->ghoul2, ent->headModel, "head"); +// gi.G2API_AttachG2Model(&ent->ghoul2[ent->headModel], &ent->ghoul2[ent->playerModel], ent->motionBone, ent->playerModel); + + ent->headRootBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->headModel], "model_root", qtrue ); + + ent->headMotionBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->headModel], "Motion", qtrue ); + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->headModel], ent->headMotionBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_Z, NEGATIVE_X, NEGATIVE_Y, NULL, 0, 0 ); + + ent->headLowerLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->headModel], "lower_lumbar", qtrue ); + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->headModel], ent->headLowerLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 ); + + ent->headFaceBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->headModel], "face", qtrue ); + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->headModel], ent->headFaceBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 ); + + ent->headHipsBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->headModel], "pelvis", qtrue ); + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->headModel], ent->headHipsBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 ); + + ent->headUpperLumbarBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->headModel], "upper_lumbar", qtrue ); + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->headModel], ent->headUpperLumbarBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 ); + + ent->headCraniumBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->headModel], "cranium", qtrue ); + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->headModel], ent->headCraniumBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 ); + + ent->headCervicalBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->headModel], "cervical", qtrue ); + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->headModel], ent->headCervicalBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 ); + + ent->headThoracicBone = gi.G2API_GetBoneIndex( &ent->ghoul2[ent->headModel], "thoracic", qtrue ); + gi.G2API_SetBoneAnglesIndex( &ent->ghoul2[ent->headModel], ent->headThoracicBone, angles, BONE_ANGLES_POSTMULT, POSITIVE_X, NEGATIVE_Y, NEGATIVE_Z, NULL, 0, 0 ); + + +// gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->headModel], "hips", G2SURFACEFLAG_NODESCENDANTS); +// gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->headModel], "head", 0x0); + G_SetHeadSurfaceOnOff( ent ); + gi.G2API_SetSurfaceOnOff( &ent->ghoul2[ent->headModel], "head_cap_torso", 0x00);//show caps so don't get such bad seams } void G_ChangePlayerModel( gentity_t *ent, const char *newModel ) @@ -1985,6 +2386,8 @@ void G_ChangePlayerModel( gentity_t *ent, const char *newModel ) } G_RemovePlayerModel( ent ); + G_RemoveHeadModel( ent ); + if ( Q_stricmp( "player", newModel ) == 0 ) { G_InitPlayerFromCvars( ent ); @@ -2024,7 +2427,8 @@ void G_ChangePlayerModel( gentity_t *ent, const char *newModel ) ClientUserinfoChanged( ent->s.number ); //Ugh, kind of a hack for now: if ( ent->client->NPC_class == CLASS_BOBAFETT - || ent->client->NPC_class == CLASS_ROCKETTROOPER ) + || ent->client->NPC_class == CLASS_ROCKETTROOPER + || ent->client->NPC_class == CLASS_MANDA) { //FIXME: remove saber, too? Boba_Precache(); // player as boba? @@ -2161,7 +2565,7 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded Q_stricmp( ent->NPC_type, "player" ) ) { // FIXME: game doesn't like it when you pass ent->NPC_type into this func. Insert all kinds of noises here --eez - char bleh[1024]; + char bleh[MAX_SPAWN_VARS_CHARS]; Q_strncpyz(bleh, ent->NPC_type, sizeof(bleh)); G_ChangePlayerModel( ent, bleh ); @@ -2170,6 +2574,8 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded { G_LoadAnimFileSet( ent, ent->NPC_type ); G_SetSkin( ent ); + G_SetHeadSurfaceOnOff( ent ); + G_SetHeadSkin( ent ); } //setup sabers @@ -2229,7 +2635,11 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded } ent->classname = "player"; ent->targetname = ent->script_targetname = "player"; - if ( ent->client->NPC_class == CLASS_NONE ) + if (g_playerCheatPowers->integer) + { + ent->client->NPC_class = CLASS_LUKE; + } + else if ( ent->client->NPC_class == CLASS_NONE ) { ent->client->NPC_class = CLASS_PLAYER; } @@ -2256,27 +2666,31 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded // give default weapons //these are precached in g_items, ClearRegisteredItems() - client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE ); + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + client->ps.weapons[i] = 0; + } + client->ps.weapons[WP_NONE] = 1; //client->ps.inventory[INV_ELECTROBINOCULARS] = 1; //ent->client->ps.inventory[INV_BACTA_CANISTER] = 1; // give EITHER the saber or the stun baton..never both if ( spawnPoint->spawnflags & 32 ) // STUN_BATON { - client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_STUN_BATON ); + client->ps.weapons[WP_STUN_BATON] = 1; client->ps.weapon = WP_STUN_BATON; } else { // give the saber because most test maps will not have the STUN BATON flag set - client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER ); //this is precached in SP_info_player_deathmatch + client->ps.weapons[WP_SABER] = 1; //this is precached in SP_info_player_deathmatch client->ps.weapon = WP_SABER; } // force the base weapon up client->ps.weaponstate = WEAPON_READY; - for ( i = FIRST_WEAPON; i < MAX_PLAYER_WEAPONS; i++ ) // don't give ammo for explosives + for ( i = FIRST_WEAPON; i < WP_NUM_WEAPONS; i++ ) // don't give ammo for explosives { - if ( (client->ps.stats[STAT_WEAPONS]&(1<ps.weapons[i]) ) {//if starting with this weapon, gimme max ammo for it client->ps.ammo[weaponData[i].ammoIndex] = ammoData[weaponData[i].ammoIndex].max; } @@ -2360,9 +2774,8 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded Q_stricmp( ent->NPC_type, "player" ) ) { // FIXME: game doesn't like it when you pass ent->NPC_type into this func. Insert all kinds of noises here --eez - char bleh[1024]; - strncpy(bleh, ent->NPC_type, strlen(ent->NPC_type)); - bleh[strlen(ent->NPC_type)] = '\0'; + char bleh[MAX_SPAWN_VARS_CHARS]; + Q_strncpyz(bleh, ent->NPC_type, sizeof(bleh)); G_ChangePlayerModel( ent, bleh ); } @@ -2370,6 +2783,8 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded { G_LoadAnimFileSet( ent, ent->NPC_type ); G_SetSkin( ent ); + G_SetHeadSurfaceOnOff( ent ); + G_SetHeadSkin( ent ); } G_ReloadSaberData( ent ); //force power levels should already be set @@ -2382,7 +2797,7 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded client->ps.commandTime = level.time - 100; ucmd = client->pers.lastCommand; ucmd.serverTime = level.time; - _VectorCopy( client->pers.cmd_angles, ucmd.angles ); + VectorCopyM( client->pers.cmd_angles, ucmd.angles ); ucmd.weapon = client->ps.weapon; // client think calls Pmove which sets the client->ps.weapon to ucmd.weapon, so ... ent->client->ps.groundEntityNum = ENTITYNUM_NONE; ClientThink( ent-g_entities, &ucmd ); @@ -2403,27 +2818,40 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded if ( spawnPoint->spawnflags & 64 ) //NOWEAPON {//player starts with absolutely no weapons - ent->client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE ); + for ( int i = 0; i < MAX_WEAPONS; i++ ) + { + ent->client->ps.weapons[i] = 0; + } + ent->client->ps.weapons[WP_NONE] = 1; ent->client->ps.ammo[weaponData[WP_NONE].ammoIndex] = 32000; ent->client->ps.weapon = WP_NONE; ent->client->ps.weaponstate = WEAPON_READY; ent->client->ps.dualSabers = qfalse; } - if ( ent->client->ps.stats[STAT_WEAPONS] & ( 1 << WP_SABER ) ) + if ( ent->client->ps.weapons[WP_SABER] ) {//set up so has lightsaber WP_SaberInitBladeData( ent ); if ( (ent->weaponModel[0] <= 0 || (ent->weaponModel[1]<=0&&ent->client->ps.dualSabers)) //one or both of the saber models is not initialized && ent->client->ps.weapon == WP_SABER )//current weapon is saber {//add the proper models WP_SaberAddG2SaberModels( ent ); + G_RemoveHolsterModels( ent ); } } if ( ent->weaponModel[0] == -1 && ent->client->ps.weapon != WP_NONE ) { - G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 ); + if ( ent->client->ps.weapon == WP_EMPLACED_GUN && !(ent->client->ps.eFlags & EF_LOCKED_TO_WEAPON) ) + { + G_CreateG2AttachedWeaponModel( ent, "models/map_objects/hoth/eweb_model.glm", ent->handRBolt, 0 ); + } + else + { + G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].worldModel, ent->handRBolt, 0 ); + } + WP_SaberAddHolsteredG2SaberModels( ent ); } - + { // fire the targets of the spawn point G_UseTargets( spawnPoint, ent ); @@ -2457,7 +2885,7 @@ qboolean ClientSpawn(gentity_t *ent, SavedGameJustLoaded_e eSavedGameJustLoaded G_CheckPlayerDarkSide(); } - if ( (ent->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SABER]) && !ent->client->ps.saberStylesKnown ) {//um, if you have a saber, you need at least 1 style to use it with... ent->client->ps.saberStylesKnown |= (1<client->ps.stats[STAT_WEAPONS] = (1 << (WP_MELEE)) - ( 1 << WP_NONE ); + for ( int i = 0; i < WP_MELEE; i++ ) + { + ent->client->ps.weapons[i] = 1; + } if ( !give_all ) return; } if ( !give_all && !Q_stricmp( name, "weaponnum" ) ) { - ent->client->ps.stats[STAT_WEAPONS] |= (1 << atoi( args )); + ent->client->ps.weapons[ atoi(args) ] = 1; return; } if ( !give_all && !Q_stricmp( name, "eweaps" ) ) //for developing, gives you all the weapons, including enemy { - ent->client->ps.stats[STAT_WEAPONS] = (unsigned)(1 << WP_NUM_WEAPONS) - ( 1 << WP_NONE ); // NOTE: this wasn't giving the last weapon in the list + for ( int i = 0; i < WP_NUM_WEAPONS; i++ ) + { + ent->client->ps.weapons[i] = 1; + } return; } @@ -611,8 +624,8 @@ UserSpawn */ extern qboolean G_CallSpawn( gentity_t *ent ); - -void UserSpawn( gentity_t *ent, const char *name ) +extern void G_ParseField( const char *key, const char *value, gentity_t *ent ); +void UserSpawn( gentity_t *ent, const char *name, int numargs ) { vec3_t origin; vec3_t vf; @@ -621,6 +634,14 @@ void UserSpawn( gentity_t *ent, const char *name ) //Spawn the ent ent2 = G_Spawn(); + + int numSpawnVars = numargs / 2; + + //entity keys now supported + for ( int i = 0 ; i < numSpawnVars ; i++ ) { + G_ParseField( gi.argv(2 + i * 2), gi.argv(3 + i * 2), ent2 ); + } + ent2->classname = G_NewString( name ); //TODO: This should ultimately make sure this is a safe spawn! @@ -654,13 +675,23 @@ Cmd_Spawn void Cmd_Spawn( gentity_t *ent ) { + int numArgs; char *name; - - name = ConcatArgs( 1 ); - + + numArgs = gi.argc(); + + if (numArgs > 1) + { + name = gi.argv(1); + } + else + { + name = ""; + } + gi.SendServerCommand( ent-g_entities, "print \"Spawning '%s'\n\"", name ); - UserSpawn( ent, name ); + UserSpawn( ent, name, numArgs - 2 ); } /* @@ -727,7 +758,7 @@ void Cmd_SetObjective_f( gentity_t *ent ) displayStatus = atoi(gi.argv(2)); status = atoi(gi.argv(3)); - ent->client->sess.mission_objectives[objectiveI].display = displayStatus; + ent->client->sess.mission_objectives[objectiveI].display = (qboolean)(displayStatus != 0); ent->client->sess.mission_objectives[objectiveI].status = status; G_CheckPlayerDarkSide(); } @@ -1102,6 +1133,7 @@ void G_SetTauntAnim( gentity_t *ent, int taunt ) case SS_MEDIUM: case SS_STRONG: case SS_DESANN: + case SS_KATARN: anim = BOTH_ENGAGETAUNT; break; case SS_DUAL: @@ -1196,6 +1228,7 @@ void G_SetTauntAnim( gentity_t *ent, int taunt ) { case SS_FAST: case SS_TAVION: + case SS_KATARN: anim = BOTH_SHOWOFF_FAST; break; case SS_MEDIUM: @@ -1233,6 +1266,7 @@ void G_SetTauntAnim( gentity_t *ent, int taunt ) { case SS_FAST: case SS_TAVION: + case SS_KATARN: anim = BOTH_VICTORY_FAST; break; case SS_MEDIUM: @@ -1346,7 +1380,7 @@ void Cmd_SaberDrop_f( gentity_t *ent, int saberNum ) && ent->weaponModel[1] <= 0 ) {//no sabers left! //remove saber from inventory - ent->client->ps.stats[STAT_WEAPONS] &= ~(1<client->ps.weapons[WP_SABER] = 0; //change weapons if ( ent->s.number < MAX_CLIENTS ) {//player @@ -1462,6 +1496,36 @@ void ClientCommand( int clientNum ) { ent = G_GetSelfForPlayerCmd(); ForceSeeing(ent); } + else if (Q_stricmp (cmd, "force_destruction") == 0) + { + ent = G_GetSelfForPlayerCmd(); + ForceDestruction(ent); + } + else if (Q_stricmp (cmd, "force_insanity") == 0) + { + ent = G_GetSelfForPlayerCmd(); + ForceInsanity(ent); + } + else if (Q_stricmp (cmd, "force_stasis") == 0) + { + ent = G_GetSelfForPlayerCmd(); + ForceStasis(ent); + } + else if (Q_stricmp (cmd, "force_blinding") == 0) + { + ent = G_GetSelfForPlayerCmd(); + ForceBlinding(ent); + } + else if (Q_stricmp (cmd, "force_deadlysight") == 0) + { + ent = G_GetSelfForPlayerCmd(); + ForceDeadlySight(ent); + } + else if (Q_stricmp (cmd, "force_invulnerability") == 0) + { + ent = G_GetSelfForPlayerCmd(); + ForceInvulnerability(ent); + } else if (Q_stricmp (cmd, "addsaberstyle") == 0) { ent = G_GetSelfForPlayerCmd(); @@ -1472,7 +1536,7 @@ void ClientCommand( int clientNum ) { if ( gi.argc() < 2 ) { gi.SendServerCommand( ent-g_entities, va("print \"usage: addsaberstyle \n\"")); - gi.SendServerCommand( ent-g_entities, va("print \"Valid styles: SS_FAST, SS_MEDIUM, SS_STRONG, SS_DESANN, SS_TAVION, SS_DUAL and SS_STAFF\n\"")); + gi.SendServerCommand( ent-g_entities, va("print \"Valid styles: SS_FAST, SS_MEDIUM, SS_STRONG, SS_DESANN, SS_TAVION, SS_KATARN, SS_DUAL and SS_STAFF\n\"")); return; } @@ -1492,7 +1556,7 @@ void ClientCommand( int clientNum ) { if ( gi.argc() < 2 ) { gi.SendServerCommand( ent-g_entities, va("print \"usage: setsaberstyle \n\"")); - gi.SendServerCommand( ent-g_entities, va("print \"Valid styles: SS_FAST, SS_MEDIUM, SS_STRONG, SS_DESANN, SS_TAVION, SS_DUAL and SS_STAFF\n\"")); + gi.SendServerCommand( ent-g_entities, va("print \"Valid styles: SS_FAST, SS_MEDIUM, SS_STRONG, SS_DESANN, SS_TAVION, SS_KATARN, SS_DUAL and SS_STAFF\n\"")); return; } @@ -1612,6 +1676,7 @@ void ClientCommand( int clientNum ) { Cmd_SaberDrop_f( ent, saberNum ); } } + else if (TryWorkshopCommand(ent)) {} else { gi.SendServerCommand( clientNum, va("print \"Unknown command %s\n\"", cmd ) ); diff --git a/code/game/g_combat.cpp b/code/game/g_combat.cpp index 73fefa2ddc..6bad8b8af0 100644 --- a/code/game/g_combat.cpp +++ b/code/game/g_combat.cpp @@ -50,6 +50,7 @@ extern cvar_t *d_slowmodeath; extern gentity_t *player; extern cvar_t *debug_subdivision; extern cvar_t *g_dismemberProbabilities; +extern cvar_t *g_forceNewPowers; gentity_t *g_lastClientDamaged; @@ -99,6 +100,9 @@ static qboolean G_Dismemberable( gentity_t *self, int hitLoc ); extern gitem_t *FindItemForAmmo( ammo_t ammo ); extern void WP_RemoveSaber( gentity_t *ent, int saberNum ); +qboolean blasterDamage(int mod); +qboolean heavyDamage(int mod); + qboolean G_GetRootSurfNameWithVariant( gentity_t *ent, const char *rootSurfName, char *returnSurfName, int returnSize ); /* @@ -186,7 +190,7 @@ gentity_t *TossClientItems( gentity_t *self ) || weapon == WP_MELEE ) {//never drop these } - else if ( weapon > WP_SABER && weapon <= MAX_PLAYER_WEAPONS )//&& self->client->ps.ammo[ weaponData[weapon].ammoIndex ] + else if ( weapon > WP_SABER && weapon < WP_NUM_WEAPONS && playerUsableWeapons[weapon] )//&& self->client->ps.ammo[ weaponData[weapon].ammoIndex ] { self->s.weapon = WP_NONE; @@ -257,6 +261,27 @@ gentity_t *TossClientItems( gentity_t *self ) case WP_STUN_BATON: dropped->count = 20; break; + case WP_TUSKEN_RIFLE: + dropped->count = 20; + break; + case WP_NOGHRI_STICK: + dropped->count = 15; + break; + case WP_E5_CARBINE: + dropped->count = 15; + break; + case WP_DC15S_CARBINE: + dropped->count = 15; + break; + case WP_Z6_ROTARY: + dropped->count = 20; + break; + case WP_DC15A_RIFLE: + dropped->count = 20; + break; + case WP_SONIC_BLASTER: + dropped->count = 20; + break; default: dropped->count = 0; break; @@ -476,7 +501,7 @@ qboolean OnSameTeam( gentity_t *ent1, gentity_t *ent2 ) // return qtrue; // } - return ( ent1->client->playerTeam == ent2->client->playerTeam ); + return (qboolean)( ent1->client->playerTeam == ent2->client->playerTeam ); } @@ -747,11 +772,6 @@ void G_SetMissionStatusText( gentity_t *attacker, int mod ) {//crushed statusTextIndex = STAT_JUDGEMENTMUCHDESIRED; } - // borg no longer exist -// else if ( attacker && attacker->client && attacker->client->playerTeam == TEAM_BORG ) -// {//assimilated -// statusTextIndex = Q_irand( IGT_RESISTANCEISFUTILE, IGT_NAMEIS8OF12 ); -// } else if ( attacker && Q_stricmp( "trigger_hurt", attacker->classname ) == 0 ) {//Killed by something that should have been clearly dangerous // statusTextIndex = Q_irand( IGT_JUDGEMENTDESIRED, IGT_JUDGEMENTMUCHDESIRED ); @@ -761,49 +781,6 @@ void G_SetMissionStatusText( gentity_t *attacker, int mod ) {//killed by a teammate statusTextIndex = STAT_INSUBORDINATION; } - /* - else if () - {//killed a teammate- note: handled above - if ( Q_irand( 0, 1 ) ) - { - statusTextIndex = IGT_YOUCAUSEDDEATHOFTEAMMATE; - } - else - { - statusTextIndex = IGT_KILLEDANINNOCENTCREWMAN; - } - } - else - { - //This next block is not contiguous - IGT_INADEQUATE, - IGT_RESPONSETIME, - IGT_SHOOTINRANGE, - IGT_TRYAGAIN, - IGT_TRAINONHOLODECK, - IGT_WHATCOLORSHIRT, - IGT_NOTIMPRESS7OF9, - IGT_NEELIXFAREDBETTER, - IGT_THATMUSTHURT, - IGT_TUVOKDISAPPOINTED, - IGT_STARFLEETNOTIFYFAMILY, - IGT_TEAMMATESWILLMISSYOU, - IGT_LESSTHANEXEMPLARY, - IGT_SACRIFICEDFORTHEWHOLE, - IGT_NOTLIVELONGANDPROSPER, - IGT_BETTERUSEOFSIMULATIONS, - } - */ - - /* - //These can be set by designers - IGT_INSUBORDINATION, - IGT_YOUCAUSEDDEATHOFTEAMMATE, - IGT_DIDNTPROTECTTECH, - IGT_DIDNTPROTECT7OF9, - IGT_NOTSTEALTHYENOUGH, - IGT_STEALTHTACTICSNECESSARY, - */ } void G_MakeTeamVulnerable( void ) @@ -1927,10 +1904,8 @@ qboolean G_LimbLost( gentity_t *ent, int hitLoc ) return qtrue; } return qfalse; - break; default: - return (ent->locationDamage[hitLoc]>=Q3_INFINITE); - break; + return (qboolean)(ent->locationDamage[hitLoc]>=Q3_INFINITE); } } @@ -2357,7 +2332,7 @@ qboolean G_GetRootSurfNameWithVariant( gentity_t *ent, const char *rootSurfName, { if ( !gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], rootSurfName ) ) {//see if the basic name without variants is on - Q_strncpyz( returnSurfName, rootSurfName, returnSize, qtrue ); + Q_strncpyz( returnSurfName, rootSurfName, returnSize ); return qtrue; } else @@ -2372,7 +2347,7 @@ qboolean G_GetRootSurfNameWithVariant( gentity_t *ent, const char *rootSurfName, } } } - Q_strncpyz( returnSurfName, rootSurfName, returnSize, qtrue ); + Q_strncpyz( returnSurfName, rootSurfName, returnSize ); return qfalse; } @@ -3826,7 +3801,7 @@ void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int NPC_FreeCombatPoint( self->NPC->combatPoint ); if ( self->NPC->group ) { - lastInGroup = (self->NPC->group->numGroup < 2); + lastInGroup = (qboolean)(self->NPC->group->numGroup < 2); AI_GroupMemberKilled( self ); AI_DeleteSelfFromGroup( self ); } @@ -3859,7 +3834,8 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); //self->owner = old; } if ( self->client->NPC_class == CLASS_BOBAFETT - || self->client->NPC_class == CLASS_ROCKETTROOPER ) + || self->client->NPC_class == CLASS_ROCKETTROOPER + || self->client->NPC_class == CLASS_MANDA) { if ( self->client->moveType == MT_FLYSWIM ) { @@ -3906,7 +3882,7 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); } } } - if ( (self->client->ps.stats[STAT_WEAPONS]&(1<client->ps.weapons[WP_SCEPTER]) ) { G_StopEffect( G_EffectIndex( "scepter/beam_warmup.efx" ), self->weaponModel[1], self->genericBolt1, self->s.number ); G_StopEffect( G_EffectIndex( "scepter/beam.efx" ), self->weaponModel[1], self->genericBolt1, self->s.number ); @@ -3960,7 +3936,8 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); || meansOfDeath == MOD_CRUSH || meansOfDeath == MOD_IMPACT || meansOfDeath == MOD_FALLING - || meansOfDeath == MOD_EXPLOSIVE_SPLASH ) ) + || meansOfDeath == MOD_EXPLOSIVE_SPLASH + || meansOfDeath == MOD_DESTRUCTION ) ) {//drop it TossClientItems( self ); self->client->ps.weapon = self->s.weapon = WP_NONE; @@ -3998,7 +3975,7 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); {//killed a client if ( self->client->playerTeam == TEAM_ENEMY || self->client->playerTeam == TEAM_FREE - || (self->NPC && self->NPC->charmedTime > level.time) ) + || (self->NPC && (self->NPC->charmedTime > level.time || self->NPC->darkCharmedTime > level.time) ) ) {//killed an enemy attacker->client->sess.missionStats.enemiesKilled++; } @@ -4191,6 +4168,8 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); self->NPC->desiredPitch = 0; self->NPC->confusionTime = 0; self->NPC->charmedTime = 0; + self->NPC->insanityTime = 0; + self->NPC->darkCharmedTime = 0; if ( self->ghoul2.size() ) { if ( self->chestBolt != -1 ) @@ -4200,10 +4179,12 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); if ( self->headBolt != -1 ) { G_StopEffect("force/confusion", self->playerModel, self->headBolt, self->s.number ); + G_StopEffect("force/drain_hand", self->playerModel, self->headBolt, self->s.number ); } WP_StopForceHealEffects( self ); } } + self->client->ps.stasisTime = 0; VectorCopy( self->currentAngles, self->client->ps.viewangles ); //FACING========================================================== if ( player && player->client && player->client->ps.viewEntity == self->s.number ) @@ -4607,7 +4588,8 @@ extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ); && meansOfDeath!=MOD_LASERTRIP && meansOfDeath!=MOD_LASERTRIP_ALT && meansOfDeath!=MOD_EXPLOSIVE - && meansOfDeath!=MOD_EXPLOSIVE_SPLASH ) + && meansOfDeath!=MOD_EXPLOSIVE_SPLASH + && meansOfDeath!=MOD_DESTRUCTION ) {//no sound when killed by headshot (explosions don't count) G_AlertTeam( self, attacker, 512, 0 ); if ( gi.VoiceVolume[self->s.number] ) @@ -4784,7 +4766,7 @@ void PlayerPain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const if ( blasterTest && chargerTest ) {//lost both side guns //take away that weapon - self->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_ATST_SIDE ); + self->client->ps.weapons[WP_ATST_SIDE] = 0; //switch to primary guns if ( self->client->ps.weapon == WP_ATST_SIDE ) { @@ -4824,7 +4806,7 @@ void PlayerPain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const } if ( damage != -1 && (mod==MOD_MELEE || damage==0/*fake damage*/ || (Q_irand( 0, 10 ) <= damage && self->client->damage_blood)) ) {//-1 == don't play pain anim - if ( ( ((mod==MOD_SABER||mod==MOD_MELEE)&&self->client->damage_blood) || mod == MOD_CRUSH ) && (self->s.weapon == WP_SABER||self->s.weapon==WP_MELEE||cg.renderingThirdPerson) )//FIXME: not only if using saber, but if in third person at all? But then 1st/third person functionality is different... + if ( ( ((mod==MOD_SABER||mod==MOD_MELEE)/*&&self->client->damage_blood*/) || mod == MOD_CRUSH ) && (self->s.weapon == WP_SABER||self->s.weapon==WP_MELEE||cg.renderingThirdPerson) )//FIXME: not only if using saber, but if in third person at all? But then 1st/third person functionality is different... {//FIXME: only strong-level saber attacks should make me play pain anim? if ( !G_CheckForStrongAttackMomentum( self ) && !PM_SpinningSaberAnim( self->client->ps.legsAnim ) && !PM_SaberInSpecialAttack( self->client->ps.torsoAnim ) @@ -5152,7 +5134,8 @@ void G_CheckKnockdown( gentity_t *targ, gentity_t *attacker, vec3_t newDir, int &&mod!=MOD_LASERTRIP &&mod!=MOD_LASERTRIP_ALT &&mod!=MOD_EXPLOSIVE - &&mod!=MOD_EXPLOSIVE_SPLASH ) + &&mod!=MOD_EXPLOSIVE_SPLASH + &&mod!=MOD_DESTRUCTION ) { return; } @@ -5462,7 +5445,8 @@ qboolean G_NonLocationSpecificDamage( int meansOfDeath ) || meansOfDeath == MOD_FORCE_GRIP || meansOfDeath == MOD_KNOCKOUT || meansOfDeath == MOD_CRUSH - || meansOfDeath == MOD_EXPLOSIVE_SPLASH ) + || meansOfDeath == MOD_EXPLOSIVE_SPLASH + || meansOfDeath == MOD_DESTRUCTION ) { return qtrue; } @@ -5495,6 +5479,7 @@ qboolean G_ImmuneToGas( gentity_t *ent ) || ent->client->NPC_class == CLASS_SWAMPTROOPER || ent->client->NPC_class == CLASS_TUSKEN || ent->client->NPC_class == CLASS_BOBAFETT + || ent->client->NPC_class == CLASS_MANDA || ent->client->NPC_class == CLASS_ROCKETTROOPER || ent->client->NPC_class == CLASS_SABER_DROID || ent->client->NPC_class == CLASS_ASSASSIN_DROID @@ -5506,6 +5491,28 @@ qboolean G_ImmuneToGas( gentity_t *ent ) return qfalse; } +qboolean G_IsJediClass( gclient_t *client ) +{ + if (!client) + { + return qfalse; + } + switch(client->NPC_class) + { + case CLASS_ALORA: + case CLASS_DESANN: + case CLASS_JEDI: + case CLASS_KYLE: + case CLASS_LUKE: + case CLASS_REBORN: + case CLASS_SHADOWTROOPER: + case CLASS_TAVION: + return qtrue; + default: + return qfalse; + } +} + extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent ); extern void G_StartRoll( gentity_t *ent, int anim ); extern void WP_ForcePowerStart( gentity_t *self, forcePowers_t forcePower, int overrideAmt ); @@ -5791,7 +5798,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const || mod == MOD_LASERTRIP_ALT || mod == MOD_EXPLOSIVE_SPLASH || mod == MOD_ENERGY_SPLASH - || mod == MOD_SABER ) + || mod == MOD_SABER + || mod == MOD_DESTRUCTION ) {//galak without shields takes quarter damage from explosives and lightsaber damage = ceil((float)damage/4.0f); } @@ -5822,6 +5830,36 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const damage *= 6;// more damage to turret things } } + + if ( targ->s.number >= MAX_CLIENTS && mod == MOD_DESTRUCTION ) //Destruction should do less damage to enemies + { + if ( targ->s.weapon == WP_SABER || G_IsJediClass(client)) + { + if (client) + { + switch(client->ps.forcePowerLevel[FP_SABER_DEFENSE]) + { + case FORCE_LEVEL_3: + damage *= 0.4; + break; + case FORCE_LEVEL_2: + damage *= 0.6; + break; + case FORCE_LEVEL_1: + damage *= 0.8; + break; + default: + break; + } + } + } + } + + //new client flags for damage reduction + if ((blasterDamage(mod) && targ->flags&FL_MAGPLATING) || (heavyDamage(mod) && targ->flags&FL_BLASTARMOR) || (mod == MOD_SABER && targ->flags&FL_CORTOSIS)) + { + damage /= 2; + } if (targ && targ->client @@ -5931,6 +5969,61 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const case MOD_EXPLOSIVE: case MOD_EXPLOSIVE_SPLASH: case MOD_SABER: + if (g_forceNewPowers->integer) + { + doSound = (Q_irand(0, 4) == 0); + switch (targ->client->ps.forcePowerLevel[FP_PROTECT]) + { + case FORCE_LEVEL_4: + //je suis invincible!!! + if (targ->client + && attacker->client + && targ->client->playerTeam == attacker->client->playerTeam + && (!targ->NPC || !targ->NPC->charmedTime)) + {//complain, but don't turn on them + G_FriendlyFireReaction(targ, attacker, dflags); + } + return; + break; + case FORCE_LEVEL_3: + //one-half damage + if (damage <= 1) + { + damage = 0; + } + else + { + damage = ceil((float)damage*0.5f);//was 0.1f); + } + break; + case FORCE_LEVEL_2: + //three-quarters damage + if (damage <= 1) + { + damage = 0; + } + else + { + damage = ceil((float)damage*0.75f); + } + break; + case FORCE_LEVEL_1: + //a little bit of protection + if (damage <= 1) + { + damage = 0; + } + else + { + damage = ceil((float)damage*0.9f); + } + break; + } + break; + } + else + {//overflow to normal behavior + } case MOD_DISRUPTOR: case MOD_SNIPER: case MOD_CONC_ALT: @@ -5952,7 +6045,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const case MOD_LAVA: case MOD_FALLING: case MOD_MELEE: - doSound = (Q_irand(0,4)==0); + doSound = (qboolean)(Q_irand(0,4)==0); switch ( targ->client->ps.forcePowerLevel[FP_PROTECT] ) { case FORCE_LEVEL_4: @@ -5960,7 +6053,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const if ( targ->client && attacker->client && targ->client->playerTeam == attacker->client->playerTeam - && (!targ->NPC || !targ->NPC->charmedTime) ) + && (!targ->NPC || (!targ->NPC->charmedTime && !targ->NPC->darkCharmedTime)) ) {//complain, but don't turn on them G_FriendlyFireReaction( targ, attacker, dflags ); } @@ -6010,9 +6103,27 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const } } //absorb - /* + if ( (targ->client->ps.forcePowersActive & (1 << FP_ABSORB)) ) { + if ( mod == MOD_DESTRUCTION ) + { + switch (targ->client->ps.forcePowerLevel[FP_ABSORB]) { + case FORCE_LEVEL_1: + damage *= 0.5f; + break; + case FORCE_LEVEL_2: + damage *= 0.25f; + break; + case FORCE_LEVEL_3: + damage *= 0.1f; + break; + default: + damage = 0; + break; + } + } + /* if ( mod == MOD_FORCE_LIGHTNING || mod == MOD_FORCE_GRIP || mod == MOD_FORCE_DRAIN ) @@ -6028,9 +6139,9 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const //make absorb sound G_SoundOnEnt( targ, CHAN_ITEM, "sound/weapons/force/absorbhit.wav" ); targ->client->ps.forcePower += absorbed; - } + }*/ } - */ + } knockback = damage; @@ -6118,7 +6229,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const if ( targ->client && attacker->client && targ->client->playerTeam == attacker->client->playerTeam - && (!targ->NPC || !targ->NPC->charmedTime) ) + && (!targ->NPC || (!targ->NPC->charmedTime && !targ->NPC->darkCharmedTime)) ) {//complain, but don't turn on them G_FriendlyFireReaction( targ, attacker, dflags ); } @@ -6218,14 +6329,14 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const //for (int i=0; icurrentOrigin, 60.0f, testDirection, testStartPos); VectorCopy(targ->currentOrigin, testEndPos); - testEndPos[0] += (random() * 8.0f) - 4.0f; - testEndPos[1] += (random() * 8.0f) - 4.0f; - testEndPos[2] += (random() * 8.0f); + testEndPos[0] += (Q_flrand(0.0f, 1.0f) * 8.0f) - 4.0f; + testEndPos[1] += (Q_flrand(0.0f, 1.0f) * 8.0f) - 4.0f; + testEndPos[2] += (Q_flrand(0.0f, 1.0f) * 8.0f); gi.trace (&testTrace, testStartPos, NULL, NULL, testEndPos, ENTITYNUM_NONE, MASK_SHOT, G2_COLLIDE, 0); @@ -6238,7 +6349,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const } // CG_DrawEdge(testStartPos, testEndPos, EDGE_IMPACT_POSSIBLE); float chanceOfFizz = gi.WE_GetChanceOfSaberFizz(); - TIMER_Set(targ, "AcidPainDebounce", 200 + (10000.0f * random() * chanceOfFizz)); + TIMER_Set(targ, "AcidPainDebounce", 200 + (10000.0f * Q_flrand(0.0f, 1.0f) * chanceOfFizz)); hitLoc = HL_CHEST; } } @@ -6495,14 +6606,15 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const // Undying If: //-------------------------------------------------------------------------- - qboolean targUndying = (!alreadyDead - && !(dflags&DAMAGE_NO_PROTECTION) - && ( - (targ->flags&FL_UNDYING) - || (dflags&DAMAGE_NO_KILL) - || ((targ->client) && (targ->client->ps.forcePowersActive & (1<flags&FL_UNDYING) || + (dflags&DAMAGE_NO_KILL) || + ((targ->client) && + (targ->client->ps.forcePowersActive & (1 << FP_RAGE)) & + !(dflags&DAMAGE_NO_PROTECTION) && + !(dflags&DAMAGE_DIE_ON_IMPACT)))); if ( targ->client && targ->client->NPC_class == CLASS_WAMPA @@ -6698,7 +6810,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, const if ( yellAtAttacker ) { - if ( !targ->NPC || !targ->NPC->charmedTime ) + if ( !targ->NPC || (!targ->NPC->charmedTime && !targ->NPC->darkCharmedTime) ) { G_FriendlyFireReaction( targ, attacker, dflags ); } @@ -7028,3 +7140,44 @@ void G_RadiusDamage ( const vec3_t origin, gentity_t *attacker, float damage, fl } } } + +qboolean blasterDamage(int mod) { + switch (mod) { + case MOD_BRYAR: + case MOD_BRYAR_ALT: + case MOD_BLASTER: + case MOD_BLASTER_ALT: + case MOD_BOWCASTER: + case MOD_BOWCASTER_ALT: + case MOD_REPEATER: + //case MOD_DEMP2, + //case MOD_DEMP2_ALT, + //NEW for JKA weapons: + case MOD_CONC: + case MOD_CONC_ALT: + case MOD_SEEKER: + case MOD_EMPLACED: + return qtrue; + default: + return qfalse; + } +} + +qboolean heavyDamage(int mod) { + switch (mod) { + case MOD_REPEATER_ALT: + case MOD_FLECHETTE: + case MOD_FLECHETTE_ALT: + case MOD_ROCKET: + case MOD_ROCKET_ALT: + case MOD_THERMAL: + case MOD_THERMAL_ALT: + case MOD_DETPACK: + case MOD_LASERTRIP: + case MOD_LASERTRIP_ALT: + case MOD_MELEE: + return qtrue; + default: + return qfalse; + } +} \ No newline at end of file diff --git a/code/game/g_emplaced.cpp b/code/game/g_emplaced.cpp index 6519a9dd58..b856f631a5 100644 --- a/code/game/g_emplaced.cpp +++ b/code/game/g_emplaced.cpp @@ -28,6 +28,8 @@ along with this program; if not, see . #include "b_local.h" #include "g_navigator.h" +#define DAMAGE_DETACH -1 + extern Vehicle_t *G_IsRidingVehicle( gentity_t *pEnt ); //lock the owner into place relative to the cannon pos @@ -228,12 +230,18 @@ void eweb_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int d G_UseTargets( self, attacker ); } - G_RadiusDamage( self->currentOrigin, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN ); + if ( damage != DAMAGE_DETACH ) + { + G_RadiusDamage( self->currentOrigin, self, self->splashDamage, self->splashRadius, self, MOD_UNKNOWN ); + } VectorCopy( self->currentOrigin, org ); org[2] += 20; - G_PlayEffect( "emplaced/explode", org ); + if ( damage != DAMAGE_DETACH ) + { + G_PlayEffect( "emplaced/explode", org ); + } // Turn the top of the eweb off. #define TURN_OFF 0x00000100//G2SURFACEFLAG_NODESCENDANTS @@ -358,7 +366,7 @@ void eweb_use( gentity_t *self, gentity_t *other, gentity_t *activator ) // swap the users weapon with the emplaced gun and add the ammo the gun has to the player activator->client->ps.weapon = self->s.weapon; Add_Ammo( activator, WP_EMPLACED_GUN, self->count ); - activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN ); + activator->client->ps.weapons[WP_EMPLACED_GUN] = 1; // Allow us to point from one to the other activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. @@ -375,7 +383,7 @@ extern void ChangeWeapon( gentity_t *ent, int newWeapon ); { // we don't want for it to draw the weapon select stuff cg.weaponSelect = WP_EMPLACED_GUN; - CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.95 ); + CG_CenterPrint( "@SPMOD_EWEB_EXIT", SCREEN_HEIGHT * 0.95 ); } VectorCopy( activator->currentOrigin, self->pos4 );//keep this around so we know when to make them play the strafe anim @@ -581,7 +589,7 @@ void emplaced_gun_use( gentity_t *self, gentity_t *other, gentity_t *activator ) // swap the users weapon with the emplaced gun and add the ammo the gun has to the player activator->client->ps.weapon = self->s.weapon; Add_Ammo( activator, WP_EMPLACED_GUN, self->count ); - activator->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_EMPLACED_GUN ); + activator->client->ps.weapons[WP_EMPLACED_GUN] = 1; // Allow us to point from one to the other activator->owner = self; // kind of dumb, but when we are locked to the weapon, we are owned by it. @@ -745,8 +753,8 @@ void emplaced_gun_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacke vec3_t ugly; ugly[YAW] = 4; - ugly[PITCH] = self->lastAngles[PITCH] * 0.8f + crandom() * 6; - ugly[ROLL] = crandom() * 7; + ugly[PITCH] = self->lastAngles[PITCH] * 0.8f + Q_flrand(-1.0f, 1.0f) * 6; + ugly[ROLL] = Q_flrand(-1.0f, 1.0f) * 7; gi.G2API_SetBoneAnglesIndex( &self->ghoul2[self->playerModel], self->lowerLumbarBone, ugly, BONE_ANGLES_POSTMULT, POSITIVE_Y, POSITIVE_Z, POSITIVE_X, NULL, 0, 0 ); VectorCopy( self->currentOrigin, org ); @@ -890,7 +898,7 @@ void G_UpdateEmplacedWeaponData( gentity_t *ent ) } } -void ExitEmplacedWeapon( gentity_t *ent ) +void ExitEmplacedWeapon( gentity_t *ent, qboolean detach = qfalse ) { // requesting to unlock from the weapon // We'll leave the gun pointed in the direction it was last facing, though we'll cut out the pitch @@ -996,7 +1004,7 @@ void ExitEmplacedWeapon( gentity_t *ent ) } // Remove the emplaced gun from our inventory - ent->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_EMPLACED_GUN ); + ent->client->ps.weapons[WP_EMPLACED_GUN] = 0; extern void ChangeWeapon( gentity_t *ent, int newWeapon ); extern void CG_ChangeWeapon( int num ); @@ -1021,6 +1029,12 @@ extern void CG_ChangeWeapon( int num ); { // when we lock or unlock from the the gun, we get our old weapon back ent->client->ps.weapon = ent->owner->s.weapon; + //player can pull the eweb gun away + if ( !ent->s.number && detach ) + { + ent->client->ps.weapon = WP_EMPLACED_GUN; + ent->client->ps.ammo[AMMO_EMPLACED] = 100; + } if ( ent->NPC ) {//BTW, if a saber-using NPC ever gets off of an emplaced gun/eweb, this will not work, look at NPC_ChangeWeapon for the proper way @@ -1030,13 +1044,20 @@ extern void CG_ChangeWeapon( int num ); { G_RemoveWeaponModels( ent ); CG_ChangeWeapon( ent->client->ps.weapon ); - if ( ent->client->ps.weapon == WP_SABER ) + if ( ent->client->ps.weapon == WP_EMPLACED_GUN && detach ) + { + G_CreateG2AttachedWeaponModel( ent, "models/map_objects/hoth/eweb_model.glm", ent->handRBolt, 0 ); + WP_SaberAddHolsteredG2SaberModels( ent ); + } + else if ( ent->client->ps.weapon == WP_SABER ) { WP_SaberAddG2SaberModels( ent ); + G_RemoveHolsterModels( ent ); } else { - G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].weaponMdl, ent->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel( ent, weaponData[ent->client->ps.weapon].worldModel, ent->handRBolt, 0 ); + WP_SaberAddHolsteredG2SaberModels( ent ); } if ( ent->s.number < MAX_CLIENTS ) @@ -1079,6 +1100,10 @@ extern void CG_ChangeWeapon( int num ); if ( !ent->NPC ) { // by keeping the owner, a dead npc can be pushed out of the chair without colliding with it + if ( ent->health > 0 && detach ) + { + GEntity_DieFunc(ent->owner, ent->owner, ent->owner, DAMAGE_DETACH, MOD_UNKNOWN ); + } ent->owner = NULL; } } @@ -1106,6 +1131,14 @@ void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd ) (*ucmd)->upmove = 0; } } + //detach eweb! + else if (( (*ucmd)->buttons & BUTTON_USE_FORCE ) && ent->owner && ent->owner->delay + 500 < level.time && ent->owner->e_UseFunc == useF_eweb_use) + { + ent->owner->s.loopSound = 0; + G_Sound( ent, G_SoundIndex( "sound/weapons/eweb/eweb_dismount.mp3" )); + ExitEmplacedWeapon( ent, qtrue ); + (*ucmd)->buttons &= ~BUTTON_USE_FORCE; + } else { // this is a crappy way to put sounds on a moving eweb.... diff --git a/code/game/g_functions.cpp b/code/game/g_functions.cpp index 822f4413e5..ae5f78f51e 100644 --- a/code/game/g_functions.cpp +++ b/code/game/g_functions.cpp @@ -138,7 +138,10 @@ void GEntity_ThinkFunc(gentity_t *self) THINKCASE( misc_weapon_shooter_fire ) THINKCASE( beacon_think ) - + + THINKCASE( shipboundary_think ) + THINKCASE( asteroid_field_think ) + default: Com_Error(ERR_DROP, "GEntity_ThinkFunc: case %d not handled!\n",self->e_ThinkFunc); break; @@ -234,7 +237,10 @@ void GEntity_TouchFunc(gentity_t *self, gentity_t *other, trace_t *trace) TOUCHCASE( prox_mine_stick ) TOUCHCASE( func_rotating_touch ) TOUCHCASE( TouchTieBomb ) - + TOUCHCASE( space_touch ) + TOUCHCASE( shipboundary_touch ) + TOUCHCASE( hyperspace_touch ) + default: Com_Error(ERR_DROP, "GEntity_TouchFunc: case %d not handled!\n",self->e_TouchFunc); } @@ -330,6 +336,7 @@ void GEntity_UseFunc(gentity_t *self, gentity_t *other, gentity_t *activator) USECASE( misc_weapon_shooter_use ) USECASE( eweb_use ) USECASE( TieFighterUse ); + USECASE( radar_icon_use ); default: Com_Error(ERR_DROP, "GEntity_UseFunc: case %d not handled!\n",self->e_UseFunc); @@ -371,6 +378,7 @@ void GEntity_PainFunc(gentity_t *self, gentity_t *inflictor, gentity_t *attacker PAINCASE( CrystalCratePain ) PAINCASE( TurretPain ) PAINCASE( eweb_pain ) + PAINCASE( NPC_GM_Pain ) default: Com_Error(ERR_DROP, "GEntity_PainFunc: case %d not handled!\n",self->e_PainFunc); diff --git a/code/game/g_functions.h b/code/game/g_functions.h index 07a4d24bba..e1e71b901e 100644 --- a/code/game/g_functions.h +++ b/code/game/g_functions.h @@ -141,6 +141,9 @@ typedef enum //rww - added for sky portals thinkF_G_PortalifyEntities, + + thinkF_shipboundary_think, + thinkF_asteroid_field_think, } thinkFunc_t; @@ -249,6 +252,9 @@ extern void misc_weapon_shooter_fire( gentity_t *self ); extern void beacon_think ( gentity_t *self ); +extern void shipboundary_think ( gentity_t *ent ); +extern void asteroid_field_think ( gentity_t *self ); + // void (*clThink)(centity_s *cent); //Think func for equivalent centity typedef enum @@ -327,6 +333,9 @@ typedef enum touchF_prox_mine_stick, touchF_func_rotating_touch, touchF_TouchTieBomb, + touchF_space_touch, + touchF_shipboundary_touch, + touchF_hyperspace_touch, } touchFunc_t; // TOUCH functions... @@ -350,6 +359,9 @@ extern void prox_mine_stick( gentity_t *self, gentity_t *other, trace_t *trace ) extern void func_rotating_touch (gentity_t *self, gentity_t *other, trace_t *trace); extern void TouchTieBomb( gentity_t *self, gentity_t *other, trace_t *trace ); extern void TieFighterUse( gentity_t *self, gentity_t *other, gentity_t *activator ); +extern void space_touch( gentity_t *self, gentity_t *other, trace_t *trace ); +extern void shipboundary_touch( gentity_t *self, gentity_t *other, trace_t *trace ); +extern void hyperspace_touch( gentity_t *self, gentity_t *other, trace_t *trace ); // void (*use)(gentity_t *self, gentity_t *other, gentity_t *activator); typedef enum @@ -434,6 +446,7 @@ typedef enum useF_misc_weapon_shooter_use, useF_eweb_use, useF_TieFighterUse, + useF_radar_icon_use, } useFunc_t; // USE functions... @@ -516,6 +529,7 @@ extern void item_spawn_use ( gentity_t *self, gentity_t *other, gentity_t *act extern void NPC_VehicleSpawnUse ( gentity_t *self, gentity_t *other, gentity_t *activator ); extern void misc_weapon_shooter_use ( gentity_t *self, gentity_t *other, gentity_t *activator ); extern void eweb_use ( gentity_t *self, gentity_t *other, gentity_t *activator ); +extern void radar_icon_use ( gentity_t *self, gentity_t *other, gentity_t *activator ); // void (*pain)(gentity_t *self, gentity_t *attacker, int damage,int mod,int hitLoc); typedef enum @@ -572,6 +586,7 @@ extern void NPC_Seeker_Pain (gentity_t *self, gentity_t *inflictor, gentity_t extern void NPC_Remote_Pain (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc=HL_NONE); extern void emplaced_gun_pain (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc=HL_NONE); extern void NPC_Mark1_Pain (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc=HL_NONE); +extern void NPC_GM_Pain (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc=HL_NONE); extern void NPC_Sentry_Pain (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc=HL_NONE); extern void NPC_Mark2_Pain (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc=HL_NONE); extern void PlayerPain (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, const vec3_t point, int damage, int mod,int hitLoc=HL_NONE); diff --git a/code/game/g_fx.cpp b/code/game/g_fx.cpp index 847179bb33..41fe80fb81 100644 --- a/code/game/g_fx.cpp +++ b/code/game/g_fx.cpp @@ -67,7 +67,7 @@ void fx_runner_think( gentity_t *ent ) AngleVectors( ent->currentAngles, ent->pos3, NULL, NULL ); MakeNormalVectors( ent->pos3, ent->pos4, temp ); // there IS a reason this is done...it's so that it doesn't break every effect in the game... - ent->nextthink = level.time + ent->delay + random() * ent->random; + ent->nextthink = level.time + ent->delay + Q_flrand(0.0f, 1.0f) * ent->random; if ( ent->spawnflags & 4 ) // damage { diff --git a/code/game/g_inventory.cpp b/code/game/g_inventory.cpp index 2ac285af1d..71720b0c64 100644 --- a/code/game/g_inventory.cpp +++ b/code/game/g_inventory.cpp @@ -90,7 +90,7 @@ qboolean INV_SecurityKeyGive( gentity_t *target, const char *keyname ) if ( target->client->ps.security_key_message[i][0] == '\0' ) {//fill in the first empty slot we find with this key target->client->ps.inventory[INV_SECURITY_KEY]++; // He got the key - Q_strncpyz( target->client->ps.security_key_message[i], keyname, MAX_SECURITY_KEY_MESSSAGE, qtrue ); + Q_strncpyz( target->client->ps.security_key_message[i], keyname, MAX_SECURITY_KEY_MESSSAGE ); return qtrue; } } diff --git a/code/game/g_itemLoad.cpp b/code/game/g_itemLoad.cpp index d8e10d7cff..bd3c58e8ae 100644 --- a/code/game/g_itemLoad.cpp +++ b/code/game/g_itemLoad.cpp @@ -157,6 +157,17 @@ static void IT_Name(const char **holdBuf) itemNum = ITM_SCEPTER_PICKUP; else if (!Q_stricmp(tokenStr,"ITM_NOGHRI_STICK_PICKUP")) itemNum = ITM_NOGHRI_STICK_PICKUP; + else if (!Q_stricmp(tokenStr,"ITM_SONIC_BLASTER_PICKUP")) + itemNum = ITM_SONIC_BLASTER_PICKUP; + else if (!Q_stricmp(tokenStr,"ITM_E5_PICKUP")) + itemNum = ITM_E5_PICKUP; + else if (!Q_stricmp(tokenStr,"ITM_DC15S_PICKUP")) + itemNum = ITM_DC15S_PICKUP; + else if (!Q_stricmp(tokenStr,"ITM_DC15A_PICKUP")) + itemNum = ITM_DC15A_PICKUP; + else if (!Q_stricmp(tokenStr,"ITM_Z6_PICKUP")) + itemNum = ITM_Z6_PICKUP; + //ammo else if (!Q_stricmp(tokenStr,"ITM_AMMO_FORCE_PICKUP")) itemNum = ITM_AMMO_FORCE_PICKUP; @@ -346,8 +357,6 @@ static void IT_Tag(const char **holdBuf) tag = WP_TRIP_MINE; else if (!Q_stricmp(tokenStr,"WP_DET_PACK")) tag = WP_DET_PACK; -// else if (!Q_stricmp(tokenStr,"WP_TRICORDER")) -// tag = WP_TRICORDER; else if (!Q_stricmp(tokenStr,"WP_BOT_LASER")) tag = WP_BOT_LASER; else if (!Q_stricmp(tokenStr,"WP_EMPLACED_GUN")) @@ -376,6 +385,16 @@ static void IT_Tag(const char **holdBuf) tag = WP_SCEPTER; else if (!Q_stricmp(tokenStr,"WP_NOGHRI_STICK")) tag = WP_NOGHRI_STICK; + else if (!Q_stricmp(tokenStr,"WP_SONIC_BLASTER")) + tag = WP_SONIC_BLASTER; + else if (!Q_stricmp(tokenStr,"WP_E5_CARBINE")) + tag = WP_E5_CARBINE; + else if (!Q_stricmp(tokenStr,"WP_DC15S_CARBINE")) + tag = WP_DC15S_CARBINE; + else if (!Q_stricmp(tokenStr,"WP_DC15A_RIFLE")) + tag = WP_DC15A_RIFLE; + else if (!Q_stricmp(tokenStr,"WP_Z6_ROTARY")) + tag = WP_Z6_ROTARY; else if (!Q_stricmp(tokenStr,"AMMO_FORCE")) tag = AMMO_FORCE; else if (!Q_stricmp(tokenStr,"AMMO_BLASTER")) diff --git a/code/game/g_items.cpp b/code/game/g_items.cpp index 79daac206d..8cd44d0bf3 100644 --- a/code/game/g_items.cpp +++ b/code/game/g_items.cpp @@ -146,13 +146,13 @@ int Add_Ammo2 (gentity_t *ent, int ammoType, int count) switch( ammoType ) { case AMMO_THERMAL: - ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_THERMAL ); + ent->client->ps.weapons[WP_THERMAL] = 1; break; case AMMO_DETPACK: - ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_DET_PACK ); + ent->client->ps.weapons[WP_DET_PACK] = 1; break; case AMMO_TRIPMINE: - ent->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_TRIP_MINE ); + ent->client->ps.weapons[WP_TRIP_MINE] = 1; break; } @@ -280,7 +280,16 @@ gentity_t *G_DropSaberItem( const char *saberType, saber_colors_t saberColor, ve newItem->spawnflags |= 64;/*ITMSF_NOGLOW*/ newItem->NPC_type = G_NewString( saberType );//saberType //FIXME: transfer per-blade color somehow? - newItem->NPC_targetname = (char *)saberColorStringForColor[saberColor]; + if (saberColor >= SABER_RGB) + { + char rgbColor[8]; + Com_sprintf(rgbColor, 8, "x%02x%02x%02x", saberColor & 0xff, (saberColor >> 8) & 0xff, (saberColor >> 16) & 0xff); + newItem->NPC_targetname = rgbColor; + } + else + { + newItem->NPC_targetname = (char *)saberColorStringForColor[saberColor]; + } newItem->count = 1; newItem->flags = FL_DROPPED_ITEM; G_SpawnItem( newItem, FindItemForWeapon( WP_SABER ) ); @@ -473,11 +482,11 @@ int Pickup_Weapon (gentity_t *ent, gentity_t *other) } // add the weapon - if ( other->client->ps.stats[STAT_WEAPONS] & ( 1 << ent->item->giTag ) ) + if ( other->client->ps.weapons[ent->item->giTag] ) { hadWeapon = qtrue; } - other->client->ps.stats[STAT_WEAPONS] |= ( 1 << ent->item->giTag ); + other->client->ps.weapons[ent->item->giTag] = 1; if ( ent->item->giTag == WP_SABER && (!hadWeapon || ent->NPC_type != NULL) ) {//didn't have a saber or it is specifying a certain kind of saber to use @@ -500,10 +509,12 @@ int Pickup_Weapon (gentity_t *ent, gentity_t *other) { other->client->ps.SaberActivate(); WP_SaberAddG2SaberModels( other ); + G_RemoveHolsterModels( ent ); } else { - G_CreateG2AttachedWeaponModel( other, weaponData[ent->item->giTag].weaponMdl, other->handRBolt, 0 ); + G_CreateG2AttachedWeaponModel( other, weaponData[ent->item->giTag].worldModel, other->handRBolt, 0 ); + WP_SaberAddHolsteredG2SaberModels( ent ); } } } @@ -587,9 +598,9 @@ int ITM_AddArmor (gentity_t *ent, int count) ent->client->ps.stats[STAT_ARMOR] += count; - if (ent->client->ps.stats[STAT_ARMOR] > ent->client->ps.stats[STAT_MAX_HEALTH]) + if (ent->client->ps.stats[STAT_ARMOR] > g_handicap_maxArmor->integer) { - ent->client->ps.stats[STAT_ARMOR] = ent->client->ps.stats[STAT_MAX_HEALTH]; + ent->client->ps.stats[STAT_ARMOR] = g_handicap_maxArmor->integer; return qfalse; } @@ -603,8 +614,8 @@ int Pickup_Armor( gentity_t *ent, gentity_t *other ) { other->client->ps.powerups[PW_BATTLESUIT] = Q3_INFINITE; other->client->ps.stats[STAT_ARMOR] += ent->item->quantity; - if ( other->client->ps.stats[STAT_ARMOR] > other->client->ps.stats[STAT_MAX_HEALTH] ) { - other->client->ps.stats[STAT_ARMOR] = other->client->ps.stats[STAT_MAX_HEALTH]; + if ( other->client->ps.stats[STAT_ARMOR] > g_handicap_maxArmor->integer ) { + other->client->ps.stats[STAT_ARMOR] = g_handicap_maxArmor->integer; } return 30; @@ -814,6 +825,15 @@ void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) { return; } } + + if ( ent->item->giType == IT_WEAPON + && ent->item->giTag == WP_EMPLACED_GUN ) + {//portable eweb + if ( ent->delay > level.time ) + {//just picked it up, don't pick up again right away + return; + } + } if ( other->s.number < MAX_CLIENTS && (ent->spawnflags&ITMSF_USEPICKUP) ) @@ -839,7 +859,7 @@ void Touch_Item (gentity_t *ent, gentity_t *other, trace_t *trace) { TIMER_Set( other, "attackDelay", 600 ); respawn = 0; } - if ( other->client->ps.stats[STAT_WEAPONS] & ( 1 << ent->item->giTag ) ) + if ( other->client->ps.weapons[ent->item->giTag] ) { bHadWeapon = qtrue; } @@ -993,7 +1013,7 @@ gentity_t *LaunchItem( gitem_t *item, const vec3_t origin, const vec3_t velocity && item->giTag != WP_TRIP_MINE && item->giTag != WP_DET_PACK ) { - VectorSet( dropped->s.angles, 0, crandom() * 180, 90.0f ); + VectorSet( dropped->s.angles, 0, Q_flrand(-1.0f, 1.0f) * 180, 90.0f ); G_SetAngles( dropped, dropped->s.angles ); } } @@ -1030,7 +1050,7 @@ gentity_t *Drop_Item( gentity_t *ent, gitem_t *item, float angle, qboolean copyt AngleVectors( angles, velocity, NULL, NULL ); VectorScale( velocity, 150, velocity ); - velocity[2] += 200 + crandom() * 50; + velocity[2] += 200 + Q_flrand(-1.0f, 1.0f) * 50; if ( copytarget ) { @@ -1096,6 +1116,7 @@ free fall from their spawn points */ extern int delayedShutDown; extern cvar_t *g_saber; +extern cvar_t *g_saber_skin[MAX_SABER_PARTS]; void FinishSpawningItem( gentity_t *ent ) { trace_t tr; vec3_t dest; @@ -1148,6 +1169,39 @@ void FinishSpawningItem( gentity_t *ent ) { && Q_stricmp( "NULL", g_saber->string ) ) {//player's saber WP_SaberParseParms( g_saber->string, &itemSaber ); + //Custom saber stuff! + if (Q_stristr(itemSaber.name, "saberbuilder")) + { + char skinRoot[MAX_QPATH] = {0}; + Q_strncpyz(skinRoot, itemSaber.model, MAX_QPATH); + int l = strlen(skinRoot); + while (l > 0 && skinRoot[l] != '/') + { //parse back to first / + l--; + } + + if (skinRoot[l] == '/') + { + l++; + skinRoot[l] = 0; + + Q_strcat(skinRoot, MAX_QPATH, "|_"); + + for (int j = 0; j < MAX_SABER_PARTS; j++) + { + Q_strcat(skinRoot, MAX_QPATH, "|"); + if (g_saber_skin[j] && g_saber_skin[j]->string && g_saber_skin[j]->string[0]) + { + Q_strcat(skinRoot, MAX_QPATH, g_saber_skin[j]->string); + } + } + + if(itemSaber.skin && gi.bIsFromZone(itemSaber.skin, TAG_G_ALLOC) ) { + gi.Free(itemSaber.skin); + } + itemSaber.skin = G_NewString(skinRoot); + } + } } else {//specific saber @@ -1155,7 +1209,20 @@ void FinishSpawningItem( gentity_t *ent ) { } //NOTE: should I keep this string around for any reason? Will I ever need it later? //ent->??? = G_NewString( itemSaber.model ); - gi.G2API_InitGhoul2Model( ent->ghoul2, itemSaber.model, G_ModelIndex( itemSaber.model ), NULL_HANDLE, NULL_HANDLE, 0, 0); + int g2Model = gi.G2API_InitGhoul2Model( ent->ghoul2, itemSaber.model, G_ModelIndex( itemSaber.model ), NULL_HANDLE, NULL_HANDLE, 0, 0); + + if ( itemSaber.skin != NULL ) + {//if this saber has a customSkin, use it + // lets see if it's out there + int saberSkin = gi.RE_RegisterSkin( itemSaber.skin ); + if ( saberSkin ) + { + // put it in the config strings + // and set the ghoul2 model to use it + gi.G2API_SetSkin( &ent->ghoul2[g2Model], G_SkinIndex( itemSaber.skin ), saberSkin ); + } + } + WP_SaberFreeStrings(itemSaber); } else @@ -1507,9 +1574,9 @@ void G_RunItem( gentity_t *ent ) { { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; - ent->s.pos.trDelta[0] += crandom() * 40.0f; // I dunno, just do this?? - ent->s.pos.trDelta[1] += crandom() * 40.0f; - ent->s.pos.trDelta[2] += random() * 20.0f; + ent->s.pos.trDelta[0] += Q_flrand(-1.0f, 1.0f) * 40.0f; // I dunno, just do this?? + ent->s.pos.trDelta[1] += Q_flrand(-1.0f, 1.0f) * 40.0f; + ent->s.pos.trDelta[2] += Q_flrand(0.0f, 1.0f) * 20.0f; } else if ( (ent->flags&FL_DROPPED_ITEM) && ent->item diff --git a/code/game/g_items.h b/code/game/g_items.h index 259417f89a..742b5f65d9 100644 --- a/code/game/g_items.h +++ b/code/game/g_items.h @@ -61,6 +61,12 @@ ITM_TUSKEN_STAFF_PICKUP, ITM_SCEPTER_PICKUP, ITM_NOGHRI_STICK_PICKUP, +ITM_SONIC_BLASTER_PICKUP, +ITM_E5_PICKUP, +ITM_DC15S_PICKUP, +ITM_DC15A_PICKUP, +ITM_Z6_PICKUP, + ITM_AMMO_FORCE_PICKUP, ITM_AMMO_BLASTER_PICKUP, ITM_AMMO_POWERCELL_PICKUP, diff --git a/code/game/g_local.h b/code/game/g_local.h index 58100532b3..d82a564b61 100644 --- a/code/game/g_local.h +++ b/code/game/g_local.h @@ -34,10 +34,14 @@ along with this program; if not, see . #include "anims.h" #include "dmstates.h" +#include +#include + //================================================================== // the "gameversion" client command will print this plus compile date -#define GAMEVERSION "OpenJK" +#define GAMEVERSION "OpenJK-DP" +//#define GAMEVERSION "Jedi Academy: Enhanced" #define BODY_QUEUE_SIZE 8 @@ -76,6 +80,15 @@ along with this program; if not, see . #define FL_NO_ANGLES 0x00200000 // No bone angle overrides, no pitch or roll in full angles #define FL_RED_CROSSHAIR 0x00400000 // Crosshair red on me +//these can affect player and NPC abilities +#define FL_DUALPISTOLS 0x00800000 //gives dual pistols if WP_BLASTER_PISTOL +#define FL_MELEEKICKS 0x01000000 //allows melee kicks +#define FL_MELEEKATAS 0x02000000 //allows melee katas +#define FL_MELEEKATA_NOFORCEFX 0x04000000 //hack to stop melee katas from doing force power effects +#define FL_CORTOSIS 0x08000000 //halves damage from saber attacks +#define FL_MAGPLATING 0x10000000 //halves damage from blaster bolts +#define FL_BLASTARMOR 0x20000000 //halves damage from heavy weapons/explosives + //Pointer safety utilities #define VALID( a ) ( a != NULL ) @@ -88,15 +101,41 @@ along with this program; if not, see . #define VALIDSTRING( a ) ( ( a != NULL ) && ( a[0] != '\0' ) ) //animations -typedef struct +class animFileSet_t { +public: char filename[MAX_QPATH]; animation_t animations[MAX_ANIMATIONS]; animevent_t torsoAnimEvents[MAX_ANIM_EVENTS]; animevent_t legsAnimEvents[MAX_ANIM_EVENTS]; unsigned char torsoAnimEventCount; unsigned char legsAnimEventCount; -} animFileSet_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(filename); + saved_game.write<>(animations); + saved_game.write<>(torsoAnimEvents); + saved_game.write<>(legsAnimEvents); + saved_game.write(torsoAnimEventCount); + saved_game.write(legsAnimEventCount); + saved_game.skip(2); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(filename); + saved_game.read<>(animations); + saved_game.read<>(torsoAnimEvents); + saved_game.read<>(legsAnimEvents); + saved_game.read(torsoAnimEventCount); + saved_game.read(legsAnimEventCount); + saved_game.skip(2); + } +}; // animFileSet_t extern stringID_table_t animTable [MAX_ANIMATIONS+1]; @@ -145,8 +184,9 @@ enum alertEventLevel_e }; // !!!!!!!!! LOADSAVE-affecting struct !!!!!!!!!! -typedef struct alertEvent_s +class alertEvent_t { +public: vec3_t position; //Where the event is located float radius; //Consideration radius alertEventLevel_e level; //Priority level of the event @@ -157,7 +197,38 @@ typedef struct alertEvent_s int ID; //unique... if get a ridiculous number, this will repeat, but should not be a problem as it's just comparing it to your lastAlertID int timestamp; //when it was created qboolean onGround; //alert is on the ground (only used for sounds) -} alertEvent_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(position); + saved_game.write(radius); + saved_game.write(level); + saved_game.write(type); + saved_game.write(owner); + saved_game.write(light); + saved_game.write(addLight); + saved_game.write(ID); + saved_game.write(timestamp); + saved_game.write(onGround); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(position); + saved_game.read(radius); + saved_game.read(level); + saved_game.read(type); + saved_game.read(owner); + saved_game.read(light); + saved_game.read(addLight); + saved_game.read(ID); + saved_game.read(timestamp); + saved_game.read(onGround); + } +}; // alertEvent_t // // this structure is cleared as each map is entered @@ -181,8 +252,9 @@ typedef struct #define WF_PUFFING 0x00000004 // puffing something // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct +class level_locals_t { +public: gclient_t *clients; // [maxclients] // store latched cvars here that we want to get at often @@ -240,7 +312,52 @@ typedef struct float mRotationAdjust; char *mTargetAdjust; qboolean hasBspInstances; -} level_locals_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(clients); + saved_game.write(maxclients); + saved_game.write(framenum); + saved_game.write(time); + saved_game.write(previousTime); + saved_game.write(globalTime); + saved_game.write(mapname); + saved_game.write(locationLinked); + saved_game.write(locationHead); + saved_game.write<>(alertEvents); + saved_game.write(numAlertEvents); + saved_game.write(curAlertID); + saved_game.write<>(groups); + saved_game.write<>(knownAnimFileSets); + saved_game.write(numKnownAnimFileSets); + saved_game.write(worldFlags); + saved_game.write(dmState); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(clients); + saved_game.read(maxclients); + saved_game.read(framenum); + saved_game.read(time); + saved_game.read(previousTime); + saved_game.read(globalTime); + saved_game.read(mapname); + saved_game.read(locationLinked); + saved_game.read(locationHead); + saved_game.read<>(alertEvents); + saved_game.read(numAlertEvents); + saved_game.read(curAlertID); + saved_game.read<>(groups); + saved_game.read<>(knownAnimFileSets); + saved_game.read(numKnownAnimFileSets); + saved_game.read(worldFlags); + saved_game.read(dmState); + } +}; // level_locals_t extern level_locals_t level; extern game_export_t globals; @@ -254,10 +371,14 @@ extern cvar_t *g_inactivity; extern cvar_t *g_debugMove; extern cvar_t *g_subtitles; extern cvar_t *g_removeDoors; - extern cvar_t *g_ICARUSDebug; +extern cvar_t *g_npcdebug; -extern cvar_t *g_npcdebug; +extern cvar_t *g_allowBunnyhopping; + +extern cvar_t *g_weaponVelocity; +extern cvar_t *g_weaponAltVelocity; +extern cvar_t *g_handicap_maxArmor; extern gentity_t *player; // @@ -298,6 +419,7 @@ void SaveRegisteredItems( void ); // int G_ModelIndex( const char *name ); int G_SoundIndex( const char *name ); +int G_IconIndex( const char* name ); /* Ghoul2 Insert Start */ @@ -426,6 +548,7 @@ void player_die (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int void AddScore( gentity_t *ent, int score ); qboolean SpotWouldTelefrag( gentity_t *spot, team_t checkteam ); void G_RemoveWeaponModels( gentity_t *ent ); +void G_RemoveHolsterModels( gentity_t *ent ); // // g_svcmds.c @@ -537,6 +660,13 @@ void G_InitSessionData( gclient_t *client, char *userinfo ); void G_InitWorldSession( void ); void G_WriteSessionData( void ); +// +// jke_aiworkshop.cpp +// +extern qboolean inAIWorkshop; +void WorkshopToggle(); +void WorkshopThink(); +qboolean TryWorkshopCommand(gentity_t* ent); // // NPC_senses.cpp @@ -607,6 +737,7 @@ qboolean TIMER_Start( gentity_t *self, const char *identifier, int duration ); qboolean TIMER_Done2( gentity_t *ent, const char *identifier, qboolean remove = qfalse ); qboolean TIMER_Exists( gentity_t *ent, const char *identifier ); void TIMER_Remove( gentity_t *ent, const char *identifier ); +std::vector > TIMER_List(gentity_t* ent); float NPC_GetHFOVPercentage( vec3_t spot, vec3_t from, vec3_t facing, float hFOV ); float NPC_GetVFOVPercentage( vec3_t spot, vec3_t from, vec3_t facing, float vFOV ); diff --git a/code/game/g_main.cpp b/code/game/g_main.cpp index aeeea5bdb1..16f3e68b79 100644 --- a/code/game/g_main.cpp +++ b/code/game/g_main.cpp @@ -36,6 +36,8 @@ along with this program; if not, see . #include "../ghoul2/ghoul2_gore.h" //rww - RAGDOLL_END +#include "qcommon/ojk_saved_game_helper.h" + extern void WP_SaberLoadParms( void ); extern qboolean G_PlayerSpawned( void ); @@ -91,7 +93,7 @@ qboolean PInUse(unsigned int entNum) { assert(entNum>=0); assert(entNum( + INT_ID('I', 'N', 'U', 'S'), + ::g_entityInUseBits); } -void ReadInUseBits(void) +void ReadInUseBits() { - gi.ReadFromSaveGame(INT_ID('I','N','U','S'), &g_entityInUseBits, sizeof(g_entityInUseBits), NULL); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.read_chunk( + INT_ID('I', 'N', 'U', 'S'), + ::g_entityInUseBits); + // This is only temporary. Once I have converted all the ent->inuse refs, // it won;t be needed -MW. for(int i=0;ienemy==player&&(!ent->NPC||ent->NPC->confusionTimeclient&&ent->client->ps.weaponTime) || (!ent->client&&ent->attackDebounceTime>level.time)) + if ( (ent->enemy==player&&(!ent->NPC||((ent->NPC->confusionTimeNPC->insanityTimeclient&&ent->client->ps.weaponTime) || (!ent->client&&ent->attackDebounceTime>level.time)) {//mad if ( ent->health > 0 ) {//alive @@ -644,7 +755,7 @@ void G_InitCvars( void ) { com_buildScript = gi.cvar ("com_buildscript", "0", 0); g_saberAutoBlocking = gi.cvar( "g_saberAutoBlocking", "1", CVAR_CHEAT );//must press +block button to do any blocking - g_saberRealisticCombat = gi.cvar( "g_saberMoreRealistic", "0", CVAR_CHEAT );//makes collision more precise, increases damage + g_saberRealisticCombat = gi.cvar( "g_saberMoreRealistic", "0", CVAR_SAVEGAME | CVAR_CHEAT );//makes collision more precise, increases damage debug_subdivision = gi.cvar( "debug_subdivision", "0", CVAR_CHEAT|CVAR_INIT );//debug for dismemberment g_dismemberProbabilities = gi.cvar ( "g_dismemberProbabilities", "1", CVAR_CHEAT );//0 = ignore probabilities, 1 = use probabilities g_saberDamageCapping = gi.cvar( "g_saberDamageCapping", "1", CVAR_CHEAT );//caps damage of sabers vs players and NPC who use sabers @@ -657,8 +768,7 @@ void G_InitCvars( void ) { g_debugMelee = gi.cvar( "g_debugMelee", "0", CVAR_CHEAT );//just for debugging/development, test kicks and grabs g_saberRestrictForce = gi.cvar( "g_saberRestrictForce", "0", CVAR_ARCHIVE );//restricts certain force powers when using a 2-handed saber or 2 sabers g_saberPickuppableDroppedSabers = gi.cvar( "g_saberPickuppableDroppedSabers", "0", CVAR_CHEAT );//lets you pick up sabers that are dropped - - g_AIsurrender = gi.cvar( "g_AIsurrender", "0", CVAR_CHEAT ); + g_AIsurrender = gi.cvar( "g_AIsurrender", "1", CVAR_CHEAT ); g_numEntities = gi.cvar( "g_numEntities", "0", 0 ); gi.cvar( "newTotalSecrets", "0", CVAR_ROM ); @@ -680,8 +790,32 @@ void G_InitCvars( void ) { g_saber2_color = gi.cvar( "g_saber2_color", "yellow", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); g_saberDarkSideSaberColor = gi.cvar( "g_saberDarkSideSaberColor", "0", CVAR_ARCHIVE ); //when you turn evil, it turns your saber red! + g_char_head_model = gi.cvar( "g_char_head_model", "", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + g_char_head_skin = gi.cvar( "g_char_head_skin", "", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + + g_char_color_2_red = gi.cvar( "g_char_color_2_red", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + g_char_color_2_green = gi.cvar( "g_char_color_2_green", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + g_char_color_2_blue = gi.cvar( "g_char_color_2_blue", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + + g_hilt_color_red = gi.cvar( "g_hilt_color_red", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + g_hilt_color_green = gi.cvar( "g_hilt_color_green", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + g_hilt_color_blue = gi.cvar( "g_hilt_color_blue", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + + g_hilt2_color_red = gi.cvar( "g_hilt2_color_red", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + g_hilt2_color_green = gi.cvar( "g_hilt2_color_green", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + g_hilt2_color_blue = gi.cvar( "g_hilt2_color_blue", "255", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + + for (int i = 0; i < MAX_SABER_PARTS; i++) + { + g_saber_skin[i] = gi.cvar( va("g_saber_skin%d", (i+1)), "", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + g_saber2_skin[i] = gi.cvar( va("g_saber2_skin%d", (i+1)), "", CVAR_ARCHIVE|CVAR_SAVEGAME|CVAR_NORESTART ); + } + + g_broadsword = gi.cvar( "broadsword", "1", 0); + g_allowBunnyhopping = gi.cvar( "g_allowBunnyhopping", "0", 0 ); + gi.cvar( "tier_storyinfo", "0", CVAR_ROM|CVAR_SAVEGAME|CVAR_NORESTART); gi.cvar( "tiers_complete", "", CVAR_ROM|CVAR_SAVEGAME|CVAR_NORESTART); @@ -690,6 +824,35 @@ void G_InitCvars( void ) { gi.cvar( "g_clearstats", "1", CVAR_ROM|CVAR_NORESTART); + + //new cvars yay - Dusty + g_autoRoll = gi.cvar("g_autoRoll", "1", CVAR_ARCHIVE); + g_saberNewCombat = gi.cvar("g_saberNewCombat", "1", CVAR_ARCHIVE | CVAR_CHEAT); + g_saberLocksEnabled = gi.cvar("g_saberLocksEnabled", "1", CVAR_ARCHIVE | CVAR_CHEAT); + g_saberLockStyle = gi.cvar("g_saberLockStyle", "1", CVAR_ARCHIVE | CVAR_CHEAT); + g_saberLockSuperBreaks = gi.cvar("g_saberLockSuperBreaks", "1", CVAR_ARCHIVE | CVAR_CHEAT); + g_saberForceDrains = gi.cvar("g_saberForceDrains", "1", CVAR_ARCHIVE | CVAR_CHEAT); + g_saberForceDrainAmount = gi.cvar("g_saberForceDrainAmount", "10", CVAR_ARCHIVE | CVAR_CHEAT); + g_saberDamageScale = gi.cvar("g_saberDamageScale", "1.0", CVAR_SAVEGAME | CVAR_CHEAT); + g_saberDamageScaleGlobal = gi.cvar("g_saberDamageScaleGlobal", "1.0", CVAR_ARCHIVE | CVAR_CHEAT); + g_forceNewPowers = gi.cvar("g_forceNewPowers", "1", CVAR_ARCHIVE | CVAR_CHEAT); + g_char_forcePowerMax = gi.cvar("g_char_forcePowerMax", "100", CVAR_CHEAT | CVAR_SAVEGAME ); + g_char_forceRegen = gi.cvar("g_char_forceRegen", "100", CVAR_CHEAT | CVAR_SAVEGAME); + g_char_parryBonus = gi.cvar("g_char_parryBonus", "0", CVAR_CHEAT | CVAR_SAVEGAME); + g_char_breakParryBonus = gi.cvar("g_char_breakParryBonus", "0", CVAR_CHEAT | CVAR_SAVEGAME); + g_weaponVelocity = gi.cvar("g_weaponVelocity", "1.0", CVAR_ARCHIVE | CVAR_CHEAT); + g_weaponAltVelocity = gi.cvar("g_weaponAltVelocity", "1.0", CVAR_ARCHIVE | CVAR_CHEAT); + //g_handicap_hp = gi.cvar("handicap_hp", "100", CVAR_ARCHIVE | CVAR_SAVEGAME | CVAR_NORESTART | CVAR_USERINFO); + //g_handicap_armor = gi.cvar("handicap_armor", "100", CVAR_ARCHIVE | CVAR_SAVEGAME | CVAR_NORESTART | CVAR_USERINFO); + g_handicap_maxArmor = gi.cvar("handicap_maxArmor", "100", CVAR_ARCHIVE | CVAR_SAVEGAME | CVAR_NORESTART); + g_handicap_matchNPChp = gi.cvar("handicap_matchNPChp", "0", CVAR_CHEAT | CVAR_ARCHIVE | CVAR_SAVEGAME | CVAR_NORESTART); + g_saberDeflectAutoAim = gi.cvar("g_saberDeflectAutoAim", "0", CVAR_SAVEGAME); + g_playerCheatPowers = gi.cvar("g_playerCheatPowers", "0", CVAR_ARCHIVE | CVAR_SAVEGAME | CVAR_CHEAT); + g_moonJump = gi.cvar("g_moonJump", "0", CVAR_ARCHIVE | CVAR_CHEAT); + g_saberNewThrows = gi.cvar("g_saberNewThrows", "1", CVAR_ARCHIVE); + g_flippedHolsters = gi.cvar( "g_flippedHolsters", "0", CVAR_ARCHIVE );//if 1, saber faces up when holstered not down + g_noIgniteTwirl = gi.cvar( "g_noIgniteTwirl", "0", CVAR_ARCHIVE );//if 1, don't do ignite twirl + g_forceRegenTime = gi.cvar( "g_forceRegenTime", "100", CVAR_ARCHIVE );//Force regen time cvar similar to MP } /* ============ @@ -704,12 +867,13 @@ InitGame int giMapChecksum; SavedGameJustLoaded_e g_eSavedGameJustLoaded; qboolean g_qbLoadTransition = qfalse; +void G_LoadExtraEntitiesFile( void ); void InitGame( const char *mapname, const char *spawntarget, int checkSum, const char *entities, int levelTime, int randomSeed, int globalTime, SavedGameJustLoaded_e eSavedGameJustLoaded, qboolean qbLoadTransition ) { //rww - default this to 0, we will auto-set it to 1 if we run into a terrain ent gi.cvar_set("RMG", "0"); - g_bCollidableRoffs = false; + g_bCollidableRoffs = qfalse; giMapChecksum = checkSum; g_eSavedGameJustLoaded = eSavedGameJustLoaded; @@ -748,7 +912,7 @@ void InitGame( const char *mapname, const char *spawntarget, int checkSum, cons ClearAllInUse(); // initialize all clients for this game level.maxclients = 1; - level.clients = (struct gclient_s *) G_Alloc( level.maxclients * sizeof(level.clients[0]) ); + level.clients = (gclient_t*) G_Alloc( level.maxclients * sizeof(level.clients[0]) ); memset(level.clients, 0, level.maxclients * sizeof(level.clients[0])); // set client fields on player @@ -778,6 +942,8 @@ void InitGame( const char *mapname, const char *spawntarget, int checkSum, cons // parse the key/value pairs and spawn gentities G_SpawnEntitiesFromString( entities ); + + G_LoadExtraEntitiesFile(); // general initialization G_FindTeams(); @@ -1129,6 +1295,11 @@ static void G_Animate ( gentity_t *self ) gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, self->startFrame, self->endFrame, BONE_ANIM_OVERRIDE_FREEZE, 1.0f, cg.time, -1, -1 ); + if ( self->headModel > 0) + { + gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->headModel], self->headRootBone, + self->startFrame, self->endFrame, BONE_ANIM_OVERRIDE_FREEZE, 1.0f, cg.time, -1, -1 ); + } return; } @@ -1912,6 +2083,8 @@ void G_RunFrame( int levelTime ) { //Look to clear out old events ClearPlayerAlertEvents(); + + WorkshopThink(); //Run the frame for all entities // for ( i = 0, ent = &g_entities[0]; i < globals.num_entities ; i++, ent++) @@ -2102,16 +2275,26 @@ extern int delayedShutDown; extern qboolean player_locked; -void G_LoadSave_WriteMiscData(void) +void G_LoadSave_WriteMiscData() { - gi.AppendToSaveGame(INT_ID('L','C','K','D'), &player_locked, sizeof(player_locked)); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.write_chunk( + INT_ID('L', 'C', 'K', 'D'), + ::player_locked); } -void G_LoadSave_ReadMiscData(void) +void G_LoadSave_ReadMiscData() { - gi.ReadFromSaveGame(INT_ID('L','C','K','D'), &player_locked, sizeof(player_locked), NULL); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.read_chunk( + INT_ID('L', 'C', 'K', 'D'), + ::player_locked); } diff --git a/code/game/g_misc.cpp b/code/game/g_misc.cpp index 1a3935beca..3ae7f418fa 100644 --- a/code/game/g_misc.cpp +++ b/code/game/g_misc.cpp @@ -192,7 +192,7 @@ void misc_dlight_use ( gentity_t *ent, gentity_t *other, gentity_t *activator ) { G_ActivateBehavior(ent,BSET_USE); - ent->misc_dlight_active = !ent->misc_dlight_active; //toggle + ent->misc_dlight_active = (qboolean)!ent->misc_dlight_active; //toggle misc_lightstyle_set (ent); } @@ -2034,7 +2034,7 @@ void shield_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *a { self->setTime = level.time + 100; - dif = 100 - activator->client->ps.stats[STAT_ARMOR]; // FIXME: define for max armor? + dif = g_handicap_maxArmor->integer - activator->client->ps.stats[STAT_ARMOR]; if ( dif > 0 && self->count ) // Already at full armor?..and do I even have anything to give { @@ -2071,7 +2071,7 @@ void shield_power_converter_use( gentity_t *self, gentity_t *other, gentity_t *a self->s.frame = 1; } } - else if ( activator->client->ps.stats[STAT_ARMOR] >= 100 ) // FIXME: define for max + else if ( activator->client->ps.stats[STAT_ARMOR] >= g_handicap_maxArmor->integer ) // FIXME: define for max { // play full sound G_Sound( self, G_SoundIndex( "sound/interface/shieldcon_done.mp3" )); @@ -2771,7 +2771,7 @@ void gas_random_jet( gentity_t *self ) G_PlayEffect( "env/mini_gasjet", pt ); - self->nextthink = level.time + random() * 16000 + 12000; // do this rarely + self->nextthink = level.time + Q_flrand(0.0f, 1.0f) * 16000 + 12000; // do this rarely } //------------------------------------------------------------ @@ -2827,7 +2827,7 @@ void SP_misc_gas_tank( gentity_t *ent ) ent->e_DieFunc = dieF_misc_model_breakable_die; ent->e_ThinkFunc = thinkF_gas_random_jet; - ent->nextthink = level.time + random() * 12000 + 6000; // do this rarely + ent->nextthink = level.time + Q_flrand(0.0f, 1.0f) * 12000 + 6000; // do this rarely } /*QUAKED misc_crystal_crate (1 0 0.25) (-34 -34 0) (34 34 44) NON_SOLID @@ -3158,3 +3158,46 @@ void SP_misc_cubemap( gentity_t *ent ) { G_FreeEntity( ent ); } + +/*QUAKED misc_radar_icon (0 0.5 0) (-4 -4 -4) (4 4 4) STARTOFF + Draws a radar icon + + STARTOFF - Starts off + icon - which icon to display + + toggles on and off when targeted + */ + +void radar_icon_use ( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + G_ActivateBehavior(ent,BSET_USE); + + ent->misc_dlight_active = (qboolean)!ent->misc_dlight_active; //toggle + if (ent->misc_dlight_active) + { + ent->svFlags |= SVF_BROADCAST; + ent->s.eFlags2 |= EF2_RADAROBJECT; + } + else + { + ent->svFlags &= ~SVF_BROADCAST; + ent->s.eFlags2 &= ~EF2_RADAROBJECT; + } +} + +void SP_misc_radar_icon( gentity_t *self ){ + G_SetOrigin( self, self->s.origin ); + G_SetAngles( self, self->s.angles ); + if ( self->radarIcon && self->radarIcon[0] ) + { + self->s.radarIcon = G_IconIndex( self->radarIcon ); + } + self->e_UseFunc = useF_radar_icon_use; + self->misc_dlight_active = qfalse; + if ( !(self->spawnflags & 1) ) + {//Turn myself on now + GEntity_UseFunc( self, self, self ); + } + gi.linkentity(self); +} + diff --git a/code/game/g_misc_model.cpp b/code/game/g_misc_model.cpp index 8a01b39dc6..e73d224bf2 100644 --- a/code/game/g_misc_model.cpp +++ b/code/game/g_misc_model.cpp @@ -384,18 +384,18 @@ void GunRackAddItem( gitem_t *gun, vec3_t org, vec3_t angs, float ffwd, float fr { if ( t == YAW ) { - it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 180 + crandom() * 14 ); + it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 180 + Q_flrand(-1.0f, 1.0f) * 14 ); } else { - it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + crandom() * 4 ); + it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + Q_flrand(-1.0f, 1.0f) * 4 ); } } else { if ( t == YAW ) { - it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 90 + crandom() * 4 ); + it_ent->s.angles[t] = AngleNormalize180( it_ent->s.angles[t] + 90 + Q_flrand(-1.0f, 1.0f) * 4 ); } } } @@ -463,7 +463,7 @@ void SP_misc_model_gun_rack( gentity_t *ent ) { for ( int i = 0; i < ct; i++ ) { - GunRackAddItem( itemList[i], ent->s.origin, ent->s.angles, crandom() * 2, ( i - 1 ) * 9 + crandom() * 2, ofz[i] ); + GunRackAddItem( itemList[i], ent->s.origin, ent->s.angles, Q_flrand(-1.0f, 1.0f) * 2, ( i - 1 ) * 9 + Q_flrand(-1.0f, 1.0f) * 2, ofz[i] ); } } @@ -638,7 +638,7 @@ void spawn_rack_goods( gentity_t *ent ) { for ( int i = 0; i < ct; i++ ) { - GunRackAddItem( itemList[i], ent->s.origin, ent->s.angles, crandom() * 0.5f, (i-1)* 8, 7.0f ); + GunRackAddItem( itemList[i], ent->s.origin, ent->s.angles, Q_flrand(-1.0f, 1.0f) * 0.5f, (i-1)* 8, 7.0f ); } } @@ -674,9 +674,9 @@ void spawn_rack_goods( gentity_t *ent ) { // since we may have to put up a health pack on the shelf, we should know where we randomly put // the gun so we don't put the pack on the same spot..so pick either the left or right side - pos = ( random() > .5 ) ? -1 : 1; + pos = ( Q_flrand(0.0f, 1.0f) > .5 ) ? -1 : 1; - GunRackAddItem( it, ent->s.origin, ent->s.angles, crandom() * 2, ( random() * 6 + 4 ) * pos, v_off ); + GunRackAddItem( it, ent->s.origin, ent->s.angles, Q_flrand(-1.0f, 1.0f) * 2, ( Q_flrand(0.0f, 1.0f) * 6 + 4 ) * pos, v_off ); } } @@ -686,7 +686,7 @@ void spawn_rack_goods( gentity_t *ent ) if ( !pos ) { // we haven't picked a side already... - pos = ( random() > .5 ) ? -1 : 1; + pos = ( Q_flrand(0.0f, 1.0f) > .5 ) ? -1 : 1; } else { @@ -694,7 +694,7 @@ void spawn_rack_goods( gentity_t *ent ) pos *= -1; } - GunRackAddItem( health, ent->s.origin, ent->s.angles, crandom() * 0.5f, ( random() * 4 + 4 ) * pos, 24 ); + GunRackAddItem( health, ent->s.origin, ent->s.angles, Q_flrand(-1.0f, 1.0f) * 0.5f, ( Q_flrand(0.0f, 1.0f) * 4 + 4 ) * pos, 24 ); } ent->s.modelindex = G_ModelIndex( "models/map_objects/kejim/weaponsrung.md3" ); @@ -753,8 +753,8 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att if ( health ) { - temp[0] = org[0] + crandom() * 8 + 16; - temp[1] = org[1] + crandom() * 8 + 16; + temp[0] = org[0] + Q_flrand(-1.0f, 1.0f) * 8 + 16; + temp[1] = org[1] + Q_flrand(-1.0f, 1.0f) * 8 + 16; LaunchItem( health, temp, vec3_origin, NULL ); } @@ -765,8 +765,8 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att if ( shields ) { - temp[0] = org[0] + crandom() * 8 - 16; - temp[1] = org[1] + crandom() * 8 + 16; + temp[0] = org[0] + Q_flrand(-1.0f, 1.0f) * 8 - 16; + temp[1] = org[1] + Q_flrand(-1.0f, 1.0f) * 8 + 16; LaunchItem( shields, temp, vec3_origin, NULL ); } @@ -778,8 +778,8 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att if ( bacta ) { - temp[0] = org[0] + crandom() * 8 - 16; - temp[1] = org[1] + crandom() * 8 - 16; + temp[0] = org[0] + Q_flrand(-1.0f, 1.0f) * 8 - 16; + temp[1] = org[1] + Q_flrand(-1.0f, 1.0f) * 8 - 16; LaunchItem( bacta, temp, vec3_origin, NULL ); } @@ -791,8 +791,8 @@ void misc_model_cargo_die( gentity_t *self, gentity_t *inflictor, gentity_t *att if ( batteries ) { - temp[0] = org[0] + crandom() * 8 + 16; - temp[1] = org[1] + crandom() * 8 - 16; + temp[0] = org[0] + Q_flrand(-1.0f, 1.0f) * 8 + 16; + temp[1] = org[1] + Q_flrand(-1.0f, 1.0f) * 8 - 16; LaunchItem( batteries, temp, vec3_origin, NULL ); } diff --git a/code/game/g_missile.cpp b/code/game/g_missile.cpp index fd243eee6c..65f4013ad7 100644 --- a/code/game/g_missile.cpp +++ b/code/game/g_missile.cpp @@ -24,92 +24,93 @@ along with this program; if not, see . #include "g_local.h" #include "g_functions.h" #include "wp_saber.h" -#include "bg_local.h" +#include "bg_local.h" #include "../cgame/cg_local.h" #include "b_local.h" #ifdef _DEBUG - #include +#include #endif //_DEBUG -extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f ); -qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ); -extern qboolean PM_SaberInParry( int move ); -extern qboolean PM_SaberInReflect( int move ); -extern qboolean PM_SaberInIdle( int move ); -extern qboolean PM_SaberInAttack( int move ); -extern qboolean PM_SaberInTransitionAny( int move ); -extern qboolean PM_SaberInSpecialAttack( int anim ); +extern qboolean InFront(vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f); +qboolean LogAccuracyHit(gentity_t *target, gentity_t *attacker); +extern qboolean PM_SaberInParry(int move); +extern qboolean PM_SaberInReflect(int move); +extern qboolean PM_SaberInIdle(int move); +extern qboolean PM_SaberInAttack(int move); +extern qboolean PM_SaberInTransitionAny(int move); +extern qboolean PM_SaberInSpecialAttack(int anim); +//extern qboolean PM_WalkingOrIdle(gentity_t *self); //------------------------------------------------------------------------- -void G_MissileBounceEffect( gentity_t *ent, vec3_t org, vec3_t dir, qboolean hitWorld ) +void G_MissileBounceEffect(gentity_t *ent, vec3_t org, vec3_t dir, qboolean hitWorld) { //FIXME: have an EV_BOUNCE_MISSILE event that checks the s.weapon and does the appropriate effect - switch( ent->s.weapon ) + switch (ent->s.weapon) { case WP_BOWCASTER: - if ( hitWorld ) + if (hitWorld) { - G_PlayEffect( "bowcaster/bounce_wall", org, dir ); + G_PlayEffect("bowcaster/bounce_wall", org, dir); } else { - G_PlayEffect( "bowcaster/deflect", ent->currentOrigin, dir ); + G_PlayEffect("bowcaster/deflect", ent->currentOrigin, dir); } break; case WP_BLASTER: case WP_BRYAR_PISTOL: case WP_BLASTER_PISTOL: - G_PlayEffect( "blaster/deflect", ent->currentOrigin, dir ); + G_PlayEffect("blaster/deflect", ent->currentOrigin, dir); break; default: - { - gentity_t *tent = G_TempEntity( org, EV_GRENADE_BOUNCE ); - VectorCopy( dir, tent->pos1 ); - tent->s.weapon = ent->s.weapon; - } + { + gentity_t *tent = G_TempEntity(org, EV_GRENADE_BOUNCE); + VectorCopy(dir, tent->pos1); + tent->s.weapon = ent->s.weapon; + } break; } } -void G_MissileReflectEffect( gentity_t *ent, vec3_t org, vec3_t dir ) +void G_MissileReflectEffect(gentity_t *ent, vec3_t org, vec3_t dir) { //FIXME: have an EV_BOUNCE_MISSILE event that checks the s.weapon and does the appropriate effect - switch( ent->s.weapon ) + switch (ent->s.weapon) { case WP_BOWCASTER: - G_PlayEffect( "bowcaster/deflect", ent->currentOrigin, dir ); + G_PlayEffect("bowcaster/deflect", ent->currentOrigin, dir); break; case WP_BLASTER: case WP_BRYAR_PISTOL: case WP_BLASTER_PISTOL: default: - G_PlayEffect( "blaster/deflect", ent->currentOrigin, dir ); + G_PlayEffect("blaster/deflect", ent->currentOrigin, dir); break; } } //------------------------------------------------------------------------- -static void G_MissileStick( gentity_t *missile, gentity_t *other, trace_t *tr ) +static void G_MissileStick(gentity_t *missile, gentity_t *other, trace_t *tr) { - if ( other->NPC || !Q_stricmp( other->classname, "misc_model_breakable" )) + if (other->NPC || !Q_stricmp(other->classname, "misc_model_breakable")) { // we bounce off of NPC's and misc model breakables because sticking to them requires too much effort vec3_t velocity; - int hitTime = level.previousTime + ( level.time - level.previousTime ) * tr->fraction; + int hitTime = level.previousTime + (level.time - level.previousTime) * tr->fraction; - EvaluateTrajectoryDelta( &missile->s.pos, hitTime, velocity ); + EvaluateTrajectoryDelta(&missile->s.pos, hitTime, velocity); - float dot = DotProduct( velocity, tr->plane.normal ); - G_SetOrigin( missile, tr->endpos ); - VectorMA( velocity, -1.6f * dot, tr->plane.normal, missile->s.pos.trDelta ); - VectorMA( missile->s.pos.trDelta, 10, tr->plane.normal, missile->s.pos.trDelta ); + float dot = DotProduct(velocity, tr->plane.normal); + G_SetOrigin(missile, tr->endpos); + VectorMA(velocity, -1.6f * dot, tr->plane.normal, missile->s.pos.trDelta); + VectorMA(missile->s.pos.trDelta, 10, tr->plane.normal, missile->s.pos.trDelta); missile->s.pos.trTime = level.time - 10; // move a bit on the first frame // check for stop - if ( tr->entityNum >= 0 && tr->entityNum < ENTITYNUM_WORLD && - tr->plane.normal[2] > 0.7 && missile->s.pos.trDelta[2] < 40 ) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7 + if (tr->entityNum >= 0 && tr->entityNum < ENTITYNUM_WORLD && + tr->plane.normal[2] > 0.7 && missile->s.pos.trDelta[2] < 40) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7 { missile->nextthink = level.time + 100; } @@ -122,14 +123,14 @@ static void G_MissileStick( gentity_t *missile, gentity_t *other, trace_t *tr ) return; // don't stick yet } - if ( missile->e_TouchFunc != touchF_NULL ) + if (missile->e_TouchFunc != touchF_NULL) { - GEntity_TouchFunc( missile, other, tr ); + GEntity_TouchFunc(missile, other, tr); } - G_AddEvent( missile, EV_MISSILE_STICK, 0 ); + G_AddEvent(missile, EV_MISSILE_STICK, 0); - if ( other->s.eType == ET_MOVER || other->e_DieFunc == dieF_funcBBrushDie || other->e_DieFunc == dieF_funcGlassDie) + if (other->s.eType == ET_MOVER || other->e_DieFunc == dieF_funcBBrushDie || other->e_DieFunc == dieF_funcGlassDie) { // movers and breakable brushes need extra info...so sticky missiles can ride lifts and blow up when the thing they are attached to goes away. missile->s.groundEntityNum = tr->entityNum; @@ -140,157 +141,247 @@ static void G_MissileStick( gentity_t *missile, gentity_t *other, trace_t *tr ) ================ G_ReflectMissile - Reflect the missile roughly back at it's owner +Reflect the missile roughly back at it's owner ================ */ -extern gentity_t *Jedi_FindEnemyInCone( gentity_t *self, gentity_t *fallback, float minDot ); -void G_ReflectMissile( gentity_t *ent, gentity_t *missile, vec3_t forward ) +vec3_t g_crosshairWorldCoord = { 0, 0, 0 }; +extern gentity_t *Jedi_FindEnemyInCone(gentity_t *self, gentity_t *fallback, float minDot); +extern cvar_t *g_saberAutoBlocking; +extern cvar_t *g_saberDeflectAutoAim; +void G_ReflectMissile(gentity_t *ent, gentity_t *missile, vec3_t forward, forcePowers_t powerToUse) { vec3_t bounce_dir; int i; float speed; + qboolean perfectReflection = qfalse; qboolean reflected = qfalse; gentity_t *owner = ent; - if ( ent->owner ) + if (ent->owner) { owner = ent->owner; } //save the original speed - speed = VectorNormalize( missile->s.pos.trDelta ); - - if ( ent && owner && owner->client && !owner->client->ps.saberInFlight && - (owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] > FORCE_LEVEL_2 || (owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE]>FORCE_LEVEL_1&&!Q_irand( 0, 3 )) ) ) - {//if high enough defense skill and saber in-hand (100% at level 3, 25% at level 2, 0% at level 1), reflections are perfectly deflected toward an enemy - gentity_t *enemy; - if ( owner->enemy && Q_irand( 0, 3 ) ) - {//toward current enemy 75% of the time - enemy = owner->enemy; + speed = VectorNormalize(missile->s.pos.trDelta); + + if (ent && owner && owner->client && + (owner->client->ps.forcePowerLevel[powerToUse] > FORCE_LEVEL_2 || (owner->client->ps.forcePowerLevel[powerToUse]>FORCE_LEVEL_1&&!Q_irand(0, 3)))) + //if high enough force skill (100% at level 3, 25% at level 2, 0% at level 1), reflections are perfectly deflected toward an enemy + { + perfectReflection = qtrue; + } + + if (powerToUse == FP_SABER_DEFENSE) + { + if (owner->client->ps.saberInFlight) + {//but need saber in-hand for perfect reflection + perfectReflection = qfalse; } - else - {//find another enemy - enemy = Jedi_FindEnemyInCone( owner, owner->enemy, 0.3f ); + + if (g_spskill->integer >= 2 && !g_saberAutoBlocking->integer && owner->client->ps.saberBlockingTime < level.time) + {//but need to be blocking for perfect reflection on higher difficulties + perfectReflection = qfalse; } - if ( enemy ) + /* + if (!PM_WalkingOrIdle(owner) || !PM_SaberInParry(owner->client->ps.saberMove)) + {//but need to be blocking for perfect reflection on higher difficulties + perfectReflection = qfalse; + } + */ + } + + if (perfectReflection) + { + if (g_saberDeflectAutoAim->integer || owner->s.clientNum >= MAX_CLIENTS) //either by autoaim (lower difficulties) { - vec3_t bullseye; - CalcEntitySpot( enemy, SPOT_HEAD, bullseye ); - bullseye[0] += Q_irand( -4, 4 ); - bullseye[1] += Q_irand( -4, 4 ); - bullseye[2] += Q_irand( -16, 4 ); - VectorSubtract( bullseye, missile->currentOrigin, bounce_dir ); - VectorNormalize( bounce_dir ); - if ( !PM_SaberInParry( owner->client->ps.saberMove ) - && !PM_SaberInReflect( owner->client->ps.saberMove ) - && !PM_SaberInIdle( owner->client->ps.saberMove ) ) + gentity_t *enemy; + if (owner->enemy && Q_irand(0, 3)) + {//toward current enemy 75% of the time + enemy = owner->enemy; + } + else + {//find another enemy + enemy = Jedi_FindEnemyInCone(owner, owner->enemy, 0.3f); + } + if (enemy) + { + vec3_t bullseye; + CalcEntitySpot(enemy, SPOT_HEAD, bullseye); + bullseye[0] += Q_irand(-4, 4); + bullseye[1] += Q_irand(-4, 4); + bullseye[2] += Q_irand(-16, 4); + VectorSubtract(bullseye, missile->currentOrigin, bounce_dir); + VectorNormalize(bounce_dir); + if (!PM_SaberInParry(owner->client->ps.saberMove) + && !PM_SaberInReflect(owner->client->ps.saberMove) + && !PM_SaberInIdle(owner->client->ps.saberMove)) + {//a bit more wild + if (PM_SaberInAttack(owner->client->ps.saberMove) + || PM_SaberInTransitionAny(owner->client->ps.saberMove) + || PM_SaberInSpecialAttack(owner->client->ps.torsoAnim)) + {//moderately more wild + for (i = 0; i < 3; i++) + { + bounce_dir[i] += Q_flrand(-0.2f, 0.2f); + } + } + else + {//mildly more wild + for (i = 0; i < 3; i++) + { + bounce_dir[i] += Q_flrand(-0.1f, 0.1f); + } + } + } + VectorNormalize(bounce_dir); + reflected = qtrue; + } + } + else //or by where the crosshair is (higher difficulties) + { + VectorSubtract(g_crosshairWorldCoord, missile->currentOrigin, bounce_dir); + VectorNormalize(bounce_dir); + if (!PM_SaberInParry(owner->client->ps.saberMove) + && !PM_SaberInReflect(owner->client->ps.saberMove) + && !PM_SaberInIdle(owner->client->ps.saberMove)) {//a bit more wild - if ( PM_SaberInAttack( owner->client->ps.saberMove ) - || PM_SaberInTransitionAny( owner->client->ps.saberMove ) - || PM_SaberInSpecialAttack( owner->client->ps.torsoAnim ) ) + if (PM_SaberInAttack(owner->client->ps.saberMove) + || PM_SaberInTransitionAny(owner->client->ps.saberMove) + || PM_SaberInSpecialAttack(owner->client->ps.torsoAnim)) {//moderately more wild - for ( i = 0; i < 3; i++ ) + for (i = 0; i < 3; i++) { - bounce_dir[i] += Q_flrand( -0.2f, 0.2f ); + bounce_dir[i] += Q_flrand(-0.2f, 0.2f); } } else {//mildly more wild - for ( i = 0; i < 3; i++ ) + for (i = 0; i < 3; i++) { - bounce_dir[i] += Q_flrand( -0.1f, 0.1f ); + bounce_dir[i] += Q_flrand(-0.1f, 0.1f); } } } - VectorNormalize( bounce_dir ); + VectorNormalize(bounce_dir); reflected = qtrue; } } - if ( !reflected ) + if (!reflected) { - if ( missile->owner && missile->s.weapon != WP_SABER ) - {//bounce back at them if you can - VectorSubtract( missile->owner->currentOrigin, missile->currentOrigin, bounce_dir ); - VectorNormalize( bounce_dir ); + if (g_saberDeflectAutoAim->integer || owner->s.clientNum >= MAX_CLIENTS) + { + if (missile->owner && missile->s.weapon != WP_SABER) + {//bounce back at them if you can + VectorSubtract(missile->owner->currentOrigin, missile->currentOrigin, bounce_dir); + VectorNormalize(bounce_dir); + } + else + { + vec3_t missile_dir; + + VectorSubtract(ent->currentOrigin, missile->currentOrigin, missile_dir); + VectorCopy(missile->s.pos.trDelta, bounce_dir); + VectorScale(bounce_dir, DotProduct(forward, missile_dir), bounce_dir); + VectorNormalize(bounce_dir); + } } - else + else //deflect off at an angle. { - vec3_t missile_dir; + vec3_t deflect_dir, missile_dir; + float forceFactor; + VectorSubtract(g_crosshairWorldCoord, missile->currentOrigin, deflect_dir); + VectorCopy(missile->s.pos.trDelta, missile_dir); + VectorNormalize(missile_dir); + VectorNormalize(deflect_dir); + + //bigger forceFactors make the reflected shots go closer to the crosshair + switch (owner->client->ps.forcePowerLevel[powerToUse]) + { + case FORCE_LEVEL_1: + forceFactor = 2.0f; + break; + case FORCE_LEVEL_2: + forceFactor = 3.0f; + break; + default: + forceFactor = 10.0f; + break; + } + + VectorMA(missile_dir, forceFactor, deflect_dir, bounce_dir); - VectorSubtract( ent->currentOrigin, missile->currentOrigin, missile_dir ); - VectorCopy( missile->s.pos.trDelta, bounce_dir ); - VectorScale( bounce_dir, DotProduct( forward, missile_dir ), bounce_dir ); - VectorNormalize( bounce_dir ); + VectorNormalize(bounce_dir); } - if ( owner->s.weapon == WP_SABER && owner->client ) + if (owner->s.weapon == WP_SABER && owner->client && powerToUse == FP_SABER_DEFENSE) {//saber - if ( owner->client->ps.saberInFlight ) + if (owner->client->ps.saberInFlight) {//reflecting off a thrown saber is totally wild - for ( i = 0; i < 3; i++ ) + for (i = 0; i < 3; i++) { - bounce_dir[i] += Q_flrand( -0.8f, 0.8f ); + bounce_dir[i] += Q_flrand(-0.8f, 0.8f); } } - else if ( owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] <= FORCE_LEVEL_1 ) - {// at level 1 - for ( i = 0; i < 3; i++ ) + else if (owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] <= FORCE_LEVEL_1) + {// at level 1 or below + for (i = 0; i < 3; i++) { - bounce_dir[i] += Q_flrand( -0.4f, 0.4f ); + bounce_dir[i] += Q_flrand(-0.4f, 0.4f); } } else {// at level 2 - for ( i = 0; i < 3; i++ ) + for (i = 0; i < 3; i++) { - bounce_dir[i] += Q_flrand( -0.2f, 0.2f ); + bounce_dir[i] += Q_flrand(-0.2f, 0.2f); } } - if ( !PM_SaberInParry( owner->client->ps.saberMove ) - && !PM_SaberInReflect( owner->client->ps.saberMove ) - && !PM_SaberInIdle( owner->client->ps.saberMove ) ) + if (!PM_SaberInParry(owner->client->ps.saberMove) + && !PM_SaberInReflect(owner->client->ps.saberMove) + && !PM_SaberInIdle(owner->client->ps.saberMove)) {//a bit more wild - if ( PM_SaberInAttack( owner->client->ps.saberMove ) - || PM_SaberInTransitionAny( owner->client->ps.saberMove ) - || PM_SaberInSpecialAttack( owner->client->ps.torsoAnim ) ) + if (PM_SaberInAttack(owner->client->ps.saberMove) + || PM_SaberInTransitionAny(owner->client->ps.saberMove) + || PM_SaberInSpecialAttack(owner->client->ps.torsoAnim)) {//really wild - for ( i = 0; i < 3; i++ ) + for (i = 0; i < 3; i++) { - bounce_dir[i] += Q_flrand( -0.3f, 0.3f ); + bounce_dir[i] += Q_flrand(-0.3f, 0.3f); } } else {//mildly more wild - for ( i = 0; i < 3; i++ ) + for (i = 0; i < 3; i++) { - bounce_dir[i] += Q_flrand( -0.1f, 0.1f ); + bounce_dir[i] += Q_flrand(-0.1f, 0.1f); } } } } else {//some other kind of reflection - for ( i = 0; i < 3; i++ ) + for (i = 0; i < 3; i++) { - bounce_dir[i] += Q_flrand( -0.2f, 0.2f ); + bounce_dir[i] += Q_flrand(-0.2f, 0.2f); } } } - VectorNormalize( bounce_dir ); - VectorScale( bounce_dir, speed, missile->s.pos.trDelta ); + VectorNormalize(bounce_dir); + VectorScale(bounce_dir, speed, missile->s.pos.trDelta); #ifdef _DEBUG - assert( !Q_isnan(missile->s.pos.trDelta[0])&&!Q_isnan(missile->s.pos.trDelta[1])&&!Q_isnan(missile->s.pos.trDelta[2])); + assert(!Q_isnan(missile->s.pos.trDelta[0]) && !Q_isnan(missile->s.pos.trDelta[1]) && !Q_isnan(missile->s.pos.trDelta[2])); #endif// _DEBUG missile->s.pos.trTime = level.time - 10; // move a bit on the very first frame - VectorCopy( missile->currentOrigin, missile->s.pos.trBase ); - if ( missile->s.weapon != WP_SABER ) + VectorCopy(missile->currentOrigin, missile->s.pos.trBase); + if (missile->s.weapon != WP_SABER) {//you are mine, now! - if ( !missile->lastEnemy ) + if (!missile->lastEnemy) {//remember who originally shot this missile missile->lastEnemy = missile->owner; } missile->owner = owner; } - if ( missile->s.weapon == WP_ROCKET_LAUNCHER ) + if (missile->s.weapon == WP_ROCKET_LAUNCHER) {//stop homing missile->e_ThinkFunc = thinkF_NULL; } @@ -302,31 +393,31 @@ G_BounceRollMissile ================ */ -void G_BounceRollMissile( gentity_t *ent, trace_t *trace ) +void G_BounceRollMissile(gentity_t *ent, trace_t *trace) { vec3_t velocity, normal; float dot, speedXY, velocityZ, normalZ; int hitTime; // reflect the velocity on the trace plane - hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; - EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); + hitTime = level.previousTime + (level.time - level.previousTime) * trace->fraction; + EvaluateTrajectoryDelta(&ent->s.pos, hitTime, velocity); //Do horizontal //FIXME: Need to roll up, down slopes velocityZ = velocity[2]; velocity[2] = 0; - speedXY = VectorLength( velocity );//friction - VectorCopy( trace->plane.normal, normal ); + speedXY = VectorLength(velocity);//friction + VectorCopy(trace->plane.normal, normal); normalZ = normal[2]; normal[2] = 0; - dot = DotProduct( velocity, normal ); - VectorMA( velocity, -2*dot, normal, ent->s.pos.trDelta ); + dot = DotProduct(velocity, normal); + VectorMA(velocity, -2 * dot, normal, ent->s.pos.trDelta); //now do the z reflection //FIXME: Bobbles when it stops - VectorSet( velocity, 0, 0, velocityZ ); - VectorSet( normal, 0, 0, normalZ ); - dot = DotProduct( velocity, normal )*-1; - if ( dot > 10 ) + VectorSet(velocity, 0, 0, velocityZ); + VectorSet(normal, 0, 0, normalZ); + dot = DotProduct(velocity, normal)*-1; + if (dot > 10) { ent->s.pos.trDelta[2] = dot*0.3f;//not very bouncy } @@ -336,23 +427,23 @@ void G_BounceRollMissile( gentity_t *ent, trace_t *trace ) } // check for stop - if ( speedXY <= 0 ) + if (speedXY <= 0) { - G_SetOrigin( ent, trace->endpos ); - VectorCopy( ent->currentAngles, ent->s.apos.trBase ); - VectorClear( ent->s.apos.trDelta ); + G_SetOrigin(ent, trace->endpos); + VectorCopy(ent->currentAngles, ent->s.apos.trBase); + VectorClear(ent->s.apos.trDelta); ent->s.apos.trType = TR_STATIONARY; return; } //FIXME: rolling needs to match direction - VectorCopy( ent->currentAngles, ent->s.apos.trBase ); - VectorCopy( ent->s.pos.trDelta, ent->s.apos.trDelta ); + VectorCopy(ent->currentAngles, ent->s.apos.trBase); + VectorCopy(ent->s.pos.trDelta, ent->s.apos.trDelta); //remember this spot - VectorCopy( trace->endpos, ent->currentOrigin ); + VectorCopy(trace->endpos, ent->currentOrigin); ent->s.pos.trTime = hitTime - 10; - VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); + VectorCopy(ent->currentOrigin, ent->s.pos.trBase); //VectorCopy( trace->plane.normal, ent->pos1 ); } @@ -362,50 +453,50 @@ G_BounceMissile ================ */ -void G_BounceMissile( gentity_t *ent, trace_t *trace ) { +void G_BounceMissile(gentity_t *ent, trace_t *trace) { vec3_t velocity; float dot; int hitTime; // reflect the velocity on the trace plane - hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; - EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); - dot = DotProduct( velocity, trace->plane.normal ); - VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta ); + hitTime = level.previousTime + (level.time - level.previousTime) * trace->fraction; + EvaluateTrajectoryDelta(&ent->s.pos, hitTime, velocity); + dot = DotProduct(velocity, trace->plane.normal); + VectorMA(velocity, -2 * dot, trace->plane.normal, ent->s.pos.trDelta); - if ( ent->s.eFlags & EF_BOUNCE_SHRAPNEL ) + if (ent->s.eFlags & EF_BOUNCE_SHRAPNEL) { - VectorScale( ent->s.pos.trDelta, 0.25f, ent->s.pos.trDelta ); + VectorScale(ent->s.pos.trDelta, 0.25f, ent->s.pos.trDelta); ent->s.pos.trType = TR_GRAVITY; // check for stop - if ( trace->plane.normal[2] > 0.7 && ent->s.pos.trDelta[2] < 40 ) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7 + if (trace->plane.normal[2] > 0.7 && ent->s.pos.trDelta[2] < 40) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7 { - G_SetOrigin( ent, trace->endpos ); + G_SetOrigin(ent, trace->endpos); ent->nextthink = level.time + 100; return; } } - else if ( ent->s.eFlags & EF_BOUNCE_HALF ) + else if (ent->s.eFlags & EF_BOUNCE_HALF) { - VectorScale( ent->s.pos.trDelta, 0.5, ent->s.pos.trDelta ); + VectorScale(ent->s.pos.trDelta, 0.5, ent->s.pos.trDelta); // check for stop - if ( trace->plane.normal[2] > 0.7 && ent->s.pos.trDelta[2] < 40 ) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7 + if (trace->plane.normal[2] > 0.7 && ent->s.pos.trDelta[2] < 40) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7 { - if ( ent->s.weapon == WP_THERMAL ) + if (ent->s.weapon == WP_THERMAL) {//roll when you "stop" ent->s.pos.trType = TR_INTERPOLATE; } else { - G_SetOrigin( ent, trace->endpos ); + G_SetOrigin(ent, trace->endpos); ent->nextthink = level.time + 500; return; } } - if ( ent->s.weapon == WP_THERMAL ) + if (ent->s.weapon == WP_THERMAL) { ent->has_bounced = qtrue; } @@ -413,14 +504,14 @@ void G_BounceMissile( gentity_t *ent, trace_t *trace ) { #if 0 // OLD--this looks so wrong. It looked wrong in EF. It just must be wrong. - VectorAdd( ent->currentOrigin, trace->plane.normal, ent->currentOrigin); + VectorAdd(ent->currentOrigin, trace->plane.normal, ent->currentOrigin); ent->s.pos.trTime = level.time - 10; #else // NEW--It would seem that we want to set our trBase to the trace endpos // and set the trTime to the actual time of impact.... - VectorAdd( trace->endpos, trace->plane.normal, ent->currentOrigin ); - if ( hitTime >= level.time ) + VectorAdd(trace->endpos, trace->plane.normal, ent->currentOrigin); + if (hitTime >= level.time) {//trace fraction must have been 1 ent->s.pos.trTime = level.time - 10; } @@ -430,13 +521,13 @@ void G_BounceMissile( gentity_t *ent, trace_t *trace ) { } #endif - VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); - VectorCopy( trace->plane.normal, ent->pos1 ); + VectorCopy(ent->currentOrigin, ent->s.pos.trBase); + VectorCopy(trace->plane.normal, ent->pos1); - if ( ent->s.weapon != WP_SABER + if (ent->s.weapon != WP_SABER && ent->s.weapon != WP_THERMAL && ent->e_clThinkFunc != clThinkF_CG_Limb - && ent->e_ThinkFunc != thinkF_LimbThink ) + && ent->e_ThinkFunc != thinkF_LimbThink) {//not a saber, bouncing thermal or limb //now you can damage the guy you came from ent->owner = NULL; @@ -450,105 +541,105 @@ G_MissileImpact ================ */ -void NoghriGasCloudThink( gentity_t *self ) +void NoghriGasCloudThink(gentity_t *self) { self->nextthink = level.time + FRAMETIME; - AddSightEvent( self->owner, self->currentOrigin, 200, AEL_DANGER, 50 ); + AddSightEvent(self->owner, self->currentOrigin, 200, AEL_DANGER, 50); - if ( self->fx_time < level.time ) + if (self->fx_time < level.time) { - vec3_t up = {0,0,1}; - G_PlayEffect( "noghri_stick/gas_cloud", self->currentOrigin, up ); + vec3_t up = { 0, 0, 1 }; + G_PlayEffect("noghri_stick/gas_cloud", self->currentOrigin, up); self->fx_time = level.time + 250; } - if ( level.time - self->s.time <= 2500 ) + if (level.time - self->s.time <= 2500) { - if ( !Q_irand( 0, 3-g_spskill->integer ) ) + if (!Q_irand(0, 3 - g_spskill->integer)) { - G_RadiusDamage( self->currentOrigin, self->owner, Q_irand( 1, 4 ), self->splashRadius, - self->owner, self->splashMethodOfDeath ); + G_RadiusDamage(self->currentOrigin, self->owner, Q_irand(1, 4), self->splashRadius, + self->owner, self->splashMethodOfDeath); } } - if ( level.time - self->s.time > 3000 ) + if (level.time - self->s.time > 3000) { - G_FreeEntity( self ); + G_FreeEntity(self); } } -void G_SpawnNoghriGasCloud( gentity_t *ent ) +void G_SpawnNoghriGasCloud(gentity_t *ent) {//FIXME: force-pushable/dispersable? ent->freeAfterEvent = qfalse; ent->e_TouchFunc = touchF_NULL; //ent->s.loopSound = G_SoundIndex( "sound/weapons/noghri/smoke.wav" ); //G_SoundOnEnt( ent, CHAN_AUTO, "sound/weapons/noghri/smoke.wav" ); - G_SetOrigin( ent, ent->currentOrigin ); + G_SetOrigin(ent, ent->currentOrigin); ent->e_ThinkFunc = thinkF_NoghriGasCloudThink; ent->nextthink = level.time + FRAMETIME; - vec3_t up = {0,0,1}; - G_PlayEffect( "noghri_stick/gas_cloud", ent->currentOrigin, up ); + vec3_t up = { 0, 0, 1 }; + G_PlayEffect("noghri_stick/gas_cloud", ent->currentOrigin, up); ent->fx_time = level.time + 250; ent->s.time = level.time; } -extern void laserTrapStick( gentity_t *ent, vec3_t endpos, vec3_t normal ); -extern qboolean W_AccuracyLoggableWeapon( int weapon, qboolean alt_fire, int mod ); -void G_MissileImpacted( gentity_t *ent, gentity_t *other, vec3_t impactPos, vec3_t normal, int hitLoc=HL_NONE ) +extern void laserTrapStick(gentity_t *ent, vec3_t endpos, vec3_t normal); +extern qboolean W_AccuracyLoggableWeapon(int weapon, qboolean alt_fire, int mod); +void G_MissileImpacted(gentity_t *ent, gentity_t *other, vec3_t impactPos, vec3_t normal, int hitLoc = HL_NONE) { // impact damage - if ( other->takedamage ) + if (other->takedamage) { // FIXME: wrong damage direction? - if ( ent->damage ) + if (ent->damage) { vec3_t velocity; - EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); - if ( VectorLength( velocity ) == 0 ) + EvaluateTrajectoryDelta(&ent->s.pos, level.time, velocity); + if (VectorLength(velocity) == 0) { velocity[2] = 1; // stepped on a grenade } int damage = ent->damage; - if( other->client ) + if (other->client) { class_t npc_class = other->client->NPC_class; // If we are a robot and we aren't currently doing the full body electricity... - if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || - npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || - npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd - npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) + if (npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || + npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || + npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd + npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY) { // special droid only behaviors - if ( other->client->ps.powerups[PW_SHOCKED] < level.time + 100 ) + if (other->client->ps.powerups[PW_SHOCKED] < level.time + 100) { // ... do the effect for a split second for some more feedback - other->s.powerups |= ( 1 << PW_SHOCKED ); + other->s.powerups |= (1 << PW_SHOCKED); other->client->ps.powerups[PW_SHOCKED] = level.time + 450; } //FIXME: throw some sparks off droids,too } } - G_Damage( other, ent, ent->owner, velocity, - impactPos, damage, - ent->dflags, ent->methodOfDeath, hitLoc); + G_Damage(other, ent, ent->owner, velocity, + impactPos, damage, + ent->dflags, ent->methodOfDeath, hitLoc); - if ( ent->s.weapon == WP_DEMP2 ) + if (ent->s.weapon == WP_DEMP2) {//a hit with demp2 decloaks saboteurs - if ( other && other->client && other->client->NPC_class == CLASS_SABOTEUR ) + if (other && other->client && other->client->NPC_class == CLASS_SABOTEUR) {//FIXME: make this disabled cloak hold for some amount of time? - Saboteur_Decloak( other, Q_irand( 3000, 10000 ) ); - if ( ent->methodOfDeath == MOD_DEMP2_ALT ) + Saboteur_Decloak(other, Q_irand(3000, 10000)); + if (ent->methodOfDeath == MOD_DEMP2_ALT) {//direct hit with alt disabled cloak forever - if ( other->NPC ) + if (other->NPC) {//permanently disable the saboteur's cloak other->NPC->aiFlags &= ~NPCAI_SHIELDS; } @@ -562,24 +653,24 @@ void G_MissileImpacted( gentity_t *ent, gentity_t *other, vec3_t impactPos, vec3 // one, rather than changing the missile into the explosion? //G_FreeEntity(ent); - if ( (other->takedamage && other->client ) || (ent->s.weapon == WP_FLECHETTE && other->contents&CONTENTS_LIGHTSABER) ) + if ((other->takedamage && other->client) || (ent->s.weapon == WP_FLECHETTE && other->contents&CONTENTS_LIGHTSABER)) { - G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( normal ) ); + G_AddEvent(ent, EV_MISSILE_HIT, DirToByte(normal)); ent->s.otherEntityNum = other->s.number; } else { - G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( normal ) ); + G_AddEvent(ent, EV_MISSILE_MISS, DirToByte(normal)); ent->s.otherEntityNum = other->s.number; } - VectorCopy( normal, ent->pos1 ); + VectorCopy(normal, ent->pos1); - if ( ent->owner )//&& ent->owner->s.number == 0 ) + if (ent->owner)//&& ent->owner->s.number == 0 ) { //Add the event - AddSoundEvent( ent->owner, ent->currentOrigin, 256, AEL_SUSPICIOUS, qfalse, qtrue ); - AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 75 ); + AddSoundEvent(ent->owner, ent->currentOrigin, 256, AEL_SUSPICIOUS, qfalse, qtrue); + AddSightEvent(ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 75); } ent->freeAfterEvent = qtrue; @@ -588,64 +679,64 @@ void G_MissileImpacted( gentity_t *ent, gentity_t *other, vec3_t impactPos, vec3 ent->s.eType = ET_GENERAL; //SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth - VectorCopy( impactPos, ent->s.pos.trBase ); + VectorCopy(impactPos, ent->s.pos.trBase); - G_SetOrigin( ent, impactPos ); + G_SetOrigin(ent, impactPos); // splash damage (doesn't apply to person directly hit) - if ( ent->splashDamage ) + if (ent->splashDamage) { - G_RadiusDamage( impactPos, ent->owner, ent->splashDamage, ent->splashRadius, - other, ent->splashMethodOfDeath ); + G_RadiusDamage(impactPos, ent->owner, ent->splashDamage, ent->splashRadius, + other, ent->splashMethodOfDeath); } - if ( ent->s.weapon == WP_NOGHRI_STICK ) + if (ent->s.weapon == WP_NOGHRI_STICK) { - G_SpawnNoghriGasCloud( ent ); + G_SpawnNoghriGasCloud(ent); } - gi.linkentity( ent ); + gi.linkentity(ent); } //------------------------------------------------ -static void G_MissileAddAlerts( gentity_t *ent ) +static void G_MissileAddAlerts(gentity_t *ent) { //Add the event - if ( ent->s.weapon == WP_THERMAL && ((ent->delay-level.time) < 2000 || ent->s.pos.trType == TR_INTERPOLATE) ) + if (ent->s.weapon == WP_THERMAL && ((ent->delay - level.time) < 2000 || ent->s.pos.trType == TR_INTERPOLATE)) {//a thermal about to explode or rolling - if ( (ent->delay-level.time) < 500 ) + if ((ent->delay - level.time) < 500) {//half a second before it explodes! - AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER_GREAT, qfalse, qtrue ); - AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER_GREAT, 20 ); + AddSoundEvent(ent->owner, ent->currentOrigin, ent->splashRadius * 2, AEL_DANGER_GREAT, qfalse, qtrue); + AddSightEvent(ent->owner, ent->currentOrigin, ent->splashRadius * 2, AEL_DANGER_GREAT, 20); } else {//2 seconds until it explodes or it's rolling - AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER, qfalse, qtrue ); - AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER, 20 ); + AddSoundEvent(ent->owner, ent->currentOrigin, ent->splashRadius * 2, AEL_DANGER, qfalse, qtrue); + AddSightEvent(ent->owner, ent->currentOrigin, ent->splashRadius * 2, AEL_DANGER, 20); } } else { - AddSoundEvent( ent->owner, ent->currentOrigin, 128, AEL_DISCOVERED ); - AddSightEvent( ent->owner, ent->currentOrigin, 256, AEL_DISCOVERED, 40 ); + AddSoundEvent(ent->owner, ent->currentOrigin, 128, AEL_DISCOVERED); + AddSightEvent(ent->owner, ent->currentOrigin, 256, AEL_DISCOVERED, 40); } } //------------------------------------------------------ -void G_MissileImpact( gentity_t *ent, trace_t *trace, int hitLoc=HL_NONE ) +void G_MissileImpact(gentity_t *ent, trace_t *trace, int hitLoc = HL_NONE) { gentity_t *other; vec3_t diff; other = &g_entities[trace->entityNum]; - if ( other == ent ) + if (other == ent) { - assert(0&&"missile hit itself!!!"); + assert(0 && "missile hit itself!!!"); return; } - if ( trace->plane.normal[0] == 0.0f && - trace->plane.normal[1] == 0.0f && - trace->plane.normal[2] == 0.0f + if (trace->plane.normal[0] == 0.0f && + trace->plane.normal[1] == 0.0f && + trace->plane.normal[2] == 0.0f ) {//model moved into missile in flight probably... trace->plane.normal[0] = -ent->s.pos.trDelta[0]; @@ -654,17 +745,17 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace, int hitLoc=HL_NONE ) VectorNormalize(trace->plane.normal); } - if ( ent->owner && (other->takedamage||other->client) ) + if (ent->owner && (other->takedamage || other->client)) { - if ( !ent->lastEnemy || ent->lastEnemy == ent->owner ) + if (!ent->lastEnemy || ent->lastEnemy == ent->owner) {//a missile that was not reflected or, if so, still is owned by original owner - if( LogAccuracyHit( other, ent->owner ) ) + if (LogAccuracyHit(other, ent->owner)) { ent->owner->client->ps.persistant[PERS_ACCURACY_HITS]++; } - if ( ent->owner->client && !ent->owner->s.number ) + if (ent->owner->client && !ent->owner->s.number) { - if ( W_AccuracyLoggableWeapon( ent->s.weapon, qfalse, ent->methodOfDeath ) ) + if (W_AccuracyLoggableWeapon(ent->s.weapon, qfalse, ent->methodOfDeath)) { ent->owner->client->sess.missionStats.hits++; } @@ -673,19 +764,20 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace, int hitLoc=HL_NONE ) } // check for bounce //OR: if the surfaceParm is has a reflect property (magnetic shielding) and the missile isn't an exploding missile - qboolean bounce = !!( (!other->takedamage && (ent->s.eFlags&(EF_BOUNCE|EF_BOUNCE_HALF))) || (((trace->surfaceFlags&SURF_FORCEFIELD)||(other->flags&FL_SHIELDED))&&!ent->splashDamage&&!ent->splashRadius&&ent->s.weapon != WP_NOGHRI_STICK) ); - if ( ent->dflags & DAMAGE_HEAVY_WEAP_CLASS ) + qboolean bounce = (qboolean)( (!other->takedamage && (ent->s.eFlags&(EF_BOUNCE|EF_BOUNCE_HALF))) || (((trace->surfaceFlags&SURF_FORCEFIELD)||(other->flags&FL_SHIELDED))&&!ent->splashDamage&&!ent->splashRadius&&ent->s.weapon != WP_NOGHRI_STICK) ); + + if (ent->dflags & DAMAGE_HEAVY_WEAP_CLASS) { // heavy class missiles generally never bounce. bounce = qfalse; } - if ( other->flags & (FL_DMG_BY_HEAVY_WEAP_ONLY | FL_SHIELDED )) + if (other->flags & (FL_DMG_BY_HEAVY_WEAP_ONLY | FL_SHIELDED)) { // Dumb assumption, but I guess we must be a shielded ion_cannon?? We should probably verify // if it's an ion_cannon that's Heavy Weapon only, we don't want to make it shielded do we...? - if ( (!strcmp( "misc_ion_cannon", other->classname )) && (other->flags & FL_SHIELDED) ) + if ((!strcmp("misc_ion_cannon", other->classname)) && (other->flags & FL_SHIELDED)) { // Anything will bounce off of us. bounce = qtrue; @@ -695,94 +787,96 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace, int hitLoc=HL_NONE ) } } - if ( ent->s.weapon == WP_DEMP2 ) + if (ent->s.weapon == WP_DEMP2) { // demp2 shots can never bounce bounce = qfalse; // in fact, alt-charge shots will not call the regular impact functions - if ( ent->alt_fire ) + if (ent->alt_fire) { // detonate at the trace end - VectorCopy( trace->endpos, ent->currentOrigin ); - VectorCopy( trace->plane.normal, ent->pos1 ); - DEMP2_AltDetonate( ent ); + VectorCopy(trace->endpos, ent->currentOrigin); + VectorCopy(trace->plane.normal, ent->pos1); + DEMP2_AltDetonate(ent); return; } } - if ( bounce ) + if (bounce) { // Check to see if there is a bounce count - if ( ent->bounceCount ) + if (ent->bounceCount) { // decrement number of bounces and then see if it should be done bouncing - if ( !(--ent->bounceCount) ) { + if (!(--ent->bounceCount)) { // He (or she) will bounce no more (after this current bounce, that is). - ent->s.eFlags &= ~( EF_BOUNCE | EF_BOUNCE_HALF ); + ent->s.eFlags &= ~(EF_BOUNCE | EF_BOUNCE_HALF); } } - if ( other->NPC ) + if (other->NPC) { - G_Damage( other, ent, ent->owner, ent->currentOrigin, ent->s.pos.trDelta, 0, DAMAGE_NO_DAMAGE, MOD_UNKNOWN ); + G_Damage(other, ent, ent->owner, ent->currentOrigin, ent->s.pos.trDelta, 0, DAMAGE_NO_DAMAGE, MOD_UNKNOWN); } - G_BounceMissile( ent, trace ); + G_BounceMissile(ent, trace); - if ( ent->owner )//&& ent->owner->s.number == 0 ) + if (ent->owner)//&& ent->owner->s.number == 0 ) { - G_MissileAddAlerts( ent ); + G_MissileAddAlerts(ent); } - G_MissileBounceEffect( ent, trace->endpos, trace->plane.normal, trace->entityNum==ENTITYNUM_WORLD ); + + G_MissileBounceEffect( ent, trace->endpos, trace->plane.normal, (qboolean)(trace->entityNum==ENTITYNUM_WORLD) ); return; } // I would glom onto the EF_BOUNCE code section above, but don't feel like risking breaking something else - if ( (!other->takedamage && ( ent->s.eFlags&(EF_BOUNCE_SHRAPNEL) ) ) - || ((trace->surfaceFlags&SURF_FORCEFIELD)&&!ent->splashDamage&&!ent->splashRadius) ) + if ((!other->takedamage && (ent->s.eFlags&(EF_BOUNCE_SHRAPNEL))) + || ((trace->surfaceFlags&SURF_FORCEFIELD) && !ent->splashDamage&&!ent->splashRadius)) { - if ( !(other->contents&CONTENTS_LIGHTSABER) + if (!(other->contents&CONTENTS_LIGHTSABER) || g_spskill->integer <= 0//on easy, it reflects all shots - || (g_spskill->integer == 1 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 )//on medium it won't reflect flechette or demp shots - || (g_spskill->integer >= 2 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_BOWCASTER && ent->s.weapon != WP_REPEATER )//on hard it won't reflect flechette, demp, repeater or bowcaster shots + || (g_spskill->integer == 1 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2)//on medium it won't reflect flechette or demp shots + || (g_spskill->integer >= 2 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_BOWCASTER && ent->s.weapon != WP_REPEATER)//on hard it won't reflect flechette, demp, repeater or bowcaster shots ) { - G_BounceMissile( ent, trace ); + G_BounceMissile(ent, trace); - if ( --ent->bounceCount < 0 ) + if (--ent->bounceCount < 0) { ent->s.eFlags &= ~EF_BOUNCE_SHRAPNEL; } - G_MissileBounceEffect( ent, trace->endpos, trace->plane.normal, trace->entityNum==ENTITYNUM_WORLD ); + + G_MissileBounceEffect( ent, trace->endpos, trace->plane.normal, (qboolean)(trace->entityNum==ENTITYNUM_WORLD) ); return; } } - if ( (!other->takedamage || (other->client && other->health <= 0)) + if ((!other->takedamage || (other->client && other->health <= 0)) && ent->s.weapon == WP_THERMAL - && !ent->alt_fire ) + && !ent->alt_fire) {//rolling thermal det - FIXME: make this an eFlag like bounce & stick!!! //G_BounceRollMissile( ent, trace ); - if ( ent->owner )//&& ent->owner->s.number == 0 ) + if (ent->owner)//&& ent->owner->s.number == 0 ) { - G_MissileAddAlerts( ent ); + G_MissileAddAlerts(ent); } //gi.linkentity( ent ); return; } // check for sticking - if ( ent->s.eFlags & EF_MISSILE_STICK ) + if (ent->s.eFlags & EF_MISSILE_STICK) { - if ( ent->owner )//&& ent->owner->s.number == 0 ) + if (ent->owner)//&& ent->owner->s.number == 0 ) { //Add the event - if ( ent->s.weapon == WP_TRIP_MINE ) + if (ent->s.weapon == WP_TRIP_MINE) { - AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius/2, AEL_DISCOVERED/*AEL_DANGER*/, qfalse, qtrue ); - AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DISCOVERED/*AEL_DANGER*/, 60 ); + AddSoundEvent(ent->owner, ent->currentOrigin, ent->splashRadius / 2, AEL_DISCOVERED/*AEL_DANGER*/, qfalse, qtrue); + AddSightEvent(ent->owner, ent->currentOrigin, ent->splashRadius * 2, AEL_DISCOVERED/*AEL_DANGER*/, 60); /* AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER, qfalse, qtrue ); AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER, 60 ); @@ -790,37 +884,37 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace, int hitLoc=HL_NONE ) } else { - AddSoundEvent( ent->owner, ent->currentOrigin, 128, AEL_DISCOVERED, qfalse, qtrue ); - AddSightEvent( ent->owner, ent->currentOrigin, 256, AEL_DISCOVERED, 10 ); + AddSoundEvent(ent->owner, ent->currentOrigin, 128, AEL_DISCOVERED, qfalse, qtrue); + AddSightEvent(ent->owner, ent->currentOrigin, 256, AEL_DISCOVERED, 10); } } - G_MissileStick( ent, other, trace ); + G_MissileStick(ent, other, trace); return; } -extern bool WP_DoingMoronicForcedAnimationForForcePowers(gentity_t *ent); + extern bool WP_DoingMoronicForcedAnimationForForcePowers(gentity_t *ent); // check for hitting a lightsaber - if ( other->contents & CONTENTS_LIGHTSABER ) + if (other->contents & CONTENTS_LIGHTSABER) { - if ( other->owner && !other->owner->s.number && other->owner->client ) + if (other->owner && !other->owner->s.number && other->owner->client) { other->owner->client->sess.missionStats.saberBlocksCnt++; } - if ( ( g_spskill->integer <= 0//on easy, it reflects all shots - || (g_spskill->integer == 1 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 )//on medium it won't reflect flechette or demp shots - || (g_spskill->integer >= 2 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_BOWCASTER && ent->s.weapon != WP_REPEATER )//on hard it won't reflect flechette, demp, repeater or bowcaster shots - ) + if ((g_spskill->integer <= 0//on easy, it reflects all shots + || (g_spskill->integer == 1 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2)//on medium it won't reflect flechette or demp shots + || (g_spskill->integer >= 2 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_BOWCASTER && ent->s.weapon != WP_REPEATER)//on hard it won't reflect flechette, demp, repeater or bowcaster shots + ) && (!ent->splashDamage || !ent->splashRadius) //this would be cool, though, to "bat" the thermal det away... - && ent->s.weapon != WP_NOGHRI_STICK )//gas bomb, don't reflect + && ent->s.weapon != WP_NOGHRI_STICK)//gas bomb, don't reflect { //FIXME: take other's owner's FP_SABER_DEFENSE into account here somehow? - if ( !other->owner || !other->owner->client || other->owner->client->ps.saberInFlight - || (InFront( ent->currentOrigin, other->owner->currentOrigin, other->owner->client->ps.viewangles, SABER_REFLECT_MISSILE_CONE ) && - !WP_DoingMoronicForcedAnimationForForcePowers(other)) )//other->owner->s.number != 0 || + if (!other->owner || !other->owner->client || other->owner->client->ps.saberInFlight + || (InFront(ent->currentOrigin, other->owner->currentOrigin, other->owner->client->ps.viewangles, SABER_REFLECT_MISSILE_CONE) && + !WP_DoingMoronicForcedAnimationForForcePowers(other)))//other->owner->s.number != 0 || {//Jedi cannot block shots from behind! int blockChance = 0; - switch ( other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] ) + switch (other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE]) {//level 1 reflects 50% of the time, level 2 reflects 75% of the time case FORCE_LEVEL_3: blockChance = 10; @@ -832,34 +926,34 @@ extern bool WP_DoingMoronicForcedAnimationForForcePowers(gentity_t *ent); blockChance = 1; break; } - if ( blockChance && (other->owner->client->ps.forcePowersActive&(1<owner->client->ps.forcePowersActive&(1 << FP_SPEED))) {//in in force speed, better chance of deflecting the shot - blockChance += other->owner->client->ps.forcePowerLevel[FP_SPEED]*2; + blockChance += other->owner->client->ps.forcePowerLevel[FP_SPEED] * 2; } - if ( Q_irand( 0, blockChance ) ) + if (Q_irand(0, blockChance)) { VectorSubtract(ent->currentOrigin, other->currentOrigin, diff); VectorNormalize(diff); - G_ReflectMissile( other, ent, diff); - if ( other->owner && other->owner->client ) + G_ReflectMissile(other, ent, diff, FP_SABER_DEFENSE); + if (other->owner && other->owner->client) { other->owner->client->ps.saberEventFlags |= SEF_DEFLECTED; } //do the effect - VectorCopy( ent->s.pos.trDelta, diff ); - VectorNormalize( diff ); - G_MissileReflectEffect( ent, trace->endpos, trace->plane.normal ); + VectorCopy(ent->s.pos.trDelta, diff); + VectorNormalize(diff); + G_MissileReflectEffect(ent, trace->endpos, trace->plane.normal); return; } } } else {//still do the bounce effect - G_MissileReflectEffect( ent, trace->endpos, trace->plane.normal ); + G_MissileReflectEffect(ent, trace->endpos, trace->plane.normal); } } - G_MissileImpacted( ent, other, trace->endpos, trace->plane.normal, hitLoc ); + G_MissileImpacted(ent, other, trace->endpos, trace->plane.normal, hitLoc); } /* @@ -869,35 +963,35 @@ G_ExplodeMissile Explode a missile without an impact ================ */ -void G_ExplodeMissile( gentity_t *ent ) +void G_ExplodeMissile(gentity_t *ent) { vec3_t dir; vec3_t origin; - EvaluateTrajectory( &ent->s.pos, level.time, origin ); - SnapVector( origin ); - G_SetOrigin( ent, origin ); + EvaluateTrajectory(&ent->s.pos, level.time, origin); + SnapVector(origin); + G_SetOrigin(ent, origin); // we don't have a valid direction, so just point straight up dir[0] = dir[1] = 0; dir[2] = 1; - if ( ent->owner )//&& ent->owner->s.number == 0 ) + if (ent->owner)//&& ent->owner->s.number == 0 ) { //Add the event - AddSoundEvent( ent->owner, ent->currentOrigin, 256, AEL_DISCOVERED, qfalse, qtrue );//FIXME: are we on ground or not? - AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 100 ); + AddSoundEvent(ent->owner, ent->currentOrigin, 256, AEL_DISCOVERED, qfalse, qtrue);//FIXME: are we on ground or not? + AddSightEvent(ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 100); } -/* ent->s.eType = ET_GENERAL; + /* ent->s.eType = ET_GENERAL; G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) ); ent->freeAfterEvent = qtrue;*/ // splash damage - if ( ent->splashDamage ) + if (ent->splashDamage) { - G_RadiusDamage( ent->currentOrigin, ent->owner, ent->splashDamage, ent->splashRadius, NULL - , ent->splashMethodOfDeath ); + G_RadiusDamage(ent->currentOrigin, ent->owner, ent->splashDamage, ent->splashRadius, NULL + , ent->splashMethodOfDeath); } G_FreeEntity(ent); @@ -905,24 +999,24 @@ void G_ExplodeMissile( gentity_t *ent ) } -void G_RunStuckMissile( gentity_t *ent ) +void G_RunStuckMissile(gentity_t *ent) { - if ( ent->takedamage ) + if (ent->takedamage) { - if ( ent->s.groundEntityNum >= 0 && ent->s.groundEntityNum < ENTITYNUM_WORLD ) + if (ent->s.groundEntityNum >= 0 && ent->s.groundEntityNum < ENTITYNUM_WORLD) { gentity_t *other = &g_entities[ent->s.groundEntityNum]; - if ( (!VectorCompare( vec3_origin, other->s.pos.trDelta ) && other->s.pos.trType != TR_STATIONARY) || - (!VectorCompare( vec3_origin, other->s.apos.trDelta ) && other->s.apos.trType != TR_STATIONARY) ) + if ((!VectorCompare(vec3_origin, other->s.pos.trDelta) && other->s.pos.trType != TR_STATIONARY) || + (!VectorCompare(vec3_origin, other->s.apos.trDelta) && other->s.apos.trType != TR_STATIONARY)) {//thing I stuck to is moving or rotating now, kill me - G_Damage( ent, other, other, NULL, NULL, 99999, 0, MOD_CRUSH ); + G_Damage(ent, other, other, NULL, NULL, 99999, 0, MOD_CRUSH); return; } } } // check think function - G_RunThink( ent ); + G_RunThink(ent); } /* @@ -932,7 +1026,7 @@ G_GroundTrace ================== */ -int G_GroundTrace( gentity_t *ent, pml_t *pPml ) +int G_GroundTrace(gentity_t *ent, pml_t *pPml) { vec3_t point; trace_t trace; @@ -941,11 +1035,11 @@ int G_GroundTrace( gentity_t *ent, pml_t *pPml ) point[1] = ent->currentOrigin[1]; point[2] = ent->currentOrigin[2] - 0.25; - gi.trace ( &trace, ent->currentOrigin, ent->mins, ent->maxs, point, ent->s.number, ent->clipmask, (EG2_Collision)0, 0 ); + gi.trace(&trace, ent->currentOrigin, ent->mins, ent->maxs, point, ent->s.number, ent->clipmask, (EG2_Collision)0, 0); pPml->groundTrace = trace; // do something corrective if the trace starts in a solid... - if ( trace.allsolid ) + if (trace.allsolid) { pPml->groundPlane = qfalse; pPml->walking = qfalse; @@ -953,7 +1047,7 @@ int G_GroundTrace( gentity_t *ent, pml_t *pPml ) } // if the trace didn't hit anything, we are in free fall - if ( trace.fraction == 1.0 ) + if (trace.fraction == 1.0) { pPml->groundPlane = qfalse; pPml->walking = qfalse; @@ -961,7 +1055,7 @@ int G_GroundTrace( gentity_t *ent, pml_t *pPml ) } // check if getting thrown off the ground - if ( ent->s.pos.trDelta[2] > 0 && DotProduct( ent->s.pos.trDelta, trace.plane.normal ) > 10 ) + if (ent->s.pos.trDelta[2] > 0 && DotProduct(ent->s.pos.trDelta, trace.plane.normal) > 10) { pPml->groundPlane = qfalse; pPml->walking = qfalse; @@ -969,7 +1063,7 @@ int G_GroundTrace( gentity_t *ent, pml_t *pPml ) } // slopes that are too steep will not be considered onground - if ( trace.plane.normal[2] < MIN_WALK_NORMAL ) + if (trace.plane.normal[2] < MIN_WALK_NORMAL) { pPml->groundPlane = qtrue; pPml->walking = qfalse; @@ -982,30 +1076,31 @@ int G_GroundTrace( gentity_t *ent, pml_t *pPml ) /* if ( ent->s.groundEntityNum == ENTITYNUM_NONE ) { - // just hit the ground + // just hit the ground } */ return trace.entityNum; } -void G_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out, float overbounce ) +void G_ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce) { float backoff; float change; int i; - backoff = DotProduct (in, normal); + backoff = DotProduct(in, normal); - if ( backoff < 0 ) { + if (backoff < 0) { backoff *= overbounce; - } else { + } + else { backoff /= overbounce; } - for ( i=0 ; i<3 ; i++ ) + for (i = 0; i<3; i++) { - change = normal[i]*backoff; + change = normal[i] * backoff; out[i] = in[i] - change; } } @@ -1026,7 +1121,7 @@ Also gets stuck inside thrower if looking down when thrown */ #define MAX_CLIP_PLANES 5 #define BUMPCLIP 1.5f -void G_RollMissile( gentity_t *ent ) +void G_RollMissile(gentity_t *ent) { int bumpcount, numbumps; vec3_t dir; @@ -1046,34 +1141,34 @@ void G_RollMissile( gentity_t *ent ) float bounceAmt = BUMPCLIP; gentity_t *hitEnt = NULL; - memset( &objPML, 0, sizeof( objPML ) ); + memset(&objPML, 0, sizeof(objPML)); - G_GroundTrace( ent, &objPML ); + G_GroundTrace(ent, &objPML); objPML.frametime = (level.time - level.previousTime)*0.001; numbumps = 4; - VectorCopy ( ent->s.pos.trDelta, primal_velocity ); + VectorCopy(ent->s.pos.trDelta, primal_velocity); - VectorCopy( ent->s.pos.trDelta, endVelocity ); + VectorCopy(ent->s.pos.trDelta, endVelocity); endVelocity[2] -= g_gravity->value * objPML.frametime; - ent->s.pos.trDelta[2] = ( ent->s.pos.trDelta[2] + endVelocity[2] ) * 0.5; + ent->s.pos.trDelta[2] = (ent->s.pos.trDelta[2] + endVelocity[2]) * 0.5; primal_velocity[2] = endVelocity[2]; - if ( objPML.groundPlane ) + if (objPML.groundPlane) {//FIXME: never happens! // slide along the ground plane - G_ClipVelocity( ent->s.pos.trDelta, objPML.groundTrace.plane.normal, ent->s.pos.trDelta, BUMPCLIP ); - VectorScale( ent->s.pos.trDelta, 0.9f, ent->s.pos.trDelta ); + G_ClipVelocity(ent->s.pos.trDelta, objPML.groundTrace.plane.normal, ent->s.pos.trDelta, BUMPCLIP); + VectorScale(ent->s.pos.trDelta, 0.9f, ent->s.pos.trDelta); } time_left = objPML.frametime; // never turn against the ground plane - if ( objPML.groundPlane ) + if (objPML.groundPlane) { numplanes = 1; - VectorCopy( objPML.groundTrace.plane.normal, planes[0] ); + VectorCopy(objPML.groundTrace.plane.normal, planes[0]); } else { @@ -1086,31 +1181,31 @@ void G_RollMissile( gentity_t *ent ) numplanes++; */ - for ( bumpcount = 0; bumpcount < numbumps; bumpcount++ ) + for (bumpcount = 0; bumpcount < numbumps; bumpcount++) { // calculate position we are trying to move to - VectorMA( ent->currentOrigin, time_left, ent->s.pos.trDelta, end ); + VectorMA(ent->currentOrigin, time_left, ent->s.pos.trDelta, end); // see if we can make it there - gi.trace ( &trace, ent->currentOrigin, ent->mins, ent->maxs, end, ent->s.number, ent->clipmask, G2_RETURNONHIT, 10 ); + gi.trace(&trace, ent->currentOrigin, ent->mins, ent->maxs, end, ent->s.number, ent->clipmask, G2_RETURNONHIT, 10); //TEMP HACK: //had to move this up above the trace.allsolid check now that for some reason ghoul2 impacts tell me I'm allsolid?! //this needs to be fixed, really - if ( trace.entityNum < ENTITYNUM_WORLD ) + if (trace.entityNum < ENTITYNUM_WORLD) {//hit another ent hitEnt = &g_entities[trace.entityNum]; - if ( hitEnt && (hitEnt->takedamage || (hitEnt->contents&CONTENTS_LIGHTSABER) ) ) + if (hitEnt && (hitEnt->takedamage || (hitEnt->contents&CONTENTS_LIGHTSABER))) { - G_MissileImpact( ent, &trace ); - if ( ent->s.eType == ET_GENERAL ) + G_MissileImpact(ent, &trace); + if (ent->s.eType == ET_GENERAL) {//exploded return; } } } - if ( trace.allsolid ) + if (trace.allsolid) { // entity is completely trapped in another solid //FIXME: this happens a lot now when we hit a G2 ent... WTF? @@ -1118,15 +1213,15 @@ void G_RollMissile( gentity_t *ent ) return;// qtrue; } - if ( trace.fraction > 0 ) + if (trace.fraction > 0) { // actually covered some distance - VectorCopy( trace.endpos, ent->currentOrigin ); + VectorCopy(trace.endpos, ent->currentOrigin); } - if ( trace.fraction == 1 ) + if (trace.fraction == 1) { - break; // moved the entire distance + break; // moved the entire distance } //pm->ps->pm_flags |= PMF_BUMPED; @@ -1138,16 +1233,16 @@ void G_RollMissile( gentity_t *ent ) /* if ( PM_ClientImpact( trace.entityNum, qtrue ) ) { - continue; + continue; } */ time_left -= time_left * trace.fraction; - if ( numplanes >= MAX_CLIP_PLANES ) + if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen - VectorClear( ent->s.pos.trDelta ); + VectorClear(ent->s.pos.trDelta); return;// qtrue; } @@ -1156,25 +1251,25 @@ void G_RollMissile( gentity_t *ent ) // out along it, which fixes some epsilon issues with // non-axial planes // - for ( i = 0 ; i < numplanes ; i++ ) + for (i = 0; i < numplanes; i++) { - if ( DotProduct( trace.plane.normal, planes[i] ) > 0.99 ) + if (DotProduct(trace.plane.normal, planes[i]) > 0.99) { - VectorAdd( trace.plane.normal, ent->s.pos.trDelta, ent->s.pos.trDelta ); + VectorAdd(trace.plane.normal, ent->s.pos.trDelta, ent->s.pos.trDelta); break; } } - if ( i < numplanes ) + if (i < numplanes) { continue; } - VectorCopy( trace.plane.normal, planes[numplanes] ); + VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; // // modify velocity so it parallels all of the clip planes // - if ( &g_entities[trace.entityNum] != NULL && g_entities[trace.entityNum].client ) + if (&g_entities[trace.entityNum] != NULL && g_entities[trace.entityNum].client) {//hit a person, bounce off much less bounceAmt = OVERCLIP; } @@ -1184,92 +1279,92 @@ void G_RollMissile( gentity_t *ent ) } // find a plane that it enters - for ( i = 0 ; i < numplanes ; i++ ) + for (i = 0; i < numplanes; i++) { - into = DotProduct( ent->s.pos.trDelta, planes[i] ); - if ( into >= 0.1 ) + into = DotProduct(ent->s.pos.trDelta, planes[i]); + if (into >= 0.1) { continue; // move doesn't interact with the plane } // see how hard we are hitting things - if ( -into > pml.impactSpeed ) + if (-into > pml.impactSpeed) { pml.impactSpeed = -into; } // slide along the plane - G_ClipVelocity( ent->s.pos.trDelta, planes[i], clipVelocity, bounceAmt ); + G_ClipVelocity(ent->s.pos.trDelta, planes[i], clipVelocity, bounceAmt); // slide along the plane - G_ClipVelocity( endVelocity, planes[i], endClipVelocity, bounceAmt ); + G_ClipVelocity(endVelocity, planes[i], endClipVelocity, bounceAmt); // see if there is a second plane that the new move enters - for ( j = 0 ; j < numplanes ; j++ ) + for (j = 0; j < numplanes; j++) { - if ( j == i ) + if (j == i) { continue; } - if ( DotProduct( clipVelocity, planes[j] ) >= 0.1 ) + if (DotProduct(clipVelocity, planes[j]) >= 0.1) { continue; // move doesn't interact with the plane } // try clipping the move to the plane - G_ClipVelocity( clipVelocity, planes[j], clipVelocity, bounceAmt ); - G_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, bounceAmt ); + G_ClipVelocity(clipVelocity, planes[j], clipVelocity, bounceAmt); + G_ClipVelocity(endClipVelocity, planes[j], endClipVelocity, bounceAmt); // see if it goes back into the first clip plane - if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) + if (DotProduct(clipVelocity, planes[i]) >= 0) { continue; } // slide the original velocity along the crease - CrossProduct (planes[i], planes[j], dir); - VectorNormalize( dir ); - d = DotProduct( dir, ent->s.pos.trDelta ); - VectorScale( dir, d, clipVelocity ); + CrossProduct(planes[i], planes[j], dir); + VectorNormalize(dir); + d = DotProduct(dir, ent->s.pos.trDelta); + VectorScale(dir, d, clipVelocity); - CrossProduct (planes[i], planes[j], dir); - VectorNormalize( dir ); - d = DotProduct( dir, endVelocity ); - VectorScale( dir, d, endClipVelocity ); + CrossProduct(planes[i], planes[j], dir); + VectorNormalize(dir); + d = DotProduct(dir, endVelocity); + VectorScale(dir, d, endClipVelocity); // see if there is a third plane the the new move enters - for ( k = 0 ; k < numplanes ; k++ ) + for (k = 0; k < numplanes; k++) { - if ( k == i || k == j ) + if (k == i || k == j) { continue; } - if ( DotProduct( clipVelocity, planes[k] ) >= 0.1 ) + if (DotProduct(clipVelocity, planes[k]) >= 0.1) { continue; // move doesn't interact with the plane } // stop dead at a triple plane interaction - VectorClear( ent->s.pos.trDelta ); + VectorClear(ent->s.pos.trDelta); return;// qtrue; } } // if we have fixed all interactions, try another move - VectorCopy( clipVelocity, ent->s.pos.trDelta ); - VectorCopy( endClipVelocity, endVelocity ); + VectorCopy(clipVelocity, ent->s.pos.trDelta); + VectorCopy(endClipVelocity, endVelocity); break; } - VectorScale( endVelocity, 0.975f, endVelocity ); + VectorScale(endVelocity, 0.975f, endVelocity); } - VectorCopy( endVelocity, ent->s.pos.trDelta ); + VectorCopy(endVelocity, ent->s.pos.trDelta); // don't change velocity if in a timer (FIXME: is this correct?) /* if ( pm->ps->pm_time ) { - VectorCopy( primal_velocity, ent->s.pos.trDelta ); + VectorCopy( primal_velocity, ent->s.pos.trDelta ); } */ @@ -1281,50 +1376,50 @@ G_RunMissile ================ */ -void G_MoverTouchPushTriggers( gentity_t *ent, vec3_t oldOrg ); -void G_RunMissile( gentity_t *ent ) +void G_MoverTouchPushTriggers(gentity_t *ent, vec3_t oldOrg); +void G_RunMissile(gentity_t *ent) { vec3_t oldOrg; trace_t tr; - int trHitLoc=HL_NONE; + int trHitLoc = HL_NONE; - if ( (ent->s.eFlags&EF_HELD_BY_SAND_CREATURE) ) + if ((ent->s.eFlags&EF_HELD_BY_SAND_CREATURE)) {//in a sand creature's mouth - if ( ent->activator ) + if (ent->activator) { mdxaBone_t boltMatrix; // Getting the bolt here //in hand - vec3_t scAngles = {0}; + vec3_t scAngles = { 0 }; scAngles[YAW] = ent->activator->currentAngles[YAW]; - gi.G2API_GetBoltMatrix( ent->activator->ghoul2, ent->activator->playerModel, ent->activator->gutBolt, - &boltMatrix, scAngles, ent->activator->currentOrigin, (cg.time?cg.time:level.time), - NULL, ent->activator->s.modelScale ); + gi.G2API_GetBoltMatrix(ent->activator->ghoul2, ent->activator->playerModel, ent->activator->gutBolt, + &boltMatrix, scAngles, ent->activator->currentOrigin, (cg.time ? cg.time : level.time), + NULL, ent->activator->s.modelScale); // Storing ent position, bolt position, and bolt axis - gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent->currentOrigin ); - G_SetOrigin( ent, ent->currentOrigin ); + gi.G2API_GiveMeVectorFromMatrix(boltMatrix, ORIGIN, ent->currentOrigin); + G_SetOrigin(ent, ent->currentOrigin); } // check think function - G_RunThink( ent ); + G_RunThink(ent); return; } - VectorCopy( ent->currentOrigin, oldOrg ); + VectorCopy(ent->currentOrigin, oldOrg); // get current position - if ( ent->s.pos.trType == TR_INTERPOLATE ) + if (ent->s.pos.trType == TR_INTERPOLATE) {//rolling missile? //FIXME: WTF?!! Sticks to stick missiles? //FIXME: they stick inside the player - G_RollMissile( ent ); - if ( ent->s.eType != ET_GENERAL ) + G_RollMissile(ent); + if (ent->s.eType != ET_GENERAL) {//didn't explode - VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); - gi.trace( &tr, oldOrg, ent->mins, ent->maxs, ent->currentOrigin, ent->s.number, ent->clipmask, G2_RETURNONHIT, 10 ); - if ( VectorCompare( ent->s.pos.trDelta, vec3_origin ) ) + VectorCopy(ent->currentOrigin, ent->s.pos.trBase); + gi.trace(&tr, oldOrg, ent->mins, ent->maxs, ent->currentOrigin, ent->s.number, ent->clipmask, G2_RETURNONHIT, 10); + if (VectorCompare(ent->s.pos.trDelta, vec3_origin)) { //VectorCopy( ent->currentAngles, ent->s.apos.trBase ); - VectorClear( ent->s.apos.trDelta ); + VectorClear(ent->s.apos.trDelta); } else { @@ -1332,19 +1427,19 @@ void G_RunMissile( gentity_t *ent ) float speed; ent->s.apos.trType = TR_INTERPOLATE; - VectorSet( ang, 0, ent->s.apos.trBase[1], 0 ); - AngleVectors( ang, fwdDir, rtDir, NULL ); - speed = VectorLength( ent->s.pos.trDelta )*4; + VectorSet(ang, 0, ent->s.apos.trBase[1], 0); + AngleVectors(ang, fwdDir, rtDir, NULL); + speed = VectorLength(ent->s.pos.trDelta) * 4; //HMM, this works along an axis-aligned dir, but not along diagonals //This is because when roll gets to 90, pitch becomes yaw, and vice-versa //Maybe need to just set the angles directly? - ent->s.apos.trDelta[0] = DotProduct( fwdDir, ent->s.pos.trDelta ); + ent->s.apos.trDelta[0] = DotProduct(fwdDir, ent->s.pos.trDelta); ent->s.apos.trDelta[1] = 0;//never spin! - ent->s.apos.trDelta[2] = DotProduct( rtDir, ent->s.pos.trDelta ); + ent->s.apos.trDelta[2] = DotProduct(rtDir, ent->s.pos.trDelta); - VectorNormalize( ent->s.apos.trDelta ); - VectorScale( ent->s.apos.trDelta, speed, ent->s.apos.trDelta ); + VectorNormalize(ent->s.apos.trDelta); + VectorScale(ent->s.apos.trDelta, speed, ent->s.apos.trDelta); ent->s.apos.trTime = level.previousTime; } @@ -1353,43 +1448,43 @@ void G_RunMissile( gentity_t *ent ) else { vec3_t origin; - EvaluateTrajectory( &ent->s.pos, level.time, origin ); + EvaluateTrajectory(&ent->s.pos, level.time, origin); // trace a line from the previous position to the current position, // ignoring interactions with the missile owner - gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, - ent->owner ? ent->owner->s.number : ent->s.number, ent->clipmask, G2_COLLIDE, 10 ); + gi.trace(&tr, ent->currentOrigin, ent->mins, ent->maxs, origin, + ent->owner ? ent->owner->s.number : ent->s.number, ent->clipmask, G2_COLLIDE, 10); - if ( tr.entityNum != ENTITYNUM_NONE ) + if (tr.entityNum != ENTITYNUM_NONE) { gentity_t *other = &g_entities[tr.entityNum]; // check for hitting a lightsaber - if ( other->contents & CONTENTS_LIGHTSABER ) + if (other->contents & CONTENTS_LIGHTSABER) {//hit a lightsaber bbox - if ( other->owner + if (other->owner && other->owner->client && !other->owner->client->ps.saberInFlight - && ( Q_irand( 0, (other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE]*other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE]) ) == 0 - || !InFront( ent->currentOrigin, other->owner->currentOrigin, other->owner->client->ps.viewangles, SABER_REFLECT_MISSILE_CONE ) ) )//other->owner->s.number == 0 && + && ((Q_irand(0, (other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] * other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE])) == 0 && other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] != 0) + || !InFront(ent->currentOrigin, other->owner->currentOrigin, other->owner->client->ps.viewangles, SABER_REFLECT_MISSILE_CONE)))//other->owner->s.number == 0 && {//Jedi cannot block shots from behind! //re-trace from here, ignoring the lightsaber - gi.trace( &tr, tr.endpos, ent->mins, ent->maxs, origin, tr.entityNum, ent->clipmask, G2_RETURNONHIT, 10 ); + gi.trace(&tr, tr.endpos, ent->mins, ent->maxs, origin, tr.entityNum, ent->clipmask, G2_RETURNONHIT, 10); } } } - VectorCopy( tr.endpos, ent->currentOrigin ); + VectorCopy(tr.endpos, ent->currentOrigin); } // get current angles - VectorMA( ent->s.apos.trBase, (level.time - ent->s.apos.trTime) * 0.001, ent->s.apos.trDelta, ent->s.apos.trBase ); + VectorMA(ent->s.apos.trBase, (level.time - ent->s.apos.trTime) * 0.001, ent->s.apos.trDelta, ent->s.apos.trBase); //FIXME: Rolling things hitting G2 polys is weird /////////////////////////////////////////////////////// -//? if ( tr.fraction != 1 ) + //? if ( tr.fraction != 1 ) { - // did we hit or go near a Ghoul2 model? -// qboolean hitModel = qfalse; - for (int i=0; i < MAX_G2_COLLISIONS; i++) + // did we hit or go near a Ghoul2 model? + // qboolean hitModel = qfalse; + for (int i = 0; i < MAX_G2_COLLISIONS; i++) { if (tr.G2CollisionMap[i].mEntityNum == -1) { @@ -1403,99 +1498,99 @@ void G_RunMissile( gentity_t *ent ) // make sure we only do this once, not for all the entrance wounds we might generate if ((coll.mFlags & G2_FRONTFACE)/* && !(hitModel)*/ && hitEnt->health) { - if (trHitLoc==HL_NONE) + if (trHitLoc == HL_NONE) { - G_GetHitLocFromSurfName( &g_entities[coll.mEntityNum], gi.G2API_GetSurfaceName( &g_entities[coll.mEntityNum].ghoul2[coll.mModelIndex], coll.mSurfaceIndex ), &trHitLoc, coll.mCollisionPosition, NULL, NULL, ent->methodOfDeath ); + G_GetHitLocFromSurfName(&g_entities[coll.mEntityNum], gi.G2API_GetSurfaceName(&g_entities[coll.mEntityNum].ghoul2[coll.mModelIndex], coll.mSurfaceIndex), &trHitLoc, coll.mCollisionPosition, NULL, NULL, ent->methodOfDeath); } break; // NOTE: the way this whole section was working, it would only get inside of this IF once anyway, might as well break out now } } } -///////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////// - if ( tr.startsolid ) + if (tr.startsolid) { tr.fraction = 0; } - gi.linkentity( ent ); + gi.linkentity(ent); - if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) ) + if (ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK)) {//stuck missiles should check some special stuff - G_RunStuckMissile( ent ); + G_RunStuckMissile(ent); return; } // check think function - G_RunThink( ent ); + G_RunThink(ent); - if ( ent->s.eType != ET_MISSILE ) + if (ent->s.eType != ET_MISSILE) { return; // exploded } - if ( ent->mass ) + if (ent->mass) { - G_MoverTouchPushTriggers( ent, oldOrg ); + G_MoverTouchPushTriggers(ent, oldOrg); } /* if ( !(ent->s.eFlags & EF_TELEPORT_BIT) ) { - G_MoverTouchTeleportTriggers( ent, oldOrg ); - if ( ent->s.eFlags & EF_TELEPORT_BIT ) - {//was teleported - return; - } + G_MoverTouchTeleportTriggers( ent, oldOrg ); + if ( ent->s.eFlags & EF_TELEPORT_BIT ) + {//was teleported + return; + } } else { - ent->s.eFlags &= ~EF_TELEPORT_BIT; + ent->s.eFlags &= ~EF_TELEPORT_BIT; } */ - AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 75 );//wakes them up when see a shot passes in front of them - if ( !Q_irand( 0, 10 ) ) + AddSightEvent(ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 75);//wakes them up when see a shot passes in front of them + if (!Q_irand(0, 10)) {//not so often... - if ( ent->splashDamage && ent->splashRadius ) + if (ent->splashDamage && ent->splashRadius) {//I'm an exploder, let people around me know danger is coming - if ( ent->s.weapon == WP_TRIP_MINE ) + if (ent->s.weapon == WP_TRIP_MINE) {//??? } else { - if ( ent->s.weapon == WP_ROCKET_LAUNCHER && ent->e_ThinkFunc == thinkF_rocketThink ) + if (ent->s.weapon == WP_ROCKET_LAUNCHER && ent->e_ThinkFunc == thinkF_rocketThink) {//homing rocket- run like hell! - AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER_GREAT, 50 ); + AddSightEvent(ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER_GREAT, 50); } else { - AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER, 50 ); + AddSightEvent(ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER, 50); } - AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER ); + AddSoundEvent(ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER); } } else {//makes them run from near misses - AddSightEvent( ent->owner, ent->currentOrigin, 48, AEL_DANGER, 50 ); + AddSightEvent(ent->owner, ent->currentOrigin, 48, AEL_DANGER, 50); } } - if ( tr.fraction == 1 ) + if (tr.fraction == 1) { - if ( ent->s.weapon == WP_THERMAL && ent->s.pos.trType == TR_INTERPOLATE ) + if (ent->s.weapon == WP_THERMAL && ent->s.pos.trType == TR_INTERPOLATE) {//a rolling thermal that didn't hit anything - G_MissileAddAlerts( ent ); + G_MissileAddAlerts(ent); } return; } // never explode or bounce on sky - if ( tr.surfaceFlags & SURF_NOIMPACT ) + if (tr.surfaceFlags & SURF_NOIMPACT) { - G_FreeEntity( ent ); + G_FreeEntity(ent); return; } - G_MissileImpact( ent, &tr, trHitLoc ); -} + G_MissileImpact(ent, &tr, trHitLoc); +} \ No newline at end of file diff --git a/code/game/g_mover.cpp b/code/game/g_mover.cpp index b8f04d70f8..2af48918d9 100644 --- a/code/game/g_mover.cpp +++ b/code/game/g_mover.cpp @@ -342,7 +342,7 @@ qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t ** VectorAdd( pusher->currentAngles, amove, pusher->currentAngles ); gi.linkentity( pusher ); - notMoving = (VectorCompare( vec3_origin, move )&&VectorCompare( vec3_origin, amove )); + notMoving = (qboolean)(VectorCompare( vec3_origin, move )&&VectorCompare( vec3_origin, amove )); // see if any solid entities are inside the final position for ( e = 0 ; e < listedEntities ; e++ ) { @@ -2149,7 +2149,7 @@ void SP_func_train (gentity_t *self) { { self->spawnflags &= ~32; // once only - gi.G2API_SetBoneAnim( &self->ghoul2[self->playerModel], "model_root", self->startFrame, self->endFrame, BONE_ANIM_OVERRIDE_LOOP, 1.0f + crandom() * 0.1f, 0, -1, -1 ); + gi.G2API_SetBoneAnim( &self->ghoul2[self->playerModel], "model_root", self->startFrame, self->endFrame, BONE_ANIM_OVERRIDE_LOOP, 1.0f + Q_flrand(-1.0f, 1.0f) * 0.1f, 0, -1, -1 ); self->endFrame = 0; // don't allow it to do anything with the animation function in G_main } } @@ -2202,6 +2202,12 @@ void SP_func_static( gentity_t *ent ) { // yes this is very very evil, but for now (pre-alpha) it's a solution ent->svFlags |= SVF_BROADCAST; // I need to rotate something that is huge and it's touching too many area portals... } + + if ( ent->radarIcon && ent->radarIcon[0] ) + { + ent->s.eFlags2 |= EF2_RADAROBJECT; + ent->s.radarIcon = G_IconIndex(ent->radarIcon); + } if ( ent->spawnflags & 4/*SWITCH_SHADER*/ ) { @@ -2218,6 +2224,15 @@ void SP_func_static( gentity_t *ent ) ent->damage = 2; } } + + int test; + G_SpawnInt( "hyperspace", "0", &test ); + if ( test ) + { + //ent->r.svFlags |= SVF_BROADCAST; // I need to rotate something that is huge and it's touching too many area portals... + // ent->s.eFlags2 |= EF2_HYPERSPACE; + } + gi.linkentity( ent ); if (level.mBSPInstanceDepth) diff --git a/code/game/g_navigator.cpp b/code/game/g_navigator.cpp index 6da921793e..c1a56b43bb 100644 --- a/code/game/g_navigator.cpp +++ b/code/game/g_navigator.cpp @@ -641,7 +641,7 @@ class CGraphUser : public TGraph::user } else if ( Edge.BlockingBreakable()) {//we had a breakable in our way, now it's gone, see if there is anything else in the way - if ( NAV::TestEdge( Edge.mNodeA, Edge.mNodeB, false ) ) + if ( NAV::TestEdge( Edge.mNodeA, Edge.mNodeB, qfalse ) ) {//clear it Edge.mFlags.clear_bit(CWayEdge::WE_BLOCKING_BREAK); } @@ -1911,7 +1911,7 @@ bool NAV::LoadFromEntitiesAndSaveToFile(const char *filename, int checksum) // Try Medium //------------ - CanGo = TestEdge( at.mNodeA, at.mNodeB, IsDebugEdge ); + CanGo = TestEdge( at.mNodeA, at.mNodeB, (qboolean)IsDebugEdge ); if (!CanGo) { at.mFlags.clear_bit(CWayEdge::WE_SIZE_LARGE); @@ -1920,7 +1920,7 @@ bool NAV::LoadFromEntitiesAndSaveToFile(const char *filename, int checksum) { gi.Printf("Nav(%s)<->(%s): Attempting Size Medium...\n", aName, bName); } - CanGo = TestEdge( at.mNodeA, at.mNodeB, IsDebugEdge ); + CanGo = TestEdge( at.mNodeA, at.mNodeB, (qboolean)IsDebugEdge ); } // If This Edge Can't Go At Any Size, Dump It @@ -5529,7 +5529,7 @@ bool STEER::Reached(gentity_t* actor, const vec3_t& target, float targetRadius, void ClearAllNavStructures(void) { TEntEdgeMap::iterator i = mEntEdgeMap.begin(); - for ( ; i != mEntEdgeMap.end(); i++) + for ( ; i != mEntEdgeMap.end(); ++i) { i->clear(); } diff --git a/code/game/g_navnew.cpp b/code/game/g_navnew.cpp index 50dfd66d1c..2cd87f368b 100644 --- a/code/game/g_navnew.cpp +++ b/code/game/g_navnew.cpp @@ -53,11 +53,11 @@ qboolean NAV_HitNavGoal( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t dest, in { diff[2] = 0; } - return ( VectorLengthSquared( diff ) <= (radius*radius) ); + return (qboolean)( VectorLengthSquared( diff ) <= (radius*radius) ); } else {//must hit exactly - return ( DistanceSquared(dest, point) <= (radius*radius) ); + return (qboolean)( DistanceSquared(dest, point) <= (radius*radius) ); } //There is probably a better way to do this, either by preserving the original // mins and maxs of the navgoal and doing this check ONLY if the radius diff --git a/code/game/g_object.cpp b/code/game/g_object.cpp index be5857c05f..db653e5b2c 100644 --- a/code/game/g_object.cpp +++ b/code/game/g_object.cpp @@ -210,7 +210,7 @@ void G_RunObject( gentity_t *ent ) G_Sound( ent, G_SoundIndex( "sound/movers/objects/objectHit.wav" ) ); } } - DoImpact( ent, traceEnt, !(tr.surfaceFlags&SURF_NODAMAGE), &tr ); + DoImpact( ent, traceEnt, (qboolean)!(tr.surfaceFlags&SURF_NODAMAGE), &tr ); } if ( !ent || (ent->takedamage&&ent->health <= 0) ) diff --git a/code/game/g_objectives.cpp b/code/game/g_objectives.cpp index 76b075d2e1..5073cf8d83 100644 --- a/code/game/g_objectives.cpp +++ b/code/game/g_objectives.cpp @@ -29,6 +29,7 @@ along with this program; if not, see . #define G_OBJECTIVES_CPP #include "objectives.h" +#include "qcommon/ojk_saved_game_helper.h" qboolean missionInfo_Updated; @@ -59,7 +60,12 @@ OBJ_SaveMissionObjectives */ void OBJ_SaveMissionObjectives( gclient_t *client ) { - gi.AppendToSaveGame(INT_ID('O','B','J','T'), client->sess.mission_objectives, sizeof(client->sess.mission_objectives)); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.write_chunk( + INT_ID('O', 'B', 'J', 'T'), + client->sess.mission_objectives); } @@ -84,7 +90,12 @@ OBJ_LoadMissionObjectives */ void OBJ_LoadMissionObjectives( gclient_t *client ) { - gi.ReadFromSaveGame(INT_ID('O','B','J','T'), (void *) &client->sess.mission_objectives, sizeof(client->sess.mission_objectives), NULL); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.read_chunk( + INT_ID('O', 'B', 'J', 'T'), + client->sess.mission_objectives); } diff --git a/code/game/g_public.h b/code/game/g_public.h index 4d667bcc3d..1ee9e5c9aa 100644 --- a/code/game/g_public.h +++ b/code/game/g_public.h @@ -25,7 +25,7 @@ along with this program; if not, see . #define __G_PUBLIC_H__ // g_public.h -- game module information visible to server -#define GAME_API_VERSION 8 +#define GAME_API_VERSION 10 // entity->svFlags // the server does not know how to interpret most of the values @@ -72,7 +72,7 @@ class CRagDollParams; //rww - RAGDOLL_END typedef struct gentity_s gentity_t; -typedef struct gclient_s gclient_t; +//typedef struct gclient_s gclient_t; typedef enum { @@ -86,10 +86,15 @@ typedef enum #ifndef GAME_INCLUDE // the server needs to know enough information to handle collision and snapshot generation +template +class PlayerStateBase; + +using playerState_t = PlayerStateBase; + struct gentity_s { entityState_t s; // communicated by server to clients - struct playerState_s *client; + playerState_t *client; qboolean inuse; qboolean linked; // qfalse if not in any good cluster @@ -181,10 +186,7 @@ typedef struct { // Savegame handling // - qboolean (*AppendToSaveGame)(unsigned int chid, const void *data, int length); - - int (*ReadFromSaveGame)(unsigned int chid, void *pvAddress, int iLength, void **ppvAddressPtr ); - int (*ReadFromSaveGameOptional)(unsigned int chid, void *pvAddress, int iLength, void **ppvAddressPtr ); + ojk::ISavedGame* saved_game; // add commands to the console as if they were typed in // for map changing, etc @@ -268,6 +270,9 @@ Ghoul2 Insert Start qboolean (*G2API_SetBoneAngles)(CGhoul2Info *ghlInfo, const char *boneName, const vec3_t angles, const int flags, const Eorientations up, const Eorientations right, const Eorientations forward, qhandle_t *modelList, int blendTime , int blendStart ); + qboolean (*G2API_SetBoneAnglesOffset)(CGhoul2Info *ghlInfo, const char *boneName, const vec3_t angles, const int flags, + const Eorientations up, const Eorientations left, const Eorientations forward, qhandle_t *modelList, + int blendTime, int AcurrentTime, const vec3_t offset); qboolean (*G2API_SetBoneAnglesIndex)(CGhoul2Info *ghlInfo, const int index, const vec3_t angles, const int flags, const Eorientations yaw, const Eorientations pitch, const Eorientations roll, qhandle_t *modelList, int blendTime, int currentTime ); @@ -358,6 +363,8 @@ Ghoul2 Insert Start void (*G2API_AddSkinGore)(CGhoul2Info_v &ghoul2,SSkinGoreData &gore); void (*G2API_ClearSkinGore)( CGhoul2Info_v &ghoul2 ); + void (*G2API_SetTintType)(CGhoul2Info *ghlInfo, g2Tints_t tintType); + void (*RMG_Init)(int terrainID); int (*CM_RegisterTerrain)(const char *info); diff --git a/code/game/g_ref.cpp b/code/game/g_ref.cpp index 25c1b2b55f..9e40bce481 100644 --- a/code/game/g_ref.cpp +++ b/code/game/g_ref.cpp @@ -77,7 +77,7 @@ void TAG_Init( void ) refTagOwner_m::iterator rtoi; //Delete all owners - for ( rtoi = refTagOwnerMap.begin(); rtoi != refTagOwnerMap.end(); rtoi++ ) + for ( rtoi = refTagOwnerMap.begin(); rtoi != refTagOwnerMap.end(); ++rtoi ) { if ( (*rtoi).second == NULL ) { @@ -88,7 +88,7 @@ void TAG_Init( void ) refTag_v::iterator rti; //Delete all tags within the owner's scope - for ( rti = ((*rtoi).second)->tags.begin(); rti != ((*rtoi).second)->tags.end(); rti++ ) + for ( rti = ((*rtoi).second)->tags.begin(); rti != ((*rtoi).second)->tags.end(); ++rti ) { if ( (*rti) == NULL ) { diff --git a/code/game/g_roff.cpp b/code/game/g_roff.cpp index 41dfbc2d2d..9ec25adfc2 100644 --- a/code/game/g_roff.cpp +++ b/code/game/g_roff.cpp @@ -25,6 +25,8 @@ along with this program; if not, see . #include "Q3_Interface.h" #include "../cgame/cg_local.h" #include "g_functions.h" +#include "qcommon/ojk_saved_game_helper.h" + // The list of precached ROFFs roff_list_t roffs[MAX_ROFFS]; int num_roffs = 0; @@ -414,7 +416,7 @@ static qboolean G_InitRoff( char *file, unsigned char *data ) } } - return false; + return qfalse; } //------------------------------------------------------- @@ -647,18 +649,30 @@ void G_Roff( gentity_t *ent ) void G_SaveCachedRoffs() { - int i, len; + int i, len = 0; + + ojk::SavedGameHelper saved_game( + ::gi.saved_game); // Write out the number of cached ROFFs - gi.AppendToSaveGame( INT_ID('R','O','F','F'), (void *)&num_roffs, sizeof(num_roffs) ); + saved_game.write_chunk( + INT_ID('R', 'O', 'F', 'F'), + ::num_roffs); // Now dump out the cached ROFF file names in order so they can be loaded on the other end for ( i = 0; i < num_roffs; i++ ) { // Dump out the string length to make things a bit easier on the other end...heh heh. len = strlen( roffs[i].fileName ) + 1; - gi.AppendToSaveGame( INT_ID('S','L','E','N'), (void *)&len, sizeof(len) ); - gi.AppendToSaveGame( INT_ID('R','S','T','R'), (void *)roffs[i].fileName, len ); + + saved_game.write_chunk( + INT_ID('S', 'L', 'E', 'N'), + len); + + saved_game.write_chunk( + INT_ID('R', 'S', 'T', 'R'), + roffs[i].fileName, + len); } } @@ -671,17 +685,32 @@ void G_SaveCachedRoffs() void G_LoadCachedRoffs() { - int i, count, len; + int i, count = 0, len = 0; char buffer[MAX_QPATH]; + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + // Get the count of goodies we need to revive - gi.ReadFromSaveGame( INT_ID('R','O','F','F'), (void *)&count, sizeof(count), NULL ); + saved_game.read_chunk( + INT_ID('R', 'O', 'F', 'F'), + count); // Now bring 'em back to life for ( i = 0; i < count; i++ ) { - gi.ReadFromSaveGame( INT_ID('S','L','E','N'), (void *)&len, sizeof(len), NULL ); - gi.ReadFromSaveGame( INT_ID('R','S','T','R'), (void *)(buffer), len, NULL ); + saved_game.read_chunk( + INT_ID('S', 'L', 'E', 'N'), + len); + + if (len < 0 || static_cast(len) >= sizeof(buffer)) + len = 0; + + saved_game.read_chunk( + INT_ID('R', 'S', 'T', 'R'), + buffer, + len); + G_LoadRoff( buffer ); } } diff --git a/code/game/g_savegame.cpp b/code/game/g_savegame.cpp index 9a0e603257..0b3d0dbed4 100644 --- a/code/game/g_savegame.cpp +++ b/code/game/g_savegame.cpp @@ -30,6 +30,7 @@ along with this program; if not, see . #include "objectives.h" #include "../cgame/cg_camera.h" #include "../qcommon/sstring.h" +#include "qcommon/ojk_saved_game_helper.h" extern void OBJ_LoadTacticalInfo(void); @@ -155,7 +156,8 @@ static const save_field_t savefields_gClient[] = {NULL, 0, F_IGNORE} }; -std::list *strList = NULL; +// TODO FIXME mrwonko: this has no business being a global variable. WTF Raven? +static std::list strList; /////////// char * ///////////// @@ -172,7 +174,7 @@ static int GetStringNum(const char *psString) return -1; } - strList->push_back( psString ); + strList.push_back( psString ); return strlen(psString) + 1; // this gives us the chunk length for the reader later } @@ -186,7 +188,13 @@ static char *GetStringPtr(int iStrlen, char *psOriginal/*may be NULL*/) assert(iStrlen+1<=(int)sizeof(sString)); - gi.ReadFromSaveGame(INT_ID('S','T','R','G'), sString, iStrlen, NULL); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.read_chunk( + INT_ID('S', 'T', 'R', 'G'), + sString, + iStrlen); // TAG_G_ALLOC is always blown away, we can never recycle if (psOriginal && gi.bIsFromZone(psOriginal, TAG_G_ALLOC)) { @@ -482,7 +490,7 @@ static void EnumerateField(const save_field_t *pField, const byte *pbBase) break; case F_BOOLPTR: - *(qboolean *)pv = !!(*(int *)pv); + *(qboolean *)pv = (qboolean)(*(int *)pv != 0); break; // These are pointers that are always recreated @@ -499,35 +507,50 @@ static void EnumerateField(const save_field_t *pField, const byte *pbBase) } } -static void EnumerateFields(const save_field_t *pFields, const byte *pbData, unsigned int ulChid, size_t iLen) +template +static void EnumerateFields( + const save_field_t* pFields, + const T* src_instance, + unsigned int ulChid) { - strList = new std::list; + strList.clear(); + + const byte* pbData = reinterpret_cast( + src_instance); // enumerate all the fields... // if (pFields) { - for (const save_field_t *pField = pFields; pField->psName; pField++) + for (auto pField = pFields; pField->psName; ++pField) { - assert(pField->iOffset < iLen); - EnumerateField(pField, pbData); + assert(pField->iOffset < sizeof(T)); + ::EnumerateField(pField, pbData); } } + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + // save out raw data... // - gi.AppendToSaveGame(ulChid, pbData, iLen); + saved_game.reset_buffer(); + + src_instance->sg_export( + saved_game); + + saved_game.write_chunk( + ulChid); // save out any associated strings.. // - std::list::iterator it = strList->begin(); - for (size_t i=0; isize(); i++, ++it) + for (const auto& it : strList) { - gi.AppendToSaveGame(INT_ID('S','T','R','G'), (void *)(*it).c_str(), (*it).length() + 1); + saved_game.write_chunk( + INT_ID('S', 'T', 'R', 'G'), + it.c_str(), + static_cast(it.length() + 1)); } - - delete strList; - strList = NULL; } @@ -651,120 +674,174 @@ static const char *SG_GetChidText( unsigned int chid ) { } extern void WP_SaberSetDefaults( saberInfo_t *saber, qboolean setColors); -static void SG_ConvertRetailSaberinfoToNewSaberinfo( void *sabData, saberInfo_t *saberNew ) + +void saberInfoRetail_t::sg_export( + saberInfo_t& dst) const { - saberInfoRetail_t *saberRetail = ((saberInfoRetail_t *)(sabData)); + ::WP_SaberSetDefaults( + &dst, + qfalse); - for ( int saberNum = 0; saberNum < 2; saberNum++ ) + if (!activeBlocking) { - WP_SaberSetDefaults( &saberNew[saberNum], qfalse ); - if ( !saberRetail[saberNum].activeBlocking ) - { - saberNew[saberNum].saberFlags |= SFL_NOT_ACTIVE_BLOCKING; - } - memcpy( saberNew[saberNum].blade, saberRetail[saberNum].blade, sizeof( saberRetail[saberNum].blade ) ); - saberNew[saberNum].breakParryBonus = saberRetail[saberNum].breakParryBonus; - saberNew[saberNum].brokenSaber1 = saberRetail[saberNum].brokenSaber1; - saberNew[saberNum].brokenSaber2 = saberRetail[saberNum].brokenSaber2; - if ( !saberRetail[saberNum].disarmable ) - { - saberNew[saberNum].saberFlags |= SFL_NOT_DISARMABLE; - } - saberNew[saberNum].disarmBonus = saberRetail[saberNum].disarmBonus; - saberNew[saberNum].forceRestrictions = saberRetail[saberNum].forceRestrictions; - saberNew[saberNum].fullName = saberRetail[saberNum].fullName; - if ( !saberRetail[saberNum].lockable ) - { - saberNew[saberNum].saberFlags |= SFL_NOT_LOCKABLE; - } - saberNew[saberNum].lockBonus = saberRetail[saberNum].lockBonus; - saberNew[saberNum].maxChain = saberRetail[saberNum].maxChain; - saberNew[saberNum].model = saberRetail[saberNum].model; - saberNew[saberNum].name = saberRetail[saberNum].name; - saberNew[saberNum].numBlades = saberRetail[saberNum].numBlades; - saberNew[saberNum].parryBonus = saberRetail[saberNum].parryBonus; - if ( saberRetail[saberNum].returnDamage ) - { - saberNew[saberNum].saberFlags |= SFL_RETURN_DAMAGE; - } - saberNew[saberNum].singleBladeStyle = saberRetail[saberNum].singleBladeStyle; - if ( saberRetail[saberNum].singleBladeThrowable ) + dst.saberFlags |= SFL_NOT_ACTIVE_BLOCKING; + } + + ::memcpy( + dst.blade, + blade, + sizeof(blade)); + + dst.breakParryBonus = breakParryBonus; + dst.brokenSaber1 = brokenSaber1; + dst.brokenSaber2 = brokenSaber2; + + if (!disarmable) + { + dst.saberFlags |= SFL_NOT_DISARMABLE; + } + + dst.disarmBonus = disarmBonus; + dst.forceRestrictions = forceRestrictions; + dst.fullName = fullName; + + if (!lockable) + { + dst.saberFlags |= SFL_NOT_LOCKABLE; + } + + dst.lockBonus = lockBonus; + dst.maxChain = maxChain; + dst.model = model; + dst.name = name; + dst.numBlades = numBlades; + dst.parryBonus = parryBonus; + + if (returnDamage) + { + dst.saberFlags |= SFL_RETURN_DAMAGE; + } + + dst.singleBladeStyle = singleBladeStyle; + + if (singleBladeThrowable) + { + dst.saberFlags |= SFL_SINGLE_BLADE_THROWABLE; + } + + dst.skin = skin; + dst.soundLoop = soundLoop; + dst.soundOff = soundOff; + dst.soundOn = soundOn; + + if (style != SS_NONE && style < SS_NUM_SABER_STYLES) + { + //OLD WAY: only allowed ONE style + //learn only this style + dst.stylesLearned = 1 << style; + + //forbid all other styles + dst.stylesForbidden = 0; + for (int styleNum = SS_NONE + 1; styleNum < SS_NUM_SABER_STYLES; ++styleNum) { - saberNew[saberNum].saberFlags |= SFL_SINGLE_BLADE_THROWABLE; - } - saberNew[saberNum].skin = saberRetail[saberNum].skin; - saberNew[saberNum].soundLoop = saberRetail[saberNum].soundLoop; - saberNew[saberNum].soundOff = saberRetail[saberNum].soundOff; - saberNew[saberNum].soundOn = saberRetail[saberNum].soundOn; - if ( saberRetail[saberNum].style != SS_NONE - && saberRetail[saberNum].style < SS_NUM_SABER_STYLES ) - {//OLD WAY: only allowed ONE style - //learn only this style - saberNew[saberNum].stylesLearned = (1<(&dst), + reinterpret_cast(&src), + src_pre_size); + + for (int i = 0; i < MAX_SABERS; ++i) { - // handle any chunks that are ok to change length (typically this is a last minute hack, - // so hopefully we won't need it any more... ;-) - // - switch (ulChid) + src.ps.saber[i].sg_export( + dst.ps.saber[i]); + } + + ::memcpy( + reinterpret_cast(&dst) + src_post_offset, + reinterpret_cast(&src) + dst_post_offset, + src_post_size); +} + +template +static void EvaluateFields( + const save_field_t* pFields, + T* pbData, + byte* pbOriginalRefData, + unsigned int ulChid) +{ + T& instance = *pbData; + + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + if (ulChid != INT_ID('G', 'C', 'L', 'I')) + { + saved_game.read_chunk( + ulChid, + instance); + } + else + { + if (!saved_game.try_read_chunk( + ulChid, + instance)) { - // example chunk handler... - // - case INT_ID('G','C','L','I'): - if ( iSize == (int)(iReadSize+((sizeof(saberInfo_t)-sizeof(saberInfoRetail_t))*2)) ) - { - gclient_t newClient; - const int preSaberDataSize = ((intptr_t)&newClient.ps.saber[0]-(intptr_t)&newClient); - memcpy( &newClient, pbData, preSaberDataSize ); - SG_ConvertRetailSaberinfoToNewSaberinfo( ((void *)(&((gclient_t *)(pbData))->ps.saber[0])), &newClient.ps.saber[0] ); - memcpy( &newClient.ps.dualSabers, pbData+preSaberDataSize+(sizeof(saberInfoRetail_t)*2), sizeof(newClient)-(preSaberDataSize+(sizeof(saberInfo_t)*2)) ); - memcpy( pbData, &newClient, sizeof(gclient_t) ); - } - else - {//opps, not a saberInfo size mismatch, some other FUBAR-ness... - G_Error(va("EvaluateFields(): variable-sized chunk '%s' without handler!",SG_GetChidText(ulChid))); - } - break; + RetailGClient retail_client; - default: - // won't return... - // - G_Error(va("EvaluateFields(): variable-sized chunk '%s' without handler!",SG_GetChidText(ulChid))); - break; + saved_game.reset_buffer_offset(); + + if (saved_game.try_read( + retail_client)) + { + copy_retail_gclient_to_current( + retail_client, + *reinterpret_cast(pbData)); + } + else + { + ::G_Error( + ::va("EvaluateFields(): variable-sized chunk '%s' without handler!", + ::SG_GetChidText(ulChid))); + } } } if (pFields) { - for (const save_field_t *pField = pFields; pField->psName; pField++) + for (const save_field_t* pField = pFields; pField->psName; ++pField) { - EvaluateField(pField, pbData, pbOriginalRefData); + ::EvaluateField( + pField, + reinterpret_cast(pbData), + pbOriginalRefData); } } } @@ -781,7 +858,7 @@ static void WriteLevelLocals () level_locals_t *temp = (level_locals_t *)gi.Malloc(sizeof(level_locals_t), TAG_TEMP_WORKSPACE, qfalse); *temp = level; // copy out all data into a temp space - EnumerateFields(savefields_LevelLocals, (byte *)temp, INT_ID('L','V','L','C'), LLOFS(LEVEL_LOCALS_T_SAVESTOP)); // sizeof(temp)); + EnumerateFields(savefields_LevelLocals, temp, INT_ID('L','V','L','C')); gi.Free(temp); } @@ -800,7 +877,7 @@ static void ReadLevelLocals () level_locals_t *temp = (level_locals_t *)gi.Malloc(sizeof(level_locals_t), TAG_TEMP_WORKSPACE, qfalse); *temp = level; // struct copy - EvaluateFields(savefields_LevelLocals, (byte *)temp, (byte *)&level, INT_ID('L','V','L','C'), LLOFS(LEVEL_LOCALS_T_SAVESTOP),qfalse); // sizeof(level_locals_t)); + EvaluateFields(savefields_LevelLocals, temp, (byte *)&level, INT_ID('L','V','L','C')); level = *temp; // struct copy level.clients = pClients; // restore clients @@ -822,7 +899,12 @@ static void WriteGEntities(qboolean qbAutosave) } } - gi.AppendToSaveGame(INT_ID('N','M','E','D'), &iCount, sizeof(iCount)); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.write_chunk( + INT_ID('N', 'M', 'E', 'D'), + iCount); for (i=0; i<(qbAutosave?1:globals.num_entities); i++) { @@ -830,7 +912,9 @@ static void WriteGEntities(qboolean qbAutosave) if ( ent->inuse) { - gi.AppendToSaveGame(INT_ID('E','D','N','M'), (void *)&i, sizeof(i)); + saved_game.write_chunk( + INT_ID('E', 'D', 'N', 'M'), + i); qboolean qbLinked = ent->linked; gi.unlinkentity( ent ); @@ -842,7 +926,7 @@ static void WriteGEntities(qboolean qbAutosave) gi.linkentity( ent ); } - EnumerateFields(savefields_gEntity, (byte *)&tempEnt, INT_ID('G','E','N','T'), sizeof(tempEnt)); + EnumerateFields(savefields_gEntity, &tempEnt, INT_ID('G','E','N','T')); // now for any fiddly bits that would be rather awkward to build into the enumerator... // @@ -850,24 +934,26 @@ static void WriteGEntities(qboolean qbAutosave) { gNPC_t npc = *ent->NPC; // NOT *tempEnt.NPC; !! :-) - EnumerateFields(savefields_gNPC, (byte *)&npc, INT_ID('G','N','P','C'), sizeof(npc)); + EnumerateFields(savefields_gNPC, &npc, INT_ID('G','N','P','C')); } if (tempEnt.client == (gclient_t *)-2) // I know, I know... { gclient_t client = *ent->client; // NOT *tempEnt.client!! - EnumerateFields(savefields_gClient, (byte *)&client, INT_ID('G','C','L','I'), sizeof(client)); + EnumerateFields(savefields_gClient, &client, INT_ID('G','C','L','I')); } if (tempEnt.parms) { - gi.AppendToSaveGame(INT_ID('P','A','R','M'), ent->parms, sizeof(*ent->parms)); + saved_game.write_chunk( + INT_ID('P', 'A', 'R', 'M'), + *ent->parms); } if (tempEnt.m_pVehicle) { Vehicle_t vehicle = *ent->m_pVehicle; // NOT *tempEnt.m_pVehicle!! - EnumerateFields(savefields_gVHIC, (byte *)&vehicle, INT_ID('V','H','I','C'), sizeof(vehicle)); + EnumerateFields(savefields_gVHIC, &vehicle, INT_ID('V','H','I','C')); } // the scary ghoul2 saver stuff... (fingers crossed) @@ -890,7 +976,10 @@ static void WriteGEntities(qboolean qbAutosave) // This saves time debugging, and makes things easier to track. // static int iBlah = 1234; - gi.AppendToSaveGame(INT_ID('I','C','O','K'), &iBlah, sizeof(iBlah)); + + saved_game.write_chunk( + INT_ID('I', 'C', 'O', 'K'), + iBlah); } if (!qbAutosave )//really shouldn't need to write these bits at all, just restore them from the ents... { @@ -900,16 +989,24 @@ static void WriteGEntities(qboolean qbAutosave) static void ReadGEntities(qboolean qbAutosave) { - int iCount; + int iCount = 0; int i; - gi.ReadFromSaveGame(INT_ID('N','M','E','D'), (void *)&iCount, sizeof(iCount), NULL); + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.read_chunk( + INT_ID('N', 'M', 'E', 'D'), + iCount); int iPreviousEntRead = -1; for (i=0; i( + INT_ID('E', 'D', 'N', 'M'), + iEntIndex); if (iEntIndex >= globals.num_entities) { @@ -944,7 +1041,7 @@ static void ReadGEntities(qboolean qbAutosave) // gi.G2API_LoadSaveCodeDestructGhoul2Info(pEnt->ghoul2); pEnt->ghoul2.kill(); - EvaluateFields(savefields_gEntity, (byte *)pEnt, (byte *)pEntOriginal, INT_ID('G','E','N','T'), sizeof(*pEnt),qfalse); + EvaluateFields(savefields_gEntity, pEnt, (byte *)pEntOriginal, INT_ID('G','E','N','T')); pEnt->ghoul2.kill(); // now for any fiddly bits... @@ -953,7 +1050,7 @@ static void ReadGEntities(qboolean qbAutosave) { gNPC_t tempNPC; - EvaluateFields(savefields_gNPC, (byte *)&tempNPC,(byte *)pEntOriginal->NPC, INT_ID('G','N','P','C'), sizeof (*pEnt->NPC),qfalse); + EvaluateFields(savefields_gNPC, &tempNPC,(byte *)pEntOriginal->NPC, INT_ID('G','N','P','C')); // so can we pinch the original's one or do we have to alloc a new one?... // @@ -989,7 +1086,7 @@ static void ReadGEntities(qboolean qbAutosave) { gclient_t tempGClient; - EvaluateFields(savefields_gClient, (byte *)&tempGClient, (byte *)pEntOriginal->client, INT_ID('G','C','L','I'), sizeof(*pEnt->client),qtrue);//qfalse); + EvaluateFields(savefields_gClient, &tempGClient, (byte *)pEntOriginal->client, INT_ID('G','C','L','I')); // can we pinch the original's client handle or do we have to alloc a new one?... // @@ -1022,7 +1119,9 @@ static void ReadGEntities(qboolean qbAutosave) { parms_t tempParms; - gi.ReadFromSaveGame(INT_ID('P','A','R','M'), &tempParms, sizeof(tempParms), NULL); + saved_game.read_chunk( + INT_ID('P', 'A', 'R', 'M'), + tempParms); // so can we pinch the original's one or do we have to alloc a new one?... // @@ -1048,7 +1147,7 @@ static void ReadGEntities(qboolean qbAutosave) { Vehicle_t tempVehicle; - EvaluateFields(savefields_gVHIC, (byte *)&tempVehicle,(byte *)pEntOriginal->m_pVehicle, INT_ID('V','H','I','C'), sizeof (*pEnt->m_pVehicle),qfalse); + EvaluateFields(savefields_gVHIC, &tempVehicle,(byte *)pEntOriginal->m_pVehicle, INT_ID('V','H','I','C')); // so can we pinch the original's one or do we have to alloc a new one?... // @@ -1073,10 +1172,10 @@ static void ReadGEntities(qboolean qbAutosave) // the scary ghoul2 stuff... (fingers crossed) // { - char *pGhoul2Data = NULL; - gi.ReadFromSaveGame(INT_ID('G','H','L','2'), 0, 0, (void**)&pGhoul2Data); - gi.G2API_LoadGhoul2Models(pEnt->ghoul2, pGhoul2Data); // if it's going to crash anywhere... - gi.Free(pGhoul2Data); + saved_game.read_chunk( + INT_ID('G', 'H', 'L', '2')); + + gi.G2API_LoadGhoul2Models(pEnt->ghoul2, nullptr); } // gi.unlinkentity (pEntOriginal); @@ -1139,7 +1238,10 @@ static void ReadGEntities(qboolean qbAutosave) // check that Icarus has loaded everything it saved out by having a marker chunk after it... // static int iBlah = 1234; - gi.ReadFromSaveGame(INT_ID('I','C','O','K'), &iBlah, sizeof(iBlah), NULL); + + saved_game.read_chunk( + INT_ID('I', 'C', 'O', 'K'), + iBlah); } if (!qbAutosave) { @@ -1156,7 +1258,7 @@ void WriteLevel(qboolean qbAutosave) // assert(level.maxclients == 1); // I'll need to know if this changes, otherwise I'll need to change the way ReadGame works gclient_t client = level.clients[0]; - EnumerateFields(savefields_gClient, (byte *)&client, INT_ID('G','C','L','I'), sizeof(client)); + EnumerateFields(savefields_gClient, &client, INT_ID('G','C','L','I')); WriteLevelLocals(); // level_locals_t level } @@ -1176,11 +1278,20 @@ void WriteLevel(qboolean qbAutosave) // put out an end-marker so that the load code can check everything was read in... // static int iDONE = 1234; - gi.AppendToSaveGame(INT_ID('D','O','N','E'), &iDONE, sizeof(iDONE)); + + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + + saved_game.write_chunk( + INT_ID('D', 'O', 'N', 'E'), + iDONE); } void ReadLevel(qboolean qbAutosave, qboolean qbLoadTransition) { + ojk::SavedGameHelper saved_game( + ::gi.saved_game); + if ( qbLoadTransition ) { // I STRONGLY SUSPECT THAT THIS WILL JUST ERR_DROP BECAUSE OF THE LOAD SWAPPING OF THE CHUNK-ORDER @@ -1202,13 +1313,13 @@ void ReadLevel(qboolean qbAutosave, qboolean qbLoadTransition) //Read & throw away gclient info gclient_t junkClient; - EvaluateFields(savefields_gClient, (byte *)&junkClient, (byte *)&level.clients[0], INT_ID('G','C','L','I'), sizeof(*level.clients), qtrue);//qfalse); + EvaluateFields(savefields_gClient, &junkClient, (byte *)&level.clients[0], INT_ID('G','C','L','I')); ReadLevelLocals(); // level_locals_t level //Read & throw away objective info - objectives_t junkObj[MAX_MISSION_OBJ]; - gi.ReadFromSaveGame(INT_ID('O','B','J','T'), (void *) &junkObj, 0, NULL); + saved_game.read_chunk( + INT_ID('O', 'B', 'J', 'T')); } else { @@ -1217,7 +1328,7 @@ void ReadLevel(qboolean qbAutosave, qboolean qbLoadTransition) assert(level.maxclients == 1); // I'll need to know if this changes, otherwise I'll need to change the way things work gclient_t GClient; - EvaluateFields(savefields_gClient, (byte *)&GClient, (byte *)&level.clients[0], INT_ID('G','C','L','I'), sizeof(*level.clients), qtrue);//qfalse); + EvaluateFields(savefields_gClient, &GClient, (byte *)&level.clients[0], INT_ID('G','C','L','I')); level.clients[0] = GClient; // struct copy ReadLevelLocals(); // level_locals_t level } @@ -1241,136 +1352,16 @@ void ReadLevel(qboolean qbAutosave, qboolean qbLoadTransition) // check that the whole file content was loaded by specifically requesting an end-marker... // static int iDONE = 1234; - gi.ReadFromSaveGame(INT_ID('D','O','N','E'), &iDONE, sizeof(iDONE), NULL); + + saved_game.read_chunk( + INT_ID('D', 'O', 'N', 'E'), + iDONE); } extern int killPlayerTimer; qboolean GameAllowedToSaveHere(void) { - return (!in_camera&&!killPlayerTimer); + return (qboolean)(!in_camera&&!killPlayerTimer); } //////////////////// eof ///////////////////// - -#if 0 -// !!!!!!!!!!!!!!!!!! loadsave affecting structure !!!!!!!!!!!!!!!!!!!!!!! -struct Vehicle_t -{ - // The entity who pilots/drives this vehicle. - // NOTE: This is redundant (since m_pParentEntity->owner _should_ be the pilot). This makes things clearer though. - gentity_t *m_pPilot; - - int m_iPilotTime; //if spawnflag to die without pilot and this < level.time then die. - qboolean m_bHasHadPilot; //qtrue once the vehicle gets its first pilot - - // The passengers of this vehicle. - gentity_t **m_ppPassengers; - - // The number of passengers currently in this vehicle. - int m_iNumPassengers; - - //the droid unit NPC for this vehicle, if any - gentity_t *m_pDroidUnit; - - // The entity from which this NPC comes from. - gentity_t *m_pParentEntity; - - // If not zero, how long to wait before we can do anything with the vehicle (we're getting on still). - // -1 = board from left, -2 = board from right, -3 = jump/quick board. -4 & -5 = throw off existing pilot - int m_iBoarding; - - // Used to check if we've just started the boarding process - bool m_bWasBoarding; - - // The speed the vehicle maintains while boarding occurs (often zero) - vec3_t m_vBoardingVelocity; - - // Time modifier (must only be used in ProcessMoveCommands() and ProcessOrientCommands() and is updated in Update()). - float m_fTimeModifier; - - // Ghoul2 Animation info. - // NOTE: Since each vehicle has their own model instance, these bolts must be local to each vehicle as well. - int m_iLeftWingBone; - int m_iRightWingBone; - //int m_iDriverTag; - int m_iExhaustTag[MAX_VEHICLE_EXHAUSTS]; - int m_iMuzzleTag[MAX_VEHICLE_MUZZLES]; - int m_iDroidUnitTag; - int m_iGunnerViewTag[MAX_VEHICLE_TURRETS];//Where to put the view origin of the gunner (index) - - // This vehicles weapon muzzles. - Muzzle m_Muzzles[MAX_VEHICLE_MUZZLES]; - - // The user commands structure. - usercmd_t m_ucmd; - - // The direction an entity will eject from the vehicle towards. - int m_EjectDir; - - // Flags that describe the vehicles behavior. - unsigned int m_ulFlags; - - // NOTE: Vehicle Type ID, Orientation, and Armor MUST be transmitted over the net. - - // Current angles of this vehicle. - vec3_t m_vOrientation; - - // How long you have strafed left or right (increments every frame that you strafe to right, decrements every frame you strafe left) - int m_fStrafeTime; - - // Previous angles of this vehicle. - vec3_t m_vPrevOrientation; - - // When control is lost on a speeder, current angular velocity is stored here and applied until landing - float m_vAngularVelocity; - - vec3_t m_vFullAngleVelocity; - - // Current armor and shields of your vehicle (explodes if armor to 0). - int m_iArmor; //hull strength - STAT_HEALTH on NPC - int m_iShields; //energy shielding - STAT_ARMOR on NPC - - // Timer for all cgame-FX...? ex: exhaust? - int m_iLastFXTime; - - // When to die. - int m_iDieTime; - - // This pointer is to a valid VehicleInfo (which could be an animal, speeder, fighter, whatever). This - // contains the functions actually used to do things to this specific kind of vehicle as well as shared - // information (max speed, type, etc...). - vehicleInfo_t *m_pVehicleInfo; - - // This trace tells us if we're within landing height. - trace_t m_LandTrace; - - //bitflag of surfaces that have broken off - int m_iRemovedSurfaces; - - // the last time this vehicle fired a turbo burst - int m_iTurboTime; - - //how long it should drop like a rock for after freed from SUSPEND - int m_iDropTime; - - int m_iSoundDebounceTimer; - - //last time we incremented the shields - int lastShieldInc; - - //so we don't hold it down and toggle it back and forth - qboolean linkWeaponToggleHeld; - - //info about our weapons (linked, ammo, etc.) - vehWeaponStatus_t weaponStatus[MAX_VEHICLE_WEAPONS]; - vehTurretStatus_t turretStatus[MAX_VEHICLE_TURRETS]; - - //the guy who was previously the pilot - gentity_t* m_pOldPilot; - - // don't need these in mp - int m_safeJumpMountTime; - float m_safeJumpMountRightDot; -}; - -#endif diff --git a/code/game/g_session.cpp b/code/game/g_session.cpp index 8a42757085..6cde682b0c 100644 --- a/code/game/g_session.cpp +++ b/code/game/g_session.cpp @@ -116,6 +116,7 @@ void G_ReadSessionData( gclient_t *client ) { char s[MAX_STRING_CHARS]; const char *var; int i; + int lightsideDisplay; var = va( "session%i", client - level.clients ); gi.Cvar_VariableStringBuffer( var, s, sizeof(s) ); @@ -139,14 +140,15 @@ void G_ReadSessionData( gclient_t *client ) { // Clear the objectives out for (i=0;i< MAX_OBJECTIVES; i++) { - client->sess.mission_objectives[i].display = 0; + client->sess.mission_objectives[i].display = qfalse; client->sess.mission_objectives[i].status = OBJECTIVE_STAT_PENDING; } // Now load the LIGHTSIDE objective. That's the only cross level objective. sscanf( var, "%i %i", - &client->sess.mission_objectives[LIGHTSIDE_OBJ].display, + &lightsideDisplay, &client->sess.mission_objectives[LIGHTSIDE_OBJ].status); + client->sess.mission_objectives[LIGHTSIDE_OBJ].display = lightsideDisplay ? qtrue : qfalse; var = va( "missionstats%i", client - level.clients ); gi.Cvar_VariableStringBuffer( var, s, sizeof(s) ); diff --git a/code/game/g_shared.h b/code/game/g_shared.h index 853c3ac4ec..cfeb97d014 100644 --- a/code/game/g_shared.h +++ b/code/game/g_shared.h @@ -32,6 +32,7 @@ along with this program; if not, see . #include "g_vehicles.h" #include "hitlocs.h" #include "bset.h" +#include "anims.h" #define FOFS(x) offsetof(gentity_t, x) @@ -91,7 +92,9 @@ typedef enum //# material_e #define MAX_CUSTOM_JEDI_SOUNDS 22 #define MAX_CUSTOM_SOUNDS (MAX_CUSTOM_JEDI_SOUNDS + MAX_CUSTOM_EXTRA_SOUNDS + MAX_CUSTOM_COMBAT_SOUNDS + MAX_CUSTOM_BASIC_SOUNDS) // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct { +class clientInfo_t +{ +public: qboolean infoValid; char name[MAX_QPATH]; @@ -118,7 +121,52 @@ typedef struct { char *customCombatSoundDir; char *customExtraSoundDir; char *customJediSoundDir; -} clientInfo_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(infoValid); + saved_game.write(name); + saved_game.write(team); + saved_game.write(score); + saved_game.write(handicap); + saved_game.write(legsModel); + saved_game.write(legsSkin); + saved_game.write(torsoModel); + saved_game.write(torsoSkin); + saved_game.write(headModel); + saved_game.write(headSkin); + saved_game.write(animFileIndex); + saved_game.write(sounds); + saved_game.write(customBasicSoundDir); + saved_game.write(customCombatSoundDir); + saved_game.write(customExtraSoundDir); + saved_game.write(customJediSoundDir); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(infoValid); + saved_game.read(name); + saved_game.read(team); + saved_game.read(score); + saved_game.read(handicap); + saved_game.read(legsModel); + saved_game.read(legsSkin); + saved_game.read(torsoModel); + saved_game.read(torsoSkin); + saved_game.read(headModel); + saved_game.read(headSkin); + saved_game.read(animFileIndex); + saved_game.read(sounds); + saved_game.read(customBasicSoundDir); + saved_game.read(customCombatSoundDir); + saved_game.read(customExtraSoundDir); + saved_game.read(customJediSoundDir); + } +}; // clientInfo_t //================================================================== @@ -150,8 +198,9 @@ typedef enum #define RF_LOCKEDANGLE 1 // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct renderInfo_s +class renderInfo_t { +public: // Legs model, or full model on one piece entities union @@ -182,6 +231,7 @@ typedef struct renderInfo_s //Fields to apply to entire model set, individual model's equivalents will modify this value byte customRGBA[4];//Red Green Blue, 0 = don't apply + byte newCustomRGBA[MAX_CVAR_TINT][4]; //Allow up to 4 PCJ lookup values to be stored here. //The resolve to configstrings which contain the name of the @@ -239,7 +289,124 @@ typedef struct renderInfo_s vec3_t targetHeadBobAngles;//head bob angles will try to get to targetHeadBobAngles int lookingDebounceTime;//When we can stop using head looking angle behavior float legsYaw;//yaw angle your legs are actually rendering at -} renderInfo_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(legsModelName); + saved_game.write(torsoModelName); + saved_game.write(headModelName); + saved_game.write(headYawRangeLeft); + saved_game.write(headYawRangeRight); + saved_game.write(headPitchRangeUp); + saved_game.write(headPitchRangeDown); + saved_game.write(torsoYawRangeLeft); + saved_game.write(torsoYawRangeRight); + saved_game.write(torsoPitchRangeUp); + saved_game.write(torsoPitchRangeDown); + saved_game.write(legsFrame); + saved_game.write(torsoFrame); + saved_game.write(legsFpsMod); + saved_game.write(torsoFpsMod); + saved_game.write(customRGBA); + saved_game.write(newCustomRGBA); + saved_game.write(boneIndex1); + saved_game.write(boneIndex2); + saved_game.write(boneIndex3); + saved_game.write(boneIndex4); + saved_game.write(boneOrient); + saved_game.write(boneAngles1); + saved_game.write(boneAngles2); + saved_game.write(boneAngles3); + saved_game.write(boneAngles4); + saved_game.write(renderFlags); + saved_game.write(muzzlePoint); + saved_game.write(muzzleDir); + saved_game.write(muzzlePointOld); + saved_game.write(muzzleDirOld); + saved_game.write(mPCalcTime); + saved_game.write(lockYaw); + saved_game.write(headPoint); + saved_game.write(headAngles); + saved_game.write(handRPoint); + saved_game.write(handLPoint); + saved_game.write(crotchPoint); + saved_game.write(footRPoint); + saved_game.write(footLPoint); + saved_game.write(torsoPoint); + saved_game.write(torsoAngles); + saved_game.write(eyePoint); + saved_game.write(eyeAngles); + saved_game.write(lookTarget); + saved_game.write(lookMode); + saved_game.write(lookTargetClearTime); + saved_game.write(lastVoiceVolume); + saved_game.write(lastHeadAngles); + saved_game.write(headBobAngles); + saved_game.write(targetHeadBobAngles); + saved_game.write(lookingDebounceTime); + saved_game.write(legsYaw); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(legsModelName); + saved_game.read(torsoModelName); + saved_game.read(headModelName); + saved_game.read(headYawRangeLeft); + saved_game.read(headYawRangeRight); + saved_game.read(headPitchRangeUp); + saved_game.read(headPitchRangeDown); + saved_game.read(torsoYawRangeLeft); + saved_game.read(torsoYawRangeRight); + saved_game.read(torsoPitchRangeUp); + saved_game.read(torsoPitchRangeDown); + saved_game.read(legsFrame); + saved_game.read(torsoFrame); + saved_game.read(legsFpsMod); + saved_game.read(torsoFpsMod); + saved_game.read(customRGBA); + saved_game.read(newCustomRGBA); + saved_game.read(boneIndex1); + saved_game.read(boneIndex2); + saved_game.read(boneIndex3); + saved_game.read(boneIndex4); + saved_game.read(boneOrient); + saved_game.read(boneAngles1); + saved_game.read(boneAngles2); + saved_game.read(boneAngles3); + saved_game.read(boneAngles4); + saved_game.read(renderFlags); + saved_game.read(muzzlePoint); + saved_game.read(muzzleDir); + saved_game.read(muzzlePointOld); + saved_game.read(muzzleDirOld); + saved_game.read(mPCalcTime); + saved_game.read(lockYaw); + saved_game.read(headPoint); + saved_game.read(headAngles); + saved_game.read(handRPoint); + saved_game.read(handLPoint); + saved_game.read(crotchPoint); + saved_game.read(footRPoint); + saved_game.read(footLPoint); + saved_game.read(torsoPoint); + saved_game.read(torsoAngles); + saved_game.read(eyePoint); + saved_game.read(eyeAngles); + saved_game.read(lookTarget); + saved_game.read(lookMode); + saved_game.read(lookTargetClearTime); + saved_game.read(lastVoiceVolume); + saved_game.read(lastHeadAngles); + saved_game.read(headBobAngles); + saved_game.read(targetHeadBobAngles); + saved_game.read(lookingDebounceTime); + saved_game.read(legsYaw); + } +}; // renderInfo_t // Movement information structure @@ -265,32 +432,11 @@ typedef enum { TEAM_BEGIN, // Beginning a team game, spawn at base TEAM_ACTIVE // Now actively playing } playerTeamStateState_t; -/* -typedef enum //# race_e -{ - RACE_NONE = 0, - RACE_HUMAN, - RACE_BORG, - RACE_KLINGON, - RACE_HIROGEN, - RACE_MALON, - RACE_STASIS, - RACE_8472, - RACE_BOT, - RACE_HARVESTER, - RACE_REAVER, - RACE_AVATAR, - RACE_PARASITE, - RACE_VULCAN, - RACE_BETAZOID, - RACE_BOLIAN, - RACE_TALAXIAN, - RACE_BAJORAN, - RACE_HOLOGRAM -} race_t; -*/ + // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct { +class playerTeamState_t +{ +public: playerTeamStateState_t state; int captures; @@ -304,21 +450,71 @@ typedef struct { float lastreturnedflag; float flagsince; float lastfraggedcarrier; -} playerTeamState_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(state); + saved_game.write(captures); + saved_game.write(basedefense); + saved_game.write(carrierdefense); + saved_game.write(flagrecovery); + saved_game.write(fragcarrier); + saved_game.write(assists); + saved_game.write(lasthurtcarrier); + saved_game.write(lastreturnedflag); + saved_game.write(flagsince); + saved_game.write(lastfraggedcarrier); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(state); + saved_game.read(captures); + saved_game.read(basedefense); + saved_game.read(carrierdefense); + saved_game.read(flagrecovery); + saved_game.read(fragcarrier); + saved_game.read(assists); + saved_game.read(lasthurtcarrier); + saved_game.read(lastreturnedflag); + saved_game.read(flagsince); + saved_game.read(lastfraggedcarrier); + } +}; // playerTeamState_t // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct objectives_s +class objectives_t { +public: qboolean display; // A displayable objective? int status; // Succeed or fail or pending -} objectives_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(display); + saved_game.write(status); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(display); + saved_game.read(status); + } +}; // objectives_t // NOTE: This is an arbitrary number greater than our current number of objectives with // some fluff just in case we add more in the future. #define MAX_MISSION_OBJ 100 // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct missionStats_s +class missionStats_t { +public: int secretsFound; // # of secret areas found int totalSecrets; // # of secret areas that could have been found int shotsFired; // total number of shots fired @@ -333,7 +529,46 @@ typedef struct missionStats_s int otherAttacksCnt; // # of times anything else on a monster was hit with saber int forceUsed[NUM_FORCE_POWERS]; // # of times each force power was used int weaponUsed[WP_NUM_WEAPONS]; // # of times each weapon was used -} missionStats_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(secretsFound); + saved_game.write(totalSecrets); + saved_game.write(shotsFired); + saved_game.write(hits); + saved_game.write(enemiesSpawned); + saved_game.write(enemiesKilled); + saved_game.write(saberThrownCnt); + saved_game.write(saberBlocksCnt); + saved_game.write(legAttacksCnt); + saved_game.write(armAttacksCnt); + saved_game.write(torsoAttacksCnt); + saved_game.write(otherAttacksCnt); + saved_game.write(forceUsed); + saved_game.write(weaponUsed); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(secretsFound); + saved_game.read(totalSecrets); + saved_game.read(shotsFired); + saved_game.read(hits); + saved_game.read(enemiesSpawned); + saved_game.read(enemiesKilled); + saved_game.read(saberThrownCnt); + saved_game.read(saberBlocksCnt); + saved_game.read(legAttacksCnt); + saved_game.read(armAttacksCnt); + saved_game.read(torsoAttacksCnt); + saved_game.read(otherAttacksCnt); + saved_game.read(forceUsed); + saved_game.read(weaponUsed); + } +}; // missionStats_t // the auto following clients don't follow a specific client // number, but instead follow the first two active players @@ -346,17 +581,40 @@ typedef struct missionStats_s // MUST be dealt with in G_InitSessionData() / G_ReadSessionData() / G_WriteSessionData() // // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct { +class clientSession_t +{ +public: int missionObjectivesShown; // Number of times mission objectives have been updated team_t sessionTeam; objectives_t mission_objectives[MAX_MISSION_OBJ]; missionStats_t missionStats; // Various totals while on a mission -} clientSession_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(missionObjectivesShown); + saved_game.write(sessionTeam); + saved_game.write<>(mission_objectives); + saved_game.write<>(missionStats); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(missionObjectivesShown); + saved_game.read(sessionTeam); + saved_game.read<>(mission_objectives); + saved_game.read<>(missionStats); + } +}; // clientSession_t // client data that stays across multiple respawns, but is cleared // on each level change or team change at ClientBegin() // !!!!!!!!!! LOADSAVE-affecting structure !!!!!!!!!! -typedef struct { +class clientPersistant_t +{ +public: clientConnected_t connected; usercmd_t lastCommand; char netname[34]; @@ -365,7 +623,36 @@ typedef struct { short cmd_angles[3]; // angles sent over in the last command playerTeamState_t teamState; // status in teamplay games -} clientPersistant_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(connected); + saved_game.write<>(lastCommand); + saved_game.write(netname); + saved_game.skip(2); + saved_game.write(maxHealth); + saved_game.write(enterTime); + saved_game.write(cmd_angles); + saved_game.skip(2); + saved_game.write<>(teamState); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(connected); + saved_game.read<>(lastCommand); + saved_game.read(netname); + saved_game.skip(2); + saved_game.read(maxHealth); + saved_game.read(enterTime); + saved_game.read(cmd_angles); + saved_game.skip(2); + saved_game.read<>(teamState); + } +}; // clientPersistant_t typedef enum { BLK_NO, @@ -402,9 +689,12 @@ typedef enum //# movetype_e // this structure is cleared on each ClientSpawn(), // except for 'client->pers' and 'client->sess' -struct gclient_s { +template +class GClientBase +{ +public: // ps MUST be the first element, because the server expects it - playerState_t ps; // communicated by server to clients + PlayerStateBase ps; // communicated by server to clients // private to game clientPersistant_t pers; @@ -503,14 +793,180 @@ struct gclient_s { //for trigger_space brushes int inSpaceSuffocation; int inSpaceIndex; + + //new fields + int saberReactivateTime; //time after something deactivates a saber for AI to turn it back on + int breakLimit; //how many defense points + int breakRecoveryTime; //how long left to recover a defense point + int breakCounter; //how many defense points have we used/strong attacks have we blocked recently? }; + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write<>(ps); + saved_game.write<>(pers); + saved_game.write<>(sess); + saved_game.write(lastCmdTime); + saved_game.write<>(usercmd); + saved_game.write(buttons); + saved_game.write(oldbuttons); + saved_game.write(latched_buttons); + saved_game.write(damage_armor); + saved_game.write(damage_blood); + saved_game.write(damage_from); + saved_game.write(damage_fromWorld); + saved_game.write(noclip); + saved_game.write(forced_forwardmove); + saved_game.write(forced_rightmove); + saved_game.write(respawnTime); + saved_game.write(idleTime); + saved_game.write(airOutTime); + saved_game.write(timeResidual); + saved_game.write(facial_blink); + saved_game.write(facial_timer); + saved_game.write(facial_anim); + saved_game.write<>(clientInfo); + saved_game.write(moveType); + saved_game.write(jetPackTime); + saved_game.write(fireDelay); + saved_game.write(breathPuffTime); + saved_game.write(playerTeam); + saved_game.write(enemyTeam); + saved_game.write(leader); + saved_game.write(NPC_class); + saved_game.write(hiddenDist); + saved_game.write(hiddenDir); + saved_game.write<>(renderInfo); + saved_game.write(dismembered); + saved_game.write(dismemberProbLegs); + saved_game.write(dismemberProbHead); + saved_game.write(dismemberProbArms); + saved_game.write(dismemberProbHands); + saved_game.write(dismemberProbWaist); + saved_game.skip(2); + saved_game.write(standheight); + saved_game.write(crouchheight); + saved_game.write(poisonDamage); + saved_game.write(poisonTime); + saved_game.write(slopeRecalcTime); + saved_game.write(pushVec); + saved_game.write(pushVecTime); + saved_game.write(noRagTime); + saved_game.write(isRagging); + saved_game.write(overridingBones); + saved_game.write(ragLastOrigin); + saved_game.write(ragLastOriginTime); + saved_game.write(pushEffectFadeTime); + saved_game.write(pushEffectOrigin); + saved_game.write(rocketLockIndex); + saved_game.write(rocketLastValidTime); + saved_game.write(rocketLockTime); + saved_game.write(rocketTargetTime); + saved_game.write(inSpaceSuffocation); + saved_game.write(inSpaceIndex); + saved_game.write(saberReactivateTime); + saved_game.write(breakLimit); + saved_game.write(breakRecoveryTime); + saved_game.write(breakCounter); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read<>(ps); + saved_game.read<>(pers); + saved_game.read<>(sess); + saved_game.read(lastCmdTime); + saved_game.read<>(usercmd); + saved_game.read(buttons); + saved_game.read(oldbuttons); + saved_game.read(latched_buttons); + saved_game.read(damage_armor); + saved_game.read(damage_blood); + saved_game.read(damage_from); + saved_game.read(damage_fromWorld); + saved_game.read(noclip); + saved_game.read(forced_forwardmove); + saved_game.read(forced_rightmove); + saved_game.read(respawnTime); + saved_game.read(idleTime); + saved_game.read(airOutTime); + saved_game.read(timeResidual); + saved_game.read(facial_blink); + saved_game.read(facial_timer); + saved_game.read(facial_anim); + saved_game.read<>(clientInfo); + saved_game.read(moveType); + saved_game.read(jetPackTime); + saved_game.read(fireDelay); + saved_game.read(breathPuffTime); + saved_game.read(playerTeam); + saved_game.read(enemyTeam); + saved_game.read(leader); + saved_game.read(NPC_class); + saved_game.read(hiddenDist); + saved_game.read(hiddenDir); + saved_game.read<>(renderInfo); + saved_game.read(dismembered); + saved_game.read(dismemberProbLegs); + saved_game.read(dismemberProbHead); + saved_game.read(dismemberProbArms); + saved_game.read(dismemberProbHands); + saved_game.read(dismemberProbWaist); + saved_game.skip(2); + saved_game.read(standheight); + saved_game.read(crouchheight); + saved_game.read(poisonDamage); + saved_game.read(poisonTime); + saved_game.read(slopeRecalcTime); + saved_game.read(pushVec); + saved_game.read(pushVecTime); + saved_game.read(noRagTime); + saved_game.read(isRagging); + saved_game.read(overridingBones); + saved_game.read(ragLastOrigin); + saved_game.read(ragLastOriginTime); + saved_game.read(pushEffectFadeTime); + saved_game.read(pushEffectOrigin); + saved_game.read(rocketLockIndex); + saved_game.read(rocketLastValidTime); + saved_game.read(rocketLockTime); + saved_game.read(rocketTargetTime); + saved_game.read(inSpaceSuffocation); + saved_game.read(inSpaceIndex); + saved_game.read(saberReactivateTime); + saved_game.read(breakLimit); + saved_game.read(breakRecoveryTime); + saved_game.read(breakCounter); + } +}; // GClientBase + + +using gclient_t = GClientBase; +using RetailGClient = GClientBase; + #define MAX_PARMS 16 #define MAX_PARM_STRING_LENGTH MAX_QPATH//was 16, had to lengthen it so they could take a valid file path -typedef struct + +class parms_t { +public: char parm[MAX_PARMS][MAX_PARM_STRING_LENGTH]; -} parms_t; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(parm); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(parm); + } +}; // parms_t #ifdef GAME_INCLUDE //these hold the place for the enums in functions.h so i don't have to recompile everytime it changes @@ -525,13 +981,15 @@ typedef struct #define MAX_FAILED_NODES 8 #define MAX_INHAND_WEAPONS 2 +#define MAX_HOLSTER_WEAPONS 2 +#define MAX_SABER_PARTS 5 typedef struct centity_s centity_t; // !!!!!!!!!!! LOADSAVE-affecting struct !!!!!!!!!!!!! struct gentity_s { entityState_t s; // communicated by server to clients - struct gclient_s *client; // NULL if not a player (unless it's NPC ( if (this->NPC != NULL) ) ... -slc) + gclient_t *client; // NULL if not a player (unless it's NPC ( if (this->NPC != NULL) ) ... -slc) qboolean inuse; qboolean linked; // qfalse if not in any good cluster @@ -681,6 +1139,7 @@ Ghoul2 Insert End int pushDebounceTime; int aimDebounceTime; int useDebounceTime; + //Unions for miscellaneous fields used under very specific circumstances union @@ -726,6 +1185,17 @@ Ghoul2 Insert End team_t noDamageTeam; // Ghoul2 Animation info + short headModel; + short headRootBone; + short headMotionBone; + short headCraniumBone; + short headCervicalBone; + short headThoracicBone; + short headUpperLumbarBone; + short headLowerLumbarBone; + short headHipsBone; + short headFaceBone; + short holsterModel[MAX_HOLSTER_WEAPONS]; short playerModel; short weaponModel[MAX_INHAND_WEAPONS]; short handRBolt; @@ -840,6 +1310,411 @@ Ghoul2 Insert End //Force effects int forcePushTime; int forcePuller; //who force-pulled me (so we don't damage them if we hit them) + + char *radarIcon; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write<>(s); + saved_game.write(client); + saved_game.write(inuse); + saved_game.write(linked); + saved_game.write(svFlags); + saved_game.write(bmodel); + saved_game.write(mins); + saved_game.write(maxs); + saved_game.write(contents); + saved_game.write(absmin); + saved_game.write(absmax); + saved_game.write(currentOrigin); + saved_game.write(currentAngles); + saved_game.write(owner); + saved_game.write<>(ghoul2); + saved_game.write(modelScale); + saved_game.write(classname); + saved_game.write(spawnflags); + saved_game.write(flags); + saved_game.write(model); + saved_game.write(model2); + saved_game.write(freetime); + saved_game.write(eventTime); + saved_game.write(freeAfterEvent); + saved_game.write(physicsBounce); + saved_game.write(clipmask); + saved_game.write(speed); + saved_game.write(resultspeed); + saved_game.write(lastMoveTime); + saved_game.write(movedir); + saved_game.write(lastOrigin); + saved_game.write(lastAngles); + saved_game.write(mass); + saved_game.write(lastImpact); + saved_game.write(watertype); + saved_game.write(waterlevel); + saved_game.write(wupdate); + saved_game.write(prev_waterlevel); + saved_game.write(angle); + saved_game.write(target); + saved_game.write(target2); + saved_game.write(target3); + saved_game.write(target4); + saved_game.write(targetJump); + saved_game.write(targetname); + saved_game.write(team); + saved_game.write(roff); + saved_game.write(roff_ctr); + saved_game.write(next_roff_time); + saved_game.write(fx_time); + saved_game.write(nextthink); + saved_game.write(e_ThinkFunc); + saved_game.write(e_clThinkFunc); + saved_game.write(e_ReachedFunc); + saved_game.write(e_BlockedFunc); + saved_game.write(e_TouchFunc); + saved_game.write(e_UseFunc); + saved_game.write(e_PainFunc); + saved_game.write(e_DieFunc); + saved_game.write(health); + saved_game.write(max_health); + saved_game.write(takedamage); + saved_game.write(material); + saved_game.write(damage); + saved_game.write(dflags); + saved_game.write(splashDamage); + saved_game.write(splashRadius); + saved_game.write(methodOfDeath); + saved_game.write(splashMethodOfDeath); + saved_game.write(locationDamage); + saved_game.write(chain); + saved_game.write(enemy); + saved_game.write(activator); + saved_game.write(teamchain); + saved_game.write(teammaster); + saved_game.write(lastEnemy); + saved_game.write(wait); + saved_game.write(random); + saved_game.write(delay); + saved_game.write(alt_fire); + saved_game.write(count); + saved_game.write(bounceCount); + saved_game.write(fly_sound_debounce_time); + saved_game.write(painDebounceTime); + saved_game.write(disconnectDebounceTime); + saved_game.write(attackDebounceTime); + saved_game.write(pushDebounceTime); + saved_game.write(aimDebounceTime); + saved_game.write(useDebounceTime); + saved_game.write(trigger_formation); + saved_game.write(spawnContents); + saved_game.write(waypoint); + saved_game.write(wayedge); + saved_game.write(lastWaypoint); + saved_game.write(lastInAirTime); + saved_game.write(noWaypointTime); + saved_game.write(combatPoint); + saved_game.write(followPos); + saved_game.write(followPosRecalcTime); + saved_game.write(followPosWaypoint); + saved_game.write(loopAnim); + saved_game.write(startFrame); + saved_game.write(endFrame); + saved_game.write(m_iIcarusID); + saved_game.write(taskID); + saved_game.write(parms); + saved_game.write(behaviorSet); + saved_game.write(script_targetname); + saved_game.write(delayScriptTime); + saved_game.write(soundSet); + saved_game.write(setTime); + saved_game.write(cameraGroup); + saved_game.write(noDamageTeam); + saved_game.write(headModel); + saved_game.write(headRootBone); + saved_game.write(headMotionBone); + saved_game.write(headCraniumBone); + saved_game.write(headCervicalBone); + saved_game.write(headThoracicBone); + saved_game.write(headUpperLumbarBone); + saved_game.write(headLowerLumbarBone); + saved_game.write(headHipsBone); + saved_game.write(headFaceBone); + saved_game.write(holsterModel); + saved_game.write(playerModel); + saved_game.write(weaponModel); + saved_game.write(handRBolt); + saved_game.write(handLBolt); + saved_game.write(headBolt); + saved_game.write(cervicalBolt); + saved_game.write(chestBolt); + saved_game.write(gutBolt); + saved_game.write(torsoBolt); + saved_game.write(crotchBolt); + saved_game.write(motionBolt); + saved_game.write(kneeLBolt); + saved_game.write(kneeRBolt); + saved_game.write(elbowLBolt); + saved_game.write(elbowRBolt); + saved_game.write(footLBolt); + saved_game.write(footRBolt); + saved_game.write(faceBone); + saved_game.write(craniumBone); + saved_game.write(cervicalBone); + saved_game.write(thoracicBone); + saved_game.write(upperLumbarBone); + saved_game.write(lowerLumbarBone); + saved_game.write(hipsBone); + saved_game.write(motionBone); + saved_game.write(rootBone); + saved_game.write(footLBone); + saved_game.write(footRBone); + saved_game.write(humerusRBone); + saved_game.write(genericBone1); + saved_game.write(genericBone2); + saved_game.write(genericBone3); + saved_game.write(genericBolt1); + saved_game.write(genericBolt2); + saved_game.write(genericBolt3); + saved_game.write(genericBolt4); + saved_game.write(genericBolt5); + saved_game.write(cinematicModel); + saved_game.write(m_pVehicle); + saved_game.write(NPC); + saved_game.write(ownername); + saved_game.write(cantHitEnemyCounter); + saved_game.write(NPC_type); + saved_game.write(NPC_targetname); + saved_game.write(NPC_target); + saved_game.write(moverState); + saved_game.write(soundPos1); + saved_game.write(sound1to2); + saved_game.write(sound2to1); + saved_game.write(soundPos2); + saved_game.write(soundLoop); + saved_game.write(nextTrain); + saved_game.write(prevTrain); + saved_game.write(pos1); + saved_game.write(pos2); + saved_game.write(pos3); + saved_game.write(sounds); + saved_game.write(closetarget); + saved_game.write(opentarget); + saved_game.write(paintarget); + saved_game.write(lockCount); + saved_game.write(radius); + saved_game.write(wpIndex); + saved_game.write(noise_index); + saved_game.write(startRGBA); + saved_game.write(finalRGBA); + saved_game.write(item); + saved_game.write(message); + saved_game.write(lightLevel); + saved_game.write(forcePushTime); + saved_game.write(forcePuller); + saved_game.write(radarIcon); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read<>(s); + saved_game.read(client); + saved_game.read(inuse); + saved_game.read(linked); + saved_game.read(svFlags); + saved_game.read(bmodel); + saved_game.read(mins); + saved_game.read(maxs); + saved_game.read(contents); + saved_game.read(absmin); + saved_game.read(absmax); + saved_game.read(currentOrigin); + saved_game.read(currentAngles); + saved_game.read(owner); + saved_game.read<>(ghoul2); + saved_game.read(modelScale); + saved_game.read(classname); + saved_game.read(spawnflags); + saved_game.read(flags); + saved_game.read(model); + saved_game.read(model2); + saved_game.read(freetime); + saved_game.read(eventTime); + saved_game.read(freeAfterEvent); + saved_game.read(physicsBounce); + saved_game.read(clipmask); + saved_game.read(speed); + saved_game.read(resultspeed); + saved_game.read(lastMoveTime); + saved_game.read(movedir); + saved_game.read(lastOrigin); + saved_game.read(lastAngles); + saved_game.read(mass); + saved_game.read(lastImpact); + saved_game.read(watertype); + saved_game.read(waterlevel); + saved_game.read(wupdate); + saved_game.read(prev_waterlevel); + saved_game.read(angle); + saved_game.read(target); + saved_game.read(target2); + saved_game.read(target3); + saved_game.read(target4); + saved_game.read(targetJump); + saved_game.read(targetname); + saved_game.read(team); + saved_game.read(roff); + saved_game.read(roff_ctr); + saved_game.read(next_roff_time); + saved_game.read(fx_time); + saved_game.read(nextthink); + saved_game.read(e_ThinkFunc); + saved_game.read(e_clThinkFunc); + saved_game.read(e_ReachedFunc); + saved_game.read(e_BlockedFunc); + saved_game.read(e_TouchFunc); + saved_game.read(e_UseFunc); + saved_game.read(e_PainFunc); + saved_game.read(e_DieFunc); + saved_game.read(health); + saved_game.read(max_health); + saved_game.read(takedamage); + saved_game.read(material); + saved_game.read(damage); + saved_game.read(dflags); + saved_game.read(splashDamage); + saved_game.read(splashRadius); + saved_game.read(methodOfDeath); + saved_game.read(splashMethodOfDeath); + saved_game.read(locationDamage); + saved_game.read(chain); + saved_game.read(enemy); + saved_game.read(activator); + saved_game.read(teamchain); + saved_game.read(teammaster); + saved_game.read(lastEnemy); + saved_game.read(wait); + saved_game.read(random); + saved_game.read(delay); + saved_game.read(alt_fire); + saved_game.read(count); + saved_game.read(bounceCount); + saved_game.read(fly_sound_debounce_time); + saved_game.read(painDebounceTime); + saved_game.read(disconnectDebounceTime); + saved_game.read(attackDebounceTime); + saved_game.read(pushDebounceTime); + saved_game.read(aimDebounceTime); + saved_game.read(useDebounceTime); + saved_game.read(trigger_formation); + saved_game.read(spawnContents); + saved_game.read(waypoint); + saved_game.read(wayedge); + saved_game.read(lastWaypoint); + saved_game.read(lastInAirTime); + saved_game.read(noWaypointTime); + saved_game.read(combatPoint); + saved_game.read(followPos); + saved_game.read(followPosRecalcTime); + saved_game.read(followPosWaypoint); + saved_game.read(loopAnim); + saved_game.read(startFrame); + saved_game.read(endFrame); + saved_game.read(m_iIcarusID); + saved_game.read(taskID); + saved_game.read(parms); + saved_game.read(behaviorSet); + saved_game.read(script_targetname); + saved_game.read(delayScriptTime); + saved_game.read(soundSet); + saved_game.read(setTime); + saved_game.read(cameraGroup); + saved_game.read(noDamageTeam); + saved_game.read(headModel); + saved_game.read(headRootBone); + saved_game.read(headMotionBone); + saved_game.read(headCraniumBone); + saved_game.read(headCervicalBone); + saved_game.read(headThoracicBone); + saved_game.read(headUpperLumbarBone); + saved_game.read(headLowerLumbarBone); + saved_game.read(headHipsBone); + saved_game.read(headFaceBone); + saved_game.read(holsterModel); + saved_game.read(playerModel); + saved_game.read(weaponModel); + saved_game.read(handRBolt); + saved_game.read(handLBolt); + saved_game.read(headBolt); + saved_game.read(cervicalBolt); + saved_game.read(chestBolt); + saved_game.read(gutBolt); + saved_game.read(torsoBolt); + saved_game.read(crotchBolt); + saved_game.read(motionBolt); + saved_game.read(kneeLBolt); + saved_game.read(kneeRBolt); + saved_game.read(elbowLBolt); + saved_game.read(elbowRBolt); + saved_game.read(footLBolt); + saved_game.read(footRBolt); + saved_game.read(faceBone); + saved_game.read(craniumBone); + saved_game.read(cervicalBone); + saved_game.read(thoracicBone); + saved_game.read(upperLumbarBone); + saved_game.read(lowerLumbarBone); + saved_game.read(hipsBone); + saved_game.read(motionBone); + saved_game.read(rootBone); + saved_game.read(footLBone); + saved_game.read(footRBone); + saved_game.read(humerusRBone); + saved_game.read(genericBone1); + saved_game.read(genericBone2); + saved_game.read(genericBone3); + saved_game.read(genericBolt1); + saved_game.read(genericBolt2); + saved_game.read(genericBolt3); + saved_game.read(genericBolt4); + saved_game.read(genericBolt5); + saved_game.read(cinematicModel); + saved_game.read(m_pVehicle); + saved_game.read(NPC); + saved_game.read(ownername); + saved_game.read(cantHitEnemyCounter); + saved_game.read(NPC_type); + saved_game.read(NPC_targetname); + saved_game.read(NPC_target); + saved_game.read(moverState); + saved_game.read(soundPos1); + saved_game.read(sound1to2); + saved_game.read(sound2to1); + saved_game.read(soundPos2); + saved_game.read(soundLoop); + saved_game.read(nextTrain); + saved_game.read(prevTrain); + saved_game.read(pos1); + saved_game.read(pos2); + saved_game.read(pos3); + saved_game.read(sounds); + saved_game.read(closetarget); + saved_game.read(opentarget); + saved_game.read(paintarget); + saved_game.read(lockCount); + saved_game.read(radius); + saved_game.read(wpIndex); + saved_game.read(noise_index); + saved_game.read(startRGBA); + saved_game.read(finalRGBA); + saved_game.read(item); + saved_game.read(message); + saved_game.read(lightLevel); + saved_game.read(forcePushTime); + saved_game.read(forcePuller); + saved_game.read(radarIcon); + } }; #endif //#ifdef GAME_INCLUDE @@ -889,6 +1764,14 @@ typedef struct weaponInfo_s { sfxHandle_t altChargeSound; sfxHandle_t selectSound; // sound played when weapon is selected + + bool bUsesGhoul2; //g2 viewmodels from eezstreet + CGhoul2Info_v ghoul2; + qhandle_t g2_flashbolt; + qhandle_t g2_effectsbolt; + int g2_index; + int g2_skin; + viewModelAnimSet_t g2_anims; } weaponInfo_t; extern sfxHandle_t CAS_GetBModelSound( const char *name, int stage ); diff --git a/code/game/g_spawn.cpp b/code/game/g_spawn.cpp index d279621b0e..4192f628a7 100644 --- a/code/game/g_spawn.cpp +++ b/code/game/g_spawn.cpp @@ -348,6 +348,7 @@ field_t fields[] = { {"soundSet", FOFS(soundSet), F_LSTRING}, {"mass", FOFS(mass), F_FLOAT}, //really only used for pushable misc_model_breakables + {"icon", FOFS(radarIcon), F_LSTRING}, //q3map stuff {"scale", 0, F_IGNORE}, @@ -415,6 +416,8 @@ void SP_trigger_location( gentity_t *ent ); void SP_trigger_visible( gentity_t *self ); void SP_trigger_space(gentity_t *self); void SP_trigger_shipboundary(gentity_t *self); +void SP_trigger_hyperspace(gentity_t *self); +void SP_trigger_asteroid_field(gentity_t *self); void SP_target_give (gentity_t *ent); void SP_target_delay (gentity_t *ent); @@ -618,6 +621,8 @@ void SP_emplaced_gun( gentity_t *self ); void SP_misc_turbobattery( gentity_t *base ); +void SP_misc_radar_icon( gentity_t *self ); + spawn_t spawns[] = { {"info_player_start", SP_info_player_start}, @@ -653,6 +658,8 @@ spawn_t spawns[] = { {"trigger_visible", SP_trigger_visible}, {"trigger_space", SP_trigger_space}, {"trigger_shipboundary", SP_trigger_shipboundary}, + { "trigger_hyperspace", SP_trigger_hyperspace }, + { "trigger_asteroid_field", SP_trigger_asteroid_field }, {"target_give", SP_target_give}, {"target_delay", SP_target_delay}, @@ -856,6 +863,8 @@ spawn_t spawns[] = { {"emplaced_gun", SP_emplaced_gun}, {"emplaced_eweb", SP_emplaced_eweb}, + + {"misc_radar_icon", SP_misc_radar_icon}, {NULL, NULL} }; @@ -1628,6 +1637,25 @@ void G_SubBSPSpawnEntitiesFromString(const char *entityString, vec3_t posOffset, } } +void G_SpawnExtraEntitiesFromString( const char *entityString ) { + const char *entities; + + entities = entityString; + + // allow calls to G_Spawn*() + spawning = qtrue; + NPCsPrecached = qfalse; + numSpawnVars = 0; + + // parse ents + while( G_ParseSpawnVars( &entities ) ) + { + G_SpawnGEntityFromSpawnVars(); + } + + spawning = qfalse; // any future calls to G_Spawn*() will be errors +} + void G_SpawnEntitiesFromString( const char *entityString ) { const char *entities; @@ -1688,3 +1716,21 @@ void G_SpawnEntitiesFromString( const char *entityString ) { } } +void G_LoadExtraEntitiesFile( void ) +{ + int len; + char *buffer; + + len = gi.FS_ReadFile( va( "mapentities/%s.eent", level.mapname), (void **) &buffer ); + + if ( len == -1 ) + { + return; + } + + G_SpawnExtraEntitiesFromString( buffer ); + + gi.FS_FreeFile( buffer ); +} + + diff --git a/code/game/g_svcmds.cpp b/code/game/g_svcmds.cpp index 2368a6dc1d..81cedf9738 100644 --- a/code/game/g_svcmds.cpp +++ b/code/game/g_svcmds.cpp @@ -30,6 +30,7 @@ along with this program; if not, see . extern void G_NextTestAxes( void ); extern void G_ChangePlayerModel( gentity_t *ent, const char *newModel ); +extern void G_ChangeHeadModel( gentity_t *ent, const char *newModel ); extern void G_InitPlayerFromCvars( gentity_t *ent ); extern void Q3_SetViewEntity(int entID, const char *name); extern qboolean G_ClearViewEntity( gentity_t *ent ); @@ -41,6 +42,8 @@ extern saber_colors_t TranslateSaberColor( const char *name ); extern qboolean WP_SaberBladeUseSecondBladeStyle( saberInfo_t *saber, int bladeNum ); extern qboolean WP_UseFirstValidSaberStyle( gentity_t *ent, int *saberAnimLevel ); +extern void G_SetSabersFromCVars( gentity_t *ent ); + extern void G_SetWeapon( gentity_t *self, int wp ); extern stringID_table_t WPTable[]; @@ -129,7 +132,7 @@ void Svcmd_EntityList_f (void) { //--------------------------- extern void G_StopCinematicSkip( void ); extern void G_StartCinematicSkip( void ); -extern void ExitEmplacedWeapon( gentity_t *ent ); +extern void ExitEmplacedWeapon( gentity_t *ent, qboolean detach = qfalse ); static void Svcmd_ExitView_f( void ) { extern cvar_t *g_skippingcin; @@ -244,11 +247,11 @@ static void Svcmd_SaberBlade_f() qboolean turnOn; if ( gi.argc() > 2 ) {//explicit - turnOn = (atoi(gi.argv(3))!=0); + turnOn = (qboolean)(atoi(gi.argv(3))!=0); } else {//toggle - turnOn = (g_entities[0].client->ps.saber[sabernum].blade[bladenum].active==qfalse); + turnOn = (qboolean)!g_entities[0].client->ps.saber[sabernum].blade[bladenum].active; } g_entities[0].client->ps.SaberBladeActivate( sabernum, bladenum, turnOn ); @@ -265,9 +268,9 @@ static void Svcmd_SaberColor_f() color[bladeNum] = gi.argv(2+bladeNum); } - if ( !VALIDSTRING( color ) || saberNum < 1 || saberNum > 2 ) + if ( saberNum < 1 || saberNum > 2 || gi.argc() < 3 ) { - gi.Printf( "Usage: saberColor ... \n" ); + gi.Printf( "Usage: saberColor ... \n" ); gi.Printf( "valid saberNums: 1 or 2\n" ); gi.Printf( "valid colors: red, orange, yellow, green, blue, and purple\n" ); @@ -299,6 +302,33 @@ static void Svcmd_SaberColor_f() } } +static void Svcmd_SaberCrystal_f() +{ + int saberNum = atoi(gi.argv(1)); + + saberNum--; + + gentity_t *self = G_GetSelfForPlayerCmd(); + + if ( saberNum == 0 || saberNum == 1 ) + { + if (!Q_stricmp(gi.argv(2), "black")) + { + self->client->ps.saber[saberNum].crystals = (saber_crystals_t)(self->client->ps.saber[saberNum].crystals^SABER_CRYSTAL_BLACK); + return; + } + else if (!Q_stricmp(gi.argv(2), "unstable")) + { + self->client->ps.saber[saberNum].crystals = (saber_crystals_t)(self->client->ps.saber[saberNum].crystals^SABER_CRYSTAL_UNSTABLE); + return; + } + } + + gi.Printf( "Usage: saberCrystal \n" ); + gi.Printf( "valid saberNums: 1 or 2\n" ); + gi.Printf( "valid crystals: black and unstable\n" ); +} + struct SetForceCmd { const char *desc; const char *cmdname; @@ -322,6 +352,13 @@ SetForceCmd SetForceTable[NUM_FORCE_POWERS] = { { "forceAbsorb", "setForceAbsorb", FORCE_LEVEL_3 }, { "forceDrain", "setForceDrain", FORCE_LEVEL_3 }, { "forceSight", "setForceSight", FORCE_LEVEL_3 }, + { "forceDestruction", "setForceDestruction", FORCE_LEVEL_3 }, + { "forceInsanity", "setForceInsanity", FORCE_LEVEL_3 }, + { "forceStasis", "setForceStasis", FORCE_LEVEL_3 }, + { "forceBlinding", "setForceBlinding", FORCE_LEVEL_3 }, + { "forceDeadlySight", "setForceDeadlySight", FORCE_LEVEL_3 }, + { "forceRepulse", "setForceRepulse", FORCE_LEVEL_3 }, + { "forceInvulnerability", "setForceInvulnerability", FORCE_LEVEL_3 }, }; static void Svcmd_ForceSetLevel_f( int forcePower ) @@ -410,16 +447,38 @@ void Svcmd_SaberAttackCycle_f( void ) G_SoundIndexOnEnt( self, CHAN_WEAPON, self->client->ps.saber[1].soundOff ); } } + if (!self->client->ps.saber[1].Active()) + { + G_RemoveWeaponModels( self ); + G_RemoveHolsterModels( self ); + if ( !self->client->ps.saberInFlight ) + { + WP_SaberAddG2SaberModels( self, qfalse ); + } + WP_SaberAddHolsteredG2SaberModels( self, qtrue ); + } } else if ( !self->client->ps.saber[0].ActiveManualOnly() ) {//first one is off, too, so just turn that one on if ( !self->client->ps.saberInFlight ) {//but only if it's in your hand! + if (!self->client->ps.saber[1].Active()) + { + G_RemoveWeaponModels( self ); + G_RemoveHolsterModels( self ); + if ( !self->client->ps.saberInFlight ) + { + WP_SaberAddG2SaberModels( self, qfalse ); + } + WP_SaberAddHolsteredG2SaberModels( self, qtrue ); + } self->client->ps.saber[0].Activate(); } } else {//turn on the second one + G_RemoveHolsterModels( self ); + WP_SaberAddG2SaberModels( self, qtrue ); self->client->ps.saber[1].Activate(); } return; @@ -557,31 +616,35 @@ void Svcmd_SaberAttackCycle_f( void ) switch ( saberAnimLevel ) { case SS_FAST: - gi.Printf( S_COLOR_BLUE"Lightsaber Combat Style: Fast\n" ); + gi.Printf( S_COLOR_BLUE "Lightsaber Combat Style: Fast\n" ); //LIGHTSABERCOMBATSTYLE_FAST break; case SS_MEDIUM: - gi.Printf( S_COLOR_YELLOW"Lightsaber Combat Style: Medium\n" ); + gi.Printf( S_COLOR_YELLOW "Lightsaber Combat Style: Medium\n" ); //LIGHTSABERCOMBATSTYLE_MEDIUM break; case SS_STRONG: - gi.Printf( S_COLOR_RED"Lightsaber Combat Style: Strong\n" ); + gi.Printf( S_COLOR_RED "Lightsaber Combat Style: Strong\n" ); //LIGHTSABERCOMBATSTYLE_STRONG break; case SS_DESANN: - gi.Printf( S_COLOR_CYAN"Lightsaber Combat Style: Desann\n" ); + gi.Printf( S_COLOR_CYAN "Lightsaber Combat Style: Desann\n" ); //LIGHTSABERCOMBATSTYLE_DESANN break; case SS_TAVION: - gi.Printf( S_COLOR_MAGENTA"Lightsaber Combat Style: Tavion\n" ); + gi.Printf( S_COLOR_MAGENTA "Lightsaber Combat Style: Tavion\n" ); + //LIGHTSABERCOMBATSTYLE_TAVION + break; + case SS_KATARN: + gi.Printf( S_COLOR_MAGENTA"Lightsaber Combat Style: Katarn\n" ); //LIGHTSABERCOMBATSTYLE_TAVION break; case SS_DUAL: - gi.Printf( S_COLOR_MAGENTA"Lightsaber Combat Style: Dual\n" ); + gi.Printf( S_COLOR_MAGENTA "Lightsaber Combat Style: Dual\n" ); //LIGHTSABERCOMBATSTYLE_TAVION break; case SS_STAFF: - gi.Printf( S_COLOR_MAGENTA"Lightsaber Combat Style: Staff\n" ); + gi.Printf( S_COLOR_MAGENTA "Lightsaber Combat Style: Staff\n" ); //LIGHTSABERCOMBATSTYLE_TAVION break; } @@ -622,437 +685,470 @@ void G_GrabEntity( gentity_t *grabber, const char *target ) } } -/* -================= -ConsoleCommand -================= -*/ -qboolean ConsoleCommand( void ) { - const char *cmd; +static void Svcmd_ICARUS_f( void ) +{ + Quake3Game()->Svcmd(); +} - cmd = gi.argv(0); +template +static void Svcmd_ForceSetLevel_f(void) +{ + Svcmd_ForceSetLevel_f(power); +} - if ( Q_stricmp (cmd, "entitylist") == 0 ) +static void Svcmd_SetForceAll_f(void) +{ + for ( int i = FP_HEAL; i < NUM_FORCE_POWERS; i++ ) { - Svcmd_EntityList_f(); - return qtrue; + Svcmd_ForceSetLevel_f( i ); } - if (Q_stricmp (cmd, "game_memory") == 0) { - Svcmd_GameMem_f(); - return qtrue; - } - - if (Q_stricmp (cmd, "nav") == 0) + if( gi.argc() > 1 ) { - if ( !g_cheats->integer ) + for ( int i = SS_NONE+1; i < SS_NUM_SABER_STYLES; i++ ) { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; + g_entities[0].client->ps.saberStylesKnown |= (1<integer ) - { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; - } - Svcmd_NPC_f (); - return qtrue; + g_entities[0].client->ps.saberStylesKnown |= (1<integer ) + const char *cmd3 = gi.argv(2); + if ( cmd3 && cmd3[0] ) { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; + gentity_t *found = NULL; + if ( (found = G_Find(NULL, FOFS(targetname), cmd2 ) ) != NULL ) + { + Quake3Game()->RunScript( found, cmd3 ); + } + else + { + //can't find cmd2 + gi.Printf( S_COLOR_RED "runscript: can't find targetname %s\n", cmd2 ); + } } - Svcmd_Use_f (); - return qtrue; - } - - if ( Q_stricmp( cmd, "ICARUS" ) == 0 ) - { - if ( !g_cheats->integer ) + else { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; + Quake3Game()->RunScript( &g_entities[0], cmd2 ); } - - Quake3Game()->Svcmd(); - - return qtrue; } - - if ( Q_stricmp( cmd, "saberColor" ) == 0 ) + else { - if ( !g_cheats->integer ) - { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; - } - Svcmd_SaberColor_f(); - return qtrue; + gi.Printf( S_COLOR_RED "usage: runscript scriptname\n" ); } +} - if ( Q_stricmp( cmd, "saber" ) == 0 ) +static void Svcmd_PlayerTeam_f(void) +{ + const char *cmd2 = gi.argv(1); + + if ( !*cmd2 || !cmd2[0] ) { - if ( !g_cheats->integer ) + gi.Printf( S_COLOR_RED "'playerteam' - change player team, requires a team name!\n" ); + gi.Printf( S_COLOR_RED "Current team is: %s\n", GetStringForID( TeamTable, g_entities[0].client->playerTeam ) ); + gi.Printf( S_COLOR_RED "Valid team names are:\n"); + for ( int n = (TEAM_FREE + 1); n < TEAM_NUM_TEAMS; n++ ) { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; + gi.Printf( S_COLOR_RED "%s\n", GetStringForID( TeamTable, n ) ); } - Svcmd_Saber_f(); - return qtrue; } - - if ( Q_stricmp( cmd, "saberblade" ) == 0 ) + else { - if ( !g_cheats->integer ) + team_t team; + + team = (team_t)GetIDForString( TeamTable, cmd2 ); + if ( team == (team_t)-1 ) + { + gi.Printf( S_COLOR_RED "'playerteam' unrecognized team name %s!\n", cmd2 ); + gi.Printf( S_COLOR_RED "Current team is: %s\n", GetStringForID( TeamTable, g_entities[0].client->playerTeam ) ); + gi.Printf( S_COLOR_RED "Valid team names are:\n"); + for ( int n = TEAM_FREE; n < TEAM_NUM_TEAMS; n++ ) + { + gi.Printf( S_COLOR_RED "%s\n", GetStringForID( TeamTable, n ) ); + } + } + else { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; + g_entities[0].client->playerTeam = team; + //FIXME: convert Imperial, Malon, Hirogen and Klingon to Scavenger? } - Svcmd_SaberBlade_f(); - return qtrue; } +} - if ( Q_stricmp( cmd, "setForceJump" ) == 0 ) - { - Svcmd_ForceSetLevel_f( FP_LEVITATION ); - return qtrue; - } - if ( Q_stricmp( cmd, "setSaberThrow" ) == 0 ) - { - Svcmd_ForceSetLevel_f( FP_SABERTHROW ); - return qtrue; - } - if ( Q_stricmp( cmd, "setForceHeal" ) == 0 ) - { - Svcmd_ForceSetLevel_f( FP_HEAL ); - return qtrue; - } - if ( Q_stricmp( cmd, "setForcePush" ) == 0 ) - { - Svcmd_ForceSetLevel_f( FP_PUSH ); - return qtrue; - } - if ( Q_stricmp( cmd, "setForcePull" ) == 0 ) - { - Svcmd_ForceSetLevel_f( FP_PULL ); - return qtrue; - } - if ( Q_stricmp( cmd, "setForceSpeed" ) == 0 ) - { - Svcmd_ForceSetLevel_f( FP_SPEED ); - return qtrue; - } - if ( Q_stricmp( cmd, "setForceGrip" ) == 0 ) - { - Svcmd_ForceSetLevel_f( FP_GRIP ); - return qtrue; - } - if ( Q_stricmp( cmd, "setForceLightning" ) == 0 ) +static void Svcmd_Control_f(void) +{ + const char *cmd2 = gi.argv(1); + if ( !*cmd2 || !cmd2[0] ) { - Svcmd_ForceSetLevel_f( FP_LIGHTNING ); - return qtrue; + if ( !G_ClearViewEntity( &g_entities[0] ) ) + { + gi.Printf( S_COLOR_RED "control \n", cmd2 ); + } } - if ( Q_stricmp( cmd, "setMindTrick" ) == 0 ) + else { - Svcmd_ForceSetLevel_f( FP_TELEPATHY ); - return qtrue; + Q3_SetViewEntity( 0, cmd2 ); } - if ( Q_stricmp( cmd, "setSaberDefense" ) == 0 ) +} + +static void Svcmd_Grab_f(void) +{ + const char *cmd2 = gi.argv(1); + if ( !*cmd2 || !cmd2[0] ) { - Svcmd_ForceSetLevel_f( FP_SABER_DEFENSE ); - return qtrue; + if ( !G_ReleaseEntity( &g_entities[0] ) ) + { + gi.Printf( S_COLOR_RED "grab \n", cmd2 ); + } } - if ( Q_stricmp( cmd, "setSaberOffense" ) == 0 ) + else { - Svcmd_ForceSetLevel_f( FP_SABER_OFFENSE ); - return qtrue; + G_GrabEntity( &g_entities[0], cmd2 ); } - if ( Q_stricmp( cmd, "setForceRage" ) == 0 ) +} + +static void Svcmd_Knockdown_f(void) +{ + G_Knockdown( &g_entities[0], &g_entities[0], vec3_origin, 300, qtrue ); +} + +static void Svcmd_PlayerModel_f(void) +{ + if ( gi.argc() == 1 ) { - Svcmd_ForceSetLevel_f( FP_RAGE ); - return qtrue; + gi.Printf( S_COLOR_RED "USAGE: playerModel \n playerModel \n playerModel player (builds player from customized menu settings)" S_COLOR_WHITE "\n" ); + gi.Printf( "playerModel = %s ", va("%s %s %s %s\n", g_char_model->string, g_char_skin_head->string, g_char_skin_torso->string, g_char_skin_legs->string ) ); } - if ( Q_stricmp( cmd, "setForceDrain" ) == 0 ) + else if ( gi.argc() == 2 ) { - Svcmd_ForceSetLevel_f( FP_DRAIN ); - return qtrue; + G_ChangePlayerModel( &g_entities[0], gi.argv(1) ); } - if ( Q_stricmp( cmd, "setForceProtect" ) == 0 ) + else if ( gi.argc() == 5 ) { - Svcmd_ForceSetLevel_f( FP_PROTECT ); - return qtrue; + //instead of setting it directly via a command, we now store it in cvars + //G_ChangePlayerModel( &g_entities[0], va("%s|%s|%s|%s", gi.argv(1), gi.argv(2), gi.argv(3), gi.argv(4)) ); + gi.cvar_set("g_char_model", gi.argv(1) ); + gi.cvar_set("g_char_skin_head", gi.argv(2) ); + gi.cvar_set("g_char_skin_torso", gi.argv(3) ); + gi.cvar_set("g_char_skin_legs", gi.argv(4) ); + G_InitPlayerFromCvars( &g_entities[0] ); } - if ( Q_stricmp( cmd, "setForceAbsorb" ) == 0 ) +} + +static void Svcmd_PlayerTint_f(void) +{ + if ( gi.argc() == 4 ) { - Svcmd_ForceSetLevel_f( FP_ABSORB ); - return qtrue; + g_entities[0].client->renderInfo.customRGBA[0] = atoi(gi.argv(1)); + g_entities[0].client->renderInfo.customRGBA[1] = atoi(gi.argv(2)); + g_entities[0].client->renderInfo.customRGBA[2] = atoi(gi.argv(3)); + gi.cvar_set("g_char_color_red", gi.argv(1) ); + gi.cvar_set("g_char_color_green", gi.argv(2) ); + gi.cvar_set("g_char_color_blue", gi.argv(3) ); } - if ( Q_stricmp( cmd, "setForceSight" ) == 0 ) + else { - Svcmd_ForceSetLevel_f( FP_SEE ); - return qtrue; + gi.Printf( S_COLOR_RED "USAGE: playerTint \n" ); + gi.Printf( "playerTint = %s\n", va("%d %d %d", g_char_color_red->integer, g_char_color_green->integer, g_char_color_blue->integer ) ); } - if ( Q_stricmp( cmd, "setForceAll" ) == 0 ) - { - if ( !g_cheats->integer ) - { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; - } +} - for ( int i = FP_HEAL; i < NUM_FORCE_POWERS; i++ ) +static void Svcmd_IKnowKungfu_f(void) +{ + gi.cvar_set( "g_debugMelee", "1" ); + G_SetWeapon( &g_entities[0], WP_MELEE ); + for ( int i = FP_FIRST; i < NUM_FORCE_POWERS; i++ ) + { + g_entities[0].client->ps.forcePowersKnown |= ( 1 << i ); + if ( i == FP_TELEPATHY ) { - Svcmd_ForceSetLevel_f( i ); + g_entities[0].client->ps.forcePowerLevel[i] = FORCE_LEVEL_4; } - for ( int i = SS_NONE+1; i < SS_NUM_SABER_STYLES; i++ ) + else { - g_entities[0].client->ps.saberStylesKnown |= (1<ps.forcePowerLevel[i] = FORCE_LEVEL_3; } - return qtrue; } - if ( Q_stricmp( cmd, "setSaberAll" ) == 0 ) - { - if ( !g_cheats->integer ) - { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; - } +} - Svcmd_ForceSetLevel_f( FP_SABERTHROW ); - Svcmd_ForceSetLevel_f( FP_SABER_DEFENSE ); - Svcmd_ForceSetLevel_f( FP_SABER_OFFENSE ); - for ( int i = SS_NONE+1; i < SS_NUM_SABER_STYLES; i++ ) - { - g_entities[0].client->ps.saberStylesKnown |= (1<client->sess.missionStats.totalSecrets < 1) + { + gi.Printf( "There are" S_COLOR_RED " NO " S_COLOR_WHITE "secrets on this map!\n" ); } - if ( Q_stricmp( cmd, "saberAttackCycle" ) == 0 ) + else if(pl->client->sess.missionStats.secretsFound == pl->client->sess.missionStats.totalSecrets) { - Svcmd_SaberAttackCycle_f(); - return qtrue; + gi.Printf( "You've found all " S_COLOR_GREEN "%i" S_COLOR_WHITE " secrets on this map!\n", pl->client->sess.missionStats.secretsFound ); } - if ( Q_stricmp( cmd, "runscript" ) == 0 ) + else { - if ( !g_cheats->integer ) - { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; - } - const char *cmd2 = gi.argv(1); - - if ( cmd2 && cmd2[0] ) - { - const char *cmd3 = gi.argv(2); - if ( cmd3 && cmd3[0] ) - { - gentity_t *found = NULL; - if ( (found = G_Find(NULL, FOFS(targetname), cmd2 ) ) != NULL ) - { - Quake3Game()->RunScript( found, cmd3 ); - } - else - { - //can't find cmd2 - gi.Printf( S_COLOR_RED"runscript: can't find targetname %s\n", cmd2 ); - } - } - else - { - Quake3Game()->RunScript( &g_entities[0], cmd2 ); - } - } - else - { - gi.Printf( S_COLOR_RED"usage: runscript scriptname\n" ); - } - //FIXME: else warning - return qtrue; + gi.Printf( "You've found " S_COLOR_GREEN "%i" S_COLOR_WHITE " out of " S_COLOR_GREEN "%i" S_COLOR_WHITE " secrets!\n", pl->client->sess.missionStats.secretsFound, pl->client->sess.missionStats.totalSecrets ); } +} + +// PADAWAN - g_spskill 0 + cg_crosshairForceHint 1 + handicap 100 +// JEDI - g_spskill 1 + cg_crosshairForceHint 1 + handicap 100 +// JEDI KNIGHT - g_spskill 2 + cg_crosshairForceHint 0 + handicap 100 +// JEDI MASTER - g_spskill 2 + cg_crosshairForceHint 0 + handicap 50 - if ( Q_stricmp( cmd, "playerteam" ) == 0 ) +extern cvar_t *g_spskill; +static void Svcmd_Difficulty_f(void) +{ + if(gi.argc() == 1) { - if ( !g_cheats->integer ) + if(g_spskill->integer == 0) { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; + gi.Printf( S_COLOR_GREEN "Current Difficulty: Padawan" S_COLOR_WHITE "\n" ); } - const char *cmd2 = gi.argv(1); - int n; - - if ( !*cmd2 || !cmd2[0] ) + else if(g_spskill->integer == 1) { - gi.Printf( S_COLOR_RED"'playerteam' - change player team, requires a team name!\n" ); - gi.Printf( S_COLOR_RED"Valid team names are:\n"); - for ( n = (TEAM_FREE + 1); n < TEAM_NUM_TEAMS; n++ ) - { - gi.Printf( S_COLOR_RED"%s\n", GetStringForID( TeamTable, n ) ); - } + gi.Printf( S_COLOR_GREEN "Current Difficulty: Jedi" S_COLOR_WHITE "\n" ); } - else + else if(g_spskill->integer == 2) { - team_t team; - - team = (team_t)GetIDForString( TeamTable, cmd2 ); - if ( team == (team_t)-1 ) + int crosshairHint = gi.Cvar_VariableIntegerValue("cg_crosshairForceHint"); + int handicap = gi.Cvar_VariableIntegerValue("handicap"); + if(handicap == 100 && crosshairHint == 0) { - gi.Printf( S_COLOR_RED"'playerteam' unrecognized team name %s!\n", cmd2 ); - gi.Printf( S_COLOR_RED"Valid team names are:\n"); - for ( n = TEAM_FREE; n < TEAM_NUM_TEAMS; n++ ) - { - gi.Printf( S_COLOR_RED"%s\n", GetStringForID( TeamTable, n ) ); - } + gi.Printf( S_COLOR_GREEN "Current Difficulty: Jedi Knight" S_COLOR_WHITE "\n" ); } - else + else if(handicap == 50 && crosshairHint == 0) { - g_entities[0].client->playerTeam = team; - //FIXME: convert Imperial, Malon, Hirogen and Klingon to Scavenger? + gi.Printf( S_COLOR_GREEN "Current Difficulty: Jedi Master" S_COLOR_WHITE "\n" ); } - } - return qtrue; - } - - if ( Q_stricmp( cmd, "control" ) == 0 ) - { - if ( !g_cheats->integer ) - { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; - } - const char *cmd2 = gi.argv(1); - if ( !*cmd2 || !cmd2[0] ) - { - if ( !G_ClearViewEntity( &g_entities[0] ) ) + else { - gi.Printf( S_COLOR_RED"control \n", cmd2 ); + gi.Printf( S_COLOR_GREEN "Current Difficulty: Jedi Knight (Custom)" S_COLOR_WHITE "\n" ); + gi.Printf( S_COLOR_GREEN "Crosshair Force Hint: %i" S_COLOR_WHITE "\n", crosshairHint != 0 ? 1 : 0 ); + gi.Printf( S_COLOR_GREEN "Handicap: %i" S_COLOR_WHITE "\n", handicap ); } } else { - Q3_SetViewEntity( 0, cmd2 ); + gi.Printf( S_COLOR_RED "Invalid difficulty cvar set! g_spskill (%i) [0-2] is valid range only" S_COLOR_WHITE "\n", g_spskill->integer ); } - return qtrue; } +} + +static void Svcmd_HeadPlayerModel_f(void) +{ + if ( gi.argc() == 2 ) + { + //this is debug type option! + G_ChangeHeadModel(&g_entities[0], gi.argv(1)); + } + else if ( gi.argc() == 3 ) + { + gi.cvar_set("g_char_head_model", gi.argv(1) ); + gi.cvar_set("g_char_head_skin", gi.argv(2) ); + G_InitPlayerFromCvars( &g_entities[0] ); + } + else + { + gi.Printf( S_COLOR_RED"USAGE: headPlayerModel " ); + } +} - if ( Q_stricmp( cmd, "grab" ) == 0 ) +static void Svcmd_NewPlayerTint_f(void) +{ + if ( gi.argc() == 5 && ((unsigned int)atoi(gi.argv(1)) < MAX_CVAR_TINT)) { - if ( !g_cheats->integer ) + unsigned int tintIndex = atoi(gi.argv(1)); + g_entities[0].client->renderInfo.newCustomRGBA[tintIndex][0] = atoi(gi.argv(2)); + g_entities[0].client->renderInfo.newCustomRGBA[tintIndex][1] = atoi(gi.argv(3)); + g_entities[0].client->renderInfo.newCustomRGBA[tintIndex][2] = atoi(gi.argv(4)); + if (tintIndex == TINT_NEW_ENT) { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; + gi.cvar_set("g_char_color_2_red", gi.argv(2) ); + gi.cvar_set("g_char_color_2_green", gi.argv(3) ); + gi.cvar_set("g_char_color_2_blue", gi.argv(4) ); } - const char *cmd2 = gi.argv(1); - if ( !*cmd2 || !cmd2[0] ) + else if (tintIndex == TINT_HILT1) { - if ( !G_ReleaseEntity( &g_entities[0] ) ) - { - gi.Printf( S_COLOR_RED"grab \n", cmd2 ); - } + gi.cvar_set("g_hilt_color_red", gi.argv(2) ); + gi.cvar_set("g_hilt_color_green", gi.argv(3) ); + gi.cvar_set("g_hilt_color_blue", gi.argv(4) ); } - else + else if (tintIndex == TINT_HILT2) { - G_GrabEntity( &g_entities[0], cmd2 ); + gi.cvar_set("g_hilt2_color_red", gi.argv(2) ); + gi.cvar_set("g_hilt2_color_green", gi.argv(3) ); + gi.cvar_set("g_hilt2_color_blue", gi.argv(4) ); } - return qtrue; } - - if ( Q_stricmp( cmd, "knockdown" ) == 0 ) + else { - if ( !g_cheats->integer ) - { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; - } - G_Knockdown( &g_entities[0], &g_entities[0], vec3_origin, 300, qtrue ); - return qtrue; + gi.Printf( S_COLOR_RED"USAGE: newPlayerTint \n" ); } +} - if ( Q_stricmp( cmd, "playerModel" ) == 0 ) - { - if ( gi.argc() == 1 ) - { - gi.Printf( S_COLOR_RED"USAGE: playerModel \n playerModel \n playerModel player (builds player from customized menu settings)\n" ); - gi.Printf( "playerModel = %s ", va("%s %s %s %s\n", g_char_model->string, g_char_skin_head->string, g_char_skin_torso->string, g_char_skin_legs->string ) ); - } - else if ( gi.argc() == 2 ) - { - G_ChangePlayerModel( &g_entities[0], gi.argv(1) ); - } - else if ( gi.argc() == 5 ) - { - //instead of setting it directly via a command, we now store it in cvars - //G_ChangePlayerModel( &g_entities[0], va("%s|%s|%s|%s", gi.argv(1), gi.argv(2), gi.argv(3), gi.argv(4)) ); - gi.cvar_set("g_char_model", gi.argv(1) ); - gi.cvar_set("g_char_skin_head", gi.argv(2) ); - gi.cvar_set("g_char_skin_torso", gi.argv(3) ); - gi.cvar_set("g_char_skin_legs", gi.argv(4) ); - G_InitPlayerFromCvars( &g_entities[0] ); - } - return qtrue; - } +static void Svcmd_CustomSaber_f(void) +{ + if ( gi.argc() == 1 ) + { + gi.Printf( S_COLOR_RED"USAGE: customSaber \n" ); + } + else if ( gi.argc() == 7 ) + { + if (atoi(gi.argv(1)) == 1) + { + gi.cvar_set("g_saber2_skin1", gi.argv(2) ); + gi.cvar_set("g_saber2_skin2", gi.argv(3) ); + gi.cvar_set("g_saber2_skin3", gi.argv(4) ); + gi.cvar_set("g_saber2_skin4", gi.argv(5) ); + gi.cvar_set("g_saber2_skin5", gi.argv(6) ); + } + else + { + gi.cvar_set("g_saber_skin1", gi.argv(2) ); + gi.cvar_set("g_saber_skin2", gi.argv(3) ); + gi.cvar_set("g_saber_skin3", gi.argv(4) ); + gi.cvar_set("g_saber_skin4", gi.argv(5) ); + gi.cvar_set("g_saber_skin5", gi.argv(6) ); + } + + G_SetSabersFromCVars(&g_entities[0]); + + if ((&g_entities[0])->client->ps.weapon == WP_SABER) + { + WP_SaberAddG2SaberModels(&g_entities[0]); + } + } +} - if ( Q_stricmp( cmd, "playerTint" ) == 0 ) - { - if ( gi.argc() == 4 ) - { - g_entities[0].client->renderInfo.customRGBA[0] = atoi(gi.argv(1)); - g_entities[0].client->renderInfo.customRGBA[1] = atoi(gi.argv(2)); - g_entities[0].client->renderInfo.customRGBA[2] = atoi(gi.argv(3)); - gi.cvar_set("g_char_color_red", gi.argv(1) ); - gi.cvar_set("g_char_color_green", gi.argv(2) ); - gi.cvar_set("g_char_color_blue", gi.argv(3) ); - } - else - { - gi.Printf( S_COLOR_RED"USAGE: playerTint \n" ); - gi.Printf( "playerTint = %s\n", va("%d %d %d", g_char_color_red->integer, g_char_color_green->integer, g_char_color_blue->integer ) ); - } - return qtrue; - } - if ( Q_stricmp( cmd, "nexttestaxes" ) == 0 ) - { - G_NextTestAxes(); - } +#define CMD_NONE (0x00000000u) +#define CMD_CHEAT (0x00000001u) +#define CMD_ALIVE (0x00000002u) + +typedef struct svcmd_s { + const char *name; + void (*func)(void); + uint32_t flags; +} svcmd_t; + +static int svcmdcmp( const void *a, const void *b ) { + return Q_stricmp( (const char *)a, ((svcmd_t*)b)->name ); +} + +// FIXME some of these should be made CMD_ALIVE too! +static svcmd_t svcmds[] = { + { "entitylist", Svcmd_EntityList_f, CMD_NONE }, + { "game_memory", Svcmd_GameMem_f, CMD_NONE }, + + { "nav", Svcmd_Nav_f, CMD_CHEAT }, + { "npc", Svcmd_NPC_f, CMD_CHEAT }, + { "use", Svcmd_Use_f, CMD_CHEAT }, + { "ICARUS", Svcmd_ICARUS_f, CMD_CHEAT }, + + { "saberColor", Svcmd_SaberColor_f, CMD_CHEAT }, + { "saber", Svcmd_Saber_f, CMD_CHEAT }, + { "saberBlade", Svcmd_SaberBlade_f, CMD_CHEAT }, + + { "setForceJump", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setSaberThrow", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceHeal", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForcePush", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForcePull", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceSpeed", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceGrip", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceLightning", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setMindTrick", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setSaberDefense", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setSaberOffense", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceRage", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceDrain", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceProtect", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceAbsorb", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceSight", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceDestruction", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceInsanity", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceStasis", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceBlinding", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceDeadlySight", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceRepulse", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceInvulnerability", Svcmd_ForceSetLevel_f, CMD_CHEAT }, + { "setForceAll", Svcmd_SetForceAll_f, CMD_CHEAT }, + { "setSaberAll", Svcmd_SetSaberAll_f, CMD_CHEAT }, + + { "saberAttackCycle", Svcmd_SaberAttackCycle_f, CMD_NONE }, + + { "runscript", Svcmd_RunScript_f, CMD_CHEAT }, + + { "playerTeam", Svcmd_PlayerTeam_f, CMD_CHEAT }, + + { "control", Svcmd_Control_f, CMD_CHEAT }, + { "grab", Svcmd_Grab_f, CMD_CHEAT }, + { "knockdown", Svcmd_Knockdown_f, CMD_CHEAT }, + { "playerModel", Svcmd_PlayerModel_f, CMD_NONE }, + { "playerTint", Svcmd_PlayerTint_f, CMD_NONE }, + + { "nexttestaxes", G_NextTestAxes, CMD_NONE }, + + { "exitview", Svcmd_ExitView_f, CMD_NONE }, + + { "iknowkungfu", Svcmd_IKnowKungfu_f, CMD_CHEAT }, + + { "secrets", Svcmd_Secrets_f, CMD_NONE }, + { "difficulty", Svcmd_Difficulty_f, CMD_NONE }, + + { "headPlayerModel", Svcmd_HeadPlayerModel_f, CMD_NONE }, + + { "newPlayerTint", Svcmd_NewPlayerTint_f, CMD_NONE }, + + { "customSaber", Svcmd_CustomSaber_f, CMD_NONE }, + + { "saberCrystal", Svcmd_SaberCrystal_f, CMD_CHEAT }, + + //{ "say", Svcmd_Say_f, qtrue }, + //{ "toggleallowvote", Svcmd_ToggleAllowVote_f, qfalse }, + //{ "toggleuserinfovalidation", Svcmd_ToggleUserinfoValidation_f, qfalse }, +}; +static const size_t numsvcmds = ARRAY_LEN( svcmds ); + +/* +================= +ConsoleCommand +================= +*/ +qboolean ConsoleCommand( void ) { + const char *cmd = gi.argv(0); + const svcmd_t *command = (const svcmd_t *)Q_LinearSearch( cmd, svcmds, numsvcmds, sizeof( svcmds[0] ), svcmdcmp ); - if ( Q_stricmp( cmd, "exitview" ) == 0 ) + if ( !command ) + return qfalse; + + if ( (command->flags & CMD_CHEAT) + && !g_cheats->integer ) { - Svcmd_ExitView_f(); + gi.Printf( "Cheats are not enabled on this server.\n" ); + return qtrue; } - - if (Q_stricmp (cmd, "iknowkungfu") == 0) + else if ( (command->flags & CMD_ALIVE) + && (g_entities[0].health <= 0) ) { - if ( !g_cheats->integer ) - { - gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); - return qtrue; - } - - gi.cvar_set( "g_debugMelee", "1" ); - G_SetWeapon( &g_entities[0], WP_MELEE ); - for ( int i = FP_FIRST; i < NUM_FORCE_POWERS; i++ ) - { - g_entities[0].client->ps.forcePowersKnown |= ( 1 << i ); - if ( i == FP_TELEPATHY ) - { - g_entities[0].client->ps.forcePowerLevel[i] = FORCE_LEVEL_4; - } - else - { - g_entities[0].client->ps.forcePowerLevel[i] = FORCE_LEVEL_3; - } - } + gi.Printf( "You must be alive to use this command.\n" ); + return qtrue; } - - return qfalse; + else + command->func(); + return qtrue; } diff --git a/code/game/g_target.cpp b/code/game/g_target.cpp index db57c221d4..27417fa1dc 100644 --- a/code/game/g_target.cpp +++ b/code/game/g_target.cpp @@ -79,7 +79,7 @@ void Use_Target_Delay( gentity_t *ent, gentity_t *other, gentity_t *activator ) { G_ActivateBehavior(ent,BSET_USE); - ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000; + ent->nextthink = level.time + ( ent->wait + ent->random * Q_flrand(-1.0f, 1.0f) ) * 1000; ent->e_ThinkFunc = thinkF_Think_Target_Delay; ent->activator = activator; } @@ -962,7 +962,10 @@ void set_mission_stats_cvars( void ) if ( wpn ) { gitem_t *wItem= FindItemForWeapon( (weapon_t)wpn); - cgi_SP_GetStringTextString( va("SP_INGAME_%s",wItem->classname ), text, sizeof( text )); + if (!cgi_SP_GetStringTextString( va("SP_INGAME_%s",wItem->classname ), text, sizeof( text ))) + { + cgi_SP_GetStringTextString( va("SPMOD_INGAME_%s",wItem->classname ), text, sizeof( text )); + } gi.cvar_set("ui_stats_fave", va("%s",text)); //pass this on to the menu } @@ -999,6 +1002,14 @@ void set_mission_stats_cvars( void ) gi.cvar_set("ui_stats_lightning", va("%d",client->sess.missionStats.forceUsed[FP_LIGHTNING])); gi.cvar_set("ui_stats_rage", va("%d",client->sess.missionStats.forceUsed[FP_RAGE])); + gi.cvar_set("ui_stats_destruction", va("%d",client->sess.missionStats.forceUsed[FP_DESTRUCTION])); + gi.cvar_set("ui_stats_insanity", va("%d",client->sess.missionStats.forceUsed[FP_INSANITY])); + gi.cvar_set("ui_stats_stasis", va("%d",client->sess.missionStats.forceUsed[FP_STASIS])); + gi.cvar_set("ui_stats_blinding", va("%d",client->sess.missionStats.forceUsed[FP_BLINDING])); + + gi.cvar_set("ui_stats_deadlysight", va("%d",client->sess.missionStats.forceUsed[FP_DEADLYSIGHT])); + gi.cvar_set("ui_stats_repulse", va("%d",client->sess.missionStats.forceUsed[FP_REPULSE])); + gi.cvar_set("ui_stats_invulnerability", va("%d",client->sess.missionStats.forceUsed[FP_INVULNERABILITY])); } #include "../cgame/cg_media.h" //access to cgs @@ -1013,7 +1024,7 @@ void target_level_change_use(gentity_t *self, gentity_t *other, gentity_t *activ } else { - G_ChangeMap( self->message, self->target, (self->spawnflags&1) ); + G_ChangeMap( self->message, self->target, (qboolean)((self->spawnflags&1) != 0) ); } if (self->count>=0) { diff --git a/code/game/g_trigger.cpp b/code/game/g_trigger.cpp index 7af83e79e5..d484c4e88a 100644 --- a/code/game/g_trigger.cpp +++ b/code/game/g_trigger.cpp @@ -88,7 +88,7 @@ void multi_trigger_run( gentity_t *ent ) if ( ent->painDebounceTime != level.time ) {//first ent to touch it this frame //ent->e_ThinkFunc = thinkF_multi_wait; - ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000; + ent->nextthink = level.time + ( ent->wait + ent->random * Q_flrand(-1.0f, 1.0f) ) * 1000; ent->painDebounceTime = level.time; } } @@ -286,7 +286,7 @@ void Touch_Multi( gentity_t *self, gentity_t *other, trace_t *trace ) //FIXME: do we care about the sniper rifle or not? - if( other->s.number == 0 && ( other->client->ps.weapon > MAX_PLAYER_WEAPONS || other->client->ps.weapon <= WP_NONE ) ) + if( other->s.number == 0 && ( other->client->ps.weapon >= WP_NUM_WEAPONS || other->client->ps.weapon <= WP_NONE || !playerUsableWeapons[other->client->ps.weapon]) ) {//don't care about non-player weapons if this is the player return; } @@ -358,7 +358,7 @@ void trigger_cleared_fire (gentity_t *self) // should start the wait timer now, because the trigger's just been cleared, so we must "wait" from this point if ( self->wait > 0 ) { - self->nextthink = level.time + ( self->wait + self->random * crandom() ) * 1000; + self->nextthink = level.time + ( self->wait + self->random * Q_flrand(-1.0f, 1.0f) ) * 1000; } } @@ -1222,7 +1222,8 @@ void hurt_touch( gentity_t *self, gentity_t *other, trace_t *trace ) if ( self->spawnflags & 32 ) {//falling death if ( other->NPC && other->client && - (other->client->NPC_class == CLASS_BOBAFETT || other->client->NPC_class == CLASS_ROCKETTROOPER )) + (other->client->NPC_class == CLASS_BOBAFETT || other->client->NPC_class == CLASS_ROCKETTROOPER + || other->client->NPC_class == CLASS_MANDA)) {//boba never falls to his death! //FIXME: fall through if jetpack broken? JET_FlyStart(other); @@ -1324,14 +1325,15 @@ void space_touch( gentity_t *self, gentity_t *other, trace_t *trace ) return; } - if (other->s.m_iVehicleNum - && other->s.m_iVehicleNum <= MAX_CLIENTS ) + if (other->s.m_iVehicleNum) {//a player client inside a vehicle gentity_t *veh = &g_entities[other->s.m_iVehicleNum]; if (veh->inuse && veh->client && veh->m_pVehicle && veh->m_pVehicle->m_pVehicleInfo->hideRider) { //if they are "inside" a vehicle, then let that protect them from THE HORRORS OF SPACE. + other->client->inSpaceSuffocation = 0; + other->client->inSpaceIndex = ENTITYNUM_NONE; return; } } @@ -1358,9 +1360,8 @@ void SP_trigger_space(gentity_t *self) { InitTrigger(self); self->contents = CONTENTS_TRIGGER; - - //FIXME: implement!!! - //self->e_TouchFunc = touchF_space_touch; + + self->e_TouchFunc = touchF_space_touch; gi.linkentity(self); } @@ -1375,6 +1376,11 @@ void shipboundary_touch( gentity_t *self, gentity_t *other, trace_t *trace ) { //only let vehicles touch return; } + + if ( other->client->ps.hyperSpaceTime && level.time - other->client->ps.hyperSpaceTime < HYPERSPACE_TIME ) + {//don't interfere with hyperspacing ships + return; + } ent = G_Find (NULL, FOFS(targetname), self->target); if (!ent || !ent->inuse) @@ -1388,9 +1394,49 @@ void shipboundary_touch( gentity_t *self, gentity_t *other, trace_t *trace ) G_Damage(other, other, other, NULL, other->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_SUICIDE); return; } + + //make sure this sucker is linked so the prediction knows where to go + gi.linkentity(ent); other->client->ps.vehTurnaroundIndex = ent->s.number; - other->client->ps.vehTurnaroundTime = level.time + self->count; + other->client->ps.vehTurnaroundTime = level.time + (self->count*2); + + //keep up the detailed checks for another 2 seconds + self->bounceCount = level.time + 2000; +} + +void shipboundary_think(gentity_t *ent) +{ + gentity_t *entityList[MAX_GENTITIES]; + int numListedEntities; + int i = 0; + gentity_t *listedEnt; + + ent->nextthink = level.time + 100; + + if (ent->bounceCount < level.time) + { //don't need to be doing this check, no one has touched recently + return; + } + + numListedEntities = gi.EntitiesInBox( ent->absmin, ent->absmax, entityList, MAX_GENTITIES ); + while (i < numListedEntities) + { + listedEnt = entityList[i]; + if (listedEnt->inuse && listedEnt->client && listedEnt->s.m_iVehicleNum) + { + if (listedEnt->NPC && + listedEnt->client->NPC_class == CLASS_VEHICLE) + { + Vehicle_t *pVeh = listedEnt->m_pVehicle; + if (pVeh && pVeh->m_pVehicleInfo->type == VH_FIGHTER) + { + shipboundary_touch(ent, listedEnt, NULL); + } + } + } + i++; + } } /*QUAKED trigger_shipboundary (.5 .5 .5) ? @@ -1417,10 +1463,339 @@ void SP_trigger_shipboundary(gentity_t *self) } //FIXME: implement! - //self->e_TouchFunc = touchF_shipboundary_touch; + self->e_TouchFunc = touchF_shipboundary_touch; + self->nextthink = level.time + 500; + self->e_ThinkFunc = thinkF_shipboundary_think; gi.linkentity(self); } + +void hyperspace_touch( gentity_t *self, gentity_t *other, trace_t *trace ) +{ + gentity_t *ent; + + if (!other || !other->inuse || !other->client || + other->s.number < MAX_CLIENTS || + !other->m_pVehicle) + { //only let vehicles touch + return; + } + + if ( other->client->ps.hyperSpaceTime && level.time - other->client->ps.hyperSpaceTime < HYPERSPACE_TIME ) + {//already hyperspacing, just keep us moving + if ( other->client->ps.eFlags2&EF2_HYPERSPACE ) + {//they've started the hyperspace but haven't been teleported yet + float timeFrac = ((float)(level.time-other->client->ps.hyperSpaceTime))/HYPERSPACE_TIME; + if ( timeFrac >= HYPERSPACE_TELEPORT_FRAC ) + {//half-way, now teleport them! + vec3_t diff, fwd, right, up, newOrg; + float fDiff, rDiff, uDiff; + //take off the flag so we only do this once + other->client->ps.eFlags &= ~qfalse; + //Get the offset from the local position + ent = G_Find (NULL, FOFS(targetname), self->target); + if (!ent || !ent->inuse) + { //this is bad + G_Error( "trigger_hyperspace has invalid target '%s'\n", self->target ); + return; + } + VectorSubtract( other->client->ps.origin, ent->s.origin, diff ); + AngleVectors( ent->s.angles, fwd, right, up ); + fDiff = DotProduct( fwd, diff ); + rDiff = DotProduct( right, diff ); + uDiff = DotProduct( up, diff ); + //Now get the base position of the destination + ent = G_Find (NULL, FOFS(targetname), self->target2); + if (!ent || !ent->inuse) + { //this is bad + G_Error( "trigger_hyperspace has invalid target2 '%s'\n", self->target2 ); + return; + } + VectorCopy( ent->s.origin, newOrg ); + //finally, add the offset into the new origin + AngleVectors( ent->s.angles, fwd, right, up ); + VectorMA( newOrg, fDiff, fwd, newOrg ); + VectorMA( newOrg, rDiff, right, newOrg ); + VectorMA( newOrg, uDiff, up, newOrg ); + //trap->Print("hyperspace from %s to %s\n", vtos(other->client->ps.origin), vtos(newOrg) ); + //now put them in the offset position, facing the angles that position wants them to be facing + TeleportPlayer( other, newOrg, ent->s.angles ); + if ( other->m_pVehicle && other->m_pVehicle->m_pPilot ) + {//teleport the pilot, too + TeleportPlayer( (gentity_t*)other->m_pVehicle->m_pPilot, newOrg, ent->s.angles ); + //FIXME: and the passengers? + } + //make them face the new angle + //other->client->ps.hyperSpaceIndex = ent->s.number; + VectorCopy( ent->s.angles, other->client->ps.hyperSpaceAngles ); + //sound + G_SoundOnEnt( other, CHAN_LOCAL, "sound/vehicles/common/hyperend.wav"); + } + } + return; + } + else + { + ent = G_Find (NULL, FOFS(targetname), self->target); + if (!ent || !ent->inuse) + { //this is bad + G_Error( "trigger_hyperspace has invalid target '%s'\n", self->target ); + return; + } + + if (!other->s.m_iVehicleNum || other->m_pVehicle->m_iRemovedSurfaces) + { //if a vehicle touches a boundary without a pilot in it or with parts missing, just blow the thing up + G_Damage(other, other, other, NULL, other->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_SUICIDE); + return; + } + //other->client->ps.hyperSpaceIndex = ent->s.number; + VectorCopy( ent->s.angles, other->client->ps.hyperSpaceAngles ); + other->client->ps.hyperSpaceTime = level.time; + } +} + +/* + void trigger_hyperspace_find_targets( gentity_t *self ) + { + gentity_t *targEnt = NULL; + targEnt = G_Find (NULL, FOFS(targetname), self->target); + if (!targEnt || !targEnt->inuse) + { //this is bad + trap->Error( ERR_DROP, "trigger_hyperspace has invalid target '%s'\n", self->target ); + return; + } + targEnt->r.svFlags |= SVF_BROADCAST;//crap, need to tell the cgame about the target_position + targEnt = G_Find (NULL, FOFS(targetname), self->target2); + if (!targEnt || !targEnt->inuse) + { //this is bad + trap->Error( ERR_DROP, "trigger_hyperspace has invalid target2 '%s'\n", self->target2 ); + return; + } + targEnt->r.svFlags |= SVF_BROADCAST;//crap, need to tell the cgame about the target_position + } + */ +/*QUAKED trigger_hyperspace (.5 .5 .5) ? + Ship will turn to face the angles of the first target_position then fly forward, playing the hyperspace effect, then pop out at a relative point around the target + + "target" whatever position the ship teleports from in relation to the target_position specified here, that's the relative position the ship will spawn at around the target2 target_position + "target2" name of target_position to teleport the ship to (will be relative to it's origin) + */ +void SP_trigger_hyperspace(gentity_t *self) +{ + //register the hyperspace end sound (start sounds are customized) + G_SoundIndex( "sound/vehicles/common/hyperend.wav" ); + + InitTrigger(self); + self->contents = CONTENTS_TRIGGER; + + if (!self->target || !self->target[0]) + { + G_Error( "trigger_hyperspace without a target." ); + } + if (!self->target2 || !self->target2[0]) + { + G_Error( "trigger_hyperspace without a target2." ); + } + + self->delay = Distance( self->absmax, self->absmin );//my size + + self->e_TouchFunc = touchF_hyperspace_touch; + + gi.linkentity(self); + + //self->think = trigger_hyperspace_find_targets; + //self->nextthink = level.time + FRAMETIME; +} + +gentity_t *asteroid_pick_random_asteroid( gentity_t *self ) +{ + int t_count = 0, pick; + gentity_t *t = NULL; + + while ( (t = G_Find (t, FOFS(targetname), self->target)) != NULL ) + { + if (t != self) + { + t_count++; + } + } + + if(!t_count) + { + return NULL; + } + + if(t_count == 1) + { + return t; + } + + //FIXME: need a seed + pick = Q_irand(1, t_count); + t_count = 0; + while ( (t = G_Find (t, FOFS(targetname), self->target)) != NULL ) + { + if (t != self) + { + t_count++; + } + else + { + continue; + } + + if(t_count == pick) + { + return t; + } + } + return NULL; +} + +int asteroid_count_num_asteroids( gentity_t *self ) +{ + int i, count = 0; + + for ( i = MAX_CLIENTS; i < ENTITYNUM_WORLD; i++ ) + { + if ( !g_entities[i].inuse ) + { + continue; + } + if ( g_entities[i].owner == self ) + { + count++; + } + } + return count; +} + +extern void SP_func_rotating (gentity_t *ent); +extern void Q3_Lerp2Origin( int taskID, int entID, vec3_t origin, float duration ); +void asteroid_field_think(gentity_t *self) +{ + int numAsteroids = asteroid_count_num_asteroids( self ); + + self->nextthink = level.time + 500; + + if ( numAsteroids < self->count ) + { + //need to spawn a new asteroid + gentity_t *newAsteroid = G_Spawn(); + if ( newAsteroid ) + { + vec3_t startSpot, endSpot, startAngles; + float dist, speed = Q_flrand( self->speed * 0.25f, self->speed * 2.0f ); + int capAxis, axis, time = 0; + gentity_t *copyAsteroid = asteroid_pick_random_asteroid( self ); + if ( copyAsteroid ) + { + newAsteroid->model = G_NewString(copyAsteroid->model); + newAsteroid->model2 = copyAsteroid->model2; + newAsteroid->health = copyAsteroid->health; + newAsteroid->spawnflags = copyAsteroid->spawnflags; + newAsteroid->mass = copyAsteroid->mass; + newAsteroid->damage = copyAsteroid->damage; + newAsteroid->speed = copyAsteroid->speed; + + G_SetOrigin( newAsteroid, copyAsteroid->s.origin ); + G_SetAngles( newAsteroid, copyAsteroid->s.angles ); + newAsteroid->classname = "func_rotating"; + + SP_func_rotating( newAsteroid ); + + VectorCopy(copyAsteroid->s.modelScale, newAsteroid->s.modelScale); + VectorSet(newAsteroid->s.modelScale, 2.0, 2.0, 2.0); + //newAsteroid->maxHealth = newAsteroid->health; + newAsteroid->radius = copyAsteroid->radius; + newAsteroid->material = copyAsteroid->material; + //CacheChunkEffects( self->material ); + + //keep track of it + newAsteroid->owner = self; + + //move it + capAxis = Q_irand( 0, 2 ); + for ( axis = 0; axis < 3; axis++ ) + { + if ( axis == capAxis ) + { + if ( Q_irand( 0, 1 ) ) + { + startSpot[axis] = self->mins[axis]; + endSpot[axis] = self->maxs[axis]; + } + else + { + startSpot[axis] = self->maxs[axis]; + endSpot[axis] = self->mins[axis]; + } + } + else + { + startSpot[axis] = self->mins[axis]+(Q_flrand(0,1.0f)*(self->maxs[axis]-self->mins[axis])); + endSpot[axis] = self->mins[axis]+(Q_flrand(0,1.0f)*(self->maxs[axis]-self->mins[axis])); + } + } + //FIXME: maybe trace from start to end to make sure nothing is in the way? How big of a trace? + + G_SetOrigin( newAsteroid, startSpot ); + dist = Distance( endSpot, startSpot ); + time = ceil(dist/speed)*1000; + Q3_Lerp2Origin( -1, newAsteroid->s.number, endSpot, time ); + + //spin it + startAngles[0] = Q_flrand( -360, 360 ); + startAngles[1] = Q_flrand( -360, 360 ); + startAngles[2] = Q_flrand( -360, 360 ); + G_SetAngles( newAsteroid, startAngles ); + newAsteroid->s.apos.trDelta[0] = Q_flrand( -100, 100 ); + newAsteroid->s.apos.trDelta[1] = Q_flrand( -100, 100 ); + newAsteroid->s.apos.trDelta[2] = Q_flrand( -100, 100 ); + newAsteroid->s.apos.trTime = level.time; + newAsteroid->s.apos.trType = TR_LINEAR; + + //remove itself when done + newAsteroid->e_ThinkFunc = thinkF_G_FreeEntity; + newAsteroid->nextthink = level.time+time; + + //think again sooner if need even more + if ( numAsteroids+1 < self->count ) + {//still need at least one more + //spawn it in 100ms + self->nextthink = level.time + 100; + } + } + } + } +} + +/*QUAKED trigger_asteroid_field (.5 .5 .5) ? + speed - how fast, on average, the asteroid moves + count - how many asteroids, max, to have at one time + target - target this at func_rotating asteroids + */ +void SP_trigger_asteroid_field(gentity_t *self) +{ + gi.SetBrushModel( self, self->model ); + self->contents = 0; + + if ( !self->count ) + { + self->health = 20; + } + + if ( !self->speed ) + { + self->speed = 10000; + } + + self->e_ThinkFunc = thinkF_asteroid_field_think; + self->nextthink = level.time + 100; + + gi.linkentity(self); +} + /* ============================================================================== @@ -1444,7 +1819,7 @@ so, the basic time between firing is a random time between void func_timer_think( gentity_t *self ) { G_UseTargets (self, self->activator); // set time before next firing - self->nextthink = level.time + 1000 * ( self->wait + crandom() * self->random ); + self->nextthink = level.time + 1000 * ( self->wait + Q_flrand(-1.0f, 1.0f) * self->random ); } void func_timer_use( gentity_t *self, gentity_t *other, gentity_t *activator ) { diff --git a/code/game/g_turret.cpp b/code/game/g_turret.cpp index 7641719143..d7f90afa61 100644 --- a/code/game/g_turret.cpp +++ b/code/game/g_turret.cpp @@ -55,7 +55,7 @@ void TurretPain( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, con if ( mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) { // DEMP2 makes the turret stop shooting for a bit..and does extra feedback - self->attackDebounceTime = level.time + 800 + random() * 500; + self->attackDebounceTime = level.time + 800 + Q_flrand(0.0f, 1.0f) * 500; G_PlayEffect( "sparks/spark_exp_nosnd", point, dir ); } @@ -257,7 +257,7 @@ void turret_head_think( gentity_t *self ) self->modelScale ); if ( (self->spawnflags&SPF_TURRETG2_TURBO) ) { - self->alt_fire = !self->alt_fire; + self->alt_fire = (qboolean)!self->alt_fire; } gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, org ); @@ -638,7 +638,7 @@ void turret_base_think( gentity_t *self ) else { // keep our enemy for a minimum of 2 seconds from now - self->bounceCount = level.time + 2000 + random() * 150; + self->bounceCount = level.time + 2000 + Q_flrand(0.0f, 1.0f) * 150; } turret_aim( self ); @@ -954,7 +954,7 @@ void finish_spawning_turret( gentity_t *base ) // How quickly to fire if ( !base->wait ) { - base->wait = 500;// + random() * 500; + base->wait = 500;// + Q_flrand(0.0f, 1.0f) * 500; } if ( !base->splashDamage ) @@ -994,7 +994,7 @@ void finish_spawning_turret( gentity_t *base ) else { // this is a random time offset for the no-enemy-search-around-mode - base->count = random() * 9000; + base->count = Q_flrand(0.0f, 1.0f) * 9000; if ( !base->health ) { @@ -1010,7 +1010,7 @@ void finish_spawning_turret( gentity_t *base ) // How quickly to fire if ( !base->wait ) { - base->wait = 150 + random() * 55; + base->wait = 150 + Q_flrand(0.0f, 1.0f) * 55; } if ( !base->splashDamage ) @@ -1543,7 +1543,7 @@ static qboolean pas_find_enemies( gentity_t *self ) G_Sound( self, G_SoundIndex( "sound/chars/turret/startup.wav" )); // Wind up turrets for a bit - self->attackDebounceTime = level.time + 900 + random() * 200; + self->attackDebounceTime = level.time + 900 + Q_flrand(0.0f, 1.0f) * 200; } G_SetEnemy( self, target ); @@ -1571,7 +1571,7 @@ void pas_adjust_enemy( gentity_t *ent ) { keep = qfalse; } - else// if ( random() > 0.5f ) + else// if ( Q_flrand(0.0f, 1.0f) > 0.5f ) { // do a trace every now and then. mdxaBone_t boltMatrix; @@ -1607,7 +1607,7 @@ void pas_adjust_enemy( gentity_t *ent ) if ( keep ) { - ent->bounceCount = level.time + 500 + random() * 150; + ent->bounceCount = level.time + 500 + Q_flrand(0.0f, 1.0f) * 150; } else if ( ent->bounceCount < level.time ) // don't ping pong on and off { @@ -1740,7 +1740,7 @@ void pas_think( gentity_t *ent ) ent->s.loopSound = 0; } - if ( ent->enemy && ent->attackDebounceTime < level.time && random() > 0.3f ) + if ( ent->enemy && ent->attackDebounceTime < level.time && Q_flrand(0.0f, 1.0f) > 0.3f ) { ent->count--; @@ -1940,7 +1940,7 @@ void ion_cannon_think( gentity_t *self ) else { // done with burst, so wait delay amount, plus a random bit - self->nextthink = level.time + ( self->delay + crandom() * self->random ); + self->nextthink = level.time + ( self->delay + Q_flrand(-1.0f, 1.0f) * self->random ); self->count = Q_irand(0,5); // 0-5 bursts // Not firing this time @@ -1972,7 +1972,7 @@ void ion_cannon_think( gentity_t *self ) } gi.G2API_SetBoneAnimIndex( &self->ghoul2[self->playerModel], self->rootBone, 0, 8, BONE_ANIM_OVERRIDE_FREEZE, 0.6f, cg.time, -1, -1 ); - self->nextthink = level.time + self->wait + crandom() * self->random; + self->nextthink = level.time + self->wait + Q_flrand(-1.0f, 1.0f) * self->random; } //---------------------------------------------------------------------------------------------- @@ -2126,7 +2126,7 @@ void SP_misc_ion_cannon( gentity_t *base ) { // start thinking now, otherwise, we'll wait until we are used base->e_ThinkFunc = thinkF_ion_cannon_think; - base->nextthink = level.time + base->wait + crandom() * base->random; + base->nextthink = level.time + base->wait + Q_flrand(-1.0f, 1.0f) * base->random; } // Bursts? diff --git a/code/game/g_utils.cpp b/code/game/g_utils.cpp index 9273fad527..4572076cd9 100644 --- a/code/game/g_utils.cpp +++ b/code/game/g_utils.cpp @@ -119,6 +119,11 @@ int G_BSPIndex( char *name ) return G_FindConfigstringIndex (name, CS_BSP_MODELS, MAX_SUB_BSP, qtrue); } +int G_IconIndex( const char *name ) { + assert( name && name[0] ); + return G_FindConfigstringIndex (name, CS_ICONS, MAX_ICONS, qtrue); +} + #define FX_ENT_RADIUS 32 //----------------------------- diff --git a/code/game/g_vehicles.cpp b/code/game/g_vehicles.cpp index d25f003bad..9351770451 100644 --- a/code/game/g_vehicles.cpp +++ b/code/game/g_vehicles.cpp @@ -418,10 +418,11 @@ void G_DriveATST( gentity_t *pEnt, gentity_t *atst ) //G_SetG2PlayerModel( pEnt, pEnt->NPC_type, NULL, NULL, NULL ); //FIXME: reset/4 their weapon - pEnt->client->ps.stats[STAT_WEAPONS] &= ~(( 1 << WP_ATST_MAIN )|( 1 << WP_ATST_SIDE )); + pEnt->client->ps.weapons[WP_ATST_MAIN] = 0; + pEnt->client->ps.weapons[WP_ATST_SIDE] = 0; pEnt->client->ps.ammo[weaponData[WP_ATST_MAIN].ammoIndex] = 0; pEnt->client->ps.ammo[weaponData[WP_ATST_SIDE].ammoIndex] = 0; - if ( pEnt->client->ps.stats[STAT_WEAPONS] & (1<client->ps.weapons[WP_BLASTER] ) { CG_ChangeWeapon( WP_BLASTER ); //camera @@ -476,7 +477,8 @@ void G_DriveATST( gentity_t *pEnt, gentity_t *atst ) item = FindItemForWeapon( WP_ATST_SIDE ); //precache the weapon CG_RegisterItemSounds( (item-bg_itemlist) ); CG_RegisterItemVisuals( (item-bg_itemlist) ); - pEnt->client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_ATST_MAIN )|( 1 << WP_ATST_SIDE ); + pEnt->client->ps.weapons[WP_ATST_MAIN] = 1; + pEnt->client->ps.weapons[WP_ATST_SIDE] = 1; pEnt->client->ps.ammo[weaponData[WP_ATST_MAIN].ammoIndex] = ammoData[weaponData[WP_ATST_MAIN].ammoIndex].max; pEnt->client->ps.ammo[weaponData[WP_ATST_SIDE].ammoIndex] = ammoData[weaponData[WP_ATST_SIDE].ammoIndex].max; CG_ChangeWeapon( WP_ATST_MAIN ); @@ -806,7 +808,7 @@ bool Board( Vehicle_t *pVeh, bgEntity_t *pEnt ) #ifndef _JK2MP //rwwFIXMEFIXMEFIXME if (ent->s.numberclient->ps.stats[ STAT_WEAPONS ] |= (1<client->ps.weapons[WP_NONE] = 1; } if ( (ent->client->ps.weapon != WP_SABER && ent->client->ps.weapon != WP_BLASTER) || !(pVeh->m_pVehicleInfo->type == VH_ANIMAL || pVeh->m_pVehicleInfo->type == VH_SPEEDER)) @@ -1632,7 +1634,7 @@ bool Initialize( Vehicle_t *pVeh ) //initialize to blaster, just since it's a basic weapon and there's no lightsaber crap...? parent->client->ps.weapon = WP_BLASTER; parent->client->ps.weaponstate = WEAPON_READY; - parent->client->ps.stats[STAT_WEAPONS] |= (1<client->ps.weapons[WP_BLASTER] = 1; //Initialize to landed (wings closed, gears down) animation { @@ -2032,7 +2034,7 @@ static bool Update( Vehicle_t *pVeh, const usercmd_t *pUmcd ) {//okay to toggle if ( pVeh->m_pVehicleInfo->weapon[i].linkable == 1 ) {//link-toggleable - pVeh->weaponStatus[i].linked = !pVeh->weaponStatus[i].linked; + pVeh->weaponStatus[i].linked = (qboolean)!pVeh->weaponStatus[i].linked; } } linkHeld = qtrue; diff --git a/code/game/g_vehicles.h b/code/game/g_vehicles.h index c2baeb5324..6296e0deea 100644 --- a/code/game/g_vehicles.h +++ b/code/game/g_vehicles.h @@ -466,6 +466,26 @@ struct Muzzle // whether this Muzzle was just fired or not (reset at muzzle flash code). bool m_bFired; + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(m_vMuzzlePos); + saved_game.write(m_vMuzzleDir); + saved_game.write(m_iMuzzleWait); + saved_game.write(m_bFired); + saved_game.skip(3); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(m_vMuzzlePos); + saved_game.read(m_vMuzzleDir); + saved_game.read(m_iMuzzleWait); + saved_game.read(m_bFired); + saved_game.skip(3); + } }; //defines for impact damage surface stuff @@ -501,6 +521,25 @@ typedef struct int lastAmmoInc; //which muzzle will fire next int nextMuzzle; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(linked); + saved_game.write(ammo); + saved_game.write(lastAmmoInc); + saved_game.write(nextMuzzle); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(linked); + saved_game.read(ammo); + saved_game.read(lastAmmoInc); + saved_game.read(nextMuzzle); + } } vehWeaponStatus_t; typedef struct @@ -515,6 +554,27 @@ typedef struct int enemyEntNum; //how long to hold on to our current enemy int enemyHoldTime; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(ammo); + saved_game.write(lastAmmoInc); + saved_game.write(nextMuzzle); + saved_game.write(enemyEntNum); + saved_game.write(enemyHoldTime); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(ammo); + saved_game.read(lastAmmoInc); + saved_game.read(nextMuzzle); + saved_game.read(enemyEntNum); + saved_game.read(enemyHoldTime); + } } vehTurretStatus_t; // This is the implementation of the vehicle interface and any of the other variables needed. This @@ -631,6 +691,101 @@ struct Vehicle_t // don't need these in mp int m_safeJumpMountTime; float m_safeJumpMountRightDot; + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(m_pPilot); + saved_game.write(m_iPilotTime); + saved_game.write(m_bHasHadPilot); + saved_game.write(m_pDroidUnit); + saved_game.write(m_pParentEntity); + saved_game.write(m_iBoarding); + saved_game.write(m_bWasBoarding); + saved_game.skip(3); + saved_game.write(m_vBoardingVelocity); + saved_game.write(m_fTimeModifier); + saved_game.write(m_iLeftWingBone); + saved_game.write(m_iRightWingBone); + saved_game.write(m_iExhaustTag); + saved_game.write(m_iMuzzleTag); + saved_game.write(m_iDroidUnitTag); + saved_game.write(m_iGunnerViewTag); + saved_game.write<>(m_Muzzles); + saved_game.write<>(m_ucmd); + saved_game.write(m_EjectDir); + saved_game.write(m_ulFlags); + saved_game.write(m_vOrientation); + saved_game.write(m_fStrafeTime); + saved_game.write(m_vPrevOrientation); + saved_game.write(m_vAngularVelocity); + saved_game.write(m_vFullAngleVelocity); + saved_game.write(m_iArmor); + saved_game.write(m_iShields); + saved_game.write(m_iLastFXTime); + saved_game.write(m_iDieTime); + saved_game.write(m_pVehicleInfo); + saved_game.write<>(m_LandTrace); + saved_game.write(m_iRemovedSurfaces); + saved_game.write(m_iTurboTime); + saved_game.write(m_iDropTime); + saved_game.write(m_iSoundDebounceTimer); + saved_game.write(lastShieldInc); + saved_game.write(linkWeaponToggleHeld); + saved_game.write<>(weaponStatus); + saved_game.write<>(turretStatus); + saved_game.write(m_pOldPilot); + saved_game.write(m_safeJumpMountTime); + saved_game.write(m_safeJumpMountRightDot); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(m_pPilot); + saved_game.read(m_iPilotTime); + saved_game.read(m_bHasHadPilot); + saved_game.read(m_pDroidUnit); + saved_game.read(m_pParentEntity); + saved_game.read(m_iBoarding); + saved_game.read(m_bWasBoarding); + saved_game.skip(3); + saved_game.read(m_vBoardingVelocity); + saved_game.read(m_fTimeModifier); + saved_game.read(m_iLeftWingBone); + saved_game.read(m_iRightWingBone); + saved_game.read(m_iExhaustTag); + saved_game.read(m_iMuzzleTag); + saved_game.read(m_iDroidUnitTag); + saved_game.read(m_iGunnerViewTag); + saved_game.read<>(m_Muzzles); + saved_game.read<>(m_ucmd); + saved_game.read(m_EjectDir); + saved_game.read(m_ulFlags); + saved_game.read(m_vOrientation); + saved_game.read(m_fStrafeTime); + saved_game.read(m_vPrevOrientation); + saved_game.read(m_vAngularVelocity); + saved_game.read(m_vFullAngleVelocity); + saved_game.read(m_iArmor); + saved_game.read(m_iShields); + saved_game.read(m_iLastFXTime); + saved_game.read(m_iDieTime); + saved_game.read(m_pVehicleInfo); + saved_game.read<>(m_LandTrace); + saved_game.read(m_iRemovedSurfaces); + saved_game.read(m_iTurboTime); + saved_game.read(m_iDropTime); + saved_game.read(m_iSoundDebounceTimer); + saved_game.read(lastShieldInc); + saved_game.read(linkWeaponToggleHeld); + saved_game.read<>(weaponStatus); + saved_game.read<>(turretStatus); + saved_game.read(m_pOldPilot); + saved_game.read(m_safeJumpMountTime); + saved_game.read(m_safeJumpMountRightDot); + } }; extern int BG_VehicleGetIndex( const char *vehicleName ); diff --git a/code/game/g_weapon.cpp b/code/game/g_weapon.cpp index 643fe5e515..ceead21339 100644 --- a/code/game/g_weapon.cpp +++ b/code/game/g_weapon.cpp @@ -38,6 +38,8 @@ vec3_t muzzle; gentity_t *ent_list[MAX_GENTITIES]; extern cvar_t *g_debugMelee; +extern cvar_t *g_weaponVelocity; +extern cvar_t *g_weaponAltVelocity; // some naughty little things that are used cg side int g_rocketLockEntNum = ENTITYNUM_NONE; @@ -76,6 +78,12 @@ float weaponSpeed[WP_NUM_WEAPONS][2] = { 0,0 },//WP_TUSKEN_STAFF, { 0,0 },//WP_SCEPTER, { 0,0 },//WP_NOGHRI_STICK, + { Q3_INFINITE,Q3_INFINITE },//WP_SONIC_BLASTER, + { BLASTER_VELOCITY,BLASTER_VELOCITY },//WP_E5_CARBINE, + { BLASTER_VELOCITY,BLASTER_VELOCITY },//WP_DC15S_CARBINE, + { BRYAR_PISTOL_VEL,BRYAR_PISTOL_VEL },//WP_DC15A_RIFLE, + { REPEATER_VELOCITY,REPEATER_VELOCITY },//WP_Z6_ROTARY, + }; float WP_SpeedOfMissileForWeapon( int wp, qboolean alt_fire ) @@ -139,6 +147,9 @@ gentity_t *CreateMissile( vec3_t org, vec3_t dir, float vel, int life, gentity_t missile->alt_fire = altFire; + if (altFire) vel *= g_weaponAltVelocity->value; + else vel *= g_weaponVelocity->value; + missile->s.pos.trType = TR_LINEAR; missile->s.pos.trTime = level.time;// - 10; // move a bit on the very first frame VectorCopy( org, missile->s.pos.trBase ); @@ -1532,7 +1543,37 @@ void FireWeapon( gentity_t *ent, qboolean alt_fire ) { WP_FireNoghriStick( ent ); } - //else does melee attack/damage/func + else + { + WP_Melee( ent ); + } + break; + + case WP_E5_CARBINE: + WP_FireBlaster( ent, alt_fire ); + break; + + case WP_DC15S_CARBINE: + WP_FireBlaster( ent, alt_fire ); + break; + + case WP_Z6_ROTARY: + WP_FireRepeater( ent, qfalse ); + break; + + case WP_DC15A_RIFLE: + WP_FireBryarPistol( ent, qfalse ); + break; + + case WP_SONIC_BLASTER: + if ( !alt_fire ) + { + WP_FireDisruptor( ent, qfalse ); + } + else + { + WP_SonicBlast( ent ); + } break; case WP_TUSKEN_STAFF: @@ -1609,13 +1650,13 @@ TOGGLE - keep firing until used again (fires at intervals of "wait") */ void misc_weapon_shooter_fire( gentity_t *self ) { - FireWeapon( self, (self->spawnflags&1) ); + FireWeapon( self, (qboolean)((self->spawnflags&1) != 0) ); if ( (self->spawnflags&2) ) {//repeat self->e_ThinkFunc = thinkF_misc_weapon_shooter_fire; if (self->random) { - self->nextthink = level.time + self->wait + (int)(random()*self->random); + self->nextthink = level.time + self->wait + (int)(Q_flrand(0.0f, 1.0f)*self->random); } else { @@ -1663,7 +1704,7 @@ extern stringID_table_t WPTable[]; void SP_misc_weapon_shooter( gentity_t *self ) { //alloc a client just for the weapon code to use - self->client = (gclient_s *)gi.Malloc(sizeof(gclient_s), TAG_G_ALLOC, qtrue); + self->client = (gclient_t *)gi.Malloc(sizeof(gclient_t), TAG_G_ALLOC, qtrue); //set weapon self->s.weapon = self->client->ps.weapon = WP_BLASTER; @@ -1697,3 +1738,67 @@ void SP_misc_weapon_shooter( gentity_t *self ) self->wait = 500; } } + +qboolean heavyWeap(int wp) +{ + switch (wp) { + case WP_FLECHETTE: + case WP_ROCKET_LAUNCHER: + case WP_CONCUSSION: + case WP_THERMAL: + return true; + } + + return false; +} + +qboolean blasterWeap(int wp) +{ + switch (wp) { + case WP_BLASTER_PISTOL: + case WP_BLASTER: + case WP_REPEATER: + case WP_BOWCASTER: + case WP_NOGHRI_STICK: + case WP_BRYAR_PISTOL: + return true; + } + + return false; +} + +qboolean lightBlasterWeap(int wp) +{ + switch (wp) { + case WP_BLASTER_PISTOL: + case WP_BLASTER: + case WP_NOGHRI_STICK: + case WP_BRYAR_PISTOL: + return true; + } + + return false; +} + +qboolean heavyBlasterWeap(int wp) +{ + switch (wp) { + case WP_REPEATER: + case WP_BOWCASTER: + return true; + } + + return false; +} + +qboolean meleeWeap(int wp) +{ + switch (wp) { + case WP_SABER: + case WP_MELEE: + case WP_STUN_BATON: + return true; + } + + return false; +} diff --git a/code/game/g_weaponLoad.cpp b/code/game/g_weaponLoad.cpp index 75f1e82c69..bd17f7826a 100644 --- a/code/game/g_weaponLoad.cpp +++ b/code/game/g_weaponLoad.cpp @@ -25,6 +25,9 @@ along with this program; if not, see . // this is excluded from PCH usage 'cos it looks kinda scary to me, being game and ui.... -Ste #include "g_local.h" +//extern float forceJumpHeight[]; +//extern cvar_t *g_weaponVelocity; +//extern cvar_t *g_weaponAltVelocity; typedef struct { const char *name; @@ -80,6 +83,11 @@ void FX_TuskenShotProjectileThink( centity_t *cent, const struct weaponInfo_s *w //Noghri projectile void FX_NoghriShotProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon ); +//Clone projectile +void FX_CloneBlasterProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon ); +void FX_CloneBlasterAltFireThink( centity_t *cent, const struct weaponInfo_s *weapon ); + + // Table used to attach an extern missile function string to the actual cgame function func_t funcs[] = { {"bryar_func", FX_BryarProjectileThink}, @@ -87,6 +95,8 @@ func_t funcs[] = { {"blaster_func", FX_BlasterProjectileThink}, {"blaster_alt_func", FX_BlasterAltFireThink}, {"bowcaster_func", FX_BowcasterProjectileThink}, + {"cloneblaster_func", FX_CloneBlasterProjectileThink}, + {"cloneblaster_alt_func", FX_CloneBlasterAltFireThink}, {"repeater_func", FX_RepeaterProjectileThink}, {"repeater_alt_func", FX_RepeaterAltProjectileThink}, {"demp2_func", FX_DEMP2_ProjectileThink}, @@ -106,6 +116,65 @@ func_t funcs[] = { {NULL, NULL} }; +qboolean playerUsableWeapons[WP_NUM_WEAPONS] = +{ + qtrue,//WP_NONE, + + // Player weapons + qtrue,//WP_SABER, + qtrue,//WP_BLASTER_PISTOL, // player and NPC weapon + qtrue,//WP_BLASTER, // player and NPC weapon + qtrue,//WP_DISRUPTOR, // player and NPC weapon + qtrue,//WP_BOWCASTER, // NPC weapon - player can pick this up, but never starts with them + qtrue,//WP_REPEATER, // NPC weapon - player can pick this up, but never starts with them + qtrue,//WP_DEMP2, // NPC weapon - player can pick this up, but never starts with them + qtrue,//WP_FLECHETTE, // NPC weapon - player can pick this up, but never starts with them + qtrue,//WP_ROCKET_LAUNCHER, // NPC weapon - player can pick this up, but never starts with them + qtrue,//WP_THERMAL, // player and NPC weapon + qtrue,//WP_TRIP_MINE, // NPC weapon - player can pick this up, but never starts with them + qtrue,//WP_DET_PACK, // NPC weapon - player can pick this up, but never starts with them + qtrue,//WP_CONCUSSION, // NPC weapon - player can pick this up, but never starts with them + + //extras + qtrue,//WP_MELEE, // player and NPC weapon - Any ol' melee attack + + //when in atst + qtrue,//WP_ATST_MAIN, + qtrue,//WP_ATST_SIDE, + + // These can never be gotten directly by the player + qtrue,//WP_STUN_BATON, // stupid weapon, should remove + + //NPC weapons + qtrue,//WP_BRYAR_PISTOL, // NPC weapon - player can pick this up, but never starts with them + + qfalse,//WP_EMPLACED_GUN, + + qfalse,//WP_BOT_LASER, // Probe droid - Laser blast + + qfalse,//WP_TURRET, // turret guns + + qfalse,//WP_TIE_FIGHTER, + + qfalse,//WP_RAPID_FIRE_CONC, + + qfalse,//WP_JAWA, + qtrue,//WP_TUSKEN_RIFLE, + qfalse,//WP_TUSKEN_STAFF, + qfalse,//WP_SCEPTER, + qtrue,//WP_NOGHRI_STICK, + + qtrue,//WP_SONIC_BLASTER, + + qtrue,//WP_E5_CARBINE, + qtrue,//WP_DC15S_CARBINE, + qtrue,//WP_DC15A_RIFLE, + qtrue,//WP_Z6_ROTARY, + + //# #eol + //WP_NUM_WEAPONS +}; + //qboolean COM_ParseInt( char **data, int *i ); //qboolean COM_ParseString( char **data, char **s ); //qboolean COM_ParseFloat( char **data, float *f ); @@ -161,6 +230,15 @@ void WPN_SplashDamage(const char **holdBuf); void WPN_SplashRadius(const char **holdBuf); void WPN_AltSplashDamage(const char **holdBuf); void WPN_AltSplashRadius(const char **holdBuf); +void WPN_Velocity(const char **holdBuf); +void WPN_AltVelocity(const char **holdBuf); +void WPN_NPCDmgMult(const char **holdBuf); +void WPN_NPCDmgAltMult(const char **holdBuf); + + +void WPN_WorldModel(const char **holdBuf); +void WPN_NoHandModel(const char **holdBuf); +void WPN_SkinFile(const char **holdBuf); // Legacy weapons.dat force fields void WPN_FuncSkip(const char **holdBuf); @@ -188,7 +266,7 @@ const int defaultDamage[] = { FLECHETTE_MINE_DAMAGE, // WP_DET_PACK // HACK, this is what the code sez. CONC_DAMAGE, // WP_CONCUSSION - 0, // WP_MELEE // handled by the melee attack function + RIGHT_PUNCH_DAMAGE, // WP_MELEE //right punch damage ATST_MAIN_DAMAGE, // WP_ATST_MAIN ATST_SIDE_MAIN_DAMAGE, // WP_ATST_SIDE @@ -207,6 +285,12 @@ const int defaultDamage[] = { 0, // WP_TUSKEN_STAFF 0, // WP_SCEPTER 0, // WP_NOGHRI_STICK + DISRUPTOR_MAIN_DAMAGE, // WP_SONIC_BLASTER, + + BLASTER_DAMAGE, // WP_E5_CARBINE, + BLASTER_DAMAGE, // WP_DC15S_CARBINE, + BRYAR_PISTOL_DAMAGE, // WP_DC15A_RIFLE, + REPEATER_DAMAGE, // WP_Z6_ROTARY, }; const int defaultAltDamage[] = { @@ -225,7 +309,7 @@ const int defaultAltDamage[] = { FLECHETTE_MINE_DAMAGE, // WP_DET_PACK // HACK, this is what the code sez. CONC_ALT_DAMAGE, // WP_CONCUSION - 0, // WP_MELEE // handled by the melee attack function + LEFT_PUNCH_DAMAGE, // WP_MELEE // handled by the melee attack function ATST_MAIN_DAMAGE, // WP_ATST_MAIN ATST_SIDE_ALT_DAMAGE, // WP_ATST_SIDE @@ -244,6 +328,12 @@ const int defaultAltDamage[] = { 0, // WP_TUSKEN_STAFF 0, // WP_SCEPTER 0, // WP_NOGHRI_STICK + 0, // WP_SONIC_BLASTER, + + BLASTER_DAMAGE, // WP_E5_CARBINE, + BLASTER_DAMAGE, // WP_DC15S_CARBINE, + 0, // WP_DC15A_RIFLE, + 0, // WP_Z6_ROTARY, }; const int defaultSplashDamage[] = { @@ -262,7 +352,7 @@ const int defaultSplashDamage[] = { FLECHETTE_MINE_SPLASH_DAMAGE, // WP_DET_PACK // HACK, this is what the code sez. CONC_SPLASH_DAMAGE, // WP_CONCUSSION - 0, // WP_MELEE + KICK_DAMAGE, // WP_MELEE 0, // WP_ATST_MAIN ATST_SIDE_MAIN_SPLASH_DAMAGE, // WP_ATST_SIDE @@ -281,6 +371,12 @@ const int defaultSplashDamage[] = { 0, // WP_TUSKEN_STAFF 0, // WP_SCEPTER 0, // WP_NOGHRI_STICK + 0, // WP_SONIC_BLASTER, + + 0, // WP_E5_CARBINE, + 0, // WP_DC15S_CARBINE, + 0, // WP_DC15A_RIFLE, + 0, // WP_Z6_ROTARY, }; const float defaultSplashRadius[] = { @@ -299,7 +395,7 @@ const float defaultSplashRadius[] = { FLECHETTE_MINE_SPLASH_RADIUS, // WP_DET_PACK // HACK, this is what the code sez. CONC_SPLASH_RADIUS, // WP_CONCUSSION - 0.0f, // WP_MELEE + HEAVY_MELEE_MULT, // WP_MELEE 0.0f, // WP_ATST_MAIN ATST_SIDE_MAIN_SPLASH_RADIUS, // WP_ATST_SIDE @@ -318,6 +414,12 @@ const float defaultSplashRadius[] = { 0.0f, // WP_TUSKEN_STAFF 0.0f, // WP_SCEPTER 0.0f, // WP_NOGHRI_STICK + 0.0f, // WP_SONIC_BLASTER, + + 0.0f, // WP_E5_CARBINE, + 0.0f, // WP_DC15S_CARBINE, + 0.0f, // WP_DC15A_RIFLE, + 0.0f, // WP_Z6_ROTARY, }; const int defaultAltSplashDamage[] = { @@ -336,7 +438,7 @@ const int defaultAltSplashDamage[] = { FLECHETTE_MINE_SPLASH_DAMAGE, // WP_DET_PACK // HACK, this is what the code sez. 0, // WP_CONCUSSION - 0, // WP_MELEE // handled by the melee attack function + SPECIAL_KICK_DAMAGE, // WP_MELEE 0, // WP_ATST_MAIN ATST_SIDE_ALT_SPLASH_DAMAGE, // WP_ATST_SIDE @@ -355,6 +457,12 @@ const int defaultAltSplashDamage[] = { 0, // WP_TUSKEN_STAFF 0, // WP_SCEPTER 0, // WP_NOGHRI_STICK + 0, // WP_SONIC_BLASTER, + + 0, // WP_E5_CARBINE, + 0, // WP_DC15S_CARBINE, + 0, // WP_DC15A_RIFLE, + 0, // WP_Z6_ROTARY, }; const float defaultAltSplashRadius[] = { @@ -373,7 +481,7 @@ const float defaultAltSplashRadius[] = { FLECHETTE_ALT_SPLASH_RAD, // WP_DET_PACK // HACK, this is what the code sez. 0.0f, // WP_CONCUSSION - 0.0f, // WP_MELEE // handled by the melee attack function + 0.0f, // WP_MELEE 0.0f, // WP_ATST_MAIN ATST_SIDE_ALT_SPLASH_RADIUS, // WP_ATST_SIDE @@ -392,9 +500,82 @@ const float defaultAltSplashRadius[] = { 0.0f, // WP_TUSKEN_STAFF 0.0f, // WP_SCEPTER 0.0f, // WP_NOGHRI_STICK + 0.0f, // WP_SONIC_BLASTER, + + 0.0f, // WP_E5_CARBINE, + 0.0f, // WP_DC15S_CARBINE, + 0.0f, // WP_DC15A_RIFLE, + 0.0f, // WP_Z6_ROTARY, +}; + +const int defaultVelocity[WP_NUM_WEAPONS] = +{ + 0,//WP_NONE, + 0,//WP_SABER, // NOTE: lots of code assumes this is the first weapon (... which is crap) so be careful -Ste. + BRYAR_PISTOL_VEL,//WP_BLASTER_PISTOL, + BLASTER_VELOCITY,//WP_BLASTER, + Q3_INFINITE,//WP_DISRUPTOR, + BOWCASTER_VELOCITY,//WP_BOWCASTER, + REPEATER_VELOCITY,//WP_REPEATER, + DEMP2_VELOCITY, //WP_DEMP2, + FLECHETTE_VEL,//WP_FLECHETTE, + ROCKET_VELOCITY,//WP_ROCKET_LAUNCHER, + TD_VELOCITY,//WP_THERMAL, + 0,//WP_TRIP_MINE, + 0,//WP_DET_PACK, + CONC_VELOCITY,//WP_CONCUSSION, + KICK_DAMAGE_RANDOMNESS,//WP_MELEE, // Any ol' melee attack + 0,//WP_STUN_BATON, + BRYAR_PISTOL_VEL,//WP_BRYAR_PISTOL, + EMPLACED_VEL,//WP_EMPLACED_GUN, + BRYAR_PISTOL_VEL,//WP_BOT_LASER, // Probe droid - Laser blast + 0,//WP_TURRET, // turret guns + ATST_MAIN_VEL,//WP_ATST_MAIN, + ATST_SIDE_MAIN_VELOCITY,//WP_ATST_SIDE, + EMPLACED_VEL,//WP_TIE_FIGHTER, + EMPLACED_VEL,//WP_RAPID_FIRE_CONC, + 0,//WP_JAWA, + TUSKEN_RIFLE_VEL,//WP_TUSKEN_RIFLE, + 0,//WP_TUSKEN_STAFF, + 0,//WP_SCEPTER, + 0,//WP_NOGHRI_STICK, +}; + +const int defaultAltVelocity[WP_NUM_WEAPONS] = +{ + 0,//WP_NONE, + 0,//WP_SABER, // NOTE: lots of code assumes this is the first weapon (... which is crap) so be careful -Ste. + BRYAR_PISTOL_VEL,//WP_BLASTER_PISTOL, + BLASTER_VELOCITY,//WP_BLASTER, + Q3_INFINITE,//WP_DISRUPTOR, + BOWCASTER_VELOCITY,//WP_BOWCASTER, + REPEATER_ALT_VELOCITY,//WP_REPEATER, + DEMP2_ALT_RANGE,//WP_DEMP2, + FLECHETTE_MINE_VEL,//WP_FLECHETTE, + ROCKET_ALT_VELOCITY,//WP_ROCKET_LAUNCHER, + TD_ALT_VELOCITY,//WP_THERMAL, + 0,//WP_TRIP_MINE, + 0,//WP_DET_PACK, + Q3_INFINITE,//WP_CONCUSSION, + HEAVY_MELEE_RANDOMNESS,//WP_MELEE, // Any ol' melee attack + 0,//WP_STUN_BATON, + BRYAR_PISTOL_VEL,//WP_BRYAR_PISTOL, + EMPLACED_VEL,//WP_EMPLACED_GUN, + BRYAR_PISTOL_VEL,//WP_BOT_LASER, // Probe droid - Laser blast + 0,//WP_TURRET, // turret guns + ATST_MAIN_VEL,//WP_ATST_MAIN, + ATST_SIDE_ALT_NPC_VELOCITY,//WP_ATST_SIDE, + EMPLACED_VEL,//WP_TIE_FIGHTER, + REPEATER_ALT_VELOCITY,//WP_RAPID_FIRE_CONC, + 0,//WP_JAWA, + TUSKEN_RIFLE_VEL,//WP_TUSKEN_RIFLE, + 0,//WP_TUSKEN_STAFF, + 0,//WP_SCEPTER, + 0,//WP_NOGHRI_STICK, }; -wpnParms_t WpnParms[] = + +wpnParms_t WpnParms[] = { { "ammo", WPN_Ammo }, //ammo { "ammoicon", WPN_AmmoIcon }, @@ -441,6 +622,11 @@ wpnParms_t WpnParms[] = { "splashRadius", WPN_SplashRadius }, { "altSplashDamage", WPN_AltSplashDamage }, { "altSplashRadius", WPN_AltSplashRadius }, + { "velocity", WPN_Velocity }, + { "altVelocity", WPN_AltVelocity }, + { "noHandModel", WPN_NoHandModel }, + { "skinFile", WPN_SkinFile }, + { "worldModel", WPN_WorldModel }, // Old legacy files contain these, so we skip them to shut up warnings { "firingforce", WPN_FuncSkip }, @@ -525,6 +711,16 @@ void WPN_WeaponType( const char **holdBuf) weaponNum = WP_SCEPTER; else if (!Q_stricmp(tokenStr,"WP_NOGHRI_STICK")) weaponNum = WP_NOGHRI_STICK; + else if (!Q_stricmp(tokenStr,"WP_SONIC_BLASTER")) + weaponNum = WP_SONIC_BLASTER; + else if (!Q_stricmp(tokenStr,"WP_E5_CARBINE")) + weaponNum = WP_E5_CARBINE; + else if (!Q_stricmp(tokenStr,"WP_DC15S_CARBINE")) + weaponNum = WP_DC15S_CARBINE; + else if (!Q_stricmp(tokenStr,"WP_DC15A_RIFLE")) + weaponNum = WP_DC15A_RIFLE; + else if (!Q_stricmp(tokenStr,"WP_Z6_ROTARY")) + weaponNum = WP_Z6_ROTARY; else { weaponNum = 0; @@ -579,6 +775,28 @@ void WPN_WeaponModel(const char **holdBuf) Q_strncpyz(weaponData[wpnParms.weaponNum].weaponMdl,tokenStr,len); } +//-------------------------------------------- +void WPN_WorldModel(const char **holdBuf) +{ + int len; + const char *tokenStr; + + if ( COM_ParseString(holdBuf,&tokenStr)) + { + return; + } + + len = strlen(tokenStr); + len++; + if (len > 64) + { + len = 64; + gi.Printf(S_COLOR_YELLOW"WARNING: worldModel too long in external WEAPONS.DAT '%s'\n", tokenStr); + } + + Q_strncpyz(weaponData[wpnParms.weaponNum].worldModel,tokenStr,len); +} + //-------------------------------------------- void WPN_WeaponIcon(const char **holdBuf) { @@ -1434,6 +1652,70 @@ void WPN_AltSplashRadius(const char **holdBuf) weaponData[wpnParms.weaponNum].altSplashRadius = tokenFlt; } +//-------------------------------------------- + +void WPN_Velocity(const char **holdBuf) +{ + float tokenFlt; + + if( COM_ParseInt(holdBuf,&tokenInt)) + { + SkipRestOfLine(holdBuf); + return; + } + + weaponData[wpnParms.weaponNum].velocity = tokenFlt; +} + +void WPN_NoHandModel(const char **holdBuf) +{ + int tokenInt; + + if( COM_ParseInt(holdBuf,&tokenInt)) + { + SkipRestOfLine(holdBuf); + return; + } + + weaponData[wpnParms.weaponNum].bNoHandModel = tokenInt ? true : false; +} + +//-------------------------------------------- + +void WPN_AltVelocity(const char **holdBuf) +{ + float tokenFlt; + + if (COM_ParseFloat(holdBuf, &tokenFlt)) + { + SkipRestOfLine(holdBuf); + return; + } + + weaponData[wpnParms.weaponNum].altVelocity = tokenFlt; +} + +void WPN_SkinFile(const char **holdBuf) +{ + const char *tokenStr; + + if ( COM_ParseString(holdBuf,&tokenStr)) + { + return; + } + size_t len = strlen( tokenStr ); + + len++; + if (len > MAX_QPATH) + { + len = MAX_QPATH; + gi.Printf(S_COLOR_YELLOW"WARNING: SkinFile '%s' too long in external WEAPONS.DAT\n", tokenStr); + } + + Q_strncpyz(weaponData[wpnParms.weaponNum].skinPath,tokenStr,len); +} + + //-------------------------------------------- static void WP_ParseParms(const char *buffer) { @@ -1483,9 +1765,15 @@ void WP_LoadWeaponParms (void) weaponData[i].altSplashDamage = defaultAltSplashDamage[i]; weaponData[i].splashRadius = defaultSplashRadius[i]; weaponData[i].altSplashRadius = defaultAltSplashRadius[i]; + weaponData[i].velocity = defaultVelocity[i]; + weaponData[i].altVelocity = defaultAltVelocity[i]; + weaponData[i].npcDmgMult = 1.0; + weaponData[i].npcAltDmgMult = 1.0; + weaponData[i].npcFireTimeMult = 1.0; //npc combat function checks for this + weaponData[i].npcAltFireTimeMult = 1.0; //npc combat function checks for this } - WP_ParseParms(buffer); + WP_ParseParms(buffer); gi.FS_FreeFile( buffer ); //let go of the buffer } diff --git a/code/game/genericparser2.cpp b/code/game/genericparser2.cpp index 4ed4b39ea8..4988c7dd2e 100644 --- a/code/game/genericparser2.cpp +++ b/code/game/genericparser2.cpp @@ -23,1110 +23,285 @@ along with this program; if not, see . // Filename:- genericparser2.cpp #include "common_headers.h" +#include "genericparser2.h" #ifdef _JK2EXE #include "../qcommon/qcommon.h" #endif -#define MAX_TOKEN_SIZE 1024 -static char token[MAX_TOKEN_SIZE]; +#include +#include -static char *GetToken(char **text, bool allowLineBreaks, bool readUntilEOL = false) -{ - char *pointer = *text; - int length = 0; - int c = 0; - bool foundLineBreak; - token[0] = 0; - if (!pointer) +static void skipWhitespace( gsl::cstring_view& text, const bool allowLineBreaks ) +{ + gsl::cstring_view::iterator whitespaceEnd = text.begin(); + while( whitespaceEnd != text.end() // No EOF + && std::isspace( *whitespaceEnd ) // No End of Whitespace + && ( allowLineBreaks || *whitespaceEnd != '\n' ) ) // No unwanted newline { - return token; + ++whitespaceEnd; } + text = { whitespaceEnd, text.end() }; +} - while(1) +static void skipWhitespaceAndComments( gsl::cstring_view& text, const bool allowLineBreaks ) +{ + skipWhitespace( text, allowLineBreaks ); + // skip single line comment + if( text.size() >= 2 && text[ 0 ] == '/' && text[ 1 ] == '/' ) { - foundLineBreak = false; - while(1) - { - c = *pointer; - if (c > ' ') - { - break; - } - if (!c) - { - *text = 0; - return token; - } - if (c == '\n') - { - foundLineBreak = true; - } - pointer++; - } - if (foundLineBreak && !allowLineBreaks) + auto commentEnd = std::find( text.begin() + 2, text.end(), '\n' ); + if( commentEnd == text.end() ) { - *text = pointer; - return token; - } - - c = *pointer; - - // skip single line comment - if (c == '/' && pointer[1] == '/') - { - pointer += 2; - while (*pointer && *pointer != '\n') - { - pointer++; - } - } - // skip multi line comments - else if (c == '/' && pointer[1] == '*') - { - pointer += 2; - while (*pointer && (*pointer != '*' || pointer[1] != '/')) - { - pointer++; - } - if (*pointer) - { - pointer += 2; - } + text = { text.end(), text.end() }; + return; } else - { // found the start of a token - break; - } - } - - if (c == '\"') - { // handle a string - pointer++; - while (1) { - c = *pointer++; - if (c == '\"') - { -// token[length++] = c; - break; - } - else if (!c) - { - break; - } - else if (length < MAX_TOKEN_SIZE) - { - token[length++] = c; - } + text = { commentEnd, text.end() }; + skipWhitespaceAndComments( text, allowLineBreaks ); + return; } } - else if (readUntilEOL) - { - // absorb all characters until EOL - while(c != '\n' && c != '\r') - { - if (c == '/' && ((*(pointer+1)) == '/' || (*(pointer+1)) == '*')) - { - break; - } - if (length < MAX_TOKEN_SIZE) - { - token[length++] = c; - } - pointer++; - c = *pointer; - } - // remove trailing white space - while(length && token[length-1] < ' ') - { - length--; - } - } - else + // skip multi line comments + if( text.size() >= 2 && text[ 0 ] == '/' && text[ 1 ] == '*' ) { - while(c > ' ') + static const std::array< char, 2 > endStr{ '*', '/' }; + auto commentEnd = std::search( text.begin(), text.end(), endStr.begin(), endStr.end() ); + if( commentEnd == text.end() ) { - if (length < MAX_TOKEN_SIZE) - { - token[length++] = c; - } - pointer++; - c = *pointer; - } - } - - if (token[0] == '\"') - { // remove start quote - length--; - memmove(token, token+1, length); - - if (length && token[length-1] == '\"') - { // remove end quote - length--; + text = { text.end(), text.end() }; + return; } - } - - if (length >= MAX_TOKEN_SIZE) - { - length = 0; - } - token[length] = 0; - *text = (char *)pointer; - - return token; -} - - - - -CTextPool::CTextPool(int initSize) : - mNext(0), - mSize(initSize), - mUsed(0) -{ -#ifdef _EXE -// mPool = (char *)Z_Malloc(mSize, TAG_GP2); - mPool = (char *)Z_Malloc(mSize, TAG_TEXTPOOL, qtrue); -#else - mPool = (char *)trap_Z_Malloc(mSize, TAG_GP2); -#endif -} - -CTextPool::~CTextPool(void) -{ -#ifdef _EXE - Z_Free(mPool); -#else - trap_Z_Free(mPool); -#endif -} - -char *CTextPool::AllocText(const char *text, bool addNULL, CTextPool **poolPtr) -{ - int length = strlen(text) + (addNULL ? 1 : 0); - - if (mUsed + length + 1> mSize) - { // extra 1 to put a null on the end - if (poolPtr) + else { - (*poolPtr)->SetNext(new CTextPool(mSize)); - *poolPtr = (*poolPtr)->GetNext(); - - return (*poolPtr)->AllocText(text, addNULL); + text = { commentEnd + endStr.size(), text.end() }; + skipWhitespace( text, allowLineBreaks ); + return; } - - return 0; } - strcpy(mPool + mUsed, text); - mUsed += length; - mPool[mUsed] = 0; - - return mPool + mUsed - length; + // found start of token + return; } -void CleanTextPool(CTextPool *pool) +static gsl::cstring_view removeTrailingWhitespace( const gsl::cstring_view& text ) { - CTextPool *next; - - while(pool) - { - next = pool->GetNext(); - delete pool; - pool = next; - } -} - - - - - - - -CGPObject::CGPObject(const char *initName) : - mName(initName), - mNext(0), - mInOrderNext(0), - mInOrderPrevious(0) -{ -} - -bool CGPObject::WriteText(CTextPool **textPool, const char *text) -{ - if (strchr(text, ' ') || !text[0]) - { - (*textPool)->AllocText("\"", false, textPool); - (*textPool)->AllocText((char *)text, false, textPool); - (*textPool)->AllocText("\"", false, textPool); - } - else - { - (*textPool)->AllocText((char *)text, false, textPool); - } - - return true; + return{ + text.begin(), + std::find_if_not( + std::reverse_iterator< const char *>( text.end() ), std::reverse_iterator< const char* >( text.begin() ), + static_cast< int( *)( int ) >( std::isspace ) + ).base() + }; } - - - - - - - - - - - - - -CGPValue::CGPValue(const char *initName, const char *initValue) : - CGPObject(initName), - mList(0) -{ - if (initValue) - { - AddValue(initValue); - } -} - -CGPValue::~CGPValue(void) -{ - CGPObject *next; - - while(mList) - { - next = mList->GetNext(); - delete mList; - mList = next; - } -} - -CGPValue *CGPValue::Duplicate(CTextPool **textPool) +/** +Skips whitespace (including lineBreaks, if desired) & comments, then reads one token. +A token can be: +- a string ("" delimited; ignores readToEOL) +- whitespace-delimited (if readToEOL == false) +- EOL- or comment-delimited (if readToEOL == true); i.e. reads to end of line or the first // or /* +@param text adjusted to start beyond the read token +*/ +static gsl::cstring_view GetToken( gsl::cstring_view& text, bool allowLineBreaks, bool readToEOL = false ) { - CGPValue *newValue; - CGPObject *iterator; - char *name; - - if (textPool) + skipWhitespaceAndComments( text, allowLineBreaks ); + // EOF + if( text.empty() ) { - name = (*textPool)->AllocText((char *)mName, true, textPool); + return{}; } - else - { - name = (char *)mName; - } - - newValue = new CGPValue(name); - iterator = mList; - while(iterator) + // string. ignores readToEOL. + if( text[ 0 ] == '"' ) { - if (textPool) + // there are no escapes, string just ends at the next " + auto tokenEnd = std::find( text.begin() + 1, text.end(), '"' ); + if( tokenEnd == text.end() ) { - name = (*textPool)->AllocText((char *)iterator->GetName(), true, textPool); + gsl::cstring_view token = { text.begin() + 1, text.end() }; + text = { text.end(), text.end() }; + return token; } else { - name = (char *)iterator->GetName(); + gsl::cstring_view token = { text.begin() + 1, tokenEnd }; + text = { tokenEnd + 1, text.end() }; + return token; } - newValue->AddValue(name); - iterator = iterator->GetNext(); - } - - return newValue; -} - -bool CGPValue::IsList(void) -{ - if (!mList || !mList->GetNext()) - { - return false; } - - return true; -} - -const char *CGPValue::GetTopValue(void) -{ - if (mList) + else if( readToEOL ) { - return mList->GetName(); - } - - return 0; -} - -void CGPValue::AddValue(const char *newValue, CTextPool **textPool) -{ - if (textPool) - { - newValue = (*textPool)->AllocText((char *)newValue, true, textPool); - } - - if (mList == 0) - { - mList = new CGPObject(newValue); - mList->SetInOrderNext(mList); + // find the first of '\n', "//" or "/*"; that's end of token + auto tokenEnd = std::find( text.begin(), text.end(), '\n' ); + static const std::array< char, 2 > commentPatterns[]{ + { { '/', '*' } }, + { { '/', '/' } } + }; + for( auto& pattern : commentPatterns ) + { + tokenEnd = std::min( + tokenEnd, + std::search( + text.begin(), tokenEnd, + pattern.begin(), pattern.end() + ) + ); + } + gsl::cstring_view token{ text.begin(), tokenEnd }; + text = { tokenEnd, text.end() }; + return removeTrailingWhitespace( token ); } else { - mList->GetInOrderNext()->SetNext(new CGPObject(newValue)); - mList->SetInOrderNext(mList->GetInOrderNext()->GetNext()); + // consume until first whitespace (if allowLineBreaks == false, that may be text.begin(); in that case token is empty.) + auto tokenEnd = std::find_if( text.begin(), text.end(), static_cast< int( *)( int ) >( std::isspace ) ); + gsl::cstring_view token{ text.begin(), tokenEnd }; + text = { tokenEnd, text.end() }; + return token; } } -bool CGPValue::Parse(char **dataPtr, CTextPool **textPool) -{ - char *token; - char *value; - while(1) - { - token = GetToken(dataPtr, true, true); - if (!token[0]) - { // end of data - error! - return false; - } - else if (Q_stricmp(token, "]") == 0) - { // ending brace for this list - break; - } - value = (*textPool)->AllocText(token, true, textPool); - AddValue(value); - } - - return true; -} -bool CGPValue::Write(CTextPool **textPool, int depth) +CGPProperty::CGPProperty( gsl::cstring_view initKey, gsl::cstring_view initValue ) + : mKey( initKey ) { - int i; - CGPObject *next; - - if (!mList) - { - return true; - } - - for(i=0;iAllocText("\t", false, textPool); - } - - WriteText(textPool, mName); - - if (!mList->GetNext()) + if( !initValue.empty() ) { - (*textPool)->AllocText("\t\t", false, textPool); - mList->WriteText(textPool, mList->GetName()); - (*textPool)->AllocText("\r\n", false, textPool); - } - else - { - (*textPool)->AllocText("\r\n", false, textPool); - - for(i=0;iAllocText("\t", false, textPool); - } - (*textPool)->AllocText("[\r\n", false, textPool); - - next = mList; - while(next) - { - for(i=0;iAllocText("\t", false, textPool); - } - mList->WriteText(textPool, next->GetName()); - (*textPool)->AllocText("\r\n", false, textPool); - - next = next->GetNext(); - } - - for(i=0;iAllocText("\t", false, textPool); - } - (*textPool)->AllocText("]\r\n", false, textPool); + mValues.push_back( initValue ); } - - return true; -} - - - - - - - - - - - - - - - - -CGPGroup::CGPGroup(const char *initName, CGPGroup *initParent) : - CGPObject(initName), - mPairs(0), - mInOrderPairs(0), - mCurrentPair(0), - mSubGroups(0), - mInOrderSubGroups(0), - mCurrentSubGroup(0), - mParent(initParent), - mWriteable(false) -{ } -CGPGroup::~CGPGroup(void) +void CGPProperty::AddValue( gsl::cstring_view newValue ) { - Clean(); + mValues.push_back( newValue ); } -int CGPGroup::GetNumSubGroups(void) -{ - int count; - CGPGroup *group; - count = 0; - group = mSubGroups; - while(group) - { - count++; - group = (CGPGroup *)group->GetNext(); - } - return(count); -} -int CGPGroup::GetNumPairs(void) -{ - int count; - CGPValue *pair; - count = 0; - pair = mPairs; - while(pair) - { - count++; - pair = (CGPValue *)pair->GetNext(); - } - return(count); -} -void CGPGroup::Clean(void) -{ - while(mPairs) - { - mCurrentPair = (CGPValue *)mPairs->GetNext(); - delete mPairs; - mPairs = mCurrentPair; - } - while(mSubGroups) - { - mCurrentSubGroup = (CGPGroup *)mSubGroups->GetNext(); - delete mSubGroups; - mSubGroups = mCurrentSubGroup; - } - mPairs = mInOrderPairs = mCurrentPair = 0; - mSubGroups = mInOrderSubGroups = mCurrentSubGroup = 0; - mParent = 0; - mWriteable = false; -} -CGPGroup *CGPGroup::Duplicate(CTextPool **textPool, CGPGroup *initParent) -{ - CGPGroup *newGroup, *subSub, *newSub; - CGPValue *newPair, *subPair; - char *name; - if (textPool) - { - name = (*textPool)->AllocText((char *)mName, true, textPool); - } - else - { - name = (char *)mName; - } - - newGroup = new CGPGroup(name); - subSub = mSubGroups; - while(subSub) - { - newSub = subSub->Duplicate(textPool, newGroup); - newGroup->AddGroup(newSub); - subSub = (CGPGroup *)subSub->GetNext(); - } - subPair = mPairs; - while(subPair) - { - newPair = subPair->Duplicate(textPool); - newGroup->AddPair(newPair); - subPair = (CGPValue *)subPair->GetNext(); - } - return newGroup; +CGPGroup::CGPGroup( const gsl::cstring_view& initName ) + : mName( initName ) +{ } -void CGPGroup::SortObject(CGPObject *object, CGPObject **unsortedList, CGPObject **sortedList, - CGPObject **lastObject) +bool CGPGroup::Parse( gsl::cstring_view& data, const bool topLevel ) { - CGPObject *test, *last; - - if (!*unsortedList) + while( true ) { - *unsortedList = *sortedList = object; - } - else - { - (*lastObject)->SetNext(object); + gsl::cstring_view token = GetToken( data, true ); - test = *sortedList; - last = 0; - while(test) + if( token.empty() ) { - if (Q_stricmp(object->GetName(), test->GetName()) < 0) + if ( topLevel ) { - break; + // top level parse; there was no opening "{", so there should be no closing one either. + return true; + } + else + { + // end of data - error! + return false; } - - last = test; - test = test->GetInOrderNext(); - } - - if (test) - { - test->SetInOrderPrevious(object); - object->SetInOrderNext(test); - } - if (last) - { - last->SetInOrderNext(object); - object->SetInOrderPrevious(last); - } - else - { - *sortedList = object; - } - } - - *lastObject = object; -} - -CGPValue *CGPGroup::AddPair(const char *name, const char *value, CTextPool **textPool) -{ - CGPValue *newPair; - - if (textPool) - { - name = (*textPool)->AllocText((char *)name, true, textPool); - if (value) - { - value = (*textPool)->AllocText((char *)value, true, textPool); } - } - - newPair = new CGPValue(name, value); - - AddPair(newPair); - - return newPair; -} - -void CGPGroup::AddPair(CGPValue *NewPair) -{ - SortObject(NewPair, (CGPObject **)&mPairs, (CGPObject **)&mInOrderPairs, - (CGPObject **)&mCurrentPair); -} - -CGPGroup *CGPGroup::AddGroup(const char *name, CTextPool **textPool) -{ - CGPGroup *newGroup; - - if (textPool) - { - name = (*textPool)->AllocText((char *)name, true, textPool); - } - - newGroup = new CGPGroup(name); - - AddGroup(newGroup); - - return newGroup; -} - -void CGPGroup::AddGroup(CGPGroup *NewGroup) -{ - SortObject(NewGroup, (CGPObject **)&mSubGroups, (CGPObject **)&mInOrderSubGroups, - (CGPObject **)&mCurrentSubGroup); -} - -CGPGroup *CGPGroup::FindSubGroup(const char *name) -{ - CGPGroup *group; - - group = mSubGroups; - while(group) - { - if(!Q_stricmp(name, group->GetName())) + else if( token == CSTRING_VIEW( "}" ) ) { - return(group); - } - group = (CGPGroup *)group->GetNext(); - } - return(NULL); -} - -bool CGPGroup::Parse(char **dataPtr, CTextPool **textPool) -{ - char *token; - char lastToken[MAX_TOKEN_SIZE]; - CGPGroup *newSubGroup; - CGPValue *newPair; - - while(1) - { - token = GetToken(dataPtr, true); - - if (!token[0]) - { // end of data - error! - if (mParent) + if( topLevel ) { + // top-level group; there was no opening "{" so there should be no closing one, either. return false; } else { - break; + // ending brace for this group + return true; } } - else if (Q_stricmp(token, "}") == 0) - { // ending brace for this group - break; - } - - strcpy(lastToken, token); + gsl::cstring_view lastToken = token; // read ahead to see what we are doing - token = GetToken(dataPtr, true, true); - if (Q_stricmp(token, "{") == 0) - { // new sub group - newSubGroup = AddGroup(lastToken, textPool); - newSubGroup->SetWriteable(mWriteable); - if (!newSubGroup->Parse(dataPtr, textPool)) + token = GetToken( data, true, true ); + if( token == CSTRING_VIEW( "{" ) ) + { + // new sub group + mSubGroups.emplace_back( lastToken ); + if( !mSubGroups.back().Parse( data, false ) ) { return false; } } - else if (Q_stricmp(token, "[") == 0) - { // new pair list - newPair = AddPair(lastToken, 0, textPool); - if (!newPair->Parse(dataPtr, textPool)) + else if( token == CSTRING_VIEW( "[" ) ) + { + // new list + mProperties.emplace_back( lastToken ); + CGPProperty& list = mProperties.back(); + while( true ) { - return false; + token = GetToken( data, true, true ); + if( token.empty() ) + { + return false; + } + if( token == CSTRING_VIEW( "]" ) ) + { + break; + } + list.AddValue( token ); } } else - { // new pair - AddPair(lastToken, token, textPool); - } - } - - return true; -} - -bool CGPGroup::Write(CTextPool **textPool, int depth) -{ - int i; - CGPValue *mPair = mPairs; - CGPGroup *mSubGroup = mSubGroups; - - if (depth >= 0) - { - for(i=0;iAllocText("\t", false, textPool); - } - WriteText(textPool, mName); - (*textPool)->AllocText("\r\n", false, textPool); - - for(i=0;iAllocText("\t", false, textPool); - } - (*textPool)->AllocText("{\r\n", false, textPool); - } - - while(mPair) - { - mPair->Write(textPool, depth+1); - mPair = (CGPValue *)mPair->GetNext(); - } - - while(mSubGroup) - { - mSubGroup->Write(textPool, depth+1); - mSubGroup = (CGPGroup *)mSubGroup->GetNext(); - } - - if (depth >= 0) - { - for(i=0;iAllocText("\t", false, textPool); - } - (*textPool)->AllocText("}\r\n", false, textPool); - } - - return true; -} - -CGPValue *CGPGroup::FindPair(const char *key) -{ - CGPValue *pair = mPairs; - - while(pair) - { - if (Q_stricmp(pair->GetName(), key) == 0) { - return pair; + // new value + mProperties.emplace_back( lastToken, token ); } - - pair = pair->GetNext(); - } - - return 0; -} - -const char *CGPGroup::FindPairValue(const char *key, const char *defaultVal) -{ - CGPValue *pair = FindPair(key); - - if (pair) - { - return pair->GetTopValue(); - } - - return defaultVal; -} - - - - - - - - - - - - - - - -CGenericParser2::CGenericParser2(void) : - mTextPool(0), - mWriteable(false) -{ -} - -CGenericParser2::~CGenericParser2(void) -{ - Clean(); -} - -bool CGenericParser2::Parse(char **dataPtr, bool cleanFirst, bool writeable) -{ - CTextPool *topPool; - - if (cleanFirst) - { - Clean(); - } - - if (!mTextPool) - { - mTextPool = new CTextPool; - } - - SetWriteable(writeable); - mTopLevel.SetWriteable(writeable); - topPool = mTextPool; - bool ret = mTopLevel.Parse(dataPtr, &topPool); - return ret; -} - -void CGenericParser2::Clean(void) -{ - mTopLevel.Clean(); - - CleanTextPool(mTextPool); - mTextPool = 0; -} - -bool CGenericParser2::Write(CTextPool *textPool) -{ - return mTopLevel.Write(&textPool, -1); -} - - - - - - - - - -// The following groups of routines are used for a C interface into GP2. -// C++ users should just use the objects as normally and not call these routines below -// -// CGenericParser2 (void *) routines -TGenericParser2 GP_Parse(char **dataPtr, bool cleanFirst, bool writeable) -{ - CGenericParser2 *parse; - - parse = new CGenericParser2; - if (parse->Parse(dataPtr, cleanFirst, writeable)) - { - return parse; - } - - delete parse; - return 0; -} - -void GP_Clean(TGenericParser2 GP2) -{ - if (!GP2) - { - return; - } - - ((CGenericParser2 *)GP2)->Clean(); -} - -void GP_Delete(TGenericParser2 *GP2) -{ - if (!GP2 || !(*GP2)) - { - return; - } - - delete ((CGenericParser2 *)(*GP2)); - (*GP2) = 0; -} - -TGPGroup GP_GetBaseParseGroup(TGenericParser2 GP2) -{ - if (!GP2) - { - return 0; - } - - return ((CGenericParser2 *)GP2)->GetBaseParseGroup(); -} - - - - -// CGPGroup (void *) routines -const char *GPG_GetName(TGPGroup GPG) -{ - if (!GPG) - { - return ""; - } - - return ((CGPGroup *)GPG)->GetName(); -} - -TGPGroup GPG_GetNext(TGPGroup GPG) -{ - if (!GPG) - { - return 0; - } - - return ((CGPGroup *)GPG)->GetNext(); -} - -TGPGroup GPG_GetInOrderNext(TGPGroup GPG) -{ - if (!GPG) - { - return 0; - } - - return ((CGPGroup *)GPG)->GetInOrderNext(); -} - -TGPGroup GPG_GetInOrderPrevious(TGPGroup GPG) -{ - if (!GPG) - { - return 0; - } - - return ((CGPGroup *)GPG)->GetInOrderPrevious(); -} - -TGPGroup GPG_GetPairs(TGPGroup GPG) -{ - if (!GPG) - { - return 0; - } - - return ((CGPGroup *)GPG)->GetPairs(); -} - -TGPGroup GPG_GetInOrderPairs(TGPGroup GPG) -{ - if (!GPG) - { - return 0; } - - return ((CGPGroup *)GPG)->GetInOrderPairs(); } -TGPGroup GPG_GetSubGroups(TGPGroup GPG) -{ - if (!GPG) - { - return 0; - } - return ((CGPGroup *)GPG)->GetSubGroups(); -} -TGPGroup GPG_GetInOrderSubGroups(TGPGroup GPG) -{ - if (!GPG) - { - return 0; - } - return ((CGPGroup *)GPG)->GetInOrderSubGroups(); -} -TGPGroup GPG_FindSubGroup(TGPGroup GPG, const char *name) -{ - if (!GPG) - { - return 0; - } - return ((CGPGroup *)GPG)->FindSubGroup(name); -} -TGPValue GPG_FindPair(TGPGroup GPG, const char *key) -{ - if (!GPG) - { - return 0; - } - return ((CGPGroup *)GPG)->FindPair(key); -} -const char *GPG_FindPairValue(TGPGroup GPG, const char *key, const char *defaultVal) -{ - if (!GPG) - { - return defaultVal; - } - return ((CGPGroup *)GPG)->FindPairValue(key, defaultVal); -} -// CGPValue (void *) routines -const char *GPV_GetName(TGPValue GPV) +bool CGenericParser2::Parse( gsl::czstring filename ) { - if (!GPV) + Clear(); + mFileContent = FS::ReadFile( filename ); + if( !mFileContent.valid() ) { - return ""; - } - - return ((CGPValue *)GPV)->GetName(); -} - -TGPValue GPV_GetNext(TGPValue GPV) -{ - if (!GPV) - { - return 0; - } - - return ((CGPValue *)GPV)->GetNext(); -} - -TGPValue GPV_GetInOrderNext(TGPValue GPV) -{ - if (!GPV) - { - return 0; - } - - return ((CGPValue *)GPV)->GetInOrderNext(); -} - -TGPValue GPV_GetInOrderPrevious(TGPValue GPV) -{ - if (!GPV) - { - return 0; - } - - return ((CGPValue *)GPV)->GetInOrderPrevious(); -} - -bool GPV_IsList(TGPValue GPV) -{ - if (!GPV) - { - return 0; - } - - return ((CGPValue *)GPV)->IsList(); -} - -const char *GPV_GetTopValue(TGPValue GPV) -{ - if (!GPV) - { - return ""; + return false; } - - return ((CGPValue *)GPV)->GetTopValue(); + auto view = mFileContent.view(); + return mTopLevel.Parse( view ); } -TGPValue GPV_GetList(TGPValue GPV) +void CGenericParser2::Clear() NOEXCEPT { - if (!GPV) - { - return 0; - } - - return ((CGPValue *)GPV)->GetList(); + mTopLevel.Clear(); } diff --git a/code/game/genericparser2.h b/code/game/genericparser2.h index a8b70ede04..9b2425f3c9 100644 --- a/code/game/genericparser2.h +++ b/code/game/genericparser2.h @@ -22,206 +22,164 @@ along with this program; if not, see . // Filename:- genericparser2.h -#ifndef GENERICPARSER2_H -#define GENERICPARSER2_H +#include +#include +#include +#include -// conditional expression is constant -// conversion from int to char, possible loss of data -// unreferenced inline funciton has been removed -#ifdef _MSC_VER -#ifdef DEBUG_LINKING - #pragma message("...including GenericParser2.h") -#endif -#endif +#include "qcommon/safe/gsl.h" +#include "qcommon/safe/memory.h" +#include "qcommon/safe/files.h" +#include "qcommon/safe/string.h" -#ifdef _JK2EXE -#define trap_Z_Malloc(x, y) Z_Malloc(x,y,qtrue) -#define trap_Z_Free(x) Z_Free(x) -#else -#define trap_Z_Malloc(x, y) gi.Malloc(x,y,qtrue) -#define trap_Z_Free(x) gi.Free(x) -#endif - - -class CTextPool; -class CGPObject; +#ifndef GENERICPARSER2_H +#define GENERICPARSER2_H -class CTextPool +namespace GP2 { -private: - char *mPool; - CTextPool *mNext; - int mSize, mUsed; + template< typename T > + using Vector = std::vector< T, Zone::Allocator< T, TAG_GP2 > >; +} +class CGPProperty +{ public: - CTextPool(int initSize = 10240); - ~CTextPool(void); - - CTextPool *GetNext(void) { return mNext; } - void SetNext(CTextPool *which) { mNext = which; } - char *GetPool(void) { return mPool; } - int GetUsed(void) { return mUsed; } - - char *AllocText(const char *text, bool addNULL = true, CTextPool **poolPtr = 0); -}; + using Values = GP2::Vector< gsl::cstring_view >; +private: + gsl::cstring_view mKey; + Values mValues; -void CleanTextPool(CTextPool *pool); - -class CGPObject -{ -protected: - const char *mName; - CGPObject *mNext, *mInOrderNext, *mInOrderPrevious; public: - CGPObject(const char *initName); - virtual ~CGPObject() {} - - const char *GetName(void) { return mName; } - CGPObject *GetNext(void) { return mNext; } - void SetNext(CGPObject *which) { mNext = which; } - CGPObject *GetInOrderNext(void) { return mInOrderNext; } - void SetInOrderNext(CGPObject *which) { mInOrderNext = which; } - CGPObject *GetInOrderPrevious(void) { return mInOrderPrevious; } - void SetInOrderPrevious(CGPObject *which) { mInOrderPrevious = which; } + CGPProperty( gsl::cstring_view initKey, gsl::cstring_view initValue = {} ); - bool WriteText(CTextPool **textPool, const char *text); + const gsl::cstring_view& GetName() const { return mKey; } + bool IsList() const NOEXCEPT + { + return mValues.size() > 1; + } + const gsl::cstring_view& GetTopValue() const NOEXCEPT + { + static gsl::cstring_view empty{}; + return mValues.empty() ? empty : mValues.front(); + } + const Values& GetValues() const NOEXCEPT + { + return mValues; + } + void AddValue( gsl::cstring_view newValue ); }; -class CGPValue : public CGPObject +class CGPGroup { +public: + /// Key-Value-Pairs + using Properties = GP2::Vector< CGPProperty >; + using SubGroups = GP2::Vector< CGPGroup >; private: - CGPObject *mList; - + Properties mProperties; + gsl::cstring_view mName = CSTRING_VIEW( "Top Level" ); + SubGroups mSubGroups; public: - CGPValue(const char *initName, const char *initValue = 0); - ~CGPValue(void); - - CGPValue *GetNext(void) { return (CGPValue *)mNext; } - - CGPValue *Duplicate(CTextPool **textPool = 0); - - bool IsList(void); - const char *GetTopValue(void); - CGPObject *GetList(void) { return mList; } - void AddValue(const char *newValue, CTextPool **textPool = 0); - - bool Parse(char **dataPtr, CTextPool **textPool); + CGPGroup() = default; + CGPGroup( const gsl::cstring_view& initName ); + // non-copyable; but just for performance reasons, since it would incur a deep copy. + CGPGroup( const CGPGroup& ) = delete; + CGPGroup& operator=( const CGPGroup& ) = delete; + // movable +#if defined( _MSC_VER ) && _MSC_VER < 1900 + // alas no default move constructors on VS2013. + // TODO DELETEME once we drop VS2013 (because fuck that). + CGPGroup( CGPGroup&& rhs ) + : mProperties( std::move( rhs.mProperties ) ) + , mName( std::move( rhs.mName ) ) + , mSubGroups( std::move( rhs.mSubGroups ) ) + { + } + CGPGroup& operator=( CGPGroup&& rhs ) + { + mProperties = std::move( rhs.mProperties ); + mName = std::move( rhs.mName ); + mSubGroups = std::move( rhs.mSubGroups ); + return *this; + } +#else + CGPGroup( CGPGroup&& ) = default; + CGPGroup& operator=( CGPGroup&& ) = default; +#endif - bool Write(CTextPool **textPool, int depth); + const Properties& GetProperties() const NOEXCEPT + { + return mProperties; + } + const SubGroups& GetSubGroups() const NOEXCEPT + { + return mSubGroups; + } + const CGPGroup* FindSubGroup( const gsl::cstring_view& name ) const NOEXCEPT + { + for( auto& sub : GetSubGroups() ) + { + if( Q::stricmp( name, sub.GetName() ) == Q::Ordering::EQ ) + { + return ⊂ + } + } + return nullptr; + } + const CGPProperty* FindProperty( const gsl::cstring_view& name ) const NOEXCEPT + { + for( auto& prop : GetProperties() ) + { + if( Q::stricmp( name, prop.GetName() ) == Q::Ordering::EQ ) + { + return ∝ + } + } + return nullptr; + } + const gsl::cstring_view& GetName() const NOEXCEPT + { + return mName; + } + void Clear() NOEXCEPT + { + // name is retained + mProperties.clear(); + mSubGroups.clear(); + } + bool Parse( gsl::cstring_view& data, const bool topLevel = true ); }; +/** +Generic Text Parser. - -class CGPGroup : public CGPObject -{ -private: - CGPValue *mPairs, *mInOrderPairs; - CGPValue *mCurrentPair; - CGPGroup *mSubGroups, *mInOrderSubGroups; - CGPGroup *mCurrentSubGroup; - CGPGroup *mParent; - bool mWriteable; - - void SortObject(CGPObject *object, CGPObject **unsortedList, CGPObject **sortedList, - CGPObject **lastObject); - -public: - CGPGroup(const char *initName = "Top Level", CGPGroup *initParent = 0); - ~CGPGroup(void); - - CGPGroup *GetParent(void) { return mParent; } - CGPGroup *GetNext(void) { return (CGPGroup *)mNext; } - int GetNumSubGroups(void); - int GetNumPairs(void); - - void Clean(void); - CGPGroup *Duplicate(CTextPool **textPool = 0, CGPGroup *initParent = 0); - - void SetWriteable(const bool writeable) { mWriteable = writeable; } - CGPValue *GetPairs(void) { return mPairs; } - CGPValue *GetInOrderPairs(void) { return mInOrderPairs; } - CGPGroup *GetSubGroups(void) { return mSubGroups; } - CGPGroup *GetInOrderSubGroups(void) { return mInOrderSubGroups; } - - CGPValue *AddPair(const char *name, const char *value, CTextPool **textPool = 0); - void AddPair(CGPValue *NewPair); - CGPGroup *AddGroup(const char *name, CTextPool **textPool = 0); - void AddGroup(CGPGroup *NewGroup); - CGPGroup *FindSubGroup(const char *name); - bool Parse(char **dataPtr, CTextPool **textPool); - bool Write(CTextPool **textPool, int depth); - - CGPValue *FindPair(const char *key); - const char *FindPairValue(const char *key, const char *defaultVal = 0); -}; - +Used to parse effect files and the dynamic music system files. Parses blocks of the form `name { \n ... \n }`; blocks can contain other blocks or properties of the form `key value`. Value can also be a list; in that case the format is `key [\n value1 \n value2\n]`. Mind the separating newlines, values are actually newline-delimited. +*/ class CGenericParser2 { private: - CGPGroup mTopLevel; - CTextPool *mTextPool; - bool mWriteable; + CGPGroup mTopLevel; + FS::FileBuffer mFileContent; public: - CGenericParser2(void); - ~CGenericParser2(void); - - void SetWriteable(const bool writeable) { mWriteable = writeable; } - CGPGroup *GetBaseParseGroup(void) { return &mTopLevel; } - bool Parse(char **dataPtr, bool cleanFirst = true, bool writeable = false); - bool Parse(char *dataPtr, bool cleanFirst = true, bool writeable = false) + const CGPGroup& GetBaseParseGroup() { - return Parse(&dataPtr, cleanFirst, writeable); + return mTopLevel; } - void Clean(void); - bool Write(CTextPool *textPool); + bool Parse( gsl::czstring filename ); + void Clear() NOEXCEPT; + bool ValidFile() const NOEXCEPT + { + return mFileContent.valid(); + } }; - - -// The following groups of routines are used for a C interface into GP2. -// C++ users should just use the objects as normally and not call these routines below -// - -typedef void *TGenericParser2; -typedef void *TGPGroup; -typedef void *TGPValue; - -// CGenericParser2 (void *) routines -TGenericParser2 GP_Parse(char **dataPtr, bool cleanFirst, bool writeable); -void GP_Clean(TGenericParser2 GP2); -void GP_Delete(TGenericParser2 *GP2); -TGPGroup GP_GetBaseParseGroup(TGenericParser2 GP2); - -// CGPGroup (void *) routines -const char *GPG_GetName(TGPGroup GPG); -TGPGroup GPG_GetNext(TGPGroup GPG); -TGPGroup GPG_GetInOrderNext(TGPGroup GPG); -TGPGroup GPG_GetInOrderPrevious(TGPGroup GPG); -TGPGroup GPG_GetPairs(TGPGroup GPG); -TGPGroup GPG_GetInOrderPairs(TGPGroup GPG); -TGPGroup GPG_GetSubGroups(TGPGroup GPG); -TGPGroup GPG_GetInOrderSubGroups(TGPGroup GPG); -TGPGroup GPG_FindSubGroup(TGPGroup GPG, const char *name); -TGPValue GPG_FindPair(TGPGroup GPG, const char *key); -const char *GPG_FindPairValue(TGPGroup GPG, const char *key, const char *defaultVal); - -// CGPValue (void *) routines -const char *GPV_GetName(TGPValue GPV); -TGPValue GPV_GetNext(TGPValue GPV); -TGPValue GPV_GetInOrderNext(TGPValue GPV); -TGPValue GPV_GetInOrderPrevious(TGPValue GPV); -bool GPV_IsList(TGPValue GPV); -const char *GPV_GetTopValue(TGPValue GPV); -TGPValue GPV_GetList(TGPValue GPV); - - #endif // #ifndef GENERICPARSER2_H diff --git a/code/game/ghoul2_shared.h b/code/game/ghoul2_shared.h index 9fcb6bb4dc..8aa09adec3 100644 --- a/code/game/ghoul2_shared.h +++ b/code/game/ghoul2_shared.h @@ -34,6 +34,13 @@ along with this program; if not, see . void G2API_SetTime(int currentTime,int clock); int G2API_GetTime(int argTime); // this may or may not return arg depending on ghoul2_time cvar +typedef enum +{ + G2_TINT_DEFAULT, + G2_TINT_SABER, + G2_TINT_SABER2, + G2_TINT_MAX +} g2Tints_t; //=================================================================== // @@ -58,6 +65,28 @@ surfaceInfo_t(): genLod(0) {} + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(offFlags); + saved_game.write(surface); + saved_game.write(genBarycentricJ); + saved_game.write(genBarycentricI); + saved_game.write(genPolySurfaceIndex); + saved_game.write(genLod); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(offFlags); + saved_game.read(surface); + saved_game.read(genBarycentricJ); + saved_game.read(genBarycentricI); + saved_game.read(genPolySurfaceIndex); + saved_game.read(genLod); + } }; #define BONE_ANGLES_PREMULT 0x0001 @@ -190,6 +219,158 @@ boneInfo_t(): matrix.matrix[2][0] = matrix.matrix[2][1] = matrix.matrix[2][2] = matrix.matrix[2][3] = 0.0f; } + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(boneNumber); + saved_game.write<>(matrix); + saved_game.write(flags); + saved_game.write(startFrame); + saved_game.write(endFrame); + saved_game.write(startTime); + saved_game.write(pauseTime); + saved_game.write(animSpeed); + saved_game.write(blendFrame); + saved_game.write(blendLerpFrame); + saved_game.write(blendTime); + saved_game.write(blendStart); + saved_game.write(boneBlendTime); + saved_game.write(boneBlendStart); + saved_game.write(newMatrix); + +#ifndef JK2_MODE + saved_game.write(lastTimeUpdated); + saved_game.write(lastContents); + saved_game.write(lastPosition); + saved_game.write(velocityEffector); + saved_game.write(lastAngles); + saved_game.write(minAngles); + saved_game.write(maxAngles); + saved_game.write(currentAngles); + saved_game.write(anglesOffset); + saved_game.write(positionOffset); + saved_game.write(radius); + saved_game.write(weight); + saved_game.write(ragIndex); + saved_game.write(velocityRoot); + saved_game.write(ragStartTime); + saved_game.write(firstTime); + saved_game.write(firstCollisionTime); + saved_game.write(restTime); + saved_game.write(RagFlags); + saved_game.write(DependentRagIndexMask); + saved_game.write<>(originalTrueBoneMatrix); + saved_game.write<>(parentTrueBoneMatrix); + saved_game.write<>(parentOriginalTrueBoneMatrix); + saved_game.write(originalOrigin); + saved_game.write(originalAngles); + saved_game.write(lastShotDir); + saved_game.write(basepose); + saved_game.write(baseposeInv); + saved_game.write(baseposeParent); + saved_game.write(baseposeInvParent); + saved_game.write(parentRawBoneIndex); + saved_game.write<>(ragOverrideMatrix); + saved_game.write<>(extraMatrix); + saved_game.write(extraVec1); + saved_game.write(extraFloat1); + saved_game.write(extraInt1); + saved_game.write(ikPosition); + saved_game.write(ikSpeed); + saved_game.write(epVelocity); + saved_game.write(epGravFactor); + saved_game.write(solidCount); + saved_game.write(physicsSettled); + saved_game.write(snapped); + saved_game.skip(2); + saved_game.write(parentBoneIndex); + saved_game.write(offsetRotation); + saved_game.write(overGradSpeed); + saved_game.write(overGoalSpot); + saved_game.write(hasOverGoal); + saved_game.skip(3); + saved_game.write<>(animFrameMatrix); + saved_game.write(hasAnimFrameMatrix); + saved_game.write(airTime); +#endif // !JK2_MODE + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(boneNumber); + saved_game.read<>(matrix); + saved_game.read(flags); + saved_game.read(startFrame); + saved_game.read(endFrame); + saved_game.read(startTime); + saved_game.read(pauseTime); + saved_game.read(animSpeed); + saved_game.read(blendFrame); + saved_game.read(blendLerpFrame); + saved_game.read(blendTime); + saved_game.read(blendStart); + saved_game.read(boneBlendTime); + saved_game.read(boneBlendStart); + saved_game.read(newMatrix); + +#ifndef JK2_MODE + saved_game.read(lastTimeUpdated); + saved_game.read(lastContents); + saved_game.read(lastPosition); + saved_game.read(velocityEffector); + saved_game.read(lastAngles); + saved_game.read(minAngles); + saved_game.read(maxAngles); + saved_game.read(currentAngles); + saved_game.read(anglesOffset); + saved_game.read(positionOffset); + saved_game.read(radius); + saved_game.read(weight); + saved_game.read(ragIndex); + saved_game.read(velocityRoot); + saved_game.read(ragStartTime); + saved_game.read(firstTime); + saved_game.read(firstCollisionTime); + saved_game.read(restTime); + saved_game.read(RagFlags); + saved_game.read(DependentRagIndexMask); + saved_game.read<>(originalTrueBoneMatrix); + saved_game.read<>(parentTrueBoneMatrix); + saved_game.read<>(parentOriginalTrueBoneMatrix); + saved_game.read(originalOrigin); + saved_game.read(originalAngles); + saved_game.read(lastShotDir); + saved_game.read(basepose); + saved_game.read(baseposeInv); + saved_game.read(baseposeParent); + saved_game.read(baseposeInvParent); + saved_game.read(parentRawBoneIndex); + saved_game.read<>(ragOverrideMatrix); + saved_game.read<>(extraMatrix); + saved_game.read(extraVec1); + saved_game.read(extraFloat1); + saved_game.read(extraInt1); + saved_game.read(ikPosition); + saved_game.read(ikSpeed); + saved_game.read(epVelocity); + saved_game.read(epGravFactor); + saved_game.read(solidCount); + saved_game.read(physicsSettled); + saved_game.read(snapped); + saved_game.skip(2); + saved_game.read(parentBoneIndex); + saved_game.read(offsetRotation); + saved_game.read(overGradSpeed); + saved_game.read(overGoalSpot); + saved_game.read(hasOverGoal); + saved_game.skip(3); + saved_game.read<>(animFrameMatrix); + saved_game.read(hasAnimFrameMatrix); + saved_game.read(airTime); +#endif // JK2_MODE + } }; //we save from top to boltUsed here. Don't bother saving the position, it gets rebuilt every frame anyway struct boltInfo_t{ @@ -203,6 +384,25 @@ struct boltInfo_t{ surfaceType(0), boltUsed(0) {} + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(boneNumber); + saved_game.write(surfaceNumber); + saved_game.write(surfaceType); + saved_game.write(boltUsed); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(boneNumber); + saved_game.read(surfaceNumber); + saved_game.read(surfaceType); + saved_game.read(boltUsed); + } }; @@ -263,9 +463,11 @@ class CGhoul2Info int mSurfaceRoot; int mLodBias; int mNewOrigin; // this contains the bolt index of the new origin for this model + #ifdef _G2_GORE int mGoreSetTag; #endif + qhandle_t mModel; // this and the next entries do NOT go across the network. They are for gameside access ONLY char mFileName[MAX_QPATH]; int mAnimFrameDefault; @@ -286,6 +488,8 @@ class CGhoul2Info const model_s *animModel; int currentAnimModelSize; const mdxaHeader_t *aHeader; + + g2Tints_t tintType; CGhoul2Info(): mModelindex(-1), @@ -312,10 +516,70 @@ class CGhoul2Info currentModelSize(0), animModel(0), currentAnimModelSize(0), - aHeader(0) + aHeader(0), + tintType(G2_TINT_DEFAULT) { mFileName[0] = 0; } + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(mModelindex); + +#ifndef JK2_MODE + saved_game.write(animModelIndexOffset); +#endif // !JK2_MODE + + saved_game.write(mCustomShader); + saved_game.write(mCustomSkin); + saved_game.write(mModelBoltLink); + saved_game.write(mSurfaceRoot); + saved_game.write(mLodBias); + saved_game.write(mNewOrigin); + +#ifdef _G2_GORE + saved_game.write(mGoreSetTag); +#endif // _G2_GORE + + saved_game.write(mModel); + saved_game.write(mFileName); + saved_game.write(mAnimFrameDefault); + saved_game.write(mSkelFrameNum); + saved_game.write(mMeshFrameNum); + saved_game.write(mFlags); + saved_game.write(tintType); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(mModelindex); + +#ifndef JK2_MODE + saved_game.read(animModelIndexOffset); +#endif // !JK2_MODE + + saved_game.read(mCustomShader); + saved_game.read(mCustomSkin); + saved_game.read(mModelBoltLink); + saved_game.read(mSurfaceRoot); + saved_game.read(mLodBias); + saved_game.read(mNewOrigin); + +#ifdef _G2_GORE + saved_game.read(mGoreSetTag); +#endif // _G2_GORE + + saved_game.read(mModel); + saved_game.read(mFileName); + saved_game.read(mAnimFrameDefault); + saved_game.read(mSkelFrameNum); + saved_game.read(mMeshFrameNum); + saved_game.read(mFlags); + saved_game.read(tintType); + } }; class CGhoul2Info_v; @@ -453,7 +717,7 @@ class CGhoul2Info_v { return 0; } - return Array().size(); + return static_cast(Array().size()); } bool IsValid() const { @@ -466,6 +730,19 @@ class CGhoul2Info_v // of making a deep copy mItem=0; } + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(mItem); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(mItem); + } }; @@ -474,7 +751,6 @@ class CGhoul2Info_v #define G2_FRONTFACE 1 #define G2_BACKFACE 0 - class CCollisionRecord { public: @@ -495,6 +771,41 @@ class CCollisionRecord mDistance(100000), mEntityNum(-1) {} + + + void sg_export( + ojk::SavedGameHelper& saved_game) const + { + saved_game.write(mDistance); + saved_game.write(mEntityNum); + saved_game.write(mModelIndex); + saved_game.write(mPolyIndex); + saved_game.write(mSurfaceIndex); + saved_game.write(mCollisionPosition); + saved_game.write(mCollisionNormal); + saved_game.write(mFlags); + saved_game.write(mMaterial); + saved_game.write(mLocation); + saved_game.write(mBarycentricI); + saved_game.write(mBarycentricJ); + } + + void sg_import( + ojk::SavedGameHelper& saved_game) + { + saved_game.read(mDistance); + saved_game.read(mEntityNum); + saved_game.read(mModelIndex); + saved_game.read(mPolyIndex); + saved_game.read(mSurfaceIndex); + saved_game.read(mCollisionPosition); + saved_game.read(mCollisionNormal); + saved_game.read(mFlags); + saved_game.read(mMaterial); + saved_game.read(mLocation); + saved_game.read(mBarycentricI); + saved_game.read(mBarycentricJ); + } }; // calling defines for the trace function diff --git a/code/game/jke_aiworkshop.cpp b/code/game/jke_aiworkshop.cpp new file mode 100644 index 0000000000..f44578c6a8 --- /dev/null +++ b/code/game/jke_aiworkshop.cpp @@ -0,0 +1,957 @@ +#include "g_local.h" +#include "cgame/cg_local.h" +#include "cgame/cg_media.h" + +#define DRAWBOX_REFRESH 500 // How long it takes to redraw the boxes +#define BOX_VERTICES 8 + +qboolean inAIWorkshop = qfalse; +qboolean showDisplay = qtrue; + +int drawBoxesTime = 0; +int selectedAI = ENTITYNUM_NONE; + +extern stringID_table_t BSTable[]; +extern stringID_table_t BSETTable[]; +extern stringID_table_t teamTable[]; +extern stringID_table_t NPCClassTable[]; +extern stringID_table_t RankTable[]; +extern stringID_table_t MoveTypeTable[]; +extern stringID_table_t FPTable[]; + +#define OL_S 0.5f +#define OL_Y 30 +#define OL_H 6 +static void WorkshopDrawEntityInformation(gentity_t* ent, int x, const char* title) { + int add = OL_H; + vec4_t textcolor = { 0.4f, 0.4f, 0.8f, 1.0f }; + + cgi_R_Font_DrawString(x, OL_Y, title, textcolor, cgs.media.qhFontSmall, -1, OL_S); + + cgi_R_Font_DrawString(x, OL_Y + add, va("NPC_type: %s", ent->NPC_type), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + + cgi_R_Font_DrawString(x, OL_Y + add, va("health: %i/%i", ent->health, ent->max_health), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + + if (ent->script_targetname) { + cgi_R_Font_DrawString(x, OL_Y + add, va("script_targetname: %s", ent->script_targetname), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + + if (ent->NPC->goalEntity) { + cgi_R_Font_DrawString(x, OL_Y + add, va("goalEnt = %i", ent->NPC->goalEntity->s.number), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + cgi_R_Font_DrawString(x, OL_Y + add, va("bs = %s", BSTable[ent->NPC->behaviorState].name), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + if (ent->NPC->combatMove) { + cgi_R_Font_DrawString(x, OL_Y + add, "-- in combat move --", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + + cgi_R_Font_DrawString(x, OL_Y + add, va("class = %s", NPCClassTable[ent->client->NPC_class].name), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + cgi_R_Font_DrawString(x, OL_Y + add, va("rank = %s (%i)", RankTable[ent->NPC->rank].name, ent->NPC->rank), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + + if (ent->NPC->scriptFlags) { + cgi_R_Font_DrawString(x, OL_Y + add, va("scriptFlags: %i", ent->NPC->scriptFlags), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + if (ent->NPC->aiFlags) { + cgi_R_Font_DrawString(x, OL_Y + add, va("aiFlags: %i", ent->NPC->aiFlags), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + + if (ent->client->noclip) { + cgi_R_Font_DrawString(x, OL_Y + add, "cheat: NOCLIP", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + + if (ent->flags & FL_GODMODE) { + cgi_R_Font_DrawString(x, OL_Y + add, "cheat: GODMODE", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + + if (ent->flags & FL_NOTARGET) { + cgi_R_Font_DrawString(x, OL_Y + add, "cheat: NOTARGET", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + + if (ent->flags & FL_UNDYING) { + cgi_R_Font_DrawString(x, OL_Y + add, "cheat: UNDEAD", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + + cgi_R_Font_DrawString(x, OL_Y + add, va("playerTeam: %s", teamTable[ent->client->playerTeam].name), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + + cgi_R_Font_DrawString(x, OL_Y + add, va("enemyTeam: %s", teamTable[ent->client->enemyTeam].name), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + + cgi_R_Font_DrawString(x, OL_Y + add, "-- assigned scripts --", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + + for (int i = BSET_SPAWN; i < NUM_BSETS; i++) { + if (ent->behaviorSet[i]) { + cgi_R_Font_DrawString(x, OL_Y + add, va("%s: %s", BSETTable[i].name, ent->behaviorSet[i]), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + } + + if (ent->parms) { + cgi_R_Font_DrawString(x, OL_Y + add, "-- parms --", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + for (int i = 0; i < MAX_PARMS; i++) { + if (ent->parms->parm[i][0]) { + cgi_R_Font_DrawString(x, OL_Y + add, va("parm%i : %s", i + 1, ent->parms->parm[i]), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + } + } + + if (ent->NPC->group && ent->NPC->group->numGroup > 1) { + cgi_R_Font_DrawString(x, OL_Y + add, "-- squad data --", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + cgi_R_Font_DrawString(x, OL_Y + add, va("morale: %i", ent->NPC->group->morale), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + cgi_R_Font_DrawString(x, OL_Y + add, va("morale debounce: %i", ent->NPC->group->moraleDebounce), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + cgi_R_Font_DrawString(x, OL_Y + add, va("last seen enemy: %i milliseconds", level.time - ent->NPC->group->lastSeenEnemyTime), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + if (ent->NPC->group->commander) { + cgi_R_Font_DrawString(x, OL_Y + add, va("commander: %i", ent->NPC->group->commander->s.number), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + + cgi_R_Font_DrawString(x, OL_Y + add, "-- squad members --", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + for (int i = 0; i < ent->NPC->group->numGroup; i++) { + AIGroupMember_t* memberAI = &ent->NPC->group->member[i]; + int memberNum = memberAI->number; + gentity_t* member = &g_entities[memberNum]; + char* memberText = va("* entity %i, closestBuddy: %i, class: %s, rank: %s (%i), health: %i/%i", + memberNum, memberAI->closestBuddy, NPCClassTable[member->client->NPC_class].name, + RankTable[member->NPC->rank].name, member->NPC->rank, member->health, member->max_health); + cgi_R_Font_DrawString(x, OL_Y + add, memberText, textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + } + + cgi_R_Font_DrawString(x, OL_Y + add, "-- currently active timers --", textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + auto timers = TIMER_List(ent); + for (auto it = timers.begin(); it != timers.end(); ++it) { + if (it->second >= 0) { + cgi_R_Font_DrawString(x, OL_Y + add, va("%s : %i", it->first.c_str(), it->second), textcolor, cgs.media.qhFontSmall, -1, OL_S); + add += OL_H; + } + } +} + +// +// Draw textual information about the NPC in our crosshairs and also the currently selected one +// +void WorkshopDrawClientsideInformation() { + if (inAIWorkshop == qfalse) { + return; + } + if (showDisplay == qfalse) { + return; + } + // Draw the information for the NPC that is in our crosshairs + if (cg.crosshairClientNum != ENTITYNUM_NONE && cg.crosshairClientNum != 0 && g_entities[cg.crosshairClientNum].client) { + gentity_t* crossEnt = &g_entities[cg.crosshairClientNum]; + WorkshopDrawEntityInformation(crossEnt, 10, "Crosshair AI"); + } + + // Draw the information for the AI that we have selected + if (selectedAI != ENTITYNUM_NONE) { + gentity_t* selectedEnt = &g_entities[selectedAI]; + WorkshopDrawEntityInformation(selectedEnt, 500, "Selected AI"); + } +} + +// +// Draws a box around the bounding box of the entity +// +void WorkshopDrawEntBox(gentity_t* ent, int colorOverride = -1) { + unsigned color = 0x666666; // G_SoundOnEnt(ent, "satan.mp3"); + if (colorOverride == -1 && selectedAI == ent->s.number) { + // Draw a box around our goal ent + if (ent->NPC->goalEntity != NULL) { + WorkshopDrawEntBox(ent->NPC->goalEntity, color); + } + color = 0x0000FFFF; // Yellow = selected AI + } + else if (colorOverride == -1 && ent->client->playerTeam == TEAM_ENEMY) { + color = 0x000000FF; // Red = enemy + } + else if (colorOverride == -1 && ent->client->playerTeam == TEAM_PLAYER) { + color = 0x0000FF00; // Green = ally + } + else if (colorOverride == -1 && ent->client->playerTeam == TEAM_NEUTRAL) { + color = 0x00FF0000; // Blue = neutral + } + else if (colorOverride != -1) { + color = colorOverride; + } + + // This is going to look like a mess and I can't really comment on this, but I drew this out manually and it works. + vec3_t vertices[BOX_VERTICES] = { 0 }; + for (int i = 0; i < BOX_VERTICES; i++) { + VectorCopy(ent->currentOrigin, vertices[i]); + } + + for (int i = 4; i < BOX_VERTICES; i++) { + vertices[i][2] += ent->mins[2]; + } + for (int i = 0; i < 4; i++) { + vertices[i][2] += ent->maxs[2]; + } + vertices[0][0] += ent->mins[0]; + vertices[1][0] += ent->mins[0]; + vertices[4][0] += ent->mins[0]; + vertices[5][0] += ent->mins[0]; + vertices[2][0] += ent->maxs[0]; + vertices[3][0] += ent->maxs[0]; + vertices[6][0] += ent->maxs[0]; + vertices[7][0] += ent->maxs[0]; + vertices[0][1] += ent->mins[1]; + vertices[2][1] += ent->mins[1]; + vertices[4][1] += ent->mins[1]; + vertices[6][1] += ent->mins[1]; + vertices[1][1] += ent->maxs[1]; + vertices[3][1] += ent->maxs[1]; + vertices[5][1] += ent->maxs[1]; + vertices[7][1] += ent->maxs[1]; + + G_DebugLine(vertices[0], vertices[1], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[2], vertices[3], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[0], vertices[2], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[1], vertices[3], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[0], vertices[4], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[1], vertices[5], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[2], vertices[6], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[3], vertices[7], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[4], vertices[5], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[6], vertices[7], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[4], vertices[6], DRAWBOX_REFRESH, color, qtrue); + G_DebugLine(vertices[5], vertices[7], DRAWBOX_REFRESH, color, qtrue); +} + +// +// Occurs every frame +// +void WorkshopThink() { + if (!inAIWorkshop) { + return; + } + + // If the selected AI is dead, deselect it + if (selectedAI != ENTITYNUM_NONE) { + gentity_t* ent = &g_entities[selectedAI]; + if (ent->health <= 0) { + selectedAI = ENTITYNUM_NONE; + } + } + if (drawBoxesTime < level.time && showDisplay) { + for (int i = 1; i < globals.num_entities; i++) { + if (!PInUse(i)) { + continue; + } + + gentity_t* found = &g_entities[i]; + if (!found->client) { + continue; + } + + WorkshopDrawEntBox(found); + drawBoxesTime = level.time + DRAWBOX_REFRESH - 50; // -50 so we get an extra frame to draw, so the boxes don't blink + } + } +} + +// +// Workshop mode just got toggled by console command +// +void WorkshopToggle(gentity_t* ent) { + inAIWorkshop = (qboolean)!inAIWorkshop; + + if (inAIWorkshop) { + // Workshop mode just got enabled + gi.Printf("AI Workshop enabled\n"); + } + else { + // Workshop mode just got disabled + gi.Printf("AI Workshop disabled\n"); + } +} + +////////////////////// +// +// Below are various commands that we can do +// +////////////////////// + +void WorkshopSelect_f(gentity_t* ent) { + vec3_t src, dest, vf; + trace_t trace; + VectorCopy(ent->client->renderInfo.eyePoint, src); + AngleVectors(ent->client->ps.viewangles, vf, NULL, NULL);//ent->client->renderInfo.eyeAngles was cg.refdef.viewangles, basically + //extend to find end of use trace + VectorMA(src, 32967.0f, vf, dest); + + //Trace ahead to find a valid target + gi.trace(&trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE | CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE, G2_NOCOLLIDE, 0); + if (trace.fraction == 1.0f || trace.entityNum < 1) + { + return; + } + + gentity_t* target = &g_entities[trace.entityNum]; + if (target->client) { + selectedAI = target->s.number; + } + else { + gi.Printf("No NPC in crosshair\n"); + } +} + +// Deselect currently selected NPC +void WorkshopDeselect_f(gentity_t* ent) { + selectedAI = ENTITYNUM_NONE; +} + +// View all behavior states +void Workshop_List_BehaviorState_f(gentity_t* ent) { + for (int i = 0; i < NUM_BSTATES; i++) { + gi.Printf(va("%s\n", BSTable[i].name)); + } +} + +// View all teams +void Workshop_List_Team_f(gentity_t* ent) { + for (int i = 0; i < TEAM_NUM_TEAMS; i++) { + gi.Printf(va("%s\n", teamTable[i].name)); + } +} + +// View all ranks +void Workshop_List_Ranks_f(gentity_t* ent) { + for (int i = 0; i < RANK_MAX; i++) { + gi.Printf(va("%s\n", RankTable[i].name)); + } +} + +// View all classes +void Workshop_List_Classes_f(gentity_t* ent) { + for (int i = 0; i < CLASS_NUM_CLASSES; i++) { + gi.Printf(va("%s\n", NPCClassTable[i].name)); + } +} + +// View all movetypes +void Workshop_List_Movetypes_f(gentity_t* ent) { + for (int i = 0; i <= MT_FLYSWIM; i++) { + gi.Printf(va("%s\n", MoveTypeTable[i].name)); + } +} + +// View all forcepowers +void Workshop_List_ForcePowers_f(gentity_t* ent) { + for (int i = 0; i <= FP_SABER_OFFENSE; i++) { + gi.Printf(va("%s\n", FPTable[i].name)); + } +} + +// View all behaviorsets +void Workshop_List_BehaviorSets_f(gentity_t* ent) { + for (int i = BSET_SPAWN; i < NUM_BSETS; i++) { + gi.Printf(va("%s\n", BSETTable[i].name)); + } +} + +// List all scriptflags +#define PRINTFLAG(x) gi.Printf("" #x " = %i\n", x) +void Workshop_List_Scriptflags_f(gentity_t* ent) { + PRINTFLAG(SCF_CROUCHED); + PRINTFLAG(SCF_WALKING); + PRINTFLAG(SCF_MORELIGHT); + PRINTFLAG(SCF_LEAN_RIGHT); + PRINTFLAG(SCF_LEAN_LEFT); + PRINTFLAG(SCF_RUNNING); + PRINTFLAG(SCF_ALT_FIRE); + PRINTFLAG(SCF_NO_RESPONSE); + PRINTFLAG(SCF_FFDEATH); + PRINTFLAG(SCF_NO_COMBAT_TALK); + PRINTFLAG(SCF_CHASE_ENEMIES); + PRINTFLAG(SCF_LOOK_FOR_ENEMIES); + PRINTFLAG(SCF_FACE_MOVE_DIR); + PRINTFLAG(SCF_IGNORE_ALERTS); + PRINTFLAG(SCF_DONT_FIRE); + PRINTFLAG(SCF_DONT_FLEE); + PRINTFLAG(SCF_FORCED_MARCH); + PRINTFLAG(SCF_NO_GROUPS); + PRINTFLAG(SCF_FIRE_WEAPON); + PRINTFLAG(SCF_NO_MIND_TRICK); + PRINTFLAG(SCF_USE_CP_NEAREST); + PRINTFLAG(SCF_NO_FORCE); + PRINTFLAG(SCF_NO_FALLTODEATH); + PRINTFLAG(SCF_NO_ACROBATICS); + PRINTFLAG(SCF_USE_SUBTITLES); + PRINTFLAG(SCF_NO_ALERT_TALK); +} + +// List all aiflags +void Workshop_List_AIFlags_f(gentity_t* ent) { + PRINTFLAG(NPCAI_CHECK_WEAPON); + PRINTFLAG(NPCAI_BURST_WEAPON); + PRINTFLAG(NPCAI_MOVING); + PRINTFLAG(NPCAI_TOUCHED_GOAL); + PRINTFLAG(NPCAI_PUSHED); + PRINTFLAG(NPCAI_NO_COLL_AVOID); + PRINTFLAG(NPCAI_BLOCKED); + PRINTFLAG(NPCAI_OFF_PATH); + PRINTFLAG(NPCAI_IN_SQUADPOINT); + PRINTFLAG(NPCAI_STRAIGHT_TO_DESTPOS); + PRINTFLAG(NPCAI_NO_SLOWDOWN); + PRINTFLAG(NPCAI_LOST); + PRINTFLAG(NPCAI_SHIELDS); + PRINTFLAG(NPCAI_GREET_ALLIES); + PRINTFLAG(NPCAI_FORM_TELE_NAV); + PRINTFLAG(NPCAI_ENROUTE_TO_HOMEWP); + PRINTFLAG(NPCAI_MATCHPLAYERWEAPON); + PRINTFLAG(NPCAI_DIE_ON_IMPACT); +} + +// Set a raw timer +void Workshop_Set_Timer_f(gentity_t* ent) { + if (gi.argc() != 3) { + gi.Printf("usage: workshop_set_timer \n"); + return; + } + const char* timerName = gi.argv(1); + int ms = atoi(gi.argv(2)); + TIMER_Set(&g_entities[selectedAI], timerName, ms); +} + +// View all timers, including ones which are dead +void Workshop_View_Timers_f(gentity_t* ent) { + auto timers = TIMER_List(&g_entities[selectedAI]); + for (auto it = timers.begin(); it != timers.end(); ++it) { + gi.Printf("%s\t\t:\t\t%i\n", it->first.c_str(), it->second); + } +} + +// Set Behavior State +void Workshop_Set_BehaviorState_f(gentity_t* ent) { + if (gi.argc() != 2) { + gi.Printf("usage: workshop_set_bstate \n"); + return; + } + int bState = GetIDForString(BSTable, gi.argv(1)); + if (bState == -1) { + gi.Printf("Invalid bstate\n"); + return; + } + g_entities[selectedAI].NPC->behaviorState = (bState_t)bState; +} + +// Set goal entity +void Workshop_Set_GoalEntity_f(gentity_t* ent) { + if (gi.argc() == 2) { + // There's a second argument. Use that. + if (!Q_stricmp(gi.argv(1), "me")) { + g_entities[selectedAI].NPC->goalEntity = g_entities; + return; + } + else { + g_entities[selectedAI].NPC->goalEntity = &g_entities[atoi(gi.argv(1))]; + } + if (g_entities[selectedAI].NPC->lastGoalEntity != NULL) { + gi.Printf("New goal entity: %i (last goal entity was %i)\n", g_entities[selectedAI].NPC->goalEntity->s.number, g_entities[selectedAI].NPC->lastGoalEntity->s.number); + } + else { + gi.Printf("New goal entity: %i\n", g_entities[selectedAI].NPC->goalEntity->s.number); + } + } + + vec3_t src, dest, vf; + trace_t trace; + VectorCopy(ent->client->renderInfo.eyePoint, src); + AngleVectors(ent->client->ps.viewangles, vf, NULL, NULL);//ent->client->renderInfo.eyeAngles was cg.refdef.viewangles, basically + //extend to find end of use trace + VectorMA(src, 32967.0f, vf, dest); + + //Trace ahead to find a valid target + gi.trace(&trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE | CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_ITEM | CONTENTS_CORPSE, G2_NOCOLLIDE, 0); + if (trace.fraction == 1.0f || trace.entityNum < 1 || trace.entityNum == ENTITYNUM_NONE || trace.entityNum == ENTITYNUM_WORLD) + { + gi.Printf("Invalid entity\n"); + return; + } + + gentity_t* target = &g_entities[trace.entityNum]; + gentity_t* selected = &g_entities[selectedAI]; + selected->NPC->lastGoalEntity = selected->NPC->goalEntity; + selected->NPC->goalEntity = target; + if (selected->NPC->lastGoalEntity != NULL) { + gi.Printf("New goal entity: %i (last goal entity was %i)\n", selected->NPC->goalEntity->s.number, selected->NPC->lastGoalEntity->s.number); + } + else { + gi.Printf("New goal entity: %i\n", selected->NPC->goalEntity->s.number); + } +} + +// Set enemy +void Workshop_Set_Enemy_f(gentity_t* ent) { + if (gi.argc() == 2) { + // There's a second argument. Use that. + if (!Q_stricmp(gi.argv(1), "me")) { + g_entities[selectedAI].lastEnemy = g_entities[selectedAI].enemy; + g_entities[selectedAI].enemy = g_entities; + } + else { + g_entities[selectedAI].enemy = &g_entities[atoi(gi.argv(1))]; + } + if (g_entities[selectedAI].lastEnemy != NULL) { + gi.Printf("New enemy: %i (last enemy was %i)\n", g_entities[selectedAI].enemy->s.number, g_entities[selectedAI].lastEnemy->s.number); + } + else { + gi.Printf("New enemy: %i\n", g_entities[selectedAI].enemy->s.number); + } + } + + vec3_t src, dest, vf; + trace_t trace; + VectorCopy(ent->client->renderInfo.eyePoint, src); + AngleVectors(ent->client->ps.viewangles, vf, NULL, NULL);//ent->client->renderInfo.eyeAngles was cg.refdef.viewangles, basically + //extend to find end of use trace + VectorMA(src, 32967.0f, vf, dest); + + //Trace ahead to find a valid target + gi.trace(&trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE | CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_ITEM | CONTENTS_CORPSE, G2_NOCOLLIDE, 0); + if (trace.fraction == 1.0f || trace.entityNum < 1 || trace.entityNum == ENTITYNUM_NONE || trace.entityNum == ENTITYNUM_WORLD) + { + gi.Printf("Invalid entity\n"); + return; + } + + gentity_t* target = &g_entities[trace.entityNum]; + gentity_t* selected = &g_entities[selectedAI]; + selected->lastEnemy = selected->enemy; + selected->enemy = target; + if (selected->lastEnemy != NULL) { + gi.Printf("New enemy: %i (last enemy was %i)\n", selected->enemy->s.number, selected->lastEnemy->s.number); + } + else { + gi.Printf("New enemy: %i\n", selected->enemy->s.number); + } +} + +// Set scriptflags +void Workshop_Set_Scriptflags_f(gentity_t* ent) { + if (gi.argc() != 2) { + gi.Printf("usage: workshop_set_scriptflags