diff --git a/.circleci/config.yml b/.circleci/config.yml
index ca9105685..1784ba1ea 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -3,7 +3,7 @@ jobs:
build:
working_directory: /root/SRB2
docker:
- - image: debian:jessie
+ - image: debian:stretch
environment:
CC: ccache gcc -m32
PKG_CONFIG_LIBDIR: /usr/lib/i386-linux-gnu/pkgconfig
@@ -36,14 +36,20 @@ jobs:
- v1-SRB2-APT
- run:
name: Install SDK
- command: apt-get -qq -y install git build-essential nasm libpng12-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 gettext ccache wget gcc-multilib upx
+ command: apt-get -qq -y --no-install-recommends install git build-essential nasm libpng-dev:i386 libsdl2-mixer-dev:i386 libgme-dev:i386 gettext ccache wget gcc-multilib upx openssh-client
- save_cache:
key: v1-SRB2-APT
paths:
- /var/cache/apt/archives
- checkout
- run:
- name: Clean build
+ name: Compile without network support and BLUA
+ command: make -C src LINUX=1 ERRORMODE=1 -k NONET=1 NO_LUA=1
+ - run:
+ name: wipe build
+ command: make -C src LINUX=1 cleandep
+ - run:
+ name: rebuild depend
command: make -C src LINUX=1 clean
- restore_cache:
keys:
diff --git a/.travis.yml b/.travis.yml
index 8a137d9d8..15a3c844c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,6 +15,7 @@ matrix:
- p7zip-full
- gcc-4.4
compiler: gcc-4.4
+ env: GCC44=1
#gcc-4.4 (Ubuntu/Linaro 4.4.7-8ubuntu1) 4.4.7
- os: linux
addons:
@@ -27,6 +28,7 @@ matrix:
- p7zip-full
- gcc-4.6
compiler: gcc-4.6
+ env: GCC46=1
#gcc-4.6 (Ubuntu/Linaro 4.6.4-6ubuntu2) 4.6.4
- os: linux
addons:
@@ -39,9 +41,11 @@ matrix:
- p7zip-full
- gcc-4.7
compiler: gcc-4.7
+ env: GCC47=1
#gcc-4.7
- os: linux
compiler: gcc
+ env: GCC48=1
#gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
- os: linux
addons:
@@ -56,6 +60,7 @@ matrix:
- p7zip-full
- gcc-4.8
compiler: gcc-4.8
+ env: GCC48=1
#gcc-4.8 (Ubuntu 4.8.5-2ubuntu1~14.04.1) 4.8.5
- os: linux
addons:
@@ -70,7 +75,7 @@ matrix:
- p7zip-full
- gcc-7
compiler: gcc-7
- env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wimplicit-fallthrough=3"
+ env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wno-implicit-fallthrough" GCC72=1
#gcc-7 (Ubuntu 7.2.0-1ubuntu1~14.04) 7.2.0 20170802
- os: linux
addons:
@@ -85,7 +90,7 @@ matrix:
- p7zip-full
- gcc-8
compiler: gcc-8
- env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wimplicit-fallthrough=3"
+ env: WFLAGS="-Wno-tautological-compare -Wno-error=implicit-fallthrough -Wno-implicit-fallthrough -Wno-error=format-overflow" GCC81=1
#gcc-8 (Ubuntu 7.2.0-1ubuntu1~14.04) 8.1.0
- os: linux
compiler: clang
@@ -216,9 +221,11 @@ matrix:
# - os: osx
# osx_image: xcode7.2
# #Apple LLVM version 7.0.2 (clang-700.1.81)
+# - os: osx
+# osx_image: xcode7.3
+# #Apple LLVM version 7.3.0 (clang-703.0.31)
- os: osx
- osx_image: xcode7.3
- #Apple LLVM version 7.3.0 (clang-703.0.31)
+ #Default: macOS 10.13 and Xcode 9.4.1
allow_failures:
- compiler: clang-3.5
- compiler: clang-3.6
@@ -227,7 +234,6 @@ matrix:
- compiler: clang-3.9
- compiler: clang-4.0
- compiler: clang-5.0
- - compiler: gcc-8
cache:
apt: true
@@ -243,6 +249,16 @@ addons:
- libgl1-mesa-dev
- libgme-dev
- p7zip-full
+ homebrew:
+ taps:
+ - mazmazz/srb2
+ packages:
+ - sdl2_mixer
+ - game-music-emu
+ - p7zip
+ - cmake
+ update: true
+
before_script:
- wget --verbose --server-response -c http://rosenthalcastle.org/srb2/SRB2-v2115-assets-2.7z -O $HOME/srb2_cache/SRB2-v2115-assets-2.7z
@@ -254,9 +270,6 @@ before_script:
- cmake .. -DCMAKE_BUILD_TYPE=Release
before_install:
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install sdl2 sdl2_mixer game-music-emu p7zip; fi
- - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install cmake||true; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then curl -O -L https://www.libsdl.org/release/SDL2-2.0.6.dmg; hdiutil attach SDL2-2.0.6.dmg; sudo cp -a /Volumes/SDL2/SDL2.framework /Library/Frameworks/; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then curl -O -L https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-2.0.1.dmg; hdiutil attach SDL2_mixer-2.0.1.dmg; sudo cp -a /Volumes/SDL2_mixer/SDL2_mixer.framework /Library/Frameworks/; fi
- mkdir -p $HOME/srb2_cache
diff --git a/CMakeLists.txt b/CMakeLists.txt
index eb91866f0..0a5507b92 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.0)
project(SRB2
- VERSION 2.1.20
+ VERSION 2.1.23
LANGUAGES C)
if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})
@@ -54,13 +54,19 @@ macro(copy_files_to_build_dir target dlllist_var)
endif()
endmacro()
-# 64-bit check
-if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
+# bitness check
+set(SRB2_SYSTEM_BITS 0)
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
message(STATUS "Target is 64-bit")
set(SRB2_SYSTEM_BITS 64)
-else()
+endif()
+if(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ message(STATUS "Target is 32-bit")
set(SRB2_SYSTEM_BITS 32)
endif()
+if(${SRB2_SYSTEM_BITS} EQUAL 0)
+ message(STATUS "Target bitness is unknown")
+endif()
# OS macros
if (UNIX)
@@ -98,10 +104,10 @@ add_subdirectory(assets)
## config.h generation
set(GIT_EXECUTABLE "git" CACHE FILEPATH "Path to git binary")
include(GitUtilities)
-git_describe(SRB2_GIT_DESCRIBE "${CMAKE_SOURCE_DIR}")
+git_latest_commit(SRB2_COMP_COMMIT "${CMAKE_SOURCE_DIR}")
git_current_branch(SRB2_GIT_BRANCH "${CMAKE_SOURCE_DIR}")
set(SRB2_COMP_BRANCH "${SRB2_GIT_BRANCH}")
-set(SRB2_COMP_REVISION "${SRB2_GIT_DESCRIBE}")
+set(SRB2_COMP_REVISION "${SRB2_COMP_COMMIT}")
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/src/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/config.h)
##### PACKAGE CONFIGURATION #####
diff --git a/SRB2_Debug.props b/SRB2_Debug.props
index 8be11c58a..74177c6ef 100644
--- a/SRB2_Debug.props
+++ b/SRB2_Debug.props
@@ -22,6 +22,7 @@
Debug
+ true
diff --git a/SRB2_Release.props b/SRB2_Release.props
index a216ea45a..905dfdcf9 100644
--- a/SRB2_Release.props
+++ b/SRB2_Release.props
@@ -24,6 +24,7 @@
DebugFastLink
true
true
+ true
diff --git a/SRB2_common.props b/SRB2_common.props
index 2fb2eb8c6..0f80ceb17 100644
--- a/SRB2_common.props
+++ b/SRB2_common.props
@@ -18,7 +18,7 @@
4244;4267
- ws2_32.lib;%(AdditionalDependencies)
+ advapi32.lib;ws2_32.lib;%(AdditionalDependencies)
Windows
false
true
diff --git a/appveyor.yml b/appveyor.yml
index 69913cfc8..f0f843fbb 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,11 +1,15 @@
-version: 2.1.20.{branch}-{build}
+version: 2.1.23.{branch}-{build}
os: MinGW
environment:
CC: ccache
CCACHE_CC: i686-w64-mingw32-gcc
+ CCACHE_CC_64: x86_64-w64-mingw32-gcc
WINDRES: windres
+ # c:\mingw-w64 i686 has gcc 6.3.0, so use c:\msys64 7.3.0 instead
MINGW_SDK: c:\msys64\mingw32
+ # c:\msys64 x86_64 has gcc 8.2.0, so use c:\mingw-w64 7.3.0 instead
+ MINGW_SDK_64: C:\mingw-w64\x86_64-7.3.0-posix-seh-rt_v5-rev0\mingw64
CFLAGS: -Wall -W -Werror -Wno-error=implicit-fallthrough -Wimplicit-fallthrough=3 -Wno-tautological-compare -Wno-error=suggest-attribute=noreturn
NASM_ZIP: nasm-2.12.01
NASM_URL: http://www.nasm.us/pub/nasm/releasebuilds/2.12.01/win64/nasm-2.12.01-win64.zip
@@ -15,65 +19,114 @@ environment:
CCACHE_URL: http://alam.srb2.org/ccache.exe
CCACHE_COMPRESS: true
CCACHE_DIR: C:\Users\appveyor\.ccache
+ # Disable UPX by default. The user can override this in their Appveyor project settings
+ NOUPX: 1
+ ##############################
+ # DEPLOYER VARIABLES
+ # DPL_ENABLED=1 builds installers for branch names starting with `deployer`.
+ # DPL_TAG_ENABLED=1 will also build installers for release tags. DPL_ENABLED=1 must also be set.
+ # Set these in the Appveyor project settings
+ ##############################
+ DPL_ENABLED: 0
+ DPL_TAG_ENABLED: 0
+ DPL_INSTALLER_NAME: SRB2-v2123
+ # Asset handling is barebones vs. Travis Deployer. We operate on 7z only.
+ # Include the README files and the OpenGL batch in the main and patch archives.
+ # The x86/x64 archives contain the DLL binaries.
+ ASSET_ARCHIVE_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-assets.7z
+ ASSET_ARCHIVE_PATCH_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-patch-assets.7z
+ ASSET_ARCHIVE_X86_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-x86-assets.7z
+ ASSET_ARCHIVE_X64_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-x64-assets.7z
+ ASSET_ARCHIVE_OPTIONAL_PATH: https://github.com/mazmazz/SRB2/releases/download/SRB2_assets/SRB2-v2122-optional-assets.7z
+ # This is overridden to 1 for release tag builds
+ ASSET_FILES_OPTIONAL_GET: 0
+ # For patches, also include the X86/X64 DLLs.
+ PACKAGE_PATCH_DLL_GET: 0
+ # Delete all asset downloads so they can be redownloaded
+ ASSET_CLEAN: 0
cache:
- nasm-2.12.01.zip
- upx391w.zip
- ccache.exe
- C:\Users\appveyor\.ccache
+- C:\Users\appveyor\srb2_cache
install:
+- if [%CONFIGURATION%] == [SDL64] ( set "X86_64=1" )
+- if [%CONFIGURATION%] == [SDL64] ( set "CONFIGURATION=SDL" )
+- if [%CONFIGURATION%] == [DD64] ( set "X86_64=1" )
+- if [%CONFIGURATION%] == [DD64] ( set "CONFIGURATION=DD" )
+- if [%X86_64%] == [1] ( set "MINGW_SDK=%MINGW_SDK_64%" )
+- if [%X86_64%] == [1] ( set "CCACHE_CC=%CCACHE_CC_64%" )
+
- if not exist "%NASM_ZIP%.zip" appveyor DownloadFile "%NASM_URL%" -FileName "%NASM_ZIP%.zip"
- 7z x -y "%NASM_ZIP%.zip" -o%TMP% >null
-- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs %TMP%\%NASM_ZIP% %MINGW_SDK%\bin nasm.exe || exit 0
+- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs "%TMP%\%NASM_ZIP%" "%MINGW_SDK%\bin" nasm.exe || exit 0
- if not exist "%UPX_ZIP%.zip" appveyor DownloadFile "%UPX_URL%" -FileName "%UPX_ZIP%.zip"
- 7z x -y "%UPX_ZIP%.zip" -o%TMP% >null
-- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs %TMP%\%UPX_ZIP% %MINGW_SDK%\bin upx.exe || exit 0
+- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs "%TMP%\%UPX_ZIP%" "%MINGW_SDK%\bin" upx.exe || exit 0
- if not exist "%CCACHE_EXE%" appveyor DownloadFile "%CCACHE_URL%" -FileName "%CCACHE_EXE%"
- ccache -M 99M
-- xcopy /Y /V /I ccache.exe %MINGW_SDK%\bin
+- xcopy /Y /V /I ccache.exe "%MINGW_SDK%\bin"
configuration:
- SDL
+- SDL64
- DD
+- DD64
matrix:
allow_failures:
- configuration: DD
+ - configuration: DD64
before_build:
-- set Path=%MINGW_SDK%\bin;%Path%
-- i686-w64-mingw32-gcc --version
+- set "Path=%MINGW_SDK%\bin;%Path%"
+- if [%X86_64%] == [1] ( x86_64-w64-mingw32-gcc --version ) else ( i686-w64-mingw32-gcc --version )
- mingw32-make --version
-- nasm -v
-- upx -V
+- if not [%X86_64%] == [1] ( nasm -v )
+- if not [%NOUPX%] == [1] ( upx -V )
- ccache -V
- ccache -s
-- set SRB2_MFLAGS=-C src MINGW=1 WARNINGMODE=1 GCC72=1 CCACHE=1 NOOBJDUMP=1
+- if [%NOUPX%] == [1] ( set "NOUPX=NOUPX=1" ) else ( set "NOUPX=" )
+- set "SRB2_MFLAGS=-C src WARNINGMODE=1 CCACHE=1 GCC72=1 NOOBJDUMP=1 %NOUPX%"
+- if [%X86_64%] == [1] ( set "MINGW_FLAGS=MINGW64=1 X86_64=1" ) else ( set "MINGW_FLAGS=MINGW=1" )
+- set "SRB2_MFLAGS=%SRB2_MFLAGS% %MINGW_FLAGS% %CONFIGURATION%=1"
build_script:
-- cmd: mingw32-make.exe %SRB2_MFLAGS% %CONFIGURATION%=1 clean
-- cmd: mingw32-make.exe %SRB2_MFLAGS% %CONFIGURATION%=1 ERRORMODE=1 -k
+- cmd: mingw32-make.exe %SRB2_MFLAGS% clean
+- cmd: mingw32-make.exe %SRB2_MFLAGS% ERRORMODE=1 -k
after_build:
+- if [%X86_64%] == [1] (
+ set "BUILD_PATH=bin\Mingw64\Release"
+ ) else (
+ set "BUILD_PATH=bin\Mingw\Release"
+ )
+- if [%X86_64%] == [1] ( set "CONFIGURATION=%CONFIGURATION%64" )
- ccache -s
- cmd: git rev-parse --short %APPVEYOR_REPO_COMMIT%>%TMP%/gitshort.txt
- cmd: set /P GITSHORT=<%TMP%/gitshort.txt
- set BUILD_ARCHIVE=%APPVEYOR_REPO_BRANCH%-%GITSHORT%-%CONFIGURATION%.7z
- set BUILDSARCHIVE=%APPVEYOR_REPO_BRANCH%-%CONFIGURATION%.7z
-- cmd: 7z a %BUILD_ARCHIVE% bin\Mingw\Release -xr!.gitignore
+- cmd: 7z a %BUILD_ARCHIVE% %BUILD_PATH% -xr!.gitignore
- appveyor PushArtifact %BUILD_ARCHIVE%
- cmd: copy %BUILD_ARCHIVE% %BUILDSARCHIVE%
- appveyor PushArtifact %BUILDSARCHIVE%
+##############################
+# DEPLOYER SCRIPT
+##############################
+- if [%DPL_ENABLED%] == [1] ( call "deployer\appveyor\deployer.bat" )
test: off
#deploy:
# - provider: FTP
# protocol: ftps
-# host:
+# host:
# secure: NsLJEPIBvmwCOj8Tg8RoRQ==
# username:
# secure: ejxi5mvk7oLYu7QtbYojajEPigMy0mokaKhuEVuDZcA=
diff --git a/assets/.gitignore b/assets/.gitignore
index 37bb465dc..9ed61ca1a 100644
--- a/assets/.gitignore
+++ b/assets/.gitignore
@@ -1,2 +1,5 @@
*
*.*
+!README.txt
+!LICENSE.txt
+!LICENSE-3RD-PARTY.txt
\ No newline at end of file
diff --git a/assets/CMakeLists.txt b/assets/CMakeLists.txt
index 292e184c7..6edb3df13 100644
--- a/assets/CMakeLists.txt
+++ b/assets/CMakeLists.txt
@@ -8,6 +8,9 @@ set(SRB2_ASSET_ALL
${CMAKE_CURRENT_SOURCE_DIR}/zones.dta
${CMAKE_CURRENT_SOURCE_DIR}/patch.dta
${CMAKE_CURRENT_SOURCE_DIR}/music.dta
+ ${CMAKE_CURRENT_SOURCE_DIR}/README.txt
+ ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt
+ ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE-3RD-PARTY.txt
)
set(SRB2_ASSET_HASHED
diff --git a/assets/LICENSE-3RD-PARTY.txt b/assets/LICENSE-3RD-PARTY.txt
new file mode 100644
index 000000000..42ea20e96
--- /dev/null
+++ b/assets/LICENSE-3RD-PARTY.txt
@@ -0,0 +1,1710 @@
+--------------------------------------------------------------------------------
+ 3-Clause BSD License
+ applies to:
+ - MiniUPnPc
+ Copyright (c) 2005-2011, Thomas BERNARD
+ All rights reserved.
+ http://miniupnp.free.fr
+--------------------------------------------------------------------------------
+
+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.
+ * The name of the author may not 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 OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+--------------------------------------------------------------------------------
+ curl License
+ applies to:
+ - curl
+ Copyright (c) 1996 - 2018, Daniel Stenberg, daniel@haxx.se,
+ and many contributors, see the THANKS file.
+ https://curl.haxx.se
+--------------------------------------------------------------------------------
+
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose
+with or without fee is hereby granted, provided that the above copyright notice
+and this permission notice appear in all copies.
+
+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 AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN
+NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
+OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall not be
+used in advertising or otherwise to promote the sale, use or other dealings in
+this Software without prior written authorization of the copyright holder.
+
+--------------------------------------------------------------------------------
+ FMOD End User License Agreement
+ applies to:
+ - FMOD Ex
+ Copyright (c), Firelight Technologies Pty, Ltd. 2004-2018.
+ https://www.fmod.com
+--------------------------------------------------------------------------------
+
+This FMOD End User Licence Agreement (EULA) is a legal agreement between you and Firelight
+Technologies Pty Ltd (ACN 099 182 448) (us or we) and governs your use of FMOD Studio and FMOD
+Engine software (FMOD).
+
+1. GRANT OF LICENCE
+This EULA grants you the right to use FMOD, in a software application (Product), for
+personal (hobbyist), educational (students and teachers) or Non-Commercial use only,
+subject to the following:
+i) Non-Commercial use does not involve any form of monetisation, sponsorship
+or promotion.
+ii) FMOD is distributed as integrated into a Product only;
+iii) FMOD is not distributed as part of any Commercial Product or service;
+iv) FMOD is not distributed as part of a game engine or tool set;
+v) FMOD is not used in any Commercial enterprise or for any Commercial
+production or subcontracting, except for the purposes of Evaluation or
+Development of a Commercial Product;
+vi) Product includes attribution in accordance with Clause 3;
+
+2. OTHER USE
+For all Commercial use, and any Non Commercial use not permitted by this license, a
+separate license is required. Refer to www.fmod.com/licensing for information.
+
+3. CREDITS AND LOGO
+All Products require an in game credit line which must include the words "FMOD" or
+"FMOD Studio" (if applicable) and "Firelight Technologies Pty Ltd". This is non
+negotiable. Refer to www.fmod.com/licensing for examples. All products require a
+logo to be presented during start up of the application, before encountering any
+menus or interactivity. See www.fmod.com/licensing for logo information. The user
+has the option to 'buy out' the requirement to have a logo at the start of the
+product.
+
+4. INTELLECTUAL PROPERTY RIGHTS
+a) We are and remain at all times the owner of FMOD (including all intellectual
+property rights in or to the Software). For the avoidance of doubt, nothing in
+this EULA may be deemed to grant or assign to you any proprietary or ownership
+interest or intellectual property rights in or to FMOD other than the rights
+licensed pursuant to clause 1.
+b) You acknowledge and agree that you have no right, title or interest in and to the
+intellectual property rights in FMOD.
+
+5. SECURITY AND RISK
+You are responsible for protecting FMOD and any related materials at all times from
+unauthorised access, use or damage.
+
+6. WARRANTY AND LIMITATION OF LIABILITY
+a) FMOD is provided by us "as is" and, to the maximum extent permitted by law,
+any express or implied warranties of any kind, including (but not limited to) all
+implied warranties of merchantability and fitness for a particular purpose are
+disclaimed.
+b) In no event shall we (and our employees, contractors and subcontractors),
+developers and contributors be liable for any direct, special, indirect or
+consequential damages whatsoever resulting from loss of use of data or profits,
+whether in an action of contract, negligence or other tortious conduct, arising
+out of or in connection with the use or performance FMOD.
+
+7. OGG VORBIS CODEC
+a) FMOD uses the Ogg Vorbis codec.
+b) (c) 2002, Xiph.Org Foundation
+c) Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+i) Redistributions of source code must retain the above copyright notice, the
+list of conditions and the following disclaimer.
+ii) Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other material provided with the distribution.
+iii) Neither the name of the Xiph.org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from this
+software without specific prior written permission.
+d) 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 FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTIAL, 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
+NEGLIENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+8. GOOGLE VR (GVR)
+FMOD includes Google VR, licensed under the Apache Licence, Version 2.0 (the Licence);
+you may not use this file except in compliance with the License. You may obtain a copy of
+the License at:
+http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software distributed under the
+License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+either express or implied. See the License for the specific language governing permissions
+and limitations under the License.
+
+9. ANDROID PLATFORM CODE
+Copyright (C) 2010 The Android Open Source Project All rights reserved.
+
+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.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+10. AUDIOGAMING AUDIOMOTORS DEMO CONTENT
+AudioGaming AudioMotors Demo Engine.agp is provided for evaluation purposes
+only and is not to be redistributed. To create your own engine content, you
+will need AudioMotors V2 Pro. A trial version be found at
+http://store.audiogaming.net/content/audiomotors-v2-pro-trial. For access to
+the full version, contact sales@fmod.com.
+
+© 2018 Firelight Technologies Pty Ltd.
+
+--------------------------------------------------------------------------------
+ GCC Runtime Library Exception, Version 3.1
+ applies to:
+ - GCC Runtime Library
+ Copyright (C) Free Software Foundation, Inc.
+ https://www.gnu.org/software/gcc/
+--------------------------------------------------------------------------------
+
+GCC RUNTIME LIBRARY EXCEPTION Version 3.1, 31 March 2009
+
+Copyright © 2009 Free Software Foundation, Inc.
+
+Everyone is permitted to copy and distribute verbatim copies of this license
+document, but changing it is not allowed.
+
+This GCC Runtime Library Exception ("Exception") is an additional permission
+under section 7 of the GNU General Public License, version 3 ("GPLv3"). It
+applies to a given file (the "Runtime Library") that bears a notice placed by
+the copyright holder of the file stating that the file is governed by GPLv3
+along with this Exception.
+
+When you use GCC to compile a program, GCC may combine portions of certain GCC
+header files and runtime libraries with the compiled program. The purpose of
+this Exception is to allow compilation of non-GPL (including proprietary)
+programs to use, in this way, the header files and runtime libraries covered by
+this Exception.
+
+0. Definitions. A file is an "Independent Module" if it either requires the
+Runtime Library for execution after a Compilation Process, or makes use of an
+interface provided by the Runtime Library, but is not otherwise based on the
+Runtime Library.
+
+"GCC" means a version of the GNU Compiler Collection, with or without
+modifications, governed by version 3 (or a specified later version) of the GNU
+General Public License (GPL) with the option of using any subsequent versions
+published by the FSF.
+
+"GPL-compatible Software" is software whose conditions of propagation,
+modification and use would permit combination with GCC in accord with the
+license of GCC.
+
+"Target Code" refers to output from any compiler for a real or virtual target
+processor architecture, in executable form or suitable for input to an
+assembler, loader, linker and/or execution phase. Notwithstanding that, Target
+Code does not include data in any format that is used as a compiler
+intermediate representation, or used for producing a compiler intermediate
+representation.
+
+The "Compilation Process" transforms code entirely represented in
+non-intermediate languages designed for human-written code, and/or in Java
+Virtual Machine byte code, into Target Code. Thus, for example, use of source
+code generators and preprocessors need not be considered part of the
+Compilation Process, since the Compilation Process can be understood as
+starting with the output of the generators or preprocessors.
+
+A Compilation Process is "Eligible" if it is done using GCC, alone or with
+other GPL-compatible software, or if it is done without using any work based on
+GCC. For example, using non-GPL-compatible Software to optimize any GCC
+intermediate representations would not qualify as an Eligible Compilation
+Process.
+
+1. Grant of Additional Permission. You have permission to propagate a work of
+Target Code formed by combining the Runtime Library with Independent Modules,
+even if such propagation would otherwise violate the terms of GPLv3, provided
+that all Target Code was generated by Eligible Compilation Processes. You may
+then convey such a combination under terms of your choice, consistent with the
+licensing of the Independent Modules.
+
+2. No Weakening of GCC Copyleft. The availability of this Exception does not
+imply any general presumption that third-party software is unaffected by the
+copyleft requirements of the license of GCC.
+
+--------------------------------------------------------------------------------
+ GNU General Public License, Version 3
+ applies to:
+ - GCC Runtime Library
+ Copyright (C) Free Software Foundation, Inc.
+ https://www.gnu.org/software/gcc/
+--------------------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program 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 3 of the License, or
+ (at your option) any later version.
+
+ 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 .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
+
+--------------------------------------------------------------------------------
+ GNU Lesser General Public License, Version 2.1
+ applies to:
+ - Game_Music_Emu
+ Shay Green
+ http://www.slack.net/~ant/
+
+ - libintl
+ Copyright (C) 1995-2018 Free Software Foundation, Inc.
+ https://www.gnu.org/software/gettext/
+
+ - mpg123
+ Copyright (c) 1995-2013 by Michael Hipp and others,
+ free software under the terms of the LGPL v2.1
+ https://www.mpg123.de
+--------------------------------------------------------------------------------
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+--------------------------------------------------------------------------------
+ libpng License
+ applies to:
+ - libpng
+ Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson
+ http://www.libpng.org/pub/png/libpng.html
+--------------------------------------------------------------------------------
+
+This copy of the libpng notices is provided for your convenience. In case of
+any discrepancy between this copy and the notices in the file png.h that is
+included in the libpng distribution, the latter shall prevail.
+
+COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+
+If you modify libpng you may insert additional notices immediately following
+this sentence.
+
+This code is released under the libpng license.
+
+libpng versions 1.0.7, July 1, 2000 through 1.6.35, July 15, 2018 are
+Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are
+derived from libpng-1.0.6, and are distributed according to the same
+disclaimer and license as libpng-1.0.6 with the following individuals
+added to the list of Contributing Authors:
+
+ Simon-Pierre Cadieux
+ Eric S. Raymond
+ Mans Rullgard
+ Cosmin Truta
+ Gilles Vollant
+ James Yu
+ Mandar Sahastrabuddhe
+ Google Inc.
+ Vadim Barkov
+
+and with the following additions to the disclaimer:
+
+ There is no warranty against interference with your enjoyment of the
+ library or against infringement. There is no warranty that our
+ efforts or the library will fulfill any of your particular purposes
+ or needs. This library is provided with all faults, and the entire
+ risk of satisfactory quality, performance, accuracy, and effort is with
+ the user.
+
+Some files in the "contrib" directory and some configure-generated
+files that are distributed with libpng have other copyright owners and
+are released under other open source licenses.
+
+libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from
+libpng-0.96, and are distributed according to the same disclaimer and
+license as libpng-0.96, with the following individuals added to the list
+of Contributing Authors:
+
+ Tom Lane
+ Glenn Randers-Pehrson
+ Willem van Schaik
+
+libpng versions 0.89, June 1996, through 0.96, May 1997, are
+Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,
+and are distributed according to the same disclaimer and license as
+libpng-0.88, with the following individuals added to the list of
+Contributing Authors:
+
+ John Bowler
+ Kevin Bracey
+ Sam Bushell
+ Magnus Holmgren
+ Greg Roelofs
+ Tom Tanner
+
+Some files in the "scripts" directory have other copyright owners
+but are released under this license.
+
+libpng versions 0.5, May 1995, through 0.88, January 1996, are
+Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
+
+For the purposes of this copyright and license, "Contributing Authors"
+is defined as the following set of individuals:
+
+ Andreas Dilger
+ Dave Martindale
+ Guy Eric Schalnat
+ Paul Schmidt
+ Tim Wegner
+
+The PNG Reference Library is supplied "AS IS". The Contributing Authors
+and Group 42, Inc. disclaim all warranties, expressed or implied,
+including, without limitation, the warranties of merchantability and of
+fitness for any purpose. The Contributing Authors and Group 42, Inc.
+assume no liability for direct, indirect, incidental, special, exemplary,
+or consequential damages, which may result from the use of the PNG
+Reference Library, even if advised of the possibility of such damage.
+
+Permission is hereby granted to use, copy, modify, and distribute this
+source code, or portions hereof, for any purpose, without fee, subject
+to the following restrictions:
+
+ 1. The origin of this source code must not be misrepresented.
+
+ 2. Altered versions must be plainly marked as such and must not
+ be misrepresented as being the original source.
+
+ 3. This Copyright notice may not be removed or altered from any
+ source or altered source distribution.
+
+The Contributing Authors and Group 42, Inc. specifically permit, without
+fee, and encourage the use of this source code as a component to
+supporting the PNG file format in commercial products. If you use this
+source code in a product, acknowledgment is not required but would be
+appreciated.
+
+END OF COPYRIGHT NOTICE, DISCLAIMER, and LICENSE.
+
+TRADEMARK:
+
+The name "libpng" has not been registered by the Copyright owner
+as a trademark in any jurisdiction. However, because libpng has
+been distributed and maintained world-wide, continually since 1995,
+the Copyright owner claims "common-law trademark protection" in any
+jurisdiction where common-law trademark is recognized.
+
+OSI CERTIFICATION:
+
+Libpng is OSI Certified Open Source Software. OSI Certified Open Source is
+a certification mark of the Open Source Initiative. OSI has not addressed
+the additional disclaimers inserted at version 1.0.7.
+
+EXPORT CONTROL:
+
+The Copyright owner believes that the Export Control Classification
+Number (ECCN) for libpng is EAR99, which means not subject to export
+controls or International Traffic in Arms Regulations (ITAR) because
+it is open source, publicly available software, that does not contain
+any encryption software. See the EAR, paragraphs 734.3(b)(3) and
+734.7(b).
+
+Glenn Randers-Pehrson
+glennrp at users.sourceforge.net
+July 15, 2018
+
+--------------------------------------------------------------------------------
+ New BSD License
+ applies to:
+ - FLAC
+ Copyright (C) 2000-2009 Josh Coalson
+ Copyright (C) 2011-2016 Xiph.Org Foundation
+ https://xiph.org/flac/api/
+
+ - Vorbis
+ Copyright (c) 2002-2008 Xiph.org Foundation
+ https://xiph.org/vorbis/
+
+ - Opus
+ Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic,
+ Jean-Marc Valin, Timothy B. Terriberry,
+ CSIRO, Gregory Maxwell, Mark Borgerding,
+ Erik de Castro Lopo
+ https://opus-codec.org
+
+ - Opus File
+ Copyright (c) 1994-2013 Xiph.Org Foundation and contributors
+ https://opus-codec.org
+--------------------------------------------------------------------------------
+
+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.
+
+[ For FLAC, Vorbis, and Opus File
+- Neither the name of the Xiph.org Foundation nor the names of its
+contributors may be used to endorse or promote products derived from
+this software without specific prior written permission.
+]
+
+[ For Opus
+- Neither the name of Internet Society, IETF or IETF Trust, nor the
+names of specific 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 FOUNDATION 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.
+
+--------------------------------------------------------------------------------
+ Public Domain
+ applies to:
+ - win_iconv
+ Yukihiro Nakadaira
+ win_iconv is placed in the public domain.
+ https://github.com/win-iconv/win-iconv
+
+ - libmodplug
+ ModPlug-XMMS and libmodplug are now in the public domain.
+ http://modplug-xmms.sourceforge.net
+--------------------------------------------------------------------------------
+
+--------------------------------------------------------------------------------
+ zlib License
+ applies to:
+ - Simple DirectMedia Layer
+ Copyright (C) 1997-2018 Sam Lantinga
+ https://www.libsdl.org/hg.php
+
+ - SDL_mixer: An audio mixer library based on the SDL library
+ Copyright (C) 1997-2018 Sam Lantinga
+ https://www.libsdl.org/projects/SDL_mixer/
+
+ - zlib
+ Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
+ https://zlib.net
+--------------------------------------------------------------------------------
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
diff --git a/assets/LICENSE.txt b/assets/LICENSE.txt
new file mode 100644
index 000000000..d159169d1
--- /dev/null
+++ b/assets/LICENSE.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program 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.
+
+ 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, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ , 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/assets/README.txt b/assets/README.txt
new file mode 100644
index 000000000..37f7d8f29
--- /dev/null
+++ b/assets/README.txt
@@ -0,0 +1,51 @@
+SONIC ROBO BLAST 2
+
+Sonic Robo Blast 2 (SRB2) is a 3D Sonic the Hedgehog fangame based on a
+modified version of Doom Legacy.
+
+LICENSE
+
+The source code for SRB2 is licensed under the GNU General Public
+License, Version 2. See LICENSE.txt for the full text of this license.
+
+SRB2 uses various third-party libraries, including SDL, SDL Mixer, and
+their dependencies. See LICENSE-3RD-PARTY.txt for the licenses of these
+libraries.
+
+SOURCE CODE
+
+You may obtain the source code for SRB2, including the source code for
+specific version releases, at the following web sites:
+
+STJr GitLab:
+https://git.magicalgirl.moe/STJr/SRB2
+
+GitHub:
+https://github.com/STJr/SRB2
+
+CONTACT
+
+You may contact Sonic Team Junior via the following web sites:
+
+SRB2.ORG:
+https://www.srb2.org
+
+SRB2 Message Board:
+https://mb.srb2.org
+
+SRB2 Official Discord:
+https://discord.gg/pYDXzpX
+
+COPYRIGHT AND DISCLAIMER
+
+Design and content on SRB2 is copyright 1998-2018 by Sonic Team Junior.
+All non-original material on SRB2.ORG is copyrighted by their
+respective owners, and no copyright infringement is intended. The owner
+of the SRB2.ORG domain is only acting as an ISP, and is therefore not
+responsible for any content on SRB2.ORG under the 1998 DMCA. This
+site, its webmaster, and its staff make no profit whatsoever (in fact,
+we lose money). Sonic Team Junior assumes no responsibility for the
+content on any Sonic Team Junior fan sites.
+
+Sonic Team Junior is in no way affiliated with SEGA or Sonic Team. We do
+not claim ownership of any of SEGA's intellectual property used in SRB2.
diff --git a/assets/debian/README.Debian b/assets/debian/README.Debian
index 4d9f067ac..68c952a4e 100644
--- a/assets/debian/README.Debian
+++ b/assets/debian/README.Debian
@@ -3,10 +3,45 @@ srb2 for Debian
SRB2 Debian package!
Hi there, to rebuild this package just use the SRB2 Makefile system, or, optionally, run
-dpkg-buildpackage in the in /bin/Resources directory. You can build these with or without a key
+dpkg-buildpackage in the in /assets directory. You can build these with or without a key
if you want, but if you want to put these on a repo, generate your own GnuPG key as per the
https://help.ubuntu.com/community/GnuPrivacyGuardHowto instructions and pass the -k
command to debuild. Make sure you export the key footprint and give them to your users to install
with apt-key add. Thanks!
-- Callum Dickinson Fri, 26 Nov 2010 18:25:31 +1300
+
+
+Signing for Launchpad PPA
+
+First, follow the above instructions to generate a GnuPG key with your identity. You will need
+to publish the fingerprint of that key to Ubuntu's key server.
+
+ https://help.ubuntu.com/community/GnuPrivacyGuardHowto#Uploading_the_key_to_Ubuntu_keyserver
+
+Next, you will have to add that key fingerprint to your Launchpad account. Go to your Launchpad
+profile and click the yellow Edit button next to "OpenPGP keys". Once you add the key, you can
+upload signed source packages and publish them onto your PPA.
+
+IF YOU UPLOAD A PACKAGE and Launchpad does NOT send you a confirmation or rejection email, that
+means your key is not set up correctly with your Launchpad account.
+
+
+Building for Launchpad PPA
+
+Use these steps to prepare building a source package for Launchpad:
+
+ 1. Highly recommend copying the assets/ folder to outside your repo folder, or else the asset
+ files may be included in the main source package, when you build that.
+ 2. cd [wherever-your-assets-folder-is]/assets/
+ 3. debuild -T clean (optional, if you already have asset files)
+
+Building the source package is a two-step process:
+
+ 1. debuild -T build (this downloads the asset files from srb2.org if necessary)
+ 2. debuild -S (builds the source package for Launchpad, including the asset files)
+
+Then follow the instructions at to upload
+to your PPA and have Launchpad build your binary deb packages.
+
+ -- Marco Zafra Mon, 26 Nov 2018 21:13:00 -0500
diff --git a/assets/debian/changelog b/assets/debian/changelog
index a316b7df7..f3a92e1cd 100644
--- a/assets/debian/changelog
+++ b/assets/debian/changelog
@@ -1,3 +1,10 @@
+srb2-data (2.1.21~7) trusty; urgency=high
+
+ * Updated for SRB2 v2.1.21
+
+ -- Marco Zafra Mon, 26 Nov 2018 14:31:00 -0500
+
+
srb2-data (2.1.14~1) unstable; urgency=low
* Updated for SRB2 v2.1.14
diff --git a/assets/debian/control b/assets/debian/control
index 123b58429..22d9643ee 100644
--- a/assets/debian/control
+++ b/assets/debian/control
@@ -3,8 +3,9 @@
Source: srb2-data
Section: games
Priority: extra
-Maintainer: Callum Dickinson
-Build-Depends: debhelper (>= 7.0.50~)
+Maintainer: Sonic Team Junior
+Build-Depends: debhelper (>= 7.0.50~),
+ wget
Standards-Version: 3.8.4
Homepage: http://www.srb2.org
@@ -15,8 +16,7 @@ Description: A cross-platform 3D Sonic fangame
fangame built using a modified version of the Doom Legacy
port of Doom. SRB2 is closely inspired by the original
Sonic games from the Sega Genesis, and attempts to recreate
- the design in 3D. While SRB2 isn't fully completed, it already
- features tons of levels, enemies, speed, and quite a lot
- of the fun that the original Sonic games provided.
+ the design in 3D. It features tons of levels, enemies, speed,
+ and quite a lot of the fun that the original Sonic games provided.
This is the data package that provides the data files that
- SRB2 requires to run, it will not work without it.
+ SRB2 requires to run; it will not work without it.
diff --git a/assets/debian/copyright b/assets/debian/copyright
index 8a8705190..97d606b0f 100644
--- a/assets/debian/copyright
+++ b/assets/debian/copyright
@@ -1,6 +1,6 @@
This work was packaged for Debian by:
- Callum Dickinson on Fri, 26 Nov 2010 15:19:16 +1300
+ Marco Zafra Mon, 26 Nov 2018 14:31:00 -0500
It was downloaded from:
@@ -12,7 +12,7 @@ Upstream Author(s):
Copyright:
- Copyright (C) 1998-2010 Sonic Team Junior
+ Copyright (C) 1998-2018 Sonic Team Junior
License:
@@ -21,6 +21,7 @@ License:
The Debian packaging is:
Copyright (C) 2010 Callum Dickinson
+ Copyright (C) 2010-2018 Sonic Team Junior
and is licensed under the GPL version 2,
see "/usr/share/common-licenses/GPL-2".
diff --git a/assets/debian/rules b/assets/debian/rules
index d86f92af2..a34a3393f 100755
--- a/assets/debian/rules
+++ b/assets/debian/rules
@@ -37,7 +37,7 @@ RM := rm -rf
DIR := $(shell pwd)
PACKAGE := $(shell cat $(DIR)/debian/control | grep 'Package:' | sed -e 's/Package: //g')
-DATAFILES := srb2.srb zones.dta player.dta rings.dta music.dta
+DATAFILES := srb2.srb zones.dta player.dta rings.dta music.dta patch.dta README.txt LICENSE.txt LICENSE-3RD-PARTY.txt
DATADIR := usr/games/SRB2
RESOURCEDIR := .
@@ -45,16 +45,21 @@ WGET := wget -P $(RESOURCEDIR) -c -nc
build:
$(MKDIR) $(DIR)/debian/tmp/$(DATADIR)
+ > $(DIR)/debian/source/include-binaries
# This will need to be updated every time SRB2 official version is
# Copy data files to their install locations, and add data files to include-binaries
for file in $(DATAFILES); do \
- $(WGET) http://alam.srb2.org/SRB2/2.1.14-Final/Resources/$$file; \
- if test "$$file" = "srb2.wad"; then \
- $(INSTALL) $(RESOURCEDIR)/$$file $(DIR)/debian/tmp/$(DATADIR)/srb2.srb; \
- else \
- $(INSTALL) $(RESOURCEDIR)/$$file $(DIR)/debian/tmp/$(DATADIR)/$$file; \
+ if [ ! -f $(RESOURCEDIR)/$$file ]; then \
+ $(WGET) http://alam.srb2.org/SRB2/2.1.21-Final/Resources/$$file; \
+ fi; \
+ if [ -f $(RESOURCEDIR)/$$file ]; then \
+ $(INSTALL) $(RESOURCEDIR)/$$file $(DIR)/debian/tmp/$(DATADIR)/$$file; \
+ echo $(RESOURCEDIR)/$$file >> $(DIR)/debian/source/include-binaries; \
+ fi; \
+ if [ ! -f $(DIR)/debian/tmp/$(DATADIR)/$$file ]; then \
+ echo $(DIR)/debian/tmp/$(DATADIR)/$$file not found and could not be downloaded!; \
+ return 1; \
fi; \
- echo $(RESOURCEDIR)/$$file >> $(DIR)/debian/source/include-binaries; \
done
binary-indep:
@@ -95,15 +100,18 @@ binary: binary-indep
dh_builddeb
clean:
- $(RM) $(RESOURCEDIR)/*.wad
- $(RM) $(RESOURCEDIR)/*.dta
- $(RM) $(RESOURCEDIR)/*.plr
- $(RM) $(RESOURCEDIR)/*.wpn
- $(RM) $(RESOURCEDIR)/*.srb
- $(RM) $(RESOURCEDIR)/*.dll
- $(RM) $(DIR)/debian/tmp/*
- $(RM) $(DIR)/debian/$(PACKAGE).install
- $(RM) $(DIR)/debian/files
- $(RM) $(DIR)/debian/source/include-binaries
+ $(RM) $(DIR)/debian/tmp/*; \
+ $(RM) $(DIR)/debian/$(PACKAGE).install; \
+ $(RM) $(DIR)/debian/files; \
+
+clean-all: clean
+ $(RM) $(RESOURCEDIR)/*.wad; \
+ $(RM) $(RESOURCEDIR)/*.dta; \
+ $(RM) $(RESOURCEDIR)/*.plr; \
+ $(RM) $(RESOURCEDIR)/*.wpn; \
+ $(RM) $(RESOURCEDIR)/*.srb; \
+ $(RM) $(RESOURCEDIR)/*.dll; \
+ $(RM) $(RESOURCEDIR)/*.txt; \
+ $(RM) $(DIR)/debian/source/include-binaries; \
.PHONY: all clean binary binary-arch binary-indep build
diff --git a/assets/debian/source/options b/assets/debian/source/options
new file mode 100644
index 000000000..8b331485a
--- /dev/null
+++ b/assets/debian/source/options
@@ -0,0 +1 @@
+tar-ignore = "tmp/*"
diff --git a/bin/Resources/exchndl.dll b/bin/Resources/exchndl.dll
deleted file mode 100644
index d836a6762..000000000
Binary files a/bin/Resources/exchndl.dll and /dev/null differ
diff --git a/bin/Resources/libgme.dll b/bin/Resources/libgme.dll
deleted file mode 100644
index ddf8b0d82..000000000
Binary files a/bin/Resources/libgme.dll and /dev/null differ
diff --git a/bin/Resources/libgme64.dll b/bin/Resources/libgme64.dll
deleted file mode 100644
index 2ba99450f..000000000
Binary files a/bin/Resources/libgme64.dll and /dev/null differ
diff --git a/cmake/Modules/GitUtilities.cmake b/cmake/Modules/GitUtilities.cmake
index 683cf9b6b..d29e6b509 100644
--- a/cmake/Modules/GitUtilities.cmake
+++ b/cmake/Modules/GitUtilities.cmake
@@ -27,5 +27,17 @@ function(git_current_branch variable path)
OUTPUT_STRIP_TRAILING_WHITESPACE
)
+ set(${variable} "${output}" PARENT_SCOPE)
+endfunction()
+
+function(git_latest_commit variable path)
+ execute_process(COMMAND ${GIT_EXECUTABLE} "rev-parse" "--short" "HEAD"
+ WORKING_DIRECTORY "${path}"
+ RESULT_VARIABLE result
+ OUTPUT_VARIABLE output
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ )
+
set(${variable} "${output}" PARENT_SCOPE)
endfunction()
\ No newline at end of file
diff --git a/debian/README.Debian b/debian/README.Debian
index bbc306b16..4b724816e 100644
--- a/debian/README.Debian
+++ b/debian/README.Debian
@@ -9,3 +9,38 @@ instructions and pass the -k command to debuild. Make sure you export the
and give them to your users to install with apt-key add. Thanks!
-- Callum Dickinson Fri, 26 Nov 2010 18:25:31 +1300
+
+
+Signing for Launchpad PPA
+
+First, follow the above instructions to generate a GnuPG key with your identity. You will need
+to publish the fingerprint of that key to Ubuntu's key server.
+
+ https://help.ubuntu.com/community/GnuPrivacyGuardHowto#Uploading_the_key_to_Ubuntu_keyserver
+
+Next, you will have to add that key fingerprint to your Launchpad account. Go to your Launchpad
+profile and click the yellow Edit button next to "OpenPGP keys". Once you add the key, you can
+upload signed source packages and publish them onto your PPA.
+
+IF YOU UPLOAD A PACKAGE and Launchpad does NOT send you a confirmation or rejection email, that
+means your key is not set up correctly with your Launchpad account.
+
+
+Building for Launchpad PPA
+
+Use these steps to prepare building a source package for Launchpad:
+
+ 1. cd [srb2repo]
+ 2. git reset --hard; git clean -fd; git clean -fx;
+ * Resets your repo folder to a committed state and removes untracked files
+ * If you built srb2-data in the assets/ folder, MAKE SURE THAT FOLDER DOES NOT HAVE ASSETS,
+ OR THEY MAY BE INCLUDED IN THE MAIN SOURCE PACKAGE!
+
+Building the source package takes just one step:
+
+ 1. debuild -S (builds the source package for Launchpad)
+
+Then follow the instructions at to upload
+to your PPA and have Launchpad build your binary deb packages.
+
+ -- Marco Zafra Mon, 26 Nov 2018 21:13:00 -0500
diff --git a/debian/README.source b/debian/README.source
index ff2dddd41..f63a42cac 100644
--- a/debian/README.source
+++ b/debian/README.source
@@ -22,6 +22,10 @@ Build instructions:
make -C src LINUX=1
+Build instructions for non-X86 devices (such as X64):
+
+make -C src LINUX=1 NONX86=1
+
Build instructions to build for Wii Linux/SRB2Wii on a PowerPC system,
follow cross-compiling instructions for cross-compiling on a x86 system:
diff --git a/debian/changelog b/debian/changelog
index b454b1abd..b06a78e2b 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+srb2 (2.1.23~9) trusty; urgency=high
+
+ * SRB2 v2.1.23 release
+
+ -- Marco Zafra Mon, 27 Nov 2018 16:45:00 -0500
+
+
srb2 (2.0.6-5) maverick; urgency=high
* Initial proper release..
diff --git a/debian/control b/debian/control
index 63b075f17..0f2d8062b 100644
--- a/debian/control
+++ b/debian/control
@@ -3,11 +3,13 @@
Source: srb2
Section: games
Priority: extra
-Maintainer: Callum Dickinson
+Maintainer: Sonic Team Junior
Build-Depends: debhelper (>= 7.0.50~),
libsdl2-dev,
libsdl2-mixer-dev,
- libpng12-dev (>= 1.2.7),
+ libpng12-dev (>= 1.2.7) | libpng-dev,
+ zlib1g-dev,
+ libgme-dev,
libglu1-dev | libglu-dev,
libosmesa6-dev | libgl-dev,
nasm [i386]
@@ -16,27 +18,26 @@ Homepage: http://www.srb2.org
Package: srb2
Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, srb2-data (= 2.1.14)
+Depends: ${shlibs:Depends}, ${misc:Depends}, srb2-data (>= 2.1.15), srb2-data (<= 2.1.23)
Description: A cross-platform 3D Sonic fangame
Sonic Robo Blast 2 is a 3D open-source Sonic the Hedgehog
fangame built using a modified version of the Doom Legacy
port of Doom. SRB2 is closely inspired by the original
Sonic games from the Sega Genesis, and attempts to recreate
- the design in 3D. While SRB2 isn't fully completed, it already
- features tons of levels, enemies, speed, and quite a lot
- of the fun that the original Sonic games provided.
+ the design in 3D. It features tons of levels, enemies, speed,
+ and quite a lot of the fun that the original Sonic games provided.
+
Package: srb2-dbg
Architecture: any
# FIXME: should be Depends: ${shlibs:Depends}, ${misc:Depends}, srb2-data (= 2.1.14), srb2 but dh_shlibdeps is being an asshat
-Depends: libc6, ${misc:Depends}, srb2-data (= 2.1.14), srb2
+Depends: libc6, ${misc:Depends}, srb2-data (>= 2.1.15), srb2-data (<= 2.1.23), srb2
Description: A cross-platform 3D Sonic fangame
Sonic Robo Blast 2 is a 3D open-source Sonic the Hedgehog
fangame built using a modified version of the Doom Legacy
port of Doom. SRB2 is closely inspired by the original
Sonic games from the Sega Genesis, and attempts to recreate
- the design in 3D. While SRB2 isn't fully completed, it already
- features tons of levels, enemies, speed, and quite a lot
- of the fun that the original Sonic games provided.
- This is a debug binary, its symbols will be loaded by gdb
+ the design in 3D. It features tons of levels, enemies, speed,
+ and quite a lot of the fun that the original Sonic games provided.
+ This is a debug binary; its symbols will be loaded by gdb
when the user starts the game with gdb for debugging.
diff --git a/debian/copyright b/debian/copyright
index 8a8705190..97d606b0f 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,6 +1,6 @@
This work was packaged for Debian by:
- Callum Dickinson on Fri, 26 Nov 2010 15:19:16 +1300
+ Marco Zafra Mon, 26 Nov 2018 14:31:00 -0500
It was downloaded from:
@@ -12,7 +12,7 @@ Upstream Author(s):
Copyright:
- Copyright (C) 1998-2010 Sonic Team Junior
+ Copyright (C) 1998-2018 Sonic Team Junior
License:
@@ -21,6 +21,7 @@ License:
The Debian packaging is:
Copyright (C) 2010 Callum Dickinson
+ Copyright (C) 2010-2018 Sonic Team Junior
and is licensed under the GPL version 2,
see "/usr/share/common-licenses/GPL-2".
diff --git a/debian/docs b/debian/docs
index b43bf86b5..dba2cd4c8 100644
--- a/debian/docs
+++ b/debian/docs
@@ -1 +1,4 @@
README.md
+assets/README.txt
+assets/LICENSE.txt
+assets/LICENSE-3RD-PARTY.txt
diff --git a/debian/rules b/debian/rules
index e49784a0f..ff80d50bf 100755
--- a/debian/rules
+++ b/debian/rules
@@ -57,21 +57,33 @@ SECTION = Games/Action
EXENAME = srb2
DBGNAME = debug/$(EXENAME)
-PKGDIR = usr/games
+PKGDIR = usr/games/SRB2
DBGDIR = usr/lib/debug/$(PKGDIR)
+LINKDIR = usr/games
PIXMAPS_DIR = usr/share/pixmaps
DESKTOP_DIR = usr/share/applications
PREFIX = $(shell test "$(CROSS_COMPILE_BUILD)" != "$(CROSS_COMPILE_HOST)" && echo "PREFIX=$(CROSS_COMPILE_HOST)")
OS = LINUX=1
NONX86 = $(shell test "`echo $(CROSS_COMPILE_HOST) | grep 'i[3-6]86'`" || echo "NONX86=1")
-MAKEARGS = $(OS) $(NONX86) $(PREFIX) EXENAME=$(EXENAME) DBGNAME=$(DBGNAME) SDL_PKGCONFIG=sdl2 PNG_PKGCONFIG=libpng NOOBJDUMP=1
+MAKEARGS = $(OS) $(NONX86) $(PREFIX) EXENAME=$(EXENAME) DBGNAME=$(DBGNAME) NOOBJDUMP=1 # SDL_PKGCONFIG=sdl2 PNG_PKGCONFIG=libpng
MENUFILE1 = ?package($(PACKAGE)):needs="X11" section="$(SECTION)"
MENUFILE2 = title="$(TITLE)" command="/$(PKGDIR)/$(PACKAGE)"
-# FIXME pkg-config dir hacks
-export PKG_CONFIG_LIBDIR = /usr/lib/$(CROSS_COMPILE_HOST)/pkgconfig
BINDIR := $(DIR)/bin/Linux/Release
+
+# FIXME pkg-config dir hacks
+# Launchpad doesn't need this; it actually makes i386 builds fail due to cross-compile
+# export PKG_CONFIG_LIBDIR = /usr/lib/$(CROSS_COMPILE_HOST)/pkgconfig
LDFLAGS += "-Wl,-rpath=/usr/lib/$(CROSS_COMPILE_HOST)"
+# Some libgme-dev packages don't use pkg-config yet, so include the linker flag ourselves
+PKG_CONFIG?=pkg-config
+LIBGME_PKGCONFIG?=libgme
+LIBGME_LDFLAGS?=$(shell $(PKG_CONFIG) $(LIBGME_PKGCONFIG) --libs)
+
+ifeq ($(LIBGME_LDFLAGS),)
+MAKEARGS += LIBGME_LDFLAGS=-lgme
+endif
+
build:
$(MKDIR) $(BINDIR)/debug
$(MAKE) -C $(DIR)/src $(MAKEARGS)
@@ -100,8 +112,8 @@ binary-arch:
echo $(DESKTOP_DIR) >> $(DIR)/debian/$(PACKAGE).install
echo $(PIXMAPS_DIR) >> $(DIR)/debian/$(PACKAGE).install
echo $(DBGDIR) > $(DIR)/debian/$(DBGPKG).install
-
-binary: binary-arch
+# Launchpad only calls binary-arch, so just move everything up
+#binary: binary-arch
# Generate .desktop specifications
echo "`echo '$(MENUFILE1)\\'`" > $(DIR)/debian/menu
echo " `echo '$(MENUFILE2)'`" >> $(DIR)/debian/menu
@@ -122,7 +134,7 @@ binary: binary-arch
# dh_installcron
# dh_installinfo
# dh_installman
- # dh_link
+ dh_link $(PKGDIR)/$(EXENAME) $(LINKDIR)/$(EXENAME)
dh_compress
dh_fixperms
# dh_perl
@@ -133,6 +145,8 @@ binary: binary-arch
dh_md5sums
dh_builddeb
+binary: binary-arch
+
clean:
$(MAKE) -C $(DIR)/src $(MAKEARGS) clean cleandep
$(RM) $(BINDIR)/*
@@ -145,4 +159,4 @@ clean:
$(RM) $(DIR)/debian/files
$(RM) $(DIR)/debian/source/include-binaries
-.PHONY: all clean binary binary-arch binary-indep build
+.PHONY: all clean binary binary-indep build
diff --git a/debian/source/options b/debian/source/options
new file mode 100644
index 000000000..841c65a6f
--- /dev/null
+++ b/debian/source/options
@@ -0,0 +1,10 @@
+tar-ignore = "assets/*.srb"
+tar-ignore = "assets/*.pk3"
+tar-ignore = "assets/*.dta"
+tar-ignore = "assets/*.wad"
+tar-ignore = "assets/debian/srb2-data/*"
+tar-ignore = "assets/debian/tmp/*"
+tar-ignore = "*.obj"
+tar-ignore = "*.dep"
+tar-ignore = ".git/*"
+tar-ignore = ".git*"
diff --git a/debian/srb2.desktop b/debian/srb2.desktop
index 661832b93..3a1cac9f6 100644
--- a/debian/srb2.desktop
+++ b/debian/srb2.desktop
@@ -1,8 +1,8 @@
[Desktop Entry]
Name=Sonic Robo Blast 2
-Comment=A free 3D Sonic the Hedgehog fan-game built using a modified ver. of the Doom Legacy source port
+Comment=A free 3D Sonic the Hedgehog fangame closely inspired by the original Sonic games on the Sega Genesis.
Encoding=UTF-8
-Exec=srb2
+Exec=/usr/games/SRB2/srb2
Icon=/usr/share/pixmaps/srb2.png
Terminal=false
Type=Application
diff --git a/deployer/appveyor/deployer.bat b/deployer/appveyor/deployer.bat
new file mode 100644
index 000000000..fae388590
--- /dev/null
+++ b/deployer/appveyor/deployer.bat
@@ -0,0 +1,195 @@
+@setlocal enableextensions enabledelayedexpansion
+
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+: Appveyor Deployer
+: See appveyor.yml for default variables
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+: Evaluate whether we should be deploying
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+if not [%DPL_ENABLED%] == [1] (
+ echo Deployer is not enabled...
+ exit /b
+)
+
+: Don't do DD installs because fmodex DLL handling is not implemented.
+if [%CONFIGURATION%] == [DD] (
+ echo Deployer does not support DD builds...
+ exit /b
+)
+
+if [%CONFIGURATION%] == [DD64] (
+ echo Deployer does not support DD builds...
+ exit /b
+)
+
+: Substring match from https://stackoverflow.com/questions/7005951/batch-file-find-if-substring-is-in-string-not-in-a-file
+: The below line says "if deployer is NOT in string"
+: Note that APPVEYOR_REPO_BRANCH for pull request builds is the BASE branch that PR is merging INTO
+if x%APPVEYOR_REPO_BRANCH:deployer=%==x%APPVEYOR_REPO_BRANCH% (
+ if not [%APPVEYOR_REPO_TAG%] == [true] (
+ echo Deployer is enabled but we are not in a release tag or a 'deployer' branch...
+ exit /b
+ ) else (
+ if not [%DPL_TAG_ENABLED%] == [1] (
+ echo Deployer is not enabled for release tags...
+ exit /b
+ )
+ )
+)
+
+: Release tags always get optional assets (music.dta)
+if [%APPVEYOR_REPO_TAG%] == [true] (
+ set "ASSET_FILES_OPTIONAL_GET=1"
+)
+
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+: Get asset archives
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+if exist "C:\Users\appveyor\srb2_cache\archives\" (
+ if [%ASSET_CLEAN%] == [1] (
+ echo Cleaning asset archives...
+ rmdir /s /q "C:\Users\appveyor\srb2_cache\archives"
+ )
+)
+
+if not exist "C:\Users\appveyor\srb2_cache\archives\" mkdir "C:\Users\appveyor\srb2_cache\archives"
+
+goto EXTRACT_ARCHIVES
+
+::::::::::::::::::::::::::::::::
+: ARCHIVE_NAME_PARTS
+: Call this like a function. %archivepath% is the path to extract parts from.
+::::::::::::::::::::::::::::::::
+
+for %%a in (%archivepath%) do (
+ set "file=%%~fa"
+ set "filepath=%%~dpa"
+ set "filename=%%~nxa"
+)
+
+set "localarchivepath=C:\Users\appveyor\srb2_cache\archives\%filename%"
+
+goto EOF
+
+::::::::::::::::::::::::::::::::
+: EXTRACT_ARCHIVES
+::::::::::::::::::::::::::::::::
+
+set "archivepath=%ASSET_ARCHIVE_PATH%"
+call :ARCHIVE_NAME_PARTS
+set "ASSET_ARCHIVE_PATH_LOCAL=%localarchivepath%"
+if not exist "%localarchivepath%" appveyor DownloadFile "%ASSET_ARCHIVE_PATH%" -FileName "%localarchivepath%"
+
+set "archivepath=%ASSET_ARCHIVE_PATCH_PATH%"
+call :ARCHIVE_NAME_PARTS
+set "ASSET_ARCHIVE_PATCH_PATH_LOCAL=%localarchivepath%"
+if not exist "%localarchivepath%" appveyor DownloadFile "%ASSET_ARCHIVE_PATCH_PATH%" -FileName "%localarchivepath%"
+
+if not [%X86_64%] == [1] (
+ set "archivepath=%ASSET_ARCHIVE_X86_PATH%"
+ call :ARCHIVE_NAME_PARTS
+ set "ASSET_ARCHIVE_X86_PATH_LOCAL=!localarchivepath!"
+ if not exist "!localarchivepath!" appveyor DownloadFile "%ASSET_ARCHIVE_X86_PATH%" -FileName "!localarchivepath!"
+)
+
+if [%X86_64%] == [1] (
+ set "archivepath=%ASSET_ARCHIVE_X64_PATH%"
+ call :ARCHIVE_NAME_PARTS
+ set "ASSET_ARCHIVE_X64_PATH_LOCAL=!localarchivepath!"
+ if not exist "!localarchivepath!" appveyor DownloadFile "%ASSET_ARCHIVE_X64_PATH%" -FileName "!localarchivepath!"
+)
+
+if [%ASSET_FILES_OPTIONAL_GET%] == [1] (
+ set "archivepath=%ASSET_ARCHIVE_OPTIONAL_PATH%"
+ call :ARCHIVE_NAME_PARTS
+ set "ASSET_ARCHIVE_OPTIONAL_PATH_LOCAL=!localarchivepath!"
+ if not exist "!localarchivepath!" appveyor DownloadFile "%ASSET_ARCHIVE_OPTIONAL_PATH%" -FileName "!localarchivepath!"
+)
+
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+: Build the installers
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+mkdir "assets\installer"
+mkdir "assets\patch"
+
+7z x -y "%ASSET_ARCHIVE_PATH_LOCAL%" -o"assets\installer" >null
+7z x -y "%ASSET_ARCHIVE_PATCH_PATH_LOCAL%" -o"assets\patch" >null
+
+: Copy optional files to full installer (music.dta)
+if [%ASSET_FILES_OPTIONAL_GET%] == [1] (
+ 7z x -y "%ASSET_ARCHIVE_OPTIONAL_PATH_LOCAL%" -o"assets\installer" >null
+)
+
+: Copy EXE -- BUILD_PATH is from appveyor.yml
+robocopy /S /ns /nc /nfl /ndl /np /njh /njs "%BUILD_PATH%" "assets\installer" /XF "*.debug" ".gitignore"
+robocopy /S /ns /nc /nfl /ndl /np /njh /njs "%BUILD_PATH%" "assets\patch" /XF "*.debug" ".gitignore"
+
+: Are we building DD? (we were supposed to exit earlier!)
+if [%CONFIGURATION%] == [DD] ( set "DPL_INSTALLER_NAME=%DPL_INSTALLER_NAME%-DD" )
+if [%CONFIGURATION%] == [DD64] ( set "DPL_INSTALLER_NAME=%DPL_INSTALLER_NAME%-DD" )
+
+: If we are not a release tag, suffix the filename
+if not [%APPVEYOR_REPO_TAG%] == [true] (
+ set "INSTALLER_SUFFIX=-%APPVEYOR_REPO_BRANCH%-%GITSHORT%-%CONFIGURATION%"
+) else (
+ set "INSTALLER_SUFFIX="
+)
+
+if not [%X86_64%] == [1] ( goto X86_INSTALL )
+
+::::::::::::::::::::::::::::::::
+: X64_INSTALL
+::::::::::::::::::::::::::::::::
+
+: Extract DLL binaries
+7z x -y "%ASSET_ARCHIVE_X64_PATH_LOCAL%" -o"assets\installer" >null
+if [%PACKAGE_PATCH_DLL_GET%] == [1] (
+ 7z x -y "!ASSET_ARCHIVE_X64_PATH_LOCAL!" -o"assets\patch" >null
+)
+
+: Build the installer
+7z a -sfx7z.sfx "%DPL_INSTALLER_NAME%-x64-Installer%INSTALLER_SUFFIX%.exe" .\assets\installer\*
+
+: Build the patch
+7z a "%DPL_INSTALLER_NAME%-x64-Patch%INSTALLER_SUFFIX%.zip" .\assets\patch\*
+
+: Upload artifacts
+appveyor PushArtifact "%DPL_INSTALLER_NAME%-x64-Installer%INSTALLER_SUFFIX%.exe"
+appveyor PushArtifact "%DPL_INSTALLER_NAME%-x64-Patch%INSTALLER_SUFFIX%.zip"
+
+: We only do x86 OR x64, one at a time, so exit now.
+goto EOF
+
+::::::::::::::::::::::::::::::::
+: X86_INSTALL
+::::::::::::::::::::::::::::::::
+
+: Extract DLL binaries
+7z x -y "%ASSET_ARCHIVE_X86_PATH_LOCAL%" -o"assets\installer" >null
+if [%PACKAGE_PATCH_DLL_GET%] == [1] (
+ 7z x -y "!ASSET_ARCHIVE_X86_PATH_LOCAL!" -o"assets\patch" >null
+)
+
+: Build the installer
+7z a -sfx7z.sfx "%DPL_INSTALLER_NAME%-Installer%INSTALLER_SUFFIX%.exe" .\assets\installer\*
+
+: Build the patch
+7z a "%DPL_INSTALLER_NAME%-Patch%INSTALLER_SUFFIX%.zip" .\assets\patch\*
+
+: Upload artifacts
+appveyor PushArtifact "%DPL_INSTALLER_NAME%-Installer%INSTALLER_SUFFIX%.exe"
+appveyor PushArtifact "%DPL_INSTALLER_NAME%-Patch%INSTALLER_SUFFIX%.zip"
+
+: We only do x86 OR x64, one at a time, so exit now
+goto EOF
+
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+: EOF
+::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
+
+endlocal
diff --git a/libs/DLL-README.txt b/libs/DLL-README.txt
new file mode 100644
index 000000000..058ec0685
--- /dev/null
+++ b/libs/DLL-README.txt
@@ -0,0 +1,43 @@
+# SRB2 - Which DLLs do I need to bundle?
+
+Updated 12/4/2018 (v2.1.21)
+
+Here are the required DLLs, per build. For each architecture, copy all the binaries from these folders:
+
+* libs\dll-binaries\[i686/x86_64]
+* libs\SDL2\[i686/x86_64]...\bin
+* libs\SDL2_mixer\[i686/x86_64]...\bin
+
+and don't forget to build r_opengl.dll for srb2dd.
+
+## srb2win, 32-bit
+
+* libs\dll-binaries\i686\exchndl.dll
+* libs\dll-binaries\i686\libgme.dll
+* libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll)
+* libs\SDL2\i686-w64-mingw32\bin\SDL2.dll
+* libs\SDL2_mixer\i686-w64-mingw32\bin\*.dll (get everything)
+
+## srb2win, 64-bit
+
+* libs\dll-binaries\x86_64\exchndl.dll
+* libs\dll-binaries\x86_64\libgme.dll
+* libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll)
+* libs\SDL2\x86_64-w64-mingw32\bin\SDL2.dll
+* libs\SDL2_mixer\x86_64-w64-mingw32\bin\*.dll (get everything)
+
+## srb2dd, 32-bit
+
+* libs\dll-binaries\i686\exchndl.dll
+* libs\dll-binaries\i686\fmodex.dll
+* libs\dll-binaries\i686\libgme.dll
+* libs\dll-binaries\i686\mgwhelp.dll (depend for exchndl.dll)
+* r_opengl.dll (build this from make)
+
+## srb2dd, 64-bit
+
+* libs\dll-binaries\x86_64\exchndl.dll
+* libs\dll-binaries\x86_64\fmodex.dll
+* libs\dll-binaries\x86_64\libgme.dll
+* libs\dll-binaries\x86_64\mgwhelp.dll (depend for exchndl.dll)
+* r_opengl.dll (build this from make)
diff --git a/libs/SDL2/lib/ARM/SDL2.lib b/libs/SDL2/lib/ARM/SDL2.lib
new file mode 100644
index 000000000..be9d86949
Binary files /dev/null and b/libs/SDL2/lib/ARM/SDL2.lib differ
diff --git a/libs/SDL2/lib/ARM64/SDL2.dll b/libs/SDL2/lib/ARM64/SDL2.dll
new file mode 100644
index 000000000..f9eac1c0b
Binary files /dev/null and b/libs/SDL2/lib/ARM64/SDL2.dll differ
diff --git a/libs/SDL2/lib/ARM64/SDL2.lib b/libs/SDL2/lib/ARM64/SDL2.lib
new file mode 100644
index 000000000..40904c308
Binary files /dev/null and b/libs/SDL2/lib/ARM64/SDL2.lib differ
diff --git a/libs/SDL2_mixer/lib/ARM/SDL2_mixer.lib b/libs/SDL2_mixer/lib/ARM/SDL2_mixer.lib
new file mode 100644
index 000000000..3886f3aa9
Binary files /dev/null and b/libs/SDL2_mixer/lib/ARM/SDL2_mixer.lib differ
diff --git a/libs/SDL2_mixer/lib/ARM64/SDL2_mixer.dll b/libs/SDL2_mixer/lib/ARM64/SDL2_mixer.dll
new file mode 100644
index 000000000..d5650b0d2
Binary files /dev/null and b/libs/SDL2_mixer/lib/ARM64/SDL2_mixer.dll differ
diff --git a/libs/SDL2_mixer/lib/ARM64/SDL2_mixer.lib b/libs/SDL2_mixer/lib/ARM64/SDL2_mixer.lib
new file mode 100644
index 000000000..58c3e6966
Binary files /dev/null and b/libs/SDL2_mixer/lib/ARM64/SDL2_mixer.lib differ
diff --git a/bin/Resources/fmod.dll b/libs/dll-binaries/i686/Old/fmod.dll
similarity index 100%
rename from bin/Resources/fmod.dll
rename to libs/dll-binaries/i686/Old/fmod.dll
diff --git a/bin/Resources/fmodexL.dll b/libs/dll-binaries/i686/Old/fmodexL.dll
similarity index 100%
rename from bin/Resources/fmodexL.dll
rename to libs/dll-binaries/i686/Old/fmodexL.dll
diff --git a/bin/Resources/libgcc_s_dw2-1.dll b/libs/dll-binaries/i686/Old/libgcc_s_dw2-1.dll
similarity index 100%
rename from bin/Resources/libgcc_s_dw2-1.dll
rename to libs/dll-binaries/i686/Old/libgcc_s_dw2-1.dll
diff --git a/bin/Resources/libintl-8.dll b/libs/dll-binaries/i686/Old/libintl-8.dll
similarity index 100%
rename from bin/Resources/libintl-8.dll
rename to libs/dll-binaries/i686/Old/libintl-8.dll
diff --git a/libs/dll-binaries/i686/exchndl.dll b/libs/dll-binaries/i686/exchndl.dll
new file mode 100644
index 000000000..d6beb764a
Binary files /dev/null and b/libs/dll-binaries/i686/exchndl.dll differ
diff --git a/bin/Resources/fmodex.dll b/libs/dll-binaries/i686/fmodex.dll
similarity index 100%
rename from bin/Resources/fmodex.dll
rename to libs/dll-binaries/i686/fmodex.dll
diff --git a/libs/dll-binaries/i686/libgme.dll b/libs/dll-binaries/i686/libgme.dll
new file mode 100644
index 000000000..9a31bc4d2
Binary files /dev/null and b/libs/dll-binaries/i686/libgme.dll differ
diff --git a/libs/dll-binaries/i686/mgwhelp.dll b/libs/dll-binaries/i686/mgwhelp.dll
new file mode 100644
index 000000000..3cf97424d
Binary files /dev/null and b/libs/dll-binaries/i686/mgwhelp.dll differ
diff --git a/bin/Resources/fmod64.dll b/libs/dll-binaries/x86_64/Old/fmod64.dll
similarity index 100%
rename from bin/Resources/fmod64.dll
rename to libs/dll-binaries/x86_64/Old/fmod64.dll
diff --git a/bin/Resources/fmodexL64.dll b/libs/dll-binaries/x86_64/Old/fmodexL64.dll
similarity index 100%
rename from bin/Resources/fmodexL64.dll
rename to libs/dll-binaries/x86_64/Old/fmodexL64.dll
diff --git a/libs/dll-binaries/x86_64/exchndl.dll b/libs/dll-binaries/x86_64/exchndl.dll
new file mode 100644
index 000000000..747d7a3d5
Binary files /dev/null and b/libs/dll-binaries/x86_64/exchndl.dll differ
diff --git a/bin/Resources/fmodex64.dll b/libs/dll-binaries/x86_64/fmodex64.dll
similarity index 100%
rename from bin/Resources/fmodex64.dll
rename to libs/dll-binaries/x86_64/fmodex64.dll
diff --git a/libs/dll-binaries/x86_64/libgme.dll b/libs/dll-binaries/x86_64/libgme.dll
new file mode 100644
index 000000000..598c2d71c
Binary files /dev/null and b/libs/dll-binaries/x86_64/libgme.dll differ
diff --git a/libs/dll-binaries/x86_64/mgwhelp.dll b/libs/dll-binaries/x86_64/mgwhelp.dll
new file mode 100644
index 000000000..4e30e140e
Binary files /dev/null and b/libs/dll-binaries/x86_64/mgwhelp.dll differ
diff --git a/libs/gme/CMakeLists.txt b/libs/gme/CMakeLists.txt
index 8beee872f..392b01856 100644
--- a/libs/gme/CMakeLists.txt
+++ b/libs/gme/CMakeLists.txt
@@ -4,7 +4,7 @@ project(libgme)
include (CheckCXXCompilerFlag)
# When version is changed, also change the one in gme/gme.h to match
-set(GME_VERSION 0.6.0 CACHE INTERNAL "libgme Version")
+set(GME_VERSION 0.6.2 CACHE INTERNAL "libgme Version")
# 2.6+ always assumes FATAL_ERROR, but 2.4 and below don't.
# Of course, 2.4 might work, in which case you're welcome to drop
@@ -57,6 +57,8 @@ if (USE_GME_NSFE AND NOT USE_GME_NSF)
SET(USE_GME_NSF 1 CACHE BOOL "Enable NES NSF music emulation" FORCE)
endif()
+option(BUILD_SHARED_LIBS "Build shared library (set to OFF for static library)" ON)
+
# Check for GCC "visibility" support.
if (CMAKE_COMPILER_IS_GNUCXX)
check_cxx_compiler_flag (-fvisibility=hidden __LIBGME_TEST_VISIBILITY)
@@ -79,10 +81,10 @@ if (CMAKE_COMPILER_IS_GNUCXX)
endif()
endif()
endif() # test visibility
-endif (CMAKE_COMPILER_IS_GNUCXX)
-# Cache this result
-set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility")
+ # Cache this result
+ set( LIBGME_HAVE_GCC_VISIBILITY ${ENABLE_VISIBILITY} CACHE BOOL "GCC support for hidden visibility")
+endif (CMAKE_COMPILER_IS_GNUCXX)
# Shared library defined here
add_subdirectory(gme)
diff --git a/libs/gme/changes.txt b/libs/gme/changes.txt
index 62391ebb5..034ba4821 100644
--- a/libs/gme/changes.txt
+++ b/libs/gme/changes.txt
@@ -1,262 +1,5 @@
Game_Music_Emu Change Log
-------------------------
-Game_Music_Emu 0.6.0
---------------------
-
-- Note: A 0.5.6 release was referenced but never tagged or packaged.
-
-- SPC improvements:
- - Switched to newer snes_spc 0.9.0 for SPC emulation. Uses fast DSP.
- - Fixed Spc_Emu::gain().
- - Fixed support for files <0x10200 bytes.
-
-- Other bugfixes:
- - Fixed a couple of GBS bugs, one involving access of memory after
- realloc.
- - Blip_Buffer works on systems where 'double' is a single-precision
- floating-point type.
- - Fix uninitialized buffer size in dual_resampler.
- - Compilation warnings squashed out as of clang 3.3-pre and gcc 4.7.2.
-
-- API changes/additions:
- - Removed documentation of C++ interface, as the C interface in gme.h is
- the only supported one.
- - Added gme_enable_accuracy() for enabling more accurate sound emulation
- options (currently affects SPC only).
-
-- Build system improvements:
- - Add pkg_config support.
- - Fix build on case-insensitive systems.
- - Allow for install on Cygwin.
- - Fix install on multilib systems, such as many 64-bit distros (CMake must
- be able to figure out your system's libsuffix, if any).
- - C++ implementation symbols are not leaked into the resultant library
- file (requires symbol visibility support).
-
-- Sample player improvements:
- - Can toggle fast/accurate emulation (with the 'A' key).
-
-Game_Music_Emu 0.5.5
---------------------
-- CMake build support has been added. You can build Game_Music_Emu as
-a shared library and install it so that you do not have to include your
-own copy if you know libgme will be present on your target system.
-Requires CMake 2.6 or higher.
-
-
-Game_Music_Emu 0.5.2
---------------------
-- *TONS* of changes and improvements. You should re-read the new header
-files and documentation as the changes will allow you to simplify your
-code a lot (it might even be simpler to just rewrite it). Existing code
-should continue to work without changes in most cases (see Deprecated
-features in gme.txt).
-
-- New file formats: AY, HES, KSS, SAP, NSFE
-
-- All-new comprehensive C interface (also usable from C++). Simplifies
-many things, especially file loading, and brings everything together in
-one header file (gme.h).
-
-- Information tags and track names and times can be accessed for all
-game music formats
-
-- New features supported by all emulators: end of track fading,
-automatic silence detection, adjustable song tempo, seek to new time in
-track
-
-- Updated mini player example to support track names and times, echo,
-tempo, and channel muting, and added visual waveform display
-
-- Improved configuration to use blargg_config.h, which you can modify
-and keep when you update to a newer libary version. Includes flag for
-library to automatically handle gzipped files using zlib (so you don't
-need to use Gzip_File_Reader anymore).
-
-- GBS: Fixed wave channel to not reset waveform when APU is powered off
-(affected Garfield). Also improved invalid bank selection (affected Game
-& Watch and others).
-
-- VGM: Added support for alternate noise shifter register
-configurations, used by other systems like the BBC Micro.
-
-- SPC: Removed IPL ROM dump from emulator, as none of the SPC files I
-scanned needed it, and an SPC file can include a copy if necessary. Also
-re-enabled supposed clamping in gaussian interpolation between the third
-and fourth lookups, though I don't know whether it matters
-
-- Added Music_Emu::load_mem() to use music data already in memory
-(without copying it)
-
-- Added Music_Emu::warning(), which reports minor problems when loading
-and playing a music file
-
-- Added Music_Emu::set_gain() for uniform adjustment of gain. Can only
-be set during initialization, so not useful as a general volume control.
-
-- Added custom operator new to ensure that no exceptions are thrown in
-the library (I'd use std::nothrow if it were part of pre-ISO (ARM) C++)
-
-- Added BLIP_BUFFER_FAST flag to blargg_config.h to use a lower quality
-bandlimited synthesis in "classic" emulators, which might help
-performance on ancient processors (measure first!). Don't use this
-unless absolutely necessary, as quality suffers.
-
-- Improved performance a bit for x86 platforms
-
-- Text files now in DOS newline format so they will open in Notepad
-properly
-
-- Removed requirement that file header structures not have any padding
-added to the end
-
-- Fixed common bug in all CPU emulators where negative program counter
-could crash emulator (occurred during a negative branch from the
-beginning of memory). Also fixed related bug in Z80 emulator for
-IX/IY+displacement mode.
-
-- Eliminated all warnings when compiling on gcc 4.0. The following
-generates no diagnostics:
-
- gcc -S gme/*.cpp -o /dev/null -ansi -fno-gnu-keywords
- -fno-nonansi-builtins -pedantic -W -Wabi -Wall -Wcast-align
- -Wcast-qual -Wchar-subscripts -Wdisabled-optimization -Werror
- -Winline -Wlong-long -Wmultichar -Winvalid-offsetof
- -Wnon-virtual-dtor -Woverloaded-virtual -Wparentheses
- -Wpointer-arith -Wredundant-decls -Wreorder -Wsign-compare
- -Wsign-promo -Wunknown-pragmas -Wwrite-strings
-
-
-Game_Music_Emu 0.3.0
---------------------
-- Added more demos, including music player using the SDL multimedia
-library for sound, and improved documentation
-
-- All: Improved interface to emulators to allow simpler setup and
-loading. Instead of various init() functions, all now support
-set_sample_rate( long rate ) and load( const char* file_path ).
-
-- All: Removed error return from start_track() and play(), and added
-error_count() to get the total number of emulation errors since the
-track was last started. See demos for examples of new usage.
-
-- All: Fixed mute_voices() muting to be preserved after loading files
-and starting tracks, instead of being cleared as it was whenever a track
-was started
-
-- VGM: Rewrote Vgm_Emu to support Sega Genesis/Mega Drive FM sound at
-any sample rate with optional FM oversampling, support for alternate
-YM2612 sound cores, and support for optional YM2413
-
-- VGM: Added tempo control, useful for slowing 60Hz NTSC Sega Genesis
-music to 50Hz PAL
-
-- VGM: Removed Vgm_Emu::track_data(), since I realized that this
-information is already present in the VGM header (oops!)
-
-- GYM: Changed Gym_Emu::track_length() operation (see Gym_Emu.h)
-
-- NSF: Added support for Sunsoft FME-7 sound chip used by Gimmick
-soundtrack
-
-- NSF: Fixed Namco 106 problems with Final Lap and others
-
-- Moved library sources to gme/ directory to reduce clutter, and merged
-boost/ functionality into blargg_common.h
-
-- Added Gzip_File_Reader for transparently using gzipped files
-
-
-Game_Music_Emu 0.2.4
---------------------
-- Created a discussion forum for problems and feedback:
-http://groups-beta.google.com/group/blargg-sound-libs
-
-- Changed error return value of Blip_Buffer::sample_rate() (also for
-Stereo_Buffer, Effects_Buffer, etc.) to blargg_err_t (defined in
-blargg_common.h), to make error reporting consistent with other
-functions. This means the "no error" return value is the opposite of
-what it was before, which will break current code which checks the error
-return value:
-
- // current code (broken)
- if ( !buf.sample_rate( samples_per_sec ) )
- out_of_memory();
-
- // quick-and-dirty fix (just remove the ! operation)
- if ( buf.sample_rate( samples_per_sec ) )
- out_of_memory();
-
- // proper fix
- blargg_err_t error = buf.sample_rate( samples_per_sec );
- if ( error )
- report_error( error );
-
-- Implemented workaround for MSVC++ 6 compiler limitations, allowing it
-to work on that compiler again
-
-- Added sample clamping to avoid wrap-around at high volumes, allowing
-higher volume with little distortion
-
-- Added to-do list and design notes
-
-- Added Music_Emu::skip( long sample_count ) to skip ahead in current
-track
-
-- Added Gym_Emu::track_length() and Vgm_Emu::track_length() for
-determining the length of non-looped GYM and VGM files
-
-- Partially implemented DMC non-linearity when its value is directly set
-using $4011, which reduces previously over-emphasized "popping" of
-percussion on some games (TMNT II in particular)
-
-- Fixed Fir_Resampler, used for SPC and GYM playback (was incorrectly
-using abs() instead of fabs()...argh)
-
-- Fixed SPC emulation bugs: eliminated clicks in Plok! soundtrack and
-now stops sample slightly earlier than the end, as the SNES does. Fixed
-a totally broken CPU addressing mode.
-
-- Fixed Konami VRC6 saw wave (was very broken before). Now VRC6 music
-sounds decent
-
-- Fixed a minor GBS emulation bug
-
-- Fixed GYM loop point bug when track was restarted before loop point
-had been reached
-
-- Made default GBS frequency equalization less muffled
-
-- Added pseudo-surround effect removal for SPC files
-
-- Added Music_Emu::voice_names() which returns names for each voice.
-
-- Added BLARGG_SOURCE_BEGIN which allows custom compiler options to be
-easily set for library sources
-
-- Changed assignment of expansion sound chips in Nsf_Emu to be spread
-more evenly when using Effects_Buffer
-
-- Changed 'size_t' values in Blip_Buffer interface to 'long'
-
-- Changed demo to generate a WAVE sound file rather than an AIFF file
-
-
-Game_Music_Emu 0.2.0
---------------------
-- Redid framework and rewrote/cleaned up emulators
-
-- Changed licensing to GNU Lesser General Public License (LGPL)
-
-- Added Sega Genesis GYM and Super Nintendo SPC emulators
-
-- Added Namco-106 and Konami VRC6 sound chip support to NSF emulator
-
-- Eliminated use of static mutable data in emulators, allowing
-multi-instance safety
-
-
-Game_Music_Emu 0.1.0
---------------------
-- First release
+Please see the git version history (e.g. git shortlog tags/0.6.0..tags/0.6.1)
+for the accurate change log.
diff --git a/libs/gme/demo/basics.c b/libs/gme/demo/basics.c
index 551782518..741574afe 100644
--- a/libs/gme/demo/basics.c
+++ b/libs/gme/demo/basics.c
@@ -1,7 +1,5 @@
/* C example that opens a game music file and records 10 seconds to "out.wav" */
-static char filename [] = "test.nsf"; /* opens this file (can be any music type) */
-
#include "gme/gme.h"
#include "Wave_Writer.h" /* wave_ functions for writing sound file */
@@ -10,10 +8,15 @@ static char filename [] = "test.nsf"; /* opens this file (can be any music type)
void handle_error( const char* str );
-int main()
+int main(int argc, char *argv[])
{
+ const char *filename = "test.nsf"; /* Default file to open */
+ if ( argc >= 2 )
+ filename = argv[1];
+
long sample_rate = 44100; /* number of samples per second */
- int track = 0; /* index of track to play (0 = first) */
+ /* index of track to play (0 = first) */
+ int track = argc >= 3 ? atoi(argv[2]) : 0;
/* Open music file in new emulator */
Music_Emu* emu;
diff --git a/libs/gme/demo/cpp_basics.cpp b/libs/gme/demo/cpp_basics.cpp
index 53fab4186..5222fe271 100644
--- a/libs/gme/demo/cpp_basics.cpp
+++ b/libs/gme/demo/cpp_basics.cpp
@@ -1,7 +1,5 @@
// C++ example that opens a game music file and records 10 seconds to "out.wav"
-static char filename [] = "test.nsf"; /* opens this file (can be any music type) */
-
#include "gme/Music_Emu.h"
#include "Wave_Writer.h"
@@ -10,10 +8,15 @@ static char filename [] = "test.nsf"; /* opens this file (can be any music type)
void handle_error( const char* str );
-int main()
+int main(int argc, char *argv[])
{
+ const char *filename = "test.nsf"; /* Default file to open */
+ if ( argc >= 2 )
+ filename = argv[1];
+
long sample_rate = 44100; // number of samples per second
- int track = 0; // index of track to play (0 = first)
+ // index of track to play (0 = first)
+ int track = argc >= 3 ? atoi(argv[2]) : 0;
// Determine file type
gme_type_t file_type;
diff --git a/libs/gme/gme.txt b/libs/gme/gme.txt
index d9a2452c7..5a7d2f560 100644
--- a/libs/gme/gme.txt
+++ b/libs/gme/gme.txt
@@ -1,10 +1,10 @@
-Game_Music_Emu 0.6.0
+Game_Music_Emu 0.6.2
--------------------
-Author : Shay Green
-Website: http://www.slack.net/~ant/libs/
-Forum : http://groups.google.com/group/blargg-sound-libs
-Source : https://code.google.com/p/game-music-emu/
-License: GNU Lesser General Public License (LGPL)
+Author : Shay Green
+Maintainer : Michael Pyne
+Website : https://bitbucket.org/mpyne/game-music-emu/
+Source : https://bitbucket.org/mpyne/game-music-emu/
+License : GNU Lesser General Public License (LGPL), see LICENSE.txt
Contents
--------
diff --git a/libs/gme/gme/CMakeLists.txt b/libs/gme/gme/CMakeLists.txt
index 3c6464fc7..534be8a85 100644
--- a/libs/gme/gme/CMakeLists.txt
+++ b/libs/gme/gme/CMakeLists.txt
@@ -143,7 +143,7 @@ add_definitions(-DBLARGG_BUILD_DLL)
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# Add library to be compiled.
-add_library(gme SHARED ${libgme_SRCS})
+add_library(gme ${libgme_SRCS})
# The version is the release. The "soversion" is the API version. As long
# as only build fixes are performed (i.e. no backwards-incompatible changes
@@ -159,4 +159,4 @@ install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX}
ARCHIVE DESTINATION lib) # DLL platforms
install(FILES ${EXPORTED_HEADERS} DESTINATION include/gme)
-install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib/pkgconfig)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig)
diff --git a/libs/gme/gme/Data_Reader.cpp b/libs/gme/gme/Data_Reader.cpp
index 5bbfbf551..f18928f4b 100644
--- a/libs/gme/gme/Data_Reader.cpp
+++ b/libs/gme/gme/Data_Reader.cpp
@@ -22,8 +22,13 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
const char Data_Reader::eof_error [] = "Unexpected end of file";
+#define RETURN_VALIDITY_CHECK( cond ) \
+ do { if ( unlikely( !(cond) ) ) return "Corrupt file"; } while(0)
+
blargg_err_t Data_Reader::read( void* p, long s )
{
+ RETURN_VALIDITY_CHECK( s > 0 );
+
long result = read_avail( p, s );
if ( result != s )
{
@@ -38,6 +43,8 @@ blargg_err_t Data_Reader::read( void* p, long s )
blargg_err_t Data_Reader::skip( long count )
{
+ RETURN_VALIDITY_CHECK( count >= 0 );
+
char buf [512];
while ( count )
{
@@ -54,7 +61,8 @@ long File_Reader::remain() const { return size() - tell(); }
blargg_err_t File_Reader::skip( long n )
{
- assert( n >= 0 );
+ RETURN_VALIDITY_CHECK( n >= 0 );
+
if ( !n )
return 0;
return seek( tell() + n );
@@ -67,13 +75,14 @@ Subset_Reader::Subset_Reader( Data_Reader* dr, long size )
in = dr;
remain_ = dr->remain();
if ( remain_ > size )
- remain_ = size;
+ remain_ = max( 0l, size );
}
long Subset_Reader::remain() const { return remain_; }
long Subset_Reader::read_avail( void* p, long s )
{
+ s = max( 0l, s );
if ( s > remain_ )
s = remain_;
remain_ -= s;
@@ -85,7 +94,7 @@ long Subset_Reader::read_avail( void* p, long s )
Remaining_Reader::Remaining_Reader( void const* h, long size, Data_Reader* r )
{
header = (char const*) h;
- header_end = header + size;
+ header_end = header + max( 0l, size );
in = r;
}
@@ -93,6 +102,7 @@ long Remaining_Reader::remain() const { return header_end - header + in->remain(
long Remaining_Reader::read_first( void* out, long count )
{
+ count = max( 0l, count );
long first = header_end - header;
if ( first )
{
@@ -107,8 +117,9 @@ long Remaining_Reader::read_first( void* out, long count )
long Remaining_Reader::read_avail( void* out, long count )
{
+ count = max( 0l, count );
long first = read_first( out, count );
- long second = count - first;
+ long second = max( 0l, count - first );
if ( second )
{
second = in->read_avail( (char*) out + first, second );
@@ -120,8 +131,9 @@ long Remaining_Reader::read_avail( void* out, long count )
blargg_err_t Remaining_Reader::read( void* out, long count )
{
+ count = max( 0l, count );
long first = read_first( out, count );
- long second = count - first;
+ long second = max( 0l, count - first );
if ( !second )
return 0;
return in->read( (char*) out + first, second );
@@ -131,7 +143,7 @@ blargg_err_t Remaining_Reader::read( void* out, long count )
Mem_File_Reader::Mem_File_Reader( const void* p, long s ) :
begin( (const char*) p ),
- size_( s )
+ size_( max( 0l, s ) )
{
pos = 0;
}
@@ -141,6 +153,7 @@ long Mem_File_Reader::size() const { return size_; }
long Mem_File_Reader::read_avail( void* p, long s )
{
long r = remain();
+ s = max( 0l, s );
if ( s > r )
s = r;
memcpy( p, begin + pos, s );
@@ -152,6 +165,7 @@ long Mem_File_Reader::tell() const { return pos; }
blargg_err_t Mem_File_Reader::seek( long n )
{
+ RETURN_VALIDITY_CHECK( n >= 0 );
if ( n > size_ )
return eof_error;
pos = n;
@@ -164,7 +178,7 @@ Callback_Reader::Callback_Reader( callback_t c, long size, void* d ) :
callback( c ),
data( d )
{
- remain_ = size;
+ remain_ = max( 0l, size );
}
long Callback_Reader::remain() const { return remain_; }
@@ -173,13 +187,14 @@ long Callback_Reader::read_avail( void* out, long count )
{
if ( count > remain_ )
count = remain_;
- if ( Callback_Reader::read( out, count ) )
+ if ( count < 0 || Callback_Reader::read( out, count ) )
count = -1;
return count;
}
blargg_err_t Callback_Reader::read( void* out, long count )
{
+ RETURN_VALIDITY_CHECK( count >= 0 );
if ( count > remain_ )
return eof_error;
return callback( data, out, count );
@@ -210,11 +225,12 @@ long Std_File_Reader::size() const
long Std_File_Reader::read_avail( void* p, long s )
{
- return fread( p, 1, s, (FILE*) file_ );
+ return fread( p, 1, max( 0l, s ), (FILE*) file_ );
}
blargg_err_t Std_File_Reader::read( void* p, long s )
{
+ RETURN_VALIDITY_CHECK( s > 0 );
if ( s == (long) fread( p, 1, s, (FILE*) file_ ) )
return 0;
if ( feof( (FILE*) file_ ) )
diff --git a/libs/gme/gme/Data_Reader.h b/libs/gme/gme/Data_Reader.h
index acf571f67..6c22b678e 100644
--- a/libs/gme/gme/Data_Reader.h
+++ b/libs/gme/gme/Data_Reader.h
@@ -129,6 +129,8 @@ private:
};
#ifdef HAVE_ZLIB_H
+#include
+
// Gzip compressed file reader
class Gzip_File_Reader : public File_Reader {
public:
@@ -143,7 +145,7 @@ public:
long tell() const;
blargg_err_t seek( long );
private:
- void* file_;
+ gzFile file_;
long size_;
};
#endif
diff --git a/libs/gme/gme/Music_Emu.cpp b/libs/gme/gme/Music_Emu.cpp
index 30b25dcfc..942e86e27 100644
--- a/libs/gme/gme/Music_Emu.cpp
+++ b/libs/gme/gme/Music_Emu.cpp
@@ -178,6 +178,11 @@ blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const
return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo;
}
+long Music_Emu::tell_samples() const
+{
+ return out_time;
+}
+
long Music_Emu::tell() const
{
blargg_long rate = sample_rate() * stereo;
@@ -185,14 +190,18 @@ long Music_Emu::tell() const
return sec * 1000 + (out_time - sec * rate) * 1000 / rate;
}
-blargg_err_t Music_Emu::seek( long msec )
+blargg_err_t Music_Emu::seek_samples( long time )
{
- blargg_long time = msec_to_samples( msec );
if ( time < out_time )
RETURN_ERR( start_track( current_track_ ) );
return skip( time - out_time );
}
+blargg_err_t Music_Emu::seek( long msec )
+{
+ return seek_samples( msec_to_samples( msec ) );
+}
+
blargg_err_t Music_Emu::skip( long count )
{
require( current_track() >= 0 ); // start_track() must have been called already
diff --git a/libs/gme/gme/Music_Emu.h b/libs/gme/gme/Music_Emu.h
index b96f4b611..d98f7ce7e 100644
--- a/libs/gme/gme/Music_Emu.h
+++ b/libs/gme/gme/Music_Emu.h
@@ -41,9 +41,15 @@ public:
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
long tell() const;
+ // Number of samples generated since beginning of track
+ long tell_samples() const;
+
// Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t seek( long msec );
+ // Equivalent to restarting track then skipping n samples
+ blargg_err_t seek_samples( long n );
+
// Skip n samples
blargg_err_t skip( long n );
diff --git a/libs/gme/gme/Nsfe_Emu.cpp b/libs/gme/gme/Nsfe_Emu.cpp
index 824a1a240..55ac4688f 100644
--- a/libs/gme/gme/Nsfe_Emu.cpp
+++ b/libs/gme/gme/Nsfe_Emu.cpp
@@ -134,6 +134,9 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
RETURN_ERR( in.read( block_header, sizeof block_header ) );
blargg_long size = get_le32( block_header [0] );
blargg_long tag = get_le32( block_header [1] );
+
+ if ( size <= 0 )
+ return "Corrupt file";
//debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) );
diff --git a/libs/gme/gme/Spc_Cpu.cpp b/libs/gme/gme/Spc_Cpu.cpp
index 90f60ed29..19aae1135 100644
--- a/libs/gme/gme/Spc_Cpu.cpp
+++ b/libs/gme/gme/Spc_Cpu.cpp
@@ -433,9 +433,7 @@ void Snes_Spc::cpu_write( int data, int addr, rel_time_t time )
#endif
// Registers other than $F2 and $F4-$F7
- //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
- // TODO: this is a bit on the fragile side
- if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
+ if ( reg != 2 && (reg < 4 || reg > 7) ) // 36%
cpu_write_smp_reg( data, time, reg );
}
// High mem/address wrap-around
diff --git a/libs/gme/gme/Spc_Cpu.h b/libs/gme/gme/Spc_Cpu.h
index 4742e0990..10c245090 100644
--- a/libs/gme/gme/Spc_Cpu.h
+++ b/libs/gme/gme/Spc_Cpu.h
@@ -76,8 +76,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
// TODO: remove non-wrapping versions?
#define SPC_NO_SP_WRAPAROUND 0
-#define SET_SP( v ) (sp = ram + 0x101 + (v))
-#define GET_SP() (sp - 0x101 - ram)
+#define SET_SP( v ) (sp = ram + 0x101 + ((uint8_t) v))
+#define GET_SP() (uint8_t (sp - 0x101 - ram))
#if SPC_NO_SP_WRAPAROUND
#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
@@ -485,7 +485,7 @@ loop:
case 0xAF: // MOV (X)+,A
WRITE_DP( 0, x, a + no_read_before_write );
- x++;
+ x = (uint8_t) (x + 1);
goto loop;
// 5. 8-BIT LOGIC OPERATION COMMANDS
@@ -808,7 +808,7 @@ loop:
unsigned temp = y * a;
a = (uint8_t) temp;
nz = ((temp >> 1) | temp) & 0x7F;
- y = temp >> 8;
+ y = (uint8_t) (temp >> 8);
nz |= y;
goto loop;
}
@@ -838,6 +838,7 @@ loop:
nz = (uint8_t) a;
a = (uint8_t) a;
+ y = (uint8_t) y;
goto loop;
}
@@ -1004,7 +1005,7 @@ loop:
case 0x7F: // RET1
temp = *sp;
SET_PC( GET_LE16( sp + 1 ) );
- sp += 3;
+ SET_SP( GET_SP() + 3 );
goto set_psw;
case 0x8E: // POP PSW
POP( temp );
diff --git a/libs/gme/gme/blargg_source.h b/libs/gme/gme/blargg_source.h
index b011777ad..b65afd30b 100644
--- a/libs/gme/gme/blargg_source.h
+++ b/libs/gme/gme/blargg_source.h
@@ -18,6 +18,19 @@ all other #include lines. */
#undef require
#define require( expr ) assert( expr )
+// Use to provide hints to compiler for optimized code layout in situations where we
+// can almost always expect a conditional to go one way or the other. Should only be
+// used in situations where an unexpected branch is truly exceptional though!
+#undef likely
+#undef unlikely
+#ifdef __GNUC__
+ #define likely( x ) __builtin_expect(x, 1)
+ #define unlikely( x ) __builtin_expect(x, 0)
+#else
+ #define likely( x ) (x)
+ #define unlikely( x ) (x)
+#endif
+
// Like printf() except output goes to debug log file. Might be defined to do
// nothing (not even evaluate its arguments).
// void debug_printf( const char* format, ... );
diff --git a/libs/gme/gme/gme.cpp b/libs/gme/gme/gme.cpp
index c05f25eb4..47709840a 100644
--- a/libs/gme/gme/gme.cpp
+++ b/libs/gme/gme/gme.cpp
@@ -337,7 +337,9 @@ BLARGG_EXPORT gme_err_t gme_play ( Music_Emu* me, int n, short* p )
BLARGG_EXPORT void gme_set_fade ( Music_Emu* me, int start_msec ) { me->set_fade( start_msec ); }
BLARGG_EXPORT int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); }
BLARGG_EXPORT int gme_tell ( Music_Emu const* me ) { return me->tell(); }
+BLARGG_EXPORT int gme_tell_samples ( Music_Emu const* me ) { return me->tell_samples(); }
BLARGG_EXPORT gme_err_t gme_seek ( Music_Emu* me, int msec ) { return me->seek( msec ); }
+BLARGG_EXPORT gme_err_t gme_seek_samples ( Music_Emu* me, int n ) { return me->seek_samples( n ); }
BLARGG_EXPORT int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); }
BLARGG_EXPORT void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); }
BLARGG_EXPORT void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); }
diff --git a/libs/gme/gme/gme.h b/libs/gme/gme/gme.h
index 1f2a2d150..cb07061b4 100644
--- a/libs/gme/gme/gme.h
+++ b/libs/gme/gme/gme.h
@@ -1,6 +1,6 @@
/* Game music emulator library C interface (also usable from C++) */
-/* Game_Music_Emu 0.6.0 */
+/* Game_Music_Emu 0.6.1 */
#ifndef GME_H
#define GME_H
@@ -8,7 +8,7 @@
extern "C" {
#endif
-#define GME_VERSION 0x000600 /* 1 byte major, 1 byte minor, 1 byte patch-level */
+#define GME_VERSION 0x000601 /* 1 byte major, 1 byte minor, 1 byte patch-level */
/* Error string returned by library functions, or NULL if no error (success) */
typedef const char* gme_err_t;
@@ -47,9 +47,15 @@ int gme_track_ended( Music_Emu const* );
/* Number of milliseconds (1000 = one second) played since beginning of track */
int gme_tell( Music_Emu const* );
+/* Number of samples generated since beginning of track */
+int gme_tell_samples( Music_Emu const* );
+
/* Seek to new time in track. Seeking backwards or far forward can take a while. */
gme_err_t gme_seek( Music_Emu*, int msec );
+/* Equivalent to restarting track then skipping n samples */
+gme_err_t gme_seek_samples( Music_Emu*, int n );
+
/******** Informational ********/
diff --git a/libs/gme/gme/libgme.pc.in b/libs/gme/gme/libgme.pc.in
index 4f420d9ed..49fd5b1df 100644
--- a/libs/gme/gme/libgme.pc.in
+++ b/libs/gme/gme/libgme.pc.in
@@ -3,7 +3,7 @@
# later are used by pkg-config.
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
-lib_suffix=
+lib_suffix=@LIB_SUFFIX@
libdir=${exec_prefix}/lib${lib_suffix}
includedir=${prefix}/include
@@ -13,3 +13,4 @@ URL: http://code.google.com/p/game-music-emu/
Version: @GME_VERSION@
Cflags: -I${includedir}
Libs: -L${libdir} -lgme
+Libs.private: -lstdc++
diff --git a/libs/gme/include/gme/gme.h b/libs/gme/include/gme/gme.h
index 1f2a2d150..cb07061b4 100644
--- a/libs/gme/include/gme/gme.h
+++ b/libs/gme/include/gme/gme.h
@@ -1,6 +1,6 @@
/* Game music emulator library C interface (also usable from C++) */
-/* Game_Music_Emu 0.6.0 */
+/* Game_Music_Emu 0.6.1 */
#ifndef GME_H
#define GME_H
@@ -8,7 +8,7 @@
extern "C" {
#endif
-#define GME_VERSION 0x000600 /* 1 byte major, 1 byte minor, 1 byte patch-level */
+#define GME_VERSION 0x000601 /* 1 byte major, 1 byte minor, 1 byte patch-level */
/* Error string returned by library functions, or NULL if no error (success) */
typedef const char* gme_err_t;
@@ -47,9 +47,15 @@ int gme_track_ended( Music_Emu const* );
/* Number of milliseconds (1000 = one second) played since beginning of track */
int gme_tell( Music_Emu const* );
+/* Number of samples generated since beginning of track */
+int gme_tell_samples( Music_Emu const* );
+
/* Seek to new time in track. Seeking backwards or far forward can take a while. */
gme_err_t gme_seek( Music_Emu*, int msec );
+/* Equivalent to restarting track then skipping n samples */
+gme_err_t gme_seek_samples( Music_Emu*, int n );
+
/******** Informational ********/
diff --git a/libs/gme/readme.txt b/libs/gme/readme.txt
index 82a501dbd..4cfe5e7a8 100644
--- a/libs/gme/readme.txt
+++ b/libs/gme/readme.txt
@@ -1,4 +1,4 @@
-Game_Music_Emu 0.6.0: Game Music Emulators
+Game_Music_Emu 0.6.2: Game Music Emulators
------------------------------------------
Game_Music_Emu is a collection of video game music file emulators that
support the following formats and systems:
@@ -34,30 +34,45 @@ several architectures, Mac OS, MorphOS, Xbox, PlayStation Portable,
GP2X, and Nintendo DS.
Author : Shay Green
-Website: http://www.slack.net/~ant/
-Forum : http://groups.google.com/group/blargg-sound-libs
+Website: https://bitbucket.org/mpyne/game-music-emu/wiki/Home
License: GNU Lesser General Public License (LGPL)
+Current Maintainer: Michael Pyne
Getting Started
---------------
Build a program consisting of demo/basics.c, demo/Wave_Writer.cpp, and
-all source files in gme/. If you have CMake 2.6 or later, execute
+all source files in gme/.
- run cmake
- cd demo
- run make
+Or, if you have CMake 2.6 or later, execute at a command prompt (from the
+extracted source directory):
-Be sure "test.nsf" is in the same directory as the program. Running it
+ mkdir build
+ cd build
+ cmake ../ # <-- Pass any needed CMake flags here
+ make # To build the library
+ cd demo
+ make # To build the demo itself
+
+Be sure "test.nsf" is in the same directory as the demo program. Running it
should generate the recording "out.wav".
+You can use "make install" to install the library. To choose where to install
+the library to, use the CMake argument "-DCMAKE_INSTALL_PREFIX=/usr/local"
+(and replace /usr/local with the base path you wish to use). Alternately, you
+can specify the base path to install to when you run "make install" by passing
+'DESTDIR=/usr/local' on the make install command line (again, replace
+/usr/local as appropriate).
+
+To build a static library instead of shared (the default), pass
+-DBUILD_SHARED_LIBS=OFF to the cmake command when running cmake.
+
A slightly more extensive demo application is available in the player/
directory. It requires SDL to build.
Read gme.txt for more information. Post to the discussion forum for
assistance.
-
Files
-----
gme.txt General notes about the library
diff --git a/libs/gme/win32/libgme.dll.a b/libs/gme/win32/libgme.dll.a
index d56d87396..2c5e95853 100644
Binary files a/libs/gme/win32/libgme.dll.a and b/libs/gme/win32/libgme.dll.a differ
diff --git a/libs/gme/win64/libgme.dll.a b/libs/gme/win64/libgme.dll.a
index 38079dc2a..8348f12de 100644
Binary files a/libs/gme/win64/libgme.dll.a and b/libs/gme/win64/libgme.dll.a differ
diff --git a/libs/libpng-src/projects/visualc10/.gitignore b/libs/libpng-src/projects/visualc10/.gitignore
index 118a15cbb..e1bec81fc 100644
--- a/libs/libpng-src/projects/visualc10/.gitignore
+++ b/libs/libpng-src/projects/visualc10/.gitignore
@@ -1,3 +1,5 @@
/Win32
/x64
/libpng.vcproj.*.*.user
+/ARM
+/ARM64
diff --git a/libs/libpng-src/projects/visualc10/libpng.vcxproj b/libs/libpng-src/projects/visualc10/libpng.vcxproj
index fb53826ec..eaa3d4ffb 100644
--- a/libs/libpng-src/projects/visualc10/libpng.vcxproj
+++ b/libs/libpng-src/projects/visualc10/libpng.vcxproj
@@ -1,6 +1,14 @@

+
+ Debug
+ ARM
+
+
+ Debug
+ ARM64
+
Debug
Win32
@@ -9,6 +17,14 @@
Debug
x64
+
+ Release
+ ARM
+
+
+ Release
+ ARM64
+
Release
Win32
@@ -21,7 +37,7 @@
{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}
libpng
- 8.1
+ 10.0.16299.0
@@ -29,21 +45,45 @@
false
v140
+
+ StaticLibrary
+ false
+ v141
+ true
+
StaticLibrary
false
v140
+
+ StaticLibrary
+ false
+ v141
+ true
+
StaticLibrary
false
v140
+
+ StaticLibrary
+ false
+ v141
+ true
+
StaticLibrary
false
v140
+
+ StaticLibrary
+ false
+ v141
+ true
+
@@ -51,29 +91,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<_ProjectFileVersion>10.0.30319.1
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
@@ -107,6 +171,38 @@
$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ ..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDLL
+ true
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ Level3
+ true
+ CompileAsC
+ true
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+ ..\..;%(AdditionalIncludeDirectories)
+
+
+ $(ProjectDir)$(Platform)\$(Configuration)\libpng.lib
+ true
+ MachineARM
+
+
+ true
+ $(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc
+
+
X64
@@ -143,6 +239,40 @@
$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc
+
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ ..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)
+ WIN32;NDEBUG;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDLL
+ true
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ Level3
+ true
+ CompileAsC
+ 4267;%(DisableSpecificWarnings)
+ true
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+ ..\..;%(AdditionalIncludeDirectories)
+
+
+ $(ProjectDir)$(Platform)\$(Configuration)\libpng.lib
+ true
+ MachineARM64
+
+
+ true
+ $(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc
+
+
Disabled
@@ -174,6 +304,36 @@
$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc
+
+
+ Disabled
+ ..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebugDLL
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ Level3
+ true
+ ProgramDatabase
+ CompileAsC
+ false
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(ProjectDir)$(Platform)\$(Configuration)\libpng.lib
+ true
+ MachineARM
+
+
+ true
+ $(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc
+
+
X64
@@ -209,164 +369,320 @@
$(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc
+
+
+
+ Disabled
+ ..\..;..\..\..\zlib;%(AdditionalIncludeDirectories)
+ WIN32;_DEBUG;DEBUG;PNG_DEBUG=1;PNG_USE_PNGVCRD;PNG_LIBPNG_SPECIALBUILD;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebugDLL
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ Level3
+ true
+ ProgramDatabase
+ CompileAsC
+ 4267;%(DisableSpecificWarnings)
+ false
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(ProjectDir)$(Platform)\$(Configuration)\libpng.lib
+ true
+ MachineARM64
+
+
+ true
+ $(ProjectDir)$(PlatformName)\$(ConfigurationName)\libpng.bsc
+
+
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(AdditionalIncludeDirectories)
+ %(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
true
+ true
true
+ true
true
+ true
true
+ true
@@ -377,17 +693,29 @@
true
+ true
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)
+ \Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)
true
+ true
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)
+ \Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)
true
+ true
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)
+ \Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)
true
+ true
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
\Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)
+ \Oogaland\Projects\orospakr.ca\srb2\tools\libpng-src\scripts;%(AdditionalIncludeDirectories)
diff --git a/libs/zlib/projects/visualc10/.gitignore b/libs/zlib/projects/visualc10/.gitignore
index 488a5428b..1c5340cd4 100644
--- a/libs/zlib/projects/visualc10/.gitignore
+++ b/libs/zlib/projects/visualc10/.gitignore
@@ -1,3 +1,5 @@
/Win32
/x64
/zlib.vcproj.*.*.user
+/ARM
+/ARM64
diff --git a/libs/zlib/projects/visualc10/zlib.vcxproj b/libs/zlib/projects/visualc10/zlib.vcxproj
index 814641d34..a7055ddb8 100644
--- a/libs/zlib/projects/visualc10/zlib.vcxproj
+++ b/libs/zlib/projects/visualc10/zlib.vcxproj
@@ -1,6 +1,14 @@

+
+ Debug
+ ARM
+
+
+ Debug
+ ARM64
+
Debug
Win32
@@ -9,6 +17,14 @@
Debug
x64
+
+ Release
+ ARM
+
+
+ Release
+ ARM64
+
Release
Win32
@@ -21,7 +37,7 @@
{73A5729C-7323-41D4-AB48-8A03C9F81603}
zlib
- 8.1
+ 10.0.16299.0
@@ -29,21 +45,45 @@
false
v140
+
+ StaticLibrary
+ false
+ v141
+ true
+
StaticLibrary
false
v140
+
+ StaticLibrary
+ false
+ v141
+ true
+
StaticLibrary
false
v140
+ true
+
+
+ StaticLibrary
+ false
+ v141
StaticLibrary
false
v140
+
+ StaticLibrary
+ false
+ v141
+ true
+
@@ -52,29 +92,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<_ProjectFileVersion>10.0.30319.1
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
$(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
@@ -106,6 +170,36 @@
$(ProjectDir)$(PlatformName)\$(ConfigurationName)\zlib.bsc
+
+
+ Disabled
+ WIN32;_DEBUG;ASMV;ASMINF;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)
+ Default
+ MultiThreadedDebugDLL
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ Level3
+ true
+ ProgramDatabase
+ CompileAsC
+ false
+ ASMV;ASMINF
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(ProjectDir)$(Platform)\$(Configuration)\zlib.lib
+ true
+ MachineARM
+
+
+ true
+ $(ProjectDir)$(PlatformName)\$(ConfigurationName)\zlib.bsc
+
+
X64
@@ -139,6 +233,36 @@
$(ProjectDir)$(PlatformName)\$(ConfigurationName)\zlib.bsc
+
+
+
+ Disabled
+ WIN32;_DEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebugDLL
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ Level3
+ true
+ ProgramDatabase
+ CompileAsC
+ false
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(ProjectDir)$(Platform)\$(Configuration)\zlib.lib
+ true
+ MachineARM64
+
+
+ true
+ $(ProjectDir)$(PlatformName)\$(ConfigurationName)\zlib.bsc
+
+
MaxSpeed
@@ -169,6 +293,37 @@
$(ProjectDir)$(PlatformName)\$(ConfigurationName)\zlib.bsc
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;ASMV;ASMINF;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDLL
+ true
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ Level3
+ true
+ CompileAsC
+ true
+ ASMV;ASMINF
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(ProjectDir)$(Platform)\$(Configuration)\zlib.lib
+ true
+ MachineARM
+
+
+ true
+ $(ProjectDir)$(PlatformName)\$(ConfigurationName)\zlib.bsc
+
+
X64
@@ -202,30 +357,77 @@
$(ProjectDir)$(PlatformName)\$(ConfigurationName)\zlib.bsc
+
+
+
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ MultiThreadedDLL
+ true
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ $(ProjectDir)$(Platform)\$(Configuration)\
+ Level3
+ true
+ CompileAsC
+ true
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(ProjectDir)$(Platform)\$(Configuration)\zlib.lib
+ true
+ MachineARM64
+
+
+ true
+ $(ProjectDir)$(PlatformName)\$(ConfigurationName)\zlib.bsc
+
+
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
@@ -233,60 +435,97 @@
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
true
+ true
../;%(AdditionalIncludeDirectories)
+ ../;%(AdditionalIncludeDirectories)
../..;%(AdditionalIncludeDirectories)
+ ../..;%(AdditionalIncludeDirectories)
true
+ true
../..;%(AdditionalIncludeDirectories)
+ ../..;%(AdditionalIncludeDirectories)
true
+ true
true
+ true
true
+ true
true
+ true
@@ -306,39 +545,67 @@
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
\Oogaland\Projects\orospakr.ca\srb2\tools\zlib\win32;%(AdditionalIncludeDirectories)
+ \Oogaland\Projects\orospakr.ca\srb2\tools\zlib\win32;%(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
\Oogaland\Projects\orospakr.ca\srb2\tools\zlib\win32;%(AdditionalIncludeDirectories)
+ \Oogaland\Projects\orospakr.ca\srb2\tools\zlib\win32;%(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
\Oogaland\Projects\orospakr.ca\srb2\tools\zlib\win32;%(AdditionalIncludeDirectories)
+ \Oogaland\Projects\orospakr.ca\srb2\tools\zlib\win32;%(AdditionalIncludeDirectories)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
\Oogaland\Projects\orospakr.ca\srb2\tools\zlib\win32;%(AdditionalIncludeDirectories)
+ \Oogaland\Projects\orospakr.ca\srb2\tools\zlib\win32;%(AdditionalIncludeDirectories)
true
+ true
true
+ true
+ true
+ true
true
+ true
true
+ true
+ true
+ true
true
+ true
true
+ true
true
+ true
true
+ true
true
+ true
true
+ true
true
+ true
true
+ true
true
+ true
true
+ true
true
+ true
true
+ true
diff --git a/srb2-vc10.sln b/srb2-vc10.sln
index ecceafd56..b4415bfc0 100644
--- a/srb2-vc10.sln
+++ b/srb2-vc10.sln
@@ -1,9 +1,9 @@

Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 14
-VisualStudioVersion = 14.0.25123.0
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.136
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Srb2win", "src\win32\Srb2win-vc10.vcxproj", "{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Srb2DD", "src\win32\Srb2win-vc10.vcxproj", "{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "libs\libpng-src\projects\visualc10\libpng.vcxproj", "{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}"
EndProject
@@ -13,56 +13,104 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "libs\zlib\projects\
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "s_openal", "src\hardware\s_openal\s_openal-vc10.vcxproj", "{E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}"
EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Srb2SDL", "src\sdl\Srb2SDL-vc10.vcxproj", "{61BA7D3C-F77D-4D31-B718-1177FE482CF2}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Srb2Win", "src\sdl\Srb2SDL-vc10.vcxproj", "{61BA7D3C-F77D-4D31-B718-1177FE482CF2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|ARM = Debug|ARM
+ Debug|ARM64 = Debug|ARM64
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
+ Release|ARM = Release|ARM
+ Release|ARM64 = Release|ARM64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Debug|ARM.ActiveCfg = Debug|ARM
+ {0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Debug|ARM.Build.0 = Debug|ARM
+ {0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Debug|ARM64.Build.0 = Debug|ARM64
{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Debug|Win32.ActiveCfg = Debug|Win32
{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Debug|Win32.Build.0 = Debug|Win32
{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Debug|x64.ActiveCfg = Debug|x64
{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Debug|x64.Build.0 = Debug|x64
+ {0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Release|ARM.ActiveCfg = Release|ARM
+ {0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Release|ARM.Build.0 = Release|ARM
+ {0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Release|ARM64.ActiveCfg = Release|ARM64
+ {0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Release|ARM64.Build.0 = Release|ARM64
{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Release|Win32.ActiveCfg = Release|Win32
{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Release|Win32.Build.0 = Release|Win32
{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Release|x64.ActiveCfg = Release|x64
{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}.Release|x64.Build.0 = Release|x64
+ {72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Debug|ARM.ActiveCfg = Debug|ARM
+ {72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Debug|ARM.Build.0 = Debug|ARM
+ {72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Debug|ARM64.Build.0 = Debug|ARM64
{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Debug|Win32.ActiveCfg = Debug|Win32
{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Debug|Win32.Build.0 = Debug|Win32
{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Debug|x64.ActiveCfg = Debug|x64
{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Debug|x64.Build.0 = Debug|x64
+ {72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Release|ARM.ActiveCfg = Release|ARM
+ {72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Release|ARM.Build.0 = Release|ARM
+ {72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Release|ARM64.ActiveCfg = Release|ARM64
+ {72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Release|ARM64.Build.0 = Release|ARM64
{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Release|Win32.ActiveCfg = Release|Win32
{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Release|Win32.Build.0 = Release|Win32
{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Release|x64.ActiveCfg = Release|x64
{72B01ACA-7A1A-4F7B-ACEF-2607299CF052}.Release|x64.Build.0 = Release|x64
+ {51137D5C-4E81-4955-AACF-EA3092006051}.Debug|ARM.ActiveCfg = Debug|ARM
+ {51137D5C-4E81-4955-AACF-EA3092006051}.Debug|ARM.Build.0 = Debug|ARM
+ {51137D5C-4E81-4955-AACF-EA3092006051}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {51137D5C-4E81-4955-AACF-EA3092006051}.Debug|ARM64.Build.0 = Debug|ARM64
{51137D5C-4E81-4955-AACF-EA3092006051}.Debug|Win32.ActiveCfg = Debug|Win32
{51137D5C-4E81-4955-AACF-EA3092006051}.Debug|Win32.Build.0 = Debug|Win32
{51137D5C-4E81-4955-AACF-EA3092006051}.Debug|x64.ActiveCfg = Debug|x64
{51137D5C-4E81-4955-AACF-EA3092006051}.Debug|x64.Build.0 = Debug|x64
+ {51137D5C-4E81-4955-AACF-EA3092006051}.Release|ARM.ActiveCfg = Release|ARM
+ {51137D5C-4E81-4955-AACF-EA3092006051}.Release|ARM.Build.0 = Release|ARM
+ {51137D5C-4E81-4955-AACF-EA3092006051}.Release|ARM64.ActiveCfg = Release|ARM64
+ {51137D5C-4E81-4955-AACF-EA3092006051}.Release|ARM64.Build.0 = Release|ARM64
{51137D5C-4E81-4955-AACF-EA3092006051}.Release|Win32.ActiveCfg = Release|Win32
{51137D5C-4E81-4955-AACF-EA3092006051}.Release|Win32.Build.0 = Release|Win32
{51137D5C-4E81-4955-AACF-EA3092006051}.Release|x64.ActiveCfg = Release|x64
{51137D5C-4E81-4955-AACF-EA3092006051}.Release|x64.Build.0 = Release|x64
+ {73A5729C-7323-41D4-AB48-8A03C9F81603}.Debug|ARM.ActiveCfg = Debug|ARM
+ {73A5729C-7323-41D4-AB48-8A03C9F81603}.Debug|ARM.Build.0 = Debug|ARM
+ {73A5729C-7323-41D4-AB48-8A03C9F81603}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {73A5729C-7323-41D4-AB48-8A03C9F81603}.Debug|ARM64.Build.0 = Debug|ARM64
{73A5729C-7323-41D4-AB48-8A03C9F81603}.Debug|Win32.ActiveCfg = Debug|Win32
{73A5729C-7323-41D4-AB48-8A03C9F81603}.Debug|Win32.Build.0 = Debug|Win32
{73A5729C-7323-41D4-AB48-8A03C9F81603}.Debug|x64.ActiveCfg = Debug|x64
{73A5729C-7323-41D4-AB48-8A03C9F81603}.Debug|x64.Build.0 = Debug|x64
+ {73A5729C-7323-41D4-AB48-8A03C9F81603}.Release|ARM.ActiveCfg = Release|ARM
+ {73A5729C-7323-41D4-AB48-8A03C9F81603}.Release|ARM.Build.0 = Release|ARM
+ {73A5729C-7323-41D4-AB48-8A03C9F81603}.Release|ARM64.ActiveCfg = Release|ARM64
+ {73A5729C-7323-41D4-AB48-8A03C9F81603}.Release|ARM64.Build.0 = Release|ARM64
{73A5729C-7323-41D4-AB48-8A03C9F81603}.Release|Win32.ActiveCfg = Release|Win32
{73A5729C-7323-41D4-AB48-8A03C9F81603}.Release|Win32.Build.0 = Release|Win32
{73A5729C-7323-41D4-AB48-8A03C9F81603}.Release|x64.ActiveCfg = Release|x64
{73A5729C-7323-41D4-AB48-8A03C9F81603}.Release|x64.Build.0 = Release|x64
+ {E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}.Debug|ARM.ActiveCfg = Debug|ARM
+ {E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}.Debug|ARM64.ActiveCfg = Debug|ARM64
{E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}.Debug|Win32.ActiveCfg = Debug|Win32
{E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}.Debug|x64.ActiveCfg = Debug|x64
+ {E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}.Release|ARM.ActiveCfg = Release|ARM
+ {E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}.Release|ARM64.ActiveCfg = Release|ARM64
{E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}.Release|Win32.ActiveCfg = Release|Win32
{E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}.Release|x64.ActiveCfg = Release|x64
+ {61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Debug|ARM.ActiveCfg = Debug|ARM
+ {61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Debug|ARM.Build.0 = Debug|ARM
+ {61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Debug|ARM64.Build.0 = Debug|ARM64
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Debug|Win32.ActiveCfg = Debug|Win32
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Debug|Win32.Build.0 = Debug|Win32
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Debug|x64.ActiveCfg = Debug|x64
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Debug|x64.Build.0 = Debug|x64
+ {61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|ARM.ActiveCfg = Release|ARM
+ {61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|ARM.Build.0 = Release|ARM
+ {61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|ARM64.ActiveCfg = Release|ARM64
+ {61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|ARM64.Build.0 = Release|ARM64
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|Win32.ActiveCfg = Release|Win32
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|Win32.Build.0 = Release|Win32
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}.Release|x64.ActiveCfg = Release|x64
@@ -71,4 +119,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {8C0B5F99-D9B8-4CB2-BA67-5D350E71C6FC}
+ EndGlobalSection
EndGlobal
diff --git a/srb2.png b/srb2.png
index 9c13eae9a..72a08f664 100644
Binary files a/srb2.png and b/srb2.png differ
diff --git a/srb2banner.png b/srb2banner.png
new file mode 100644
index 000000000..9c13eae9a
Binary files /dev/null and b/srb2banner.png differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6a8b7e3f1..a6fab34ff 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -227,6 +227,12 @@ set(SRB2_CONFIG_YASM OFF CACHE BOOL
set(SRB2_CONFIG_STATIC_OPENGL OFF CACHE BOOL
"Use statically linked OpenGL. NOT RECOMMENDED.")
+### use internal libraries?
+if(${CMAKE_SYSTEM} MATCHES "Windows") ###set on Windows only
+ set(SRB2_CONFIG_USE_INTERNAL_LIBRARIES OFF CACHE BOOL
+ "Use SRB2's internal copies of required dependencies (SDL2, PNG, zlib, GME).")
+endif()
+
if(${SRB2_CONFIG_HAVE_BLUA})
add_definitions(-DHAVE_BLUA)
set(SRB2_LUA_SOURCES
@@ -315,7 +321,17 @@ if(${SRB2_CONFIG_HAVE_BLUA})
endif()
if(${SRB2_CONFIG_HAVE_GME})
- find_package(GME)
+ if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
+ set(GME_FOUND ON)
+ set(GME_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/gme/include)
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ set(GME_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/gme/win64 -lgme")
+ else() # 32-bit
+ set(GME_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/gme/win32 -lgme")
+ endif()
+ else()
+ find_package(GME)
+ endif()
if(${GME_FOUND})
set(SRB2_HAVE_GME ON)
add_definitions(-DHAVE_LIBGME)
@@ -325,9 +341,20 @@ if(${SRB2_CONFIG_HAVE_GME})
endif()
if(${SRB2_CONFIG_HAVE_ZLIB})
- find_package(ZLIB)
+ if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
+ set(ZLIB_FOUND ON)
+ set(ZLIB_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/zlib)
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ set(ZLIB_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/zlib/win32 -lz64")
+ else() # 32-bit
+ set(ZLIB_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/zlib/win32 -lz32")
+ endif()
+ else()
+ find_package(ZLIB)
+ endif()
if(${ZLIB_FOUND})
set(SRB2_HAVE_ZLIB ON)
+ add_definitions(-DHAVE_ZLIB)
else()
message(WARNING "You have specified that ZLIB is available but it was not found. SRB2 may not compile correctly.")
endif()
@@ -335,11 +362,27 @@ endif()
if(${SRB2_CONFIG_HAVE_PNG} AND ${SRB2_CONFIG_HAVE_ZLIB})
if (${ZLIB_FOUND})
- find_package(PNG)
+ if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
+ set(PNG_FOUND ON)
+ set(PNG_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/libpng-src)
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ set(PNG_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/libpng-src/projects -lpng64")
+ else() # 32-bit
+ set(PNG_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/libpng-src/projects -lpng32")
+ endif()
+ else()
+ find_package(PNG)
+ endif()
if(${PNG_FOUND})
set(SRB2_HAVE_PNG ON)
add_definitions(-DHAVE_PNG)
add_definitions(-D_LARGEFILE64_SOURCE)
+ set(SRB2_PNG_SOURCES apng.c)
+ set(SRB2_PNG_HEADERS apng.h)
+ prepend_sources(SRB2_PNG_SOURCES)
+ prepend_sources(SRB2_PNG_HEADERS)
+ source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS}
+ ${SRB2_PNG_SOURCES} ${SRB2_PNG_HEADERS})
else()
message(WARNING "You have specified that PNG is available but it was not found. SRB2 may not compile correctly.")
endif()
diff --git a/src/Makefile b/src/Makefile
index 8dda87564..5407a4d5e 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,7 +2,7 @@
# GNU Make makefile for SRB2
#############################################################################
# Copyright (C) 1998-2000 by DooM Legacy Team.
-# Copyright (C) 2003-2014 by Sonic Team Junior.
+# Copyright (C) 2003-2018 by Sonic Team Junior.
#
# This program is free software distributed under the
# terms of the GNU General Public License, version 2.
@@ -98,8 +98,18 @@ endif
ifdef LINUX64
LINUX=1
NONX86=1
+# LINUX64 does not imply X86_64=1; could mean ARM64 or Itanium
endif
+ifdef MINGW64
+MINGW=1
+NONX86=1
+NOASM=1
+# MINGW64 should not necessarily imply X86_64=1, but we make that assumption elsewhere
+# Once that changes, remove this
+X86_64=1
+endif #ifdef MINGW64
+
ifdef HAIKU
SDL=1
endif
@@ -144,11 +154,6 @@ ifdef MINGW
include win32/Makefile.cfg
endif #ifdef MINGW
-ifdef MINGW64
-MINGW=1
-include win32/Makefile.cfg
-endif #ifdef MINGW64
-
ifdef UNIX
UNIXCOMMON=1
endif
@@ -231,11 +236,13 @@ OPTS += -DCOMPVERSION
ifndef NONX86
ifndef GCC29
- M5=-march=pentium
- M4=-march=i486
+ ARCHOPTS?=-march=pentium
else
- M5=-mpentium
- M4=-m486
+ ARCHOPTS?=-mpentium
+endif
+else
+ifdef X86_64
+ ARCHOPTS?=-march=nocona
endif
endif
@@ -274,6 +281,8 @@ endif
LIBS+=$(PNG_LDFLAGS)
CFLAGS+=$(PNG_CFLAGS)
+
+OBJS+=$(OBJDIR)/apng.o
endif
ifdef HAVE_LIBGME
@@ -356,7 +365,7 @@ else
WINDRESFLAGS = -DNDEBUG
CFLAGS+=-O3
endif
- CFLAGS+=-g $(OPTS) $(M5) $(WINDRESFLAGS)
+ CFLAGS+=-g $(OPTS) $(ARCHOPTS) $(WINDRESFLAGS)
ifdef YASM
ifdef STABS
@@ -725,15 +734,15 @@ endif
ifndef NOHS
$(OBJDIR)/s_ds3d.o: hardware/s_ds3d/s_ds3d.c hardware/hw3dsdrv.h \
hardware/hw_dll.h
- $(CC) $(M5) -Os -o $(OBJDIR)/s_ds3d.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_ds3d/s_ds3d.c
+ $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_ds3d.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_ds3d/s_ds3d.c
$(OBJDIR)/s_fmod.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \
hardware/hw_dll.h
- $(CC) $(M5) -Os -o $(OBJDIR)/s_fmod.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_fmod/s_fmod.c
+ $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_fmod.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_fmod/s_fmod.c
$(OBJDIR)/s_openal.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \
hardware/hw_dll.h
- $(CC) $(M5) -Os -o $(OBJDIR)/s_openal.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_openal/s_openal.c
+ $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_openal.o $(WFLAGS) -D_WINDOWS -mwindows -c hardware/s_openal/s_openal.c
endif
endif
endif
@@ -763,11 +772,11 @@ else
$(OBJDIR)/s_fmod.o: hardware/s_fmod/s_fmod.c hardware/hw3dsdrv.h \
hardware/hw_dll.h
- $(CC) $(M5) -Os -o $(OBJDIR)/s_fmod.o -DHW3SOUND -DUNIXCOMMON -shared -nostartfiles -c hardware/s_fmod/s_fmod.c
+ $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_fmod.o -DHW3SOUND -DUNIXCOMMON -shared -nostartfiles -c hardware/s_fmod/s_fmod.c
$(OBJDIR)/s_openal.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \
hardware/hw_dll.h
- $(CC) $(M5) -Os -o $(OBJDIR)/s_openal.o -DHW3SOUND -DUNIXCOMMON -shared -nostartfiles -c hardware/s_openal/s_openal.c
+ $(CC) $(ARCHOPTS) -Os -o $(OBJDIR)/s_openal.o -DHW3SOUND -DUNIXCOMMON -shared -nostartfiles -c hardware/s_openal/s_openal.c
endif
endif
diff --git a/src/Makefile.cfg b/src/Makefile.cfg
index 6a1367275..2b05b7097 100644
--- a/src/Makefile.cfg
+++ b/src/Makefile.cfg
@@ -7,6 +7,10 @@
# and other things
#
+ifdef GCC81
+GCC80=1
+endif
+
ifdef GCC80
GCC72=1
endif
@@ -20,7 +24,7 @@ GCC64=1
endif
ifdef GCC64
-GCC64=1
+GCC63=1
endif
ifdef GCC63
@@ -108,12 +112,11 @@ ifndef GCC295
WFLAGS+=-Wno-div-by-zero
endif
#WFLAGS+=-Wsystem-headers
-ifndef ERRORMODE
-#WFLAGS+=-Wfloat-equal
-endif
+WFLAGS+=-Wfloat-equal
#WFLAGS+=-Wtraditional
ifdef VCHELP
WFLAGS+=-Wdeclaration-after-statement
+ WFLAGS+=-Wno-error=declaration-after-statement
endif
WFLAGS+=-Wundef
ifndef GCC295
@@ -182,15 +185,6 @@ endif
ifdef GCC46
WFLAGS+=-Wno-suggest-attribute=noreturn
endif
-ifdef GCC71
-WFLAGS+=-Wno-error=implicit-fallthrough -Wimplicit-fallthrough=3
-endif
-
-ifndef MINGW
-ifdef GCC45
-WFLAGS+=-Wunsuffixed-float-constants
-endif
-endif
ifdef NOLDWARNING
LDFLAGS+=-Wl,--as-needed
@@ -205,6 +199,9 @@ WFLAGS+=$(OLDWFLAGS)
ifdef GCC43
#WFLAGS+=-Wno-error=clobbered
endif
+ifdef GCC44
+ WFLAGS+=-Wno-error=array-bounds
+endif
ifdef GCC46
WFLAGS+=-Wno-error=suggest-attribute=noreturn
endif
@@ -214,6 +211,19 @@ endif
ifdef GCC61
WFLAGS+=-Wno-tautological-compare -Wno-error=tautological-compare
endif
+ifdef GCC71
+ WFLAGS+=-Wno-error=implicit-fallthrough
+ WFLAGS+=-Wno-implicit-fallthrough
+endif
+ifdef GCC80
+ WFLAGS+=-Wno-error=format-overflow
+ WFLAGS+=-Wno-error=stringop-truncation
+ WFLAGS+=-Wno-error=stringop-overflow
+ WFLAGS+=-Wno-format-overflow
+ WFLAGS+=-Wno-stringop-truncation
+ WFLAGS+=-Wno-stringop-overflow
+ WFLAGS+=-Wno-error=multistatement-macros
+endif
#indicate platform and what interface use with
diff --git a/src/am_map.c b/src/am_map.c
index 7e012a304..6d7042f1c 100644
--- a/src/am_map.c
+++ b/src/am_map.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -11,8 +11,8 @@
/// \file am_map.c
/// \brief Code for the 'automap', former Doom feature used for DEVMODE testing
-#include "g_game.h"
#include "am_map.h"
+#include "g_game.h"
#include "g_input.h"
#include "p_local.h"
#include "p_slopes.h"
@@ -33,7 +33,6 @@ static const UINT8 GRAYSRANGE = 16;
static const UINT8 BROWNS = (3*16);
static const UINT8 YELLOWS = (7*16);
static const UINT8 GREENS = (10*16);
-static const UINT8 GREENRANGE = 16;
static const UINT8 DBLACK = 31;
static const UINT8 DWHITE = 0;
@@ -46,8 +45,6 @@ static const UINT8 NOCLIMBYELLOWS = (11*16);
// Automap colors
#define BACKGROUND DBLACK
-#define YOURCOLORS DWHITE
-#define YOURRANGE 0
#define WALLCOLORS (REDS + REDRANGE/2)
#define WALLRANGE (REDRANGE/2)
#define NOCLIMBWALLCOLORS (NOCLIMBREDS + NOCLIMBREDRANGE/2)
@@ -64,31 +61,23 @@ static const UINT8 NOCLIMBYELLOWS = (11*16);
#define CDWALLCOLORS YELLOWS
#define NOCLIMBCDWALLCOLORS NOCLIMBYELLOWS
#define THINGCOLORS GREENS
-#define THINGRANGE GREENRANGE
-#define SECRETWALLCOLORS WALLCOLORS
-#define SECRETWALLRANGE WALLRANGE
#define GRIDCOLORS (GRAYS + GRAYSRANGE/2)
-#define GRIDRANGE 0
#define XHAIRCOLORS GRAYS
-// drawing stuff
-#define FB 0
-
-#define AM_PANDOWNKEY KEY_DOWNARROW
+// controls
#define AM_PANUPKEY KEY_UPARROW
-#define AM_PANRIGHTKEY KEY_RIGHTARROW
+#define AM_PANDOWNKEY KEY_DOWNARROW
#define AM_PANLEFTKEY KEY_LEFTARROW
+#define AM_PANRIGHTKEY KEY_RIGHTARROW
+
#define AM_ZOOMINKEY '='
#define AM_ZOOMOUTKEY '-'
-#define AM_STARTKEY KEY_TAB
-#define AM_ENDKEY KEY_TAB
#define AM_GOBIGKEY '0'
+
#define AM_FOLLOWKEY 'f'
#define AM_GRIDKEY 'g'
-#define AM_MARKKEY 'm'
-#define AM_CLEARMARKKEY 'c'
-#define AM_NUMMARKPOINTS 10
+#define AM_TOGGLEKEY KEY_TAB
// scale on entry
#define INITSCALEMTOF (FRACUNIT/5)
@@ -109,6 +98,9 @@ static const UINT8 NOCLIMBYELLOWS = (11*16);
#define CXMTOF(x) (f_x + MTOF((x)-m_x))
#define CYMTOF(y) (f_y + (f_h - MTOF((y)-m_y)))
+#define MAPBITS (FRACBITS-4)
+#define FRACTOMAPBITS (FRACBITS-MAPBITS)
+
typedef struct
{
fixed_t x, y;
@@ -129,7 +121,10 @@ typedef struct
// A line drawing of the player pointing right,
// starting from the middle.
//
+
+#define PLAYERRADIUS (16*(1<
@@ -162,27 +157,15 @@ static const mline_t thintriangle_guy[] = {
#undef R
#define NUMTHINTRIANGLEGUYLINES (sizeof (thintriangle_guy)/sizeof (mline_t))
-static INT32 bigstate; //added : 24-01-98 : moved here, toggle between
- // user view and large view (full map view)
-
-static INT32 grid = 0;
-
-static INT32 leveljuststarted = 1; // kluge until AM_LevelInit() is called
+static boolean bigstate; // user view and large view (full map view)
+static boolean draw_grid = false;
boolean automapactive = false;
boolean am_recalc = false; //added : 05-02-98 : true when screen size changes
+static boolean am_stopped = true;
-// location of window on screen
-static INT32 f_x;
-static INT32 f_y;
-
-// size of window on screen
-static INT32 f_w;
-static INT32 f_h;
-
-static INT32 lightlev; // used for funky strobing effect
-static UINT8 *fb; // pseudo-frame buffer
-static INT32 amclock;
+static INT32 f_x, f_y; // location of window on screen (always zero for both)
+static INT32 f_w, f_h; // size of window on screen (always the screen width and height respectively)
static mpoint_t m_paninc; // how far the window pans each tic (map coords)
static fixed_t mtof_zoommul; // how far the window zooms in each tic (map coords)
@@ -206,11 +189,6 @@ static fixed_t max_y;
static fixed_t max_w; // max_x-min_x,
static fixed_t max_h; // max_y-min_y
-// based on player size
-static fixed_t min_w;
-static fixed_t min_h;
-
-
static fixed_t min_scale_mtof; // used to tell when to stop zooming out
static fixed_t max_scale_mtof; // used to tell when to stop zooming in
@@ -228,13 +206,7 @@ static fixed_t scale_ftom;
static player_t *plr; // the player represented by an arrow
-static patch_t *marknums[10]; // numbers used for marking by the automap
-static mpoint_t markpoints[AM_NUMMARKPOINTS]; // where the points are
-static INT32 markpointnum = 0; // next point to be assigned
-
-static INT32 followplayer = 1; // specifies whether to follow the player around
-
-static boolean stopped = true;
+static INT32 followplayer = true; // specifies whether to follow the player around
// function for drawing lines, depends on rendermode
typedef void (*AMDRAWFLINEFUNC) (const fline_t *fl, INT32 color);
@@ -273,8 +245,8 @@ static inline void AM_restoreScaleAndLoc(void)
}
else
{
- m_x = plr->mo->x - m_w/2;
- m_y = plr->mo->y - m_h/2;
+ m_x = (plr->mo->x >> FRACTOMAPBITS) - m_w/2;
+ m_y = (plr->mo->y >> FRACTOMAPBITS) - m_h/2;
}
m_x2 = m_x + m_w;
m_y2 = m_y + m_h;
@@ -284,15 +256,6 @@ static inline void AM_restoreScaleAndLoc(void)
scale_ftom = FixedDiv(FRACUNIT, scale_mtof);
}
-/** Adds a marker at the current location.
- */
-static inline void AM_addMark(void)
-{
- markpoints[markpointnum].x = m_x + m_w/2;
- markpoints[markpointnum].y = m_y + m_h/2;
- markpointnum = (markpointnum + 1) % AM_NUMMARKPOINTS;
-}
-
/** Determines the bounding box around all vertices.
* This is used to set global variables controlling the zoom range.
*/
@@ -318,11 +281,8 @@ static void AM_findMinMaxBoundaries(void)
max_y = vertexes[i].y;
}
- max_w = max_x - min_x;
- max_h = max_y - min_y;
-
- min_w = 2*PLAYERRADIUS; // const? never changed?
- min_h = 2*PLAYERRADIUS;
+ max_w = (max_x >>= FRACTOMAPBITS) - (min_x >>= FRACTOMAPBITS);
+ max_h = (max_y >>= FRACTOMAPBITS) - (min_y >>= FRACTOMAPBITS);
a = FixedDiv(f_w<mo->x - m_w/2;
- m_y = plr->mo->y - m_h/2;
+ if (plr != NULL && plr->mo != NULL)
+ {
+ m_x = (plr->mo->x >> FRACTOMAPBITS) - m_w/2;
+ m_y = (plr->mo->y >> FRACTOMAPBITS) - m_h/2;
+ }
AM_changeWindowLoc();
// for saving & restoring
@@ -392,41 +351,21 @@ static void AM_initVariables(void)
old_m_h = m_h;
}
-static const UINT8 *maplump; // pointer to the raw data for the automap background.
-
-/** Clears all map markers.
- */
-static void AM_clearMarks(void)
-{
- INT32 i;
-
- for (i = 0; i < AM_NUMMARKPOINTS; i++)
- markpoints[i].x = -1; // means empty
- markpointnum = 0;
-}
-
//
// should be called at the start of every level
// right now, i figure it out myself
//
static void AM_LevelInit(void)
{
- leveljuststarted = 0;
-
f_x = f_y = 0;
f_w = vid.width;
f_h = vid.height;
- if (rendermode == render_soft)
- AM_drawFline = AM_drawFline_soft;
-#ifdef HWRENDER // not win32 only 19990829 by Kin
- else if (rendermode != render_none)
+ AM_drawFline = AM_drawFline_soft;
+#ifdef HWRENDER
+ if (rendermode == render_opengl)
AM_drawFline = HWR_drawAMline;
#endif
- else
- I_Error("Automap can't run without a render system");
-
- AM_clearMarks();
AM_findMinMaxBoundaries();
scale_mtof = FixedDiv(min_scale_mtof*10, 7*FRACUNIT);
@@ -442,7 +381,7 @@ static void AM_LevelInit(void)
void AM_Stop(void)
{
automapactive = false;
- stopped = true;
+ am_stopped = true;
}
/** Enables automap.
@@ -453,15 +392,14 @@ static inline void AM_Start(void)
{
static INT32 lastlevel = -1;
- if (!stopped)
+ if (!am_stopped)
AM_Stop();
- stopped = false;
+ am_stopped = false;
if (lastlevel != gamemap || am_recalc) // screen size changed
{
- am_recalc = false;
-
AM_LevelInit();
lastlevel = gamemap;
+ am_recalc = false;
}
AM_initVariables();
}
@@ -499,7 +437,7 @@ boolean AM_Responder(event_t *ev)
{
if (!automapactive)
{
- if (ev->type == ev_keydown && ev->data1 == AM_STARTKEY)
+ if (ev->type == ev_keydown && ev->data1 == AM_TOGGLEKEY)
{
//faB: prevent alt-tab in win32 version to activate automap just before
// minimizing the app; doesn't do any harm to the DOS version
@@ -511,10 +449,8 @@ boolean AM_Responder(event_t *ev)
}
}
}
-
else if (ev->type == ev_keydown)
{
-
rc = true;
switch (ev->data1)
{
@@ -550,7 +486,7 @@ boolean AM_Responder(event_t *ev)
mtof_zoommul = M_ZOOMIN;
ftom_zoommul = M_ZOOMOUT;
break;
- case AM_ENDKEY:
+ case AM_TOGGLEKEY:
AM_Stop();
break;
case AM_GOBIGKEY:
@@ -568,13 +504,7 @@ boolean AM_Responder(event_t *ev)
f_oldloc.x = INT32_MAX;
break;
case AM_GRIDKEY:
- grid = !grid;
- break;
- case AM_MARKKEY:
- AM_addMark();
- break;
- case AM_CLEARMARKKEY:
- AM_clearMarks();
+ draw_grid = !draw_grid;
break;
default:
rc = false;
@@ -628,8 +558,8 @@ static inline void AM_doFollowPlayer(void)
{
if (f_oldloc.x != plr->mo->x || f_oldloc.y != plr->mo->y)
{
- m_x = FTOM(MTOF(plr->mo->x)) - m_w/2;
- m_y = FTOM(MTOF(plr->mo->y)) - m_h/2;
+ m_x = FTOM(MTOF(plr->mo->x >> FRACTOMAPBITS)) - m_w/2;
+ m_y = FTOM(MTOF(plr->mo->y >> FRACTOMAPBITS)) - m_h/2;
m_x2 = m_x + m_w;
m_y2 = m_y + m_h;
f_oldloc.x = plr->mo->x;
@@ -647,8 +577,6 @@ void AM_Ticker(void)
if (dedicated || !automapactive)
return;
- amclock++;
-
if (followplayer)
AM_doFollowPlayer();
@@ -667,72 +595,7 @@ void AM_Ticker(void)
*/
static void AM_clearFB(INT32 color)
{
-#ifdef HWRENDER
- if (rendermode != render_soft && rendermode != render_none)
- {
- HWR_clearAutomap();
- return;
- }
-#endif
-
- if (!maplump)
- memset(fb, color, f_w*f_h*vid.bpp);
- else
- {
- INT32 dmapx, dmapy, i, y;
- static INT32 mapxstart, mapystart;
- UINT8 *dest = screens[0];
- const UINT8 *src;
-#define MAPLUMPHEIGHT (200 - 42)
-
- if (followplayer)
- {
- static vertex_t oldplr;
-
- dmapx = MTOF(plr->mo->x) - MTOF(oldplr.x); //fixed point
- dmapy = MTOF(oldplr.y) - MTOF(plr->mo->y);
-
- oldplr.x = plr->mo->x;
- oldplr.y = plr->mo->y;
- mapxstart += dmapx>>1;
- mapystart += dmapy>>1;
-
- while (mapxstart >= BASEVIDWIDTH)
- mapxstart -= BASEVIDWIDTH;
- while (mapxstart < 0)
- mapxstart += BASEVIDWIDTH;
- while (mapystart >= MAPLUMPHEIGHT)
- mapystart -= MAPLUMPHEIGHT;
- while (mapystart < 0)
- mapystart += MAPLUMPHEIGHT;
- }
- else
- {
- mapxstart += (MTOF(m_paninc.x)>>1);
- mapystart -= (MTOF(m_paninc.y)>>1);
- if (mapxstart >= BASEVIDWIDTH)
- mapxstart -= BASEVIDWIDTH;
- if (mapxstart < 0)
- mapxstart += BASEVIDWIDTH;
- if (mapystart >= MAPLUMPHEIGHT)
- mapystart -= MAPLUMPHEIGHT;
- if (mapystart < 0)
- mapystart += MAPLUMPHEIGHT;
- }
-
- //blit the automap background to the screen.
- for (y = 0; y < f_h; y++)
- {
- src = maplump + mapxstart + (y + mapystart)*BASEVIDWIDTH;
- for (i = 0; i < BASEVIDWIDTH*vid.dupx; i++)
- {
- while (src > maplump + BASEVIDWIDTH*MAPLUMPHEIGHT)
- src -= BASEVIDWIDTH*MAPLUMPHEIGHT;
- *dest++ = *src++;
- }
- dest += vid.width - vid.dupx*BASEVIDWIDTH;
- }
- }
+ V_DrawFill(f_x, f_y, f_w, f_h, color|V_NOSCALESTART);
}
/** Performs automap clipping of lines.
@@ -867,7 +730,7 @@ static boolean AM_clipMline(const mline_t *ml, fline_t *fl)
//
static void AM_drawFline_soft(const fline_t *fl, INT32 color)
{
- register INT32 x, y, dx, dy, sx, sy, ax, ay, d;
+ INT32 x, y, dx, dy, sx, sy, ax, ay, d;
#ifdef _DEBUG
static INT32 num = 0;
@@ -883,7 +746,7 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color)
}
#endif
-#define PUTDOT(xx,yy,cc) fb[(yy)*f_w + (xx)]=(UINT8)(cc)
+ #define PUTDOT(xx,yy,cc) V_DrawFill(xx,yy,1,1,cc|V_NOSCALESTART);
dx = fl->b.x - fl->a.x;
ax = 2 * (dx < 0 ? -dx : dx);
@@ -901,7 +764,7 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color)
d = ay - ax/2;
for (;;)
{
- PUTDOT(x, y, color);
+ PUTDOT(x, y, color)
if (x == fl->b.x)
return;
if (d >= 0)
@@ -918,7 +781,7 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color)
d = ax - ay/2;
for (;;)
{
- PUTDOT(x, y, color);
+ PUTDOT(x, y, color)
if (y == fl->b.y)
return;
if (d >= 0)
@@ -930,6 +793,8 @@ static void AM_drawFline_soft(const fline_t *fl, INT32 color)
d += ax;
}
}
+
+ #undef PUTDOT
}
//
@@ -1000,15 +865,15 @@ static inline void AM_drawWalls(void)
for (i = 0; i < numlines; i++)
{
- l.a.x = lines[i].v1->x;
- l.a.y = lines[i].v1->y;
- l.b.x = lines[i].v2->x;
- l.b.y = lines[i].v2->y;
+ l.a.x = lines[i].v1->x >> FRACTOMAPBITS;
+ l.a.y = lines[i].v1->y >> FRACTOMAPBITS;
+ l.b.x = lines[i].v2->x >> FRACTOMAPBITS;
+ l.b.y = lines[i].v2->y >> FRACTOMAPBITS;
#ifdef ESLOPE
#define SLOPEPARAMS(slope, end1, end2, normalheight) \
if (slope) { \
- end1 = P_GetZAt(slope, l.a.x, l.a.y); \
- end2 = P_GetZAt(slope, l.b.x, l.b.y); \
+ end1 = P_GetZAt(slope, lines[i].v1->x, lines[i].v1->y); \
+ end2 = P_GetZAt(slope, lines[i].v2->x, lines[i].v2->y); \
} else \
end1 = end2 = normalheight;
@@ -1021,17 +886,12 @@ static inline void AM_drawWalls(void)
#undef SLOPEPARAMS
#endif
-// AM_drawMline(&l, GRAYS + 3); // Old, everything-is-gray automap
if (!lines[i].backsector) // 1-sided
{
if (lines[i].flags & ML_NOCLIMB)
- {
- AM_drawMline(&l, NOCLIMBWALLCOLORS+lightlev);
- }
+ AM_drawMline(&l, NOCLIMBWALLCOLORS);
else
- {
- AM_drawMline(&l, WALLCOLORS+lightlev);
- }
+ AM_drawMline(&l, WALLCOLORS);
}
#ifdef ESLOPE
else if ((backf1 == backc1 && backf2 == backc2) // Back is thok barrier
@@ -1048,24 +908,16 @@ static inline void AM_drawWalls(void)
#endif
{
if (lines[i].flags & ML_NOCLIMB)
- {
- AM_drawMline(&l, NOCLIMBTSWALLCOLORS+lightlev);
- }
+ AM_drawMline(&l, NOCLIMBTSWALLCOLORS);
else
- {
- AM_drawMline(&l, TSWALLCOLORS+lightlev);
- }
+ AM_drawMline(&l, TSWALLCOLORS);
}
else
{
if (lines[i].flags & ML_NOCLIMB)
- {
- AM_drawMline(&l, NOCLIMBTHOKWALLCOLORS+lightlev);
- }
+ AM_drawMline(&l, NOCLIMBTHOKWALLCOLORS);
else
- {
- AM_drawMline(&l, THOKWALLCOLORS+lightlev);
- }
+ AM_drawMline(&l, THOKWALLCOLORS);
}
}
else
@@ -1077,7 +929,7 @@ static inline void AM_drawWalls(void)
if (lines[i].backsector->floorheight
!= lines[i].frontsector->floorheight) {
#endif
- AM_drawMline(&l, NOCLIMBFDWALLCOLORS + lightlev); // floor level change
+ AM_drawMline(&l, NOCLIMBFDWALLCOLORS); // floor level change
}
#ifdef ESLOPE
else if (backc1 != frontc1 || backc2 != frontc2) {
@@ -1085,11 +937,10 @@ static inline void AM_drawWalls(void)
else if (lines[i].backsector->ceilingheight
!= lines[i].frontsector->ceilingheight) {
#endif
- AM_drawMline(&l, NOCLIMBCDWALLCOLORS+lightlev); // ceiling level change
- }
- else {
- AM_drawMline(&l, NOCLIMBTSWALLCOLORS+lightlev);
+ AM_drawMline(&l, NOCLIMBCDWALLCOLORS); // ceiling level change
}
+ else
+ AM_drawMline(&l, NOCLIMBTSWALLCOLORS);
}
else
{
@@ -1099,7 +950,7 @@ static inline void AM_drawWalls(void)
if (lines[i].backsector->floorheight
!= lines[i].frontsector->floorheight) {
#endif
- AM_drawMline(&l, FDWALLCOLORS + lightlev); // floor level change
+ AM_drawMline(&l, FDWALLCOLORS); // floor level change
}
#ifdef ESLOPE
else if (backc1 != frontc1 || backc2 != frontc2) {
@@ -1107,11 +958,10 @@ static inline void AM_drawWalls(void)
else if (lines[i].backsector->ceilingheight
!= lines[i].frontsector->ceilingheight) {
#endif
- AM_drawMline(&l, CDWALLCOLORS+lightlev); // ceiling level change
- }
- else {
- AM_drawMline(&l, TSWALLCOLORS+lightlev);
+ AM_drawMline(&l, CDWALLCOLORS); // ceiling level change
}
+ else
+ AM_drawMline(&l, TSWALLCOLORS);
}
}
}
@@ -1172,6 +1022,11 @@ static void AM_drawLineCharacter(const mline_t *lineguy, size_t lineguylines, fi
l.b.x += x;
l.b.y += y;
+ l.a.x >>= FRACTOMAPBITS;
+ l.a.y >>= FRACTOMAPBITS;
+ l.b.x >>= FRACTOMAPBITS;
+ l.b.y >>= FRACTOMAPBITS;
+
AM_drawMline(&l, color);
}
}
@@ -1180,83 +1035,51 @@ static inline void AM_drawPlayers(void)
{
INT32 i;
player_t *p;
- INT32 color;
+ INT32 color = GREENS;
if (!multiplayer)
{
- AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0,
- plr->mo->angle, DWHITE, plr->mo->x, plr->mo->y);
+ AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, plr->mo->angle, DWHITE, plr->mo->x, plr->mo->y);
return;
}
- // multiplayer
+ // multiplayer (how??)
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
p = &players[i];
- if (p->skincolor == 0)
- color = GREENS;
- else
+ if (p->skincolor > 0)
color = R_GetTranslationColormap(TC_DEFAULT, p->skincolor, GTC_CACHE)[GREENS + 8];
- AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, p->mo->angle,
- color, p->mo->x, p->mo->y);
+ AM_drawLineCharacter(player_arrow, NUMPLYRLINES, 0, p->mo->angle, color, p->mo->x, p->mo->y);
}
}
-static inline void AM_drawThings(INT32 colors, INT32 colorrange)
+static inline void AM_drawThings(UINT8 colors)
{
size_t i;
mobj_t *t;
- (void)colorrange;
for (i = 0; i < numsectors; i++)
{
t = sectors[i].thinglist;
while (t)
{
- AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES,
- 16<angle, colors + lightlev, t->x, t->y);
+ AM_drawLineCharacter(thintriangle_guy, NUMTHINTRIANGLEGUYLINES, 16<angle, colors, t->x, t->y);
t = t->snext;
}
}
}
-static inline void AM_drawMarks(void)
-{
- INT32 i, fx, fy, w, h;
-
- for (i = 0; i < AM_NUMMARKPOINTS; i++)
- {
- if (markpoints[i].x != -1 && marknums[i])
- {
- // w = SHORT(marknums[i]->width);
- // h = SHORT(marknums[i]->height);
- w = 5; // because something's wrong with the wad, i guess
- h = 6; // because something's wrong with the wad, i guess
- fx = CXMTOF(markpoints[i].x);
- fy = CYMTOF(markpoints[i].y);
- if (fx >= f_x && fx <= f_w - w && fy >= f_y && fy <= f_h - h)
- V_DrawPatch(fx, fy, FB, marknums[i]);
- }
- }
-}
-
/** Draws the crosshair, actually just a dot in software mode.
*
* \param color Color for the crosshair.
*/
-static inline void AM_drawCrosshair(INT32 color)
+static inline void AM_drawCrosshair(UINT8 color)
{
- if (rendermode != render_soft)
- return; // BP: should be putpixel here
-
- if (scr_bpp == 1)
- fb[(f_w*(f_h + 1))/2] = (UINT8)color; // single point for now
- else
- *((INT16 *)(void *)fb + (f_w*(f_h + 1))/2) = (INT16)color;
+ V_DrawFill(f_w/2 + f_x, f_h/2 + f_y, 1, 1, color|V_NOSCALESTART);
}
/** Draws the automap.
@@ -1267,13 +1090,10 @@ void AM_Drawer(void)
return;
AM_clearFB(BACKGROUND);
- if (grid)
- AM_drawGrid(GRIDCOLORS);
+ if (draw_grid) AM_drawGrid(GRIDCOLORS);
AM_drawWalls();
AM_drawPlayers();
- AM_drawThings(THINGCOLORS, THINGRANGE);
+ AM_drawThings(THINGCOLORS);
AM_drawCrosshair(XHAIRCOLORS);
-
- AM_drawMarks();
}
diff --git a/src/am_map.h b/src/am_map.h
index df145848b..4e8c782a9 100644
--- a/src/am_map.h
+++ b/src/am_map.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/android/i_sound.c b/src/android/i_sound.c
index 2bb304424..b5a1c3646 100644
--- a/src/android/i_sound.c
+++ b/src/android/i_sound.c
@@ -96,6 +96,37 @@ boolean I_SetSongSpeed(float speed)
return false;
}
+/// ------------------------
+// MUSIC SEEKING
+/// ------------------------
+
+UINT32 I_GetSongLength(void)
+{
+ return 0;
+}
+
+boolean I_SetSongLoopPoint(UINT32 looppoint)
+{
+ (void)looppoint;
+ return false;
+}
+
+UINT32 I_GetSongLoopPoint(void)
+{
+ return 0;
+}
+
+boolean I_SetSongPosition(UINT32 position)
+{
+ (void)position;
+ return false;
+}
+
+UINT32 I_GetSongPosition(void)
+{
+ return 0;
+}
+
/// ------------------------
// MUSIC PLAYBACK
/// ------------------------
@@ -140,3 +171,44 @@ void I_SetMusicVolume(INT32 volume)
{
(void)volume;
}
+
+/// ------------------------
+// MUSIC FADING
+/// ------------------------
+
+void I_SetInternalMusicVolume(UINT8 volume)
+{
+ (void)volume;
+}
+
+void I_StopFadingSong(void)
+{
+}
+
+boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void));
+{
+ (void)target_volume;
+ (void)source_volume;
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void));
+{
+ (void)target_volume;
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeOutStopSong(UINT32 ms)
+{
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeInPlaySong(UINT32 ms, boolean looping)
+{
+ (void)ms;
+ (void)looping;
+ return false;
+}
diff --git a/src/apng.c b/src/apng.c
new file mode 100644
index 000000000..694b3d1e8
--- /dev/null
+++ b/src/apng.c
@@ -0,0 +1,289 @@
+/*
+Copyright 2019, James R.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "apng.h"
+
+#define APNG_INFO_acTL 0x20000U
+
+#define APNG_WROTE_acTL 0x10000U
+
+struct apng_info_def
+{
+ png_uint_32 mode;
+ png_uint_32 valid;
+
+ png_uint_32 num_frames;
+ png_uint_32 num_plays;
+
+ long start_acTL;/* acTL is written here */
+
+ png_flush_ptr output_flush_fn;
+ apng_seek_ptr output_seek_fn;
+ apng_tell_ptr output_tell_fn;
+
+ apng_set_acTL_ptr set_acTL_fn;
+};
+
+/* PROTOS (FUCK COMPILER) */
+void apng_seek (png_structp, apng_const_infop, size_t);
+size_t apng_tell (png_structp, apng_const_infop);
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+void apng_flush (png_structp, apng_infop);
+#ifdef PNG_STDIO_SUPPORTED
+void apng_default_flush (png_structp);
+#endif/* PNG_STDIO_SUPPORTED */
+#endif/* PNG_WRITE_FLUSH_SUPPORTED */
+#ifdef PNG_STDIO_SUPPORTED
+void apng_default_seek (png_structp, size_t);
+size_t apng_default_tell (png_structp);
+#endif/* PNG_STDIO_SUPPORTED */
+void apng_write_IEND (png_structp);
+void apng_write_acTL (png_structp, png_uint_32, png_uint_32);
+#ifndef PNG_WRITE_APNG_SUPPORTED
+png_uint_32 apng_set_acTL_dummy (png_structp, png_infop,
+ png_uint_32, png_uint_32);
+#endif/* PNG_WRITE_APNG_SUPPORTED */
+
+apng_infop
+apng_create_info_struct (png_structp pngp)
+{
+ apng_infop ainfop;
+ (void)pngp;
+ if (( ainfop = calloc(sizeof (apng_info),1) ))
+ {
+ apng_set_write_fn(pngp, ainfop, 0, 0, 0, 0, 0);
+ apng_set_set_acTL_fn(pngp, ainfop, 0);
+ }
+ return ainfop;
+}
+
+void
+apng_destroy_info_struct (png_structp pngp, apng_infopp ainfopp)
+{
+ (void)pngp;
+ if (!( pngp && ainfopp ))
+ return;
+
+ free((*ainfopp));
+}
+
+void
+apng_seek (png_structp pngp, apng_const_infop ainfop, size_t l)
+{
+ (*(ainfop->output_seek_fn))(pngp, l);
+}
+
+size_t
+apng_tell (png_structp pngp, apng_const_infop ainfop)
+{
+ return (*(ainfop->output_tell_fn))(pngp);
+}
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+void
+apng_flush (png_structp pngp, apng_infop ainfop)
+{
+ if (ainfop->output_flush_fn)
+ (*(ainfop->output_flush_fn))(pngp);
+}
+
+#ifdef PNG_STDIO_SUPPORTED
+void
+apng_default_flush (png_structp pngp)
+{
+ if (!( pngp ))
+ return;
+
+ fflush((png_FILE_p)png_get_io_ptr);
+}
+#endif/* PNG_STDIO_SUPPORTED */
+#endif/* PNG_WRITE_FLUSH_SUPPORTED */
+
+#ifdef PNG_STDIO_SUPPORTED
+void
+apng_default_seek (png_structp pngp, size_t l)
+{
+ if (!( pngp ))
+ return;
+
+ if (fseek((png_FILE_p)png_get_io_ptr(pngp), (long)l, SEEK_SET) == -1)
+ png_error(pngp, "Seek Error");
+}
+
+size_t
+apng_default_tell (png_structp pngp)
+{
+ long l;
+
+ if (!( pngp ))
+ {
+ png_error(pngp, "Call to apng_default_tell with NULL pngp failed");
+ }
+
+ if (( l = ftell((png_FILE_p)png_get_io_ptr(pngp)) ) == -1)
+ png_error(pngp, "Tell Error");
+
+ return (size_t)l;
+}
+#endif/* PNG_STDIO_SUPPORTED */
+
+void
+apng_set_write_fn (png_structp pngp, apng_infop ainfop, png_voidp iop,
+ png_rw_ptr write_f, png_flush_ptr flush_f,
+ apng_seek_ptr seek_f, apng_tell_ptr tell_f)
+{
+ if (!( pngp && ainfop ))
+ return;
+
+ png_set_write_fn(pngp, iop, write_f, flush_f);
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+#ifdef PNG_STDIO_SUPPORTED
+ if (!flush_f)
+ ainfop->output_flush_fn = &apng_default_flush;
+ else
+#endif/* PNG_STDIO_SUPPORTED */
+ ainfop->output_flush_fn = flush_f;
+#endif/* PNG_WRITE_FLUSH_SUPPORTED */
+#ifdef PNG_STDIO_SUPPORTED
+ if (!seek_f)
+ ainfop->output_seek_fn = &apng_default_seek;
+ else
+#endif/* PNG_STDIO_SUPPORTED */
+ ainfop->output_seek_fn = seek_f;
+#ifdef PNG_STDIO_SUPPORTED
+ if (!seek_f)
+ ainfop->output_tell_fn = apng_default_tell;
+ else
+#endif/* PNG_STDIO_SUPPORTED */
+ ainfop->output_tell_fn = tell_f;
+}
+
+void
+apng_write_IEND (png_structp pngp)
+{
+ png_byte chunkc[] = "IEND";
+ png_write_chunk(pngp, chunkc, 0, 0);
+}
+
+void
+apng_write_acTL (png_structp pngp, png_uint_32 frames, png_uint_32 plays)
+{
+ png_byte chunkc[] = "acTL";
+ png_byte buf[8];
+ png_save_uint_32(buf, frames);
+ png_save_uint_32(buf + 4, plays);
+ png_write_chunk(pngp, chunkc, buf, 8);
+}
+
+png_uint_32
+apng_set_acTL (png_structp pngp, png_infop infop, apng_infop ainfop,
+ png_uint_32 frames, png_uint_32 plays)
+{
+ (void)pngp;
+ (void)infop;
+ if (!( pngp && infop && ainfop ))
+ return 0;
+
+ ainfop->num_frames = frames;
+ ainfop->num_plays = plays;
+
+ ainfop->valid |= APNG_INFO_acTL;
+
+ return 1;
+}
+
+void
+apng_write_info_before_PLTE (png_structp pngp, png_infop infop,
+ apng_infop ainfop)
+{
+ if (!( pngp && infop && ainfop ))
+ return;
+
+ png_write_info_before_PLTE(pngp, infop);
+
+ if (( ainfop->valid & APNG_INFO_acTL )&&!( ainfop->mode & APNG_WROTE_acTL ))
+ {
+ ainfop->start_acTL = apng_tell(pngp, ainfop);
+
+ apng_write_acTL(pngp, 0, 0);
+ /* modified for runtime dynamic linking */
+ (*(ainfop->set_acTL_fn))(pngp, infop, PNG_UINT_31_MAX, 0);
+
+ ainfop->mode |= APNG_WROTE_acTL;
+ }
+}
+
+void
+apng_write_info (png_structp pngp, png_infop infop,
+ apng_infop ainfop)
+{
+ apng_write_info_before_PLTE(pngp, infop, ainfop);
+ png_write_info(pngp, infop);
+}
+
+void
+apng_write_end (png_structp pngp, png_infop infop, apng_infop ainfop)
+{
+ (void)infop;
+ apng_write_IEND(pngp);
+ apng_seek(pngp, ainfop, ainfop->start_acTL);
+ apng_write_acTL(pngp, ainfop->num_frames, ainfop->num_plays);
+
+#ifdef PNG_WRITE_FLUSH_SUPPORTED
+#ifdef PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED
+ apng_flush(pngp, infop);
+#endif/* PNG_WRITE_FLUSH_SUPPORTED */
+#endif/* PNG_WRITE_FLUSH_AFTER_IEND_SUPPORTED */
+}
+
+#ifndef PNG_WRITE_APNG_SUPPORTED
+png_uint_32
+apng_set_acTL_dummy (png_structp pngp, png_infop infop,
+ png_uint_32 frames, png_uint_32 plays)
+{
+ (void)pngp;
+ (void)infop;
+ (void)frames;
+ (void)plays;
+ return 0;
+}
+#endif/* PNG_WRITE_APNG_SUPPORTED */
+
+/* Dynamic runtime linking capable! (Hopefully.) */
+void
+apng_set_set_acTL_fn (png_structp pngp, apng_infop ainfop,
+ apng_set_acTL_ptr set_acTL_f)
+{
+ (void)pngp;
+ if (!ainfop->set_acTL_fn)
+#ifndef PNG_WRITE_APNG_SUPPORTED
+ ainfop->set_acTL_fn = &apng_set_acTL_dummy;
+#else
+ ainfop->set_acTL_fn = &png_set_acTL;
+#endif/* PNG_WRITE_APNG_SUPPORTED */
+ else
+ ainfop->set_acTL_fn = set_acTL_f;
+}
diff --git a/src/apng.h b/src/apng.h
new file mode 100644
index 000000000..aa7fac3df
--- /dev/null
+++ b/src/apng.h
@@ -0,0 +1,82 @@
+/*
+Copyright 2019, James R.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef APNG_H
+#define APNG_H
+
+#ifndef _MSC_VER
+#ifndef _WII
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+#endif
+#endif
+
+#ifndef _LFS64_LARGEFILE
+#define _LFS64_LARGEFILE
+#endif
+
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 0
+#endif
+
+#include
+
+typedef struct apng_info_def apng_info;
+typedef apng_info * apng_infop;
+typedef const apng_info * apng_const_infop;
+typedef apng_info * * apng_infopp;
+
+typedef void (*apng_seek_ptr)(png_structp, size_t);
+typedef size_t (*apng_tell_ptr)(png_structp);
+
+typedef png_uint_32 (*apng_set_acTL_ptr)(png_structp, png_infop,
+ png_uint_32, png_uint_32);
+
+apng_infop apng_create_info_struct (png_structp png_ptr);
+
+void apng_destroy_info_struct (png_structp png_ptr,
+ apng_infopp info_ptr_ptr);
+
+/* Call the following functions in place of the libpng counterparts. */
+
+png_uint_32 apng_set_acTL (png_structp png_ptr, png_infop info_ptr,
+ apng_infop ainfo_ptr,
+ png_uint_32 num_frames, png_uint_32 num_plays);
+
+void apng_write_info_before_PLTE (png_structp png_ptr, png_infop info_ptr,
+ apng_infop ainfo_ptr);
+void apng_write_info (png_structp png_ptr, png_infop info_ptr,
+ apng_infop ainfo_ptr);
+
+void apng_write_end (png_structp png_ptr, png_infop info_ptr,
+ apng_infop ainfo_ptr);
+
+void apng_set_write_fn (png_structp png_ptr, apng_infop ainfo_ptr,
+ png_voidp io_ptr,
+ png_rw_ptr write_data_fn, png_flush_ptr output_flush_fn,
+ apng_seek_ptr output_seek_fn, apng_tell_ptr output_tell_fn);
+
+void apng_set_set_acTL_fn (png_structp png_ptr, apng_infop ainfo_ptr,
+ apng_set_acTL_ptr set_acTL_fn);
+
+#endif/* APNG_H */
diff --git a/src/asm_defs.inc b/src/asm_defs.inc
index db59d2c69..e494a676e 100644
--- a/src/asm_defs.inc
+++ b/src/asm_defs.inc
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2014 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/b_bot.c b/src/b_bot.c
index 5f884896f..17211b353 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2007-2016 by John "JTE" Muniz.
-// Copyright (C) 2011-2016 by Sonic Team Junior.
+// Copyright (C) 2011-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/b_bot.h b/src/b_bot.h
index 259405f38..20b2803b6 100644
--- a/src/b_bot.h
+++ b/src/b_bot.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2007-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/byteptr.h b/src/byteptr.h
index 364e6520c..aa09d6be4 100644
--- a/src/byteptr.h
+++ b/src/byteptr.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/command.c b/src/command.c
index 10af3a576..62e673322 100644
--- a/src/command.c
+++ b/src/command.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -32,6 +32,7 @@
#include "hu_stuff.h"
#include "p_setup.h"
#include "lua_script.h"
+#include "d_netfil.h" // findfile
//========
// protos.
@@ -49,6 +50,8 @@ static void COM_Wait_f(void);
static void COM_Help_f(void);
static void COM_Toggle_f(void);
+static void CV_EnforceExecVersion(void);
+static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr);
static boolean CV_Command(void);
static consvar_t *CV_FindVar(const char *name);
static const char *CV_StringValue(const char *var_name);
@@ -62,6 +65,18 @@ CV_PossibleValue_t CV_YesNo[] = {{0, "No"}, {1, "Yes"}, {0, NULL}};
CV_PossibleValue_t CV_Unsigned[] = {{0, "MIN"}, {999999999, "MAX"}, {0, NULL}};
CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
+// Filter consvars by EXECVERSION
+// First implementation is 26 (2.1.21), so earlier configs default at 25 (2.1.20)
+// Also set CV_HIDEN during runtime, after config is loaded
+static boolean execversion_enabled = false;
+consvar_t cv_execversion = {"execversion","25",CV_CALL,CV_Unsigned, CV_EnforceExecVersion, 0, NULL, NULL, 0, 0, NULL};
+
+// for default joyaxis detection
+static boolean joyaxis_default = false;
+static boolean joyaxis2_default = false;
+static INT32 joyaxis_count = 0;
+static INT32 joyaxis2_count = 0;
+
#define COM_BUF_SIZE 8192 // command buffer size
#define MAX_ALIAS_RECURSION 100 // max recursion allowed for aliases
@@ -629,6 +644,7 @@ static void COM_CEchoDuration_f(void)
static void COM_Exec_f(void)
{
UINT8 *buf = NULL;
+ char filename[256];
if (COM_Argc() < 2 || COM_Argc() > 3)
{
@@ -637,13 +653,23 @@ static void COM_Exec_f(void)
}
// load file
+ // Try with Argv passed verbatim first, for back compat
FIL_ReadFile(COM_Argv(1), &buf);
if (!buf)
{
- if (!COM_CheckParm("-noerror"))
- CONS_Printf(M_GetText("couldn't execute file %s\n"), COM_Argv(1));
- return;
+ // Now try by searching the file path
+ // filename is modified with the full found path
+ strcpy(filename, COM_Argv(1));
+ if (findfile(filename, NULL, true) != FS_NOTFOUND)
+ FIL_ReadFile(filename, &buf);
+
+ if (!buf)
+ {
+ if (!COM_CheckParm("-noerror"))
+ CONS_Printf(M_GetText("couldn't execute file %s\n"), COM_Argv(1));
+ return;
+ }
}
if (!COM_CheckParm("-silent"))
@@ -1078,7 +1104,7 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth)
if (var->flags & CV_FLOAT)
{
double d = atof(valstr);
- if (!d && valstr[0] != '0')
+ if (fpclassify(d) == FP_ZERO && valstr[0] != '0')
v = INT32_MIN;
else
v = (INT32)(d * FRACUNIT);
@@ -1241,7 +1267,7 @@ static void Got_NetVar(UINT8 **p, INT32 playernum)
char *svalue;
UINT8 stealth = false;
- if (playernum != serverplayer && playernum != adminplayer && !serverloading)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum) && !serverloading)
{
// not from server or remote admin, must be hacked/buggy client
CONS_Alert(CONS_WARNING, M_GetText("Illegal netvar command received from %s\n"), player_names[playernum]);
@@ -1367,7 +1393,7 @@ static void CV_SetCVar(consvar_t *var, const char *value, boolean stealth)
// send the value of the variable
UINT8 buf[128];
UINT8 *p = buf;
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
{
CONS_Printf(M_GetText("Only the server or admin can change: %s %s\n"), var->name, var->string);
return;
@@ -1611,6 +1637,210 @@ void CV_AddValue(consvar_t *var, INT32 increment)
var->changed = 1; // user has changed it now
}
+void CV_InitFilterVar(void)
+{
+ joyaxis_default = joyaxis2_default = true;
+ joyaxis_count = joyaxis2_count = 0;
+}
+
+void CV_ToggleExecVersion(boolean enable)
+{
+ execversion_enabled = enable;
+}
+
+static void CV_EnforceExecVersion(void)
+{
+ if (!execversion_enabled)
+ CV_StealthSetValue(&cv_execversion, EXECVERSION);
+}
+
+static boolean CV_FilterJoyAxisVars(consvar_t *v, const char *valstr)
+{
+ // If ALL axis settings are previous defaults, set them to the new defaults
+ // EXECVERSION < 26 (2.1.21)
+
+ if (joyaxis_default)
+ {
+#if !defined (_WII) && !defined (WMINPUT)
+ if (!stricmp(v->name, "joyaxis_turn"))
+ {
+ if (joyaxis_count > 6) return false;
+ // we're currently setting the new defaults, don't interfere
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "X-Axis")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#if !defined (PSP)
+ if (!stricmp(v->name, "joyaxis_move"))
+ {
+ if (joyaxis_count > 6) return false;
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "Y-Axis")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#endif
+#if !defined (_arch_dreamcast) && !defined (_XBOX) && !defined (PSP)
+ if (!stricmp(v->name, "joyaxis_side"))
+ {
+ if (joyaxis_count > 6) return false;
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "Z-Axis")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#endif
+#if !defined (_XBOX) && !defined (PSP)
+ if (!stricmp(v->name, "joyaxis_look"))
+ {
+ if (joyaxis_count > 6) return false;
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "None")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#endif
+ if (!stricmp(v->name, "joyaxis_fire")
+ || !stricmp(v->name, "joyaxis_firenormal"))
+ {
+ if (joyaxis_count > 6) return false;
+ else if (joyaxis_count == 6) return true;
+
+ if (!stricmp(valstr, "None")) joyaxis_count++;
+ else joyaxis_default = false;
+ }
+#endif
+ // reset all axis settings to defaults
+ if (joyaxis_count == 6)
+ {
+ COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis.name, cv_turnaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis.name, cv_moveaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis.name, cv_sideaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis.name, cv_lookaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis.name, cv_fireaxis.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis.name, cv_firenaxis.defaultvalue));
+ joyaxis_count++;
+ return false;
+ }
+ }
+
+ if (joyaxis2_default)
+ {
+#if !defined (_WII) && !defined (WMINPUT)
+ if (!stricmp(v->name, "joyaxis2_turn"))
+ {
+ if (joyaxis2_count > 6) return false;
+ // we're currently setting the new defaults, don't interfere
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "X-Axis")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+// #if !defined (PSP)
+ if (!stricmp(v->name, "joyaxis2_move"))
+ {
+ if (joyaxis2_count > 6) return false;
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "Y-Axis")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+// #endif
+#if !defined (_arch_dreamcast) && !defined (_XBOX) && !defined (PSP)
+ if (!stricmp(v->name, "joyaxis2_side"))
+ {
+ if (joyaxis2_count > 6) return false;
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "Z-Axis")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+#endif
+#if !defined (_XBOX) // && !defined (PSP)
+ if (!stricmp(v->name, "joyaxis2_look"))
+ {
+ if (joyaxis2_count > 6) return false;
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "None")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+#endif
+ if (!stricmp(v->name, "joyaxis2_fire")
+ || !stricmp(v->name, "joyaxis2_firenormal"))
+ {
+ if (joyaxis2_count > 6) return false;
+ else if (joyaxis2_count == 6) return true;
+
+ if (!stricmp(valstr, "None")) joyaxis2_count++;
+ else joyaxis2_default = false;
+ }
+#endif
+
+ // reset all axis settings to defaults
+ if (joyaxis2_count == 6)
+ {
+ COM_BufInsertText(va("%s \"%s\"\n", cv_turnaxis2.name, cv_turnaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_moveaxis2.name, cv_moveaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_sideaxis2.name, cv_sideaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_lookaxis2.name, cv_lookaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_fireaxis2.name, cv_fireaxis2.defaultvalue));
+ COM_BufInsertText(va("%s \"%s\"\n", cv_firenaxis2.name, cv_firenaxis2.defaultvalue));
+ joyaxis2_count++;
+ return false;
+ }
+ }
+
+ // we haven't reached our counts yet, or we're not default
+ return true;
+}
+
+static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr)
+{
+ // True means allow the CV change, False means block it
+
+ // We only care about CV_SAVE because this filters the user's config files
+ // We do this same check in CV_Command
+ if (!(v->flags & CV_SAVE))
+ return true;
+
+ if (GETMAJOREXECVERSION(cv_execversion.value) < 26) // 26 = 2.1.21
+ {
+ // MOUSE SETTINGS
+ // alwaysfreelook split between first and third person (chasefreelook)
+ // mousemove was on by default, which invalidates the current approach
+ if (!stricmp(v->name, "alwaysmlook")
+ || !stricmp(v->name, "alwaysmlook2")
+ || !stricmp(v->name, "mousemove")
+ || !stricmp(v->name, "mousemove2"))
+ return false;
+
+ // mousesens was changed from 35 to 20 due to oversensitivity
+ if ((!stricmp(v->name, "mousesens")
+ || !stricmp(v->name, "mousesens2")
+ || !stricmp(v->name, "mouseysens")
+ || !stricmp(v->name, "mouseysens2"))
+ && atoi(valstr) == 35)
+ return false;
+
+ // JOYSTICK DEFAULTS
+ // use_joystick was changed from 0 to 1 to automatically use a joystick if available
+#if defined(HAVE_SDL) || defined(_WINDOWS)
+ if ((!stricmp(v->name, "use_joystick")
+ || !stricmp(v->name, "use_joystick2"))
+ && atoi(valstr) == 0)
+ return false;
+#endif
+
+ // axis defaults were changed to be friendly to 360 controllers
+ // if ALL axis settings are defaults, then change them to new values
+ if (!CV_FilterJoyAxisVars(v, valstr))
+ return false;
+ }
+ return true;
+}
+
/** Displays or changes a variable from the console.
* Since the user is presumed to have been directly responsible
* for this change, the variable is marked as changed this game.
@@ -1635,8 +1865,11 @@ static boolean CV_Command(void)
return true;
}
- CV_Set(v, COM_Argv(1));
- v->changed = 1; // now it's been changed by (presumably) the user
+ if (!(v->flags & CV_SAVE) || CV_FilterVarByVersion(v, COM_Argv(1)))
+ {
+ CV_Set(v, COM_Argv(1));
+ v->changed = 1; // now it's been changed by (presumably) the user
+ }
return true;
}
diff --git a/src/command.h b/src/command.h
index 989ead8cf..e6767825c 100644
--- a/src/command.h
+++ b/src/command.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -125,6 +125,13 @@ extern CV_PossibleValue_t CV_OnOff[];
extern CV_PossibleValue_t CV_YesNo[];
extern CV_PossibleValue_t CV_Unsigned[];
extern CV_PossibleValue_t CV_Natural[];
+
+// Filter consvars by version
+extern consvar_t cv_execversion;
+
+void CV_InitFilterVar(void);
+void CV_ToggleExecVersion(boolean enable);
+
// register a variable for use at the console
void CV_RegisterVar(consvar_t *variable);
diff --git a/src/config.h.in b/src/config.h.in
index 174b34430..fc32aef82 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -13,7 +13,6 @@
#define ASSET_HASH_SRB2_PK3 "${SRB2_ASSET_srb2.pk3_HASH}"
#define ASSET_HASH_PLAYER_DTA "${SRB2_ASSET_player.dta_HASH}"
-#define ASSET_HASH_RINGS_DTA "${SRB2_ASSET_rings.dta_HASH}"
#define ASSET_HASH_ZONES_DTA "${SRB2_ASSET_zones.dta_HASH}"
#ifdef USE_PATCH_DTA
#define ASSET_HASH_PATCH_PK3 "${SRB2_ASSET_patch.pk3_HASH}"
@@ -21,20 +20,18 @@
#define SRB2_COMP_REVISION "${SRB2_COMP_REVISION}"
#define SRB2_COMP_BRANCH "${SRB2_COMP_BRANCH}"
-#define SRB2_GIT_DESCRIBE "${SRB2_GIT_DESCRIBE}"
-#define SRB2_GIT_BRANCH "${SRB2_GIT_BRANCH}"
#define CMAKE_ASSETS_DIR "${CMAKE_SOURCE_DIR}/assets"
#else
/* Manually defined asset hashes for non-CMake builds
- * Last updated 2015 / 05 / 03
+ * Last updated 2015 / 05 / 03 - v2.1.15 - main assets
+ * Last updated 2018 / ?? / ?? - v2.2 - patch.pk3
*/
#define ASSET_HASH_SRB2_PK3 "c1b9577687f8a795104aef4600720ea7"
#define ASSET_HASH_ZONES_DTA "303838c6c534d9540288360fa49cca60"
#define ASSET_HASH_PLAYER_DTA "cfca0f1c73023cbbd8f844f45480f799"
-#define ASSET_HASH_RINGS_DTA "85901ad4bf94637e5753d2ac2c03ea26"
#ifdef USE_PATCH_DTA
#define ASSET_HASH_PATCH_PK3 "dbbf8bc6121618ee3be2d5b14650429b"
#endif
diff --git a/src/console.c b/src/console.c
index f4234d949..81b62851c 100644
--- a/src/console.c
+++ b/src/console.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -56,10 +56,7 @@ static boolean consoleready; // console prompt is ready
INT32 con_destlines; // vid lines used by console at final position
static INT32 con_curlines; // vid lines currently used by console
- INT32 con_clipviewtop; // clip value for planes & sprites, so that the
- // part of the view covered by the console is not
- // drawn when not needed, this must be -1 when
- // console is off
+ INT32 con_clipviewtop; // (useless)
static INT32 con_hudlines; // number of console heads up message lines
static INT32 con_hudtime[MAXHUDLINES]; // remaining time of display for hud msg lines
@@ -96,11 +93,10 @@ static size_t input_len; // length of current line, used to bound cursor and suc
// protos.
static void CON_InputInit(void);
static void CON_RecalcSize(void);
+static void CON_ChangeHeight(void);
static void CONS_hudlines_Change(void);
static void CONS_backcolor_Change(void);
-static void CON_DrawBackpic(patch_t *pic, INT32 startx, INT32 destwidth);
-//static void CON_DrawBackpic2(pic_t *pic, INT32 startx, INT32 destwidth);
//======================================================================
// CONSOLE VARS AND COMMANDS
@@ -131,11 +127,24 @@ static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}
// whether to use console background picture, or translucent mode
static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+// \todo SRB2-CHAT 2.1 colors -- pending translation to 2.2 palette indexes
+#if 0
+static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Black"}, {2, "Sepia"},
+ {3, "Brown"}, {4, "Pink"}, {5, "Raspberry"},
+ {6, "Red"}, {7, "Creamsicle"}, {8, "Orange"},
+ {9, "Gold"}, {10,"Yellow"}, {11,"Emerald"},
+ {12,"Green"}, {13,"Cyan"}, {14,"Steel"},
+ {15,"Periwinkle"}, {16,"Blue"}, {17,"Purple"},
+ {18,"Lavender"},
+ {0, NULL}};
+#else
static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Gray"}, {2, "Brown"},
{3, "Red"}, {4, "Orange"}, {5, "Yellow"},
{6, "Green"}, {7, "Blue"}, {8, "Purple"},
{9, "Magenta"}, {10, "Aqua"},
{0, NULL}};
+#endif
+
consvar_t cons_backcolor = {"con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL};
static void CON_Print(char *msg);
@@ -238,13 +247,42 @@ static UINT8 promptbgcolor = UINT8_MAX;
void CON_SetupBackColormapEx(INT32 color, boolean prompt)
{
UINT16 i, palsum;
- UINT8 j, palindex, shift;
+ UINT8 j, palindex;
UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
+ INT32 shift = 6;
if (color == INT32_MAX)
color = cons_backcolor.value;
shift = 6; // 12 colors -- shift of 7 means 6 colors
+
+ // \todo SRB2-CHAT colors, pending translation to 2.2 palette indexes
+#if 0
+ switch (color)
+ {
+ case 0: palindex = 15; break; // White
+ case 1: palindex = 31; break; // Gray
+ case 2: palindex = 47; break; // Sepia
+ case 3: palindex = 63; break; // Brown
+ case 4: palindex = 150; shift = 7; break; // Pink
+ case 5: palindex = 127; shift = 7; break; // Raspberry
+ case 6: palindex = 143; break; // Red
+ case 7: palindex = 86; shift = 7; break; // Creamsicle
+ case 8: palindex = 95; break; // Orange
+ case 9: palindex = 119; shift = 7; break; // Gold
+ case 10: palindex = 111; break; // Yellow
+ case 11: palindex = 191; shift = 7; break; // Emerald
+ case 12: palindex = 175; break; // Green
+ case 13: palindex = 219; break; // Cyan
+ case 14: palindex = 207; shift = 7; break; // Steel
+ case 15: palindex = 230; shift = 7; break; // Periwinkle
+ case 16: palindex = 239; break; // Blue
+ case 17: palindex = 199; shift = 7; break; // Purple
+ case 18: palindex = 255; shift = 7; break; // Lavender
+ // Default green
+ default: palindex = 175; break;
+ }
+#else
switch (color)
{
case 0: palindex = 15; break; // White
@@ -261,6 +299,7 @@ void CON_SetupBackColormapEx(INT32 color, boolean prompt)
// Default green
default: palindex = 175; color = 11; break;
}
+#endif
if (prompt)
{
@@ -461,6 +500,12 @@ static void CON_RecalcSize(void)
con_destlines = vid.height;
}
+ if (con_destlines > 0) // Resize console if already open
+ {
+ CON_ChangeHeight();
+ con_curlines = con_destlines;
+ }
+
// check for change of video width
if (conw == con_width)
return; // didn't change
@@ -510,6 +555,20 @@ static void CON_RecalcSize(void)
Z_Free(tmp_buffer);
}
+static void CON_ChangeHeight(void)
+{
+ INT32 minheight = 20 * con_scalefactor; // 20 = 8+8+4
+
+ // toggle console in
+ con_destlines = (cons_height.value*vid.height)/100;
+ if (con_destlines < minheight)
+ con_destlines = minheight;
+ else if (con_destlines > vid.height)
+ con_destlines = vid.height;
+
+ con_destlines &= ~0x3; // multiple of text row height
+}
+
// Handles Console moves in/out of screen (per frame)
//
static void CON_MoveConsole(void)
@@ -598,16 +657,7 @@ void CON_Ticker(void)
CON_ClearHUD();
}
else
- {
- // toggle console in
- con_destlines = (cons_height.value*vid.height)/100;
- if (con_destlines < minheight)
- con_destlines = minheight;
- else if (con_destlines > vid.height)
- con_destlines = vid.height;
-
- con_destlines &= ~0x3; // multiple of text row height
- }
+ CON_ChangeHeight();
}
// console movement
@@ -871,7 +921,7 @@ boolean CON_Responder(event_t *ev)
// ...why shouldn't it eat the key? if it doesn't, it just means you
// can control Sonic from the console, which is silly
- return true; //return false;
+ return true;//return false;
}
// command completion forward (tab) and backward (shift-tab)
@@ -1065,15 +1115,30 @@ boolean CON_Responder(event_t *ev)
else if (key == KEY_KPADSLASH)
key = '/';
- if (shiftdown)
+ // capslock
+ if (key == KEY_CAPSLOCK) // it's a toggle.
+ {
+ if (capslock)
+ capslock = false;
+ else
+ capslock = true;
+ return true;
+ }
+
+ if (key >= 'a' && key <= 'z')
+ {
+ if (capslock ^ shiftdown)
+ key = shiftxform[key];
+ }
+ else if (shiftdown)
key = shiftxform[key];
// enter a char into the command prompt
if (key < 32 || key > 127)
- return true; // even if key can't be printed, eat it anyway
+ return true;
// add key to cmd line here
- if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers
+ if (key >= 'A' && key <= 'Z' && !(shiftdown ^ capslock)) //this is only really necessary for dedicated servers
key = key + 'a' - 'A';
if (input_sel != input_cur)
@@ -1105,6 +1170,7 @@ static void CON_Print(char *msg)
{
size_t l;
INT32 controlchars = 0; // for color changing
+ char color = '\x80'; // keep color across lines
if (msg == NULL)
return;
@@ -1130,7 +1196,7 @@ static void CON_Print(char *msg)
{
if (*msg & 0x80)
{
- con_line[con_cx++] = *(msg++);
+ color = con_line[con_cx++] = *(msg++);
controlchars++;
continue;
}
@@ -1138,12 +1204,14 @@ static void CON_Print(char *msg)
{
con_cy--;
CON_Linefeed();
+ color = '\x80';
controlchars = 0;
}
else if (*msg == '\n') // linefeed
{
CON_Linefeed();
- controlchars = 0;
+ con_line[con_cx++] = color;
+ controlchars = 1;
}
else if (*msg == ' ') // space
{
@@ -1151,7 +1219,8 @@ static void CON_Print(char *msg)
if (con_cx - controlchars >= con_width-11)
{
CON_Linefeed();
- controlchars = 0;
+ con_line[con_cx++] = color;
+ controlchars = 1;
}
}
else if (*msg == '\t')
@@ -1166,7 +1235,8 @@ static void CON_Print(char *msg)
if (con_cx - controlchars >= con_width-11)
{
CON_Linefeed();
- controlchars = 0;
+ con_line[con_cx++] = color;
+ controlchars = 1;
}
}
msg++;
@@ -1183,7 +1253,8 @@ static void CON_Print(char *msg)
if ((con_cx - controlchars) + l > con_width-11)
{
CON_Linefeed();
- controlchars = 0;
+ con_line[con_cx++] = color;
+ controlchars = 1;
}
// a word at a time
@@ -1255,24 +1326,15 @@ void CONS_Printf(const char *fmt, ...)
if (con_startup)
{
#ifdef _WINDOWS
- static lumpnum_t con_backpic_lumpnum = UINT32_MAX;
- patch_t *con_backpic;
+ patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_CACHE);
- if (con_backpic_lumpnum == UINT32_MAX)
- con_backpic_lumpnum = W_GetNumForName("CONSBACK");
+ // Jimita: CON_DrawBackpic just called V_DrawScaledPatch
+ V_DrawScaledPatch(0, 0, 0, con_backpic);
- // We load the raw lump, even in hardware mode
- con_backpic = (patch_t*)W_CacheLumpNum(con_backpic_lumpnum, PU_CACHE);
-
- // show startup screen and message using only 'software' graphics
- // (rendermode may be hardware accelerated, but the video mode is not set yet)
- CON_DrawBackpic(con_backpic, 0, vid.width); // put console background
- I_LoadingScreen(txt);
-
- Z_Unlock(con_backpic);
+ W_UnlockCachedPatch(con_backpic);
+ I_LoadingScreen(txt); // Win32/OS2 only
#else
- // here we display the console background and console text
- // (no hardware accelerated support for these versions)
+ // here we display the console text
CON_Drawer();
I_FinishUpdate(); // page flip or blit buffer
#endif
@@ -1458,8 +1520,8 @@ static void CON_DrawHudlines(void)
if (con_hudlines <= 0)
return;
- if (chat_on)
- y = charheight; // leave place for chat input in the first row of text
+ if (chat_on && OLDCHAT)
+ y = charheight; // leave place for chat input in the first row of text (only do it if consolechat is on.)
else
y = 0;
@@ -1499,64 +1561,6 @@ static void CON_DrawHudlines(void)
con_clearlines = y; // this is handled by HU_Erase();
}
-// Scale a pic_t at 'startx' pos, to 'destwidth' columns.
-// startx, destwidth is resolution dependent
-// Used to draw console borders, console background.
-// The pic must be sized BASEVIDHEIGHT height.
-static void CON_DrawBackpic(patch_t *pic, INT32 startx, INT32 destwidth)
-{
- (void)startx;
- (void)destwidth;
- V_DrawScaledPatch(0, 0, 0, pic);
-}
-
-#if 0
-static inline void CON_DrawBackpic2(pic_t *pic, INT32 startx, INT32 destwidth)
-{
- INT32 x, y;
- INT32 v;
- UINT8 *src, *dest;
- const UINT8 *deststop;
- INT32 frac, fracstep;
-
- dest = screens[0]+startx;
- deststop = screens[0] + vid.rowbytes * vid.height;
-
- for (y = 0; y < con_curlines; y++, dest += vid.width)
- {
- // scale the picture to the resolution
- v = SHORT(pic->height) - ((con_curlines - y) * (BASEVIDHEIGHT-1) / vid.height) - 1;
-
- src = pic->data + v*SHORT(pic->width);
-
- // in case of the console backpic, simplify
- if (SHORT(pic->width) == destwidth)
- M_Memcpy(dest, src, destwidth);
- else
- {
- // scale pic to screen width
- frac = 0;
- fracstep = (SHORT(pic->width)<<16)/destwidth;
- for (x = 0; x < destwidth; x += 4)
- {
- if (dest+x > deststop) break;
- dest[x] = src[frac>>FRACBITS];
- frac += fracstep;
- if (dest+x+1 > deststop) break;
- dest[x+1] = src[frac>>FRACBITS];
- frac += fracstep;
- if (dest+x+2 > deststop) break;
- dest[x+2] = src[frac>>FRACBITS];
- frac += fracstep;
- if (dest+x+3 > deststop) break;
- dest[x+3] = src[frac>>FRACBITS];
- frac += fracstep;
- }
- }
- }
-}
-#endif
-
// draw the console background, text, and prompt if enough place
//
static void CON_DrawConsole(void)
@@ -1579,18 +1583,10 @@ static void CON_DrawConsole(void)
// draw console background
if (cons_backpic.value || con_forcepic)
{
- static lumpnum_t con_backpic_lumpnum = UINT32_MAX;
- patch_t *con_backpic;
+ patch_t *con_backpic = W_CachePatchName("CONSBACK", PU_CACHE);
- if (con_backpic_lumpnum == UINT32_MAX)
- con_backpic_lumpnum = W_GetNumForName("CONSBACK");
-
- con_backpic = (patch_t*)W_CachePatchNum(con_backpic_lumpnum, PU_CACHE);
-
- if (rendermode != render_soft)
- V_DrawScaledPatch(0, 0, 0, con_backpic);
- else if (rendermode != render_none)
- CON_DrawBackpic(con_backpic, 0, vid.width); // picture as background
+ // Jimita: CON_DrawBackpic just called V_DrawScaledPatch
+ V_DrawScaledPatch(0, 0, 0, con_backpic);
W_UnlockCachedPatch(con_backpic);
}
@@ -1608,8 +1604,7 @@ static void CON_DrawConsole(void)
i = con_cy - con_scrollup;
// skip the last empty line due to the cursor being at the start of a new line
- if (!con_scrollup && !con_cx)
- i--;
+ i--;
i -= (con_curlines - minheight) / charheight;
diff --git a/src/console.h b/src/console.h
index c194f44bf..f62808033 100644
--- a/src/console.h
+++ b/src/console.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index b24a0f89a..0d4384b66 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -23,6 +23,7 @@
#include "g_game.h"
#include "hu_stuff.h"
#include "keys.h"
+#include "g_input.h" // JOY1
#include "m_menu.h"
#include "console.h"
#include "d_netfil.h"
@@ -1384,7 +1385,6 @@ static boolean SV_SendServerConfig(INT32 node)
netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
netbuffer->u.servercfg.gametype = (UINT8)gametype;
netbuffer->u.servercfg.modifiedgame = (UINT8)modifiedgame;
- netbuffer->u.servercfg.adminplayer = (SINT8)adminplayer;
// we fill these structs with FFs so that any players not in game get sent as 0xFFFF
// which is nice and easy for us to detect
@@ -1392,8 +1392,12 @@ static boolean SV_SendServerConfig(INT32 node)
memset(netbuffer->u.servercfg.playercolor, 0xFF, sizeof(netbuffer->u.servercfg.playercolor));
memset(netbuffer->u.servercfg.playeravailabilities, 0xFF, sizeof(netbuffer->u.servercfg.playeravailabilities));
+ memset(netbuffer->u.servercfg.adminplayers, -1, sizeof(netbuffer->u.servercfg.adminplayers));
+
for (i = 0; i < MAXPLAYERS; i++)
{
+ netbuffer->u.servercfg.adminplayers[i] = (SINT8)adminplayers[i];
+
if (!playeringame[i])
continue;
netbuffer->u.servercfg.playerskins[i] = (UINT8)players[i].skin;
@@ -1644,6 +1648,8 @@ static void SendAskInfo(INT32 node, boolean viams)
serverelem_t serverlist[MAXSERVERLIST];
UINT32 serverlistcount = 0;
+#define FORCECLOSE 0x8000
+
static void SL_ClearServerList(INT32 connectedserver)
{
UINT32 i;
@@ -1651,7 +1657,7 @@ static void SL_ClearServerList(INT32 connectedserver)
for (i = 0; i < serverlistcount; i++)
if (connectedserver != serverlist[i].node)
{
- Net_CloseConnection(serverlist[i].node);
+ Net_CloseConnection(serverlist[i].node|FORCECLOSE);
serverlist[i].node = 0;
}
serverlistcount = 0;
@@ -1733,12 +1739,25 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
// Make sure MS version matches our own, to
// thwart nefarious servers who lie to the MS.
- if(strcmp(version, server_list[i].version) == 0)
+ if (strcmp(version, server_list[i].version) == 0)
{
INT32 node = I_NetMakeNodewPort(server_list[i].ip, server_list[i].port);
if (node == -1)
break; // no more node free
SendAskInfo(node, true);
+ // Force close the connection so that servers can't eat
+ // up nodes forever if we never get a reply back from them
+ // (usually when they've not forwarded their ports).
+ //
+ // Don't worry, we'll get in contact with the working
+ // servers again when they send SERVERINFO to us later!
+ //
+ // (Note: as a side effect this probably means every
+ // server in the list will probably be using the same node (e.g. node 1),
+ // not that it matters which nodes they use when
+ // the connections are closed afterwards anyway)
+ // -- Monster Iestyn 12/11/18
+ Net_CloseConnection(node|FORCECLOSE);
}
}
}
@@ -1965,7 +1984,7 @@ static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic
I_OsPolling();
key = I_GetKey();
- if (key == KEY_ESCAPE)
+ if (key == KEY_ESCAPE || key == KEY_JOY1+1)
{
CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
@@ -2049,7 +2068,7 @@ static void CL_ConnectToServer(boolean viams)
G_SetGamestate(GS_WAITINGPLAYERS);
wipegamestate = GS_WAITINGPLAYERS;
- adminplayer = -1;
+ ClearAdminPlayers();
pnumnodes = 1;
oldtic = I_GetTime() - 1;
#ifndef NONET
@@ -2412,6 +2431,8 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason)
#ifdef HAVE_BLUA
LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting
+#else
+ (void)reason;
#endif
// Reset player data
@@ -2426,8 +2447,10 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason)
// Reset the name
sprintf(player_names[playernum], "Player %d", playernum+1);
- if (playernum == adminplayer)
- adminplayer = -1; // don't stay admin after you're gone
+ if (IsPlayerAdmin(playernum))
+ {
+ RemoveAdminPlayer(playernum); // don't stay admin after you're gone
+ }
if (playernum == displayplayer)
displayplayer = consoleplayer; // don't look through someone's view who isn't there
@@ -2545,7 +2568,7 @@ static void Command_Nodes(void)
if (I_GetNodeAddress && (address = I_GetNodeAddress(playernode[i])) != NULL)
CONS_Printf(" - %s", address);
- if (i == adminplayer)
+ if (IsPlayerAdmin(i))
CONS_Printf(M_GetText(" (verified admin)"));
if (players[i].spectator)
@@ -2570,7 +2593,7 @@ static void Command_Ban(void)
return;
}
- if (server || adminplayer == consoleplayer)
+ if (server || IsPlayerAdmin(consoleplayer))
{
UINT8 buf[3 + MAX_REASONLENGTH];
UINT8 *p = buf;
@@ -2636,7 +2659,7 @@ static void Command_Kick(void)
return;
}
- if (server || adminplayer == consoleplayer)
+ if (server || IsPlayerAdmin(consoleplayer))
{
UINT8 buf[3 + MAX_REASONLENGTH];
UINT8 *p = buf;
@@ -2645,13 +2668,16 @@ static void Command_Kick(void)
if (pn == -1 || pn == 0)
return;
- // Special case if we are trying to kick a player who is downloading the game state:
- // trigger a timeout instead of kicking them, because a kick would only
- // take effect after they have finished downloading
- if (sendingsavegame[playernode[pn]])
+ if (server)
{
- Net_ConnectionTimeout(playernode[pn]);
- return;
+ // Special case if we are trying to kick a player who is downloading the game state:
+ // trigger a timeout instead of kicking them, because a kick would only
+ // take effect after they have finished downloading
+ if (sendingsavegame[playernode[pn]])
+ {
+ Net_ConnectionTimeout(playernode[pn]);
+ return;
+ }
}
WRITESINT8(p, pn);
@@ -2694,7 +2720,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
pnum = READUINT8(*p);
msg = READUINT8(*p);
- if (pnum == serverplayer && playernum == adminplayer)
+ if (pnum == serverplayer && IsPlayerAdmin(playernum))
{
CONS_Printf(M_GetText("Server is being shut down remotely. Goodbye!\n"));
@@ -2705,7 +2731,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
}
// Is playernum authorized to make this kick?
- if (playernum != serverplayer && playernum != adminplayer
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum)
&& !(playerpernode[playernode[playernum]] == 2
&& nodetoplayer2[playernode[playernum]] == pnum))
{
@@ -2754,7 +2780,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
msg = KICK_MSG_CON_FAIL;
}
- CONS_Printf("\x82%s ", player_names[pnum]);
+ //CONS_Printf("\x82%s ", player_names[pnum]);
// If a verified admin banned someone, the server needs to know about it.
// If the playernum isn't zero (the server) then the server needs to record the ban.
@@ -2771,17 +2797,17 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
switch (msg)
{
case KICK_MSG_GO_AWAY:
- CONS_Printf(M_GetText("has been kicked (Go away)\n"));
+ HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false);
kickreason = KR_KICK;
break;
#ifdef NEWPING
case KICK_MSG_PING_HIGH:
- CONS_Printf(M_GetText("left the game (Broke ping limit)\n"));
+ HU_AddChatText(va("\x82*%s left the game (Broke ping limit)", player_names[pnum]), false);
kickreason = KR_PINGLIMIT;
break;
#endif
case KICK_MSG_CON_FAIL:
- CONS_Printf(M_GetText("left the game (Synch failure)\n"));
+ HU_AddChatText(va("\x82*%s left the game (Synch Failure)", player_names[pnum]), false);
kickreason = KR_SYNCH;
if (M_CheckParm("-consisdump")) // Helps debugging some problems
@@ -2818,26 +2844,26 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
}
break;
case KICK_MSG_TIMEOUT:
- CONS_Printf(M_GetText("left the game (Connection timeout)\n"));
+ HU_AddChatText(va("\x82*%s left the game (Connection timeout)", player_names[pnum]), false);
kickreason = KR_TIMEOUT;
break;
case KICK_MSG_PLAYER_QUIT:
if (netgame) // not splitscreen/bots
- CONS_Printf(M_GetText("left the game\n"));
+ HU_AddChatText(va("\x82*%s left the game", player_names[pnum]), false);
kickreason = KR_LEAVE;
break;
case KICK_MSG_BANNED:
- CONS_Printf(M_GetText("has been banned (Don't come back)\n"));
+ HU_AddChatText(va("\x82*%s has been banned (Don't come back)", player_names[pnum]), false);
kickreason = KR_BAN;
break;
case KICK_MSG_CUSTOM_KICK:
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
- CONS_Printf(M_GetText("has been kicked (%s)\n"), reason);
+ HU_AddChatText(va("\x82*%s has been kicked (%s)", player_names[pnum], reason), false);
kickreason = KR_KICK;
break;
case KICK_MSG_CUSTOM_BAN:
READSTRINGN(*p, reason, MAX_REASONLENGTH+1);
- CONS_Printf(M_GetText("has been banned (%s)\n"), reason);
+ HU_AddChatText(va("\x82*%s has been banned (%s)", player_names[pnum], reason), false);
kickreason = KR_BAN;
break;
}
@@ -2976,6 +3002,7 @@ void SV_ResetServer(void)
playeringame[i] = false;
playernode[i] = UINT8_MAX;
sprintf(player_names[i], "Player %d", i + 1);
+ adminplayers[i] = -1; // Populate the entire adminplayers array with -1.
}
mynode = 0;
@@ -3050,7 +3077,7 @@ void D_QuitNetGame(void)
}
D_CloseConnection();
- adminplayer = -1;
+ ClearAdminPlayers();
DEBFILE("===========================================================================\n"
" Log finish\n"
@@ -3081,7 +3108,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
INT16 node, newplayernum;
boolean splitscreenplayer;
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
// protect against hacked/buggy client
CONS_Alert(CONS_WARNING, M_GetText("Illegal add player command received from %s\n"), player_names[playernum]);
@@ -3110,9 +3137,6 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
if (newplayernum+1 > doomcom->numslots)
doomcom->numslots = (INT16)(newplayernum+1);
- if (netgame)
- CONS_Printf(M_GetText("Player %d has joined the game (node %d)\n"), newplayernum+1, node);
-
// the server is creating my player
if (node == mynode)
{
@@ -3134,11 +3158,17 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
D_SendPlayerConfig();
addedtogame = true;
}
- else if (server && netgame && cv_showjoinaddress.value)
+
+ if (netgame)
{
- const char *address;
- if (I_GetNodeAddress && (address = I_GetNodeAddress(node)) != NULL)
- CONS_Printf(M_GetText("Player Address is %s\n"), address);
+ if (server && cv_showjoinaddress.value)
+ {
+ const char *address;
+ if (I_GetNodeAddress && (address = I_GetNodeAddress(node)) != NULL)
+ HU_AddChatText(va("\x82*Player %d has joined the game (node %d) (%s)", newplayernum+1, node, address), false); // merge join notification + IP to avoid clogging console/chat.
+ }
+ else
+ HU_AddChatText(va("\x82*Player %d has joined the game (node %d)", newplayernum+1, node), false); // if you don't wanna see the join address.
}
if (server && multiplayer && motd[0] != '\0')
@@ -3606,7 +3636,8 @@ static void HandlePacketFromAwayNode(SINT8 node)
maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
gametype = netbuffer->u.servercfg.gametype;
modifiedgame = netbuffer->u.servercfg.modifiedgame;
- adminplayer = netbuffer->u.servercfg.adminplayer;
+ for (j = 0; j < MAXPLAYERS; j++)
+ adminplayers[j] = netbuffer->u.servercfg.adminplayers[j];
memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
}
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index d5edd79d7..4ed7ff957 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -291,7 +291,7 @@ typedef struct
UINT8 gametype;
UINT8 modifiedgame;
- SINT8 adminplayer; // Needs to be signed
+ SINT8 adminplayers[MAXPLAYERS]; // Needs to be signed
char server_context[8]; // Unique context id, generated at server startup.
@@ -439,9 +439,9 @@ extern doomdata_t *netbuffer;
extern consvar_t cv_playbackspeed;
-#define BASEPACKETSIZE ((size_t)&(((doomdata_t *)0)->u))
-#define FILETXHEADER ((size_t)((filetx_pak *)0)->data)
-#define BASESERVERTICSSIZE ((size_t)&(((doomdata_t *)0)->u.serverpak.cmds[0]))
+#define BASEPACKETSIZE offsetof(doomdata_t, u)
+#define FILETXHEADER offsetof(filetx_pak, data)
+#define BASESERVERTICSSIZE offsetof(doomdata_t, u.serverpak.cmds[0])
#define KICK_MSG_GO_AWAY 1
#define KICK_MSG_CON_FAIL 2
diff --git a/src/d_event.h b/src/d_event.h
index b0d0e3c58..e9374efaf 100644
--- a/src/d_event.h
+++ b/src/d_event.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/d_main.c b/src/d_main.c
index 6ee78cabb..2ae75c047 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -160,6 +160,7 @@ void D_PostEvent_end(void) {};
UINT8 shiftdown = 0; // 0x1 left, 0x2 right
UINT8 ctrldown = 0; // 0x1 left, 0x2 right
UINT8 altdown = 0; // 0x1 left, 0x2 right
+boolean capslock = 0; // gee i wonder what this does.
//
// D_ModifierKeyResponder
// Sets global shift/ctrl/alt variables, never actually eats events
@@ -320,8 +321,7 @@ static void D_Display(void)
if (!gametic)
break;
HU_Erase();
- if (automapactive)
- AM_Drawer();
+ AM_Drawer();
break;
case GS_INTERMISSION:
@@ -763,7 +763,6 @@ void D_StartTitle(void)
advancedemo = false;
F_InitMenuPresValues();
F_StartTitleScreen();
- CON_ToggleOff();
currentMenu = &MainDef; // reset the current menu ID
@@ -826,7 +825,7 @@ static void IdentifyVersion(void)
const char *srb2waddir = NULL;
#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
- // change to the directory where 'srb2.srb' is found
+ // change to the directory where 'srb2.pk3' is found
srb2waddir = I_LocateWad();
#endif
@@ -886,25 +885,20 @@ static void IdentifyVersion(void)
#if !defined (HAVE_SDL) || defined (HAVE_MIXER)
{
- const char *musicfile = "music.dta";
- const char *musicpath = va(pandf,srb2waddir,musicfile);
- int ms = W_VerifyNMUSlumps(musicpath); // Don't forget the music!
- if (ms == 1)
- D_AddFile(musicpath);
- else if (ms == 0)
- I_Error("File %s has been modified with non-music lumps",musicfile);
- }
-#endif
+#define MUSICTEST(str) \
+ {\
+ const char *musicpath = va(pandf,srb2waddir,str);\
+ int ms = W_VerifyNMUSlumps(musicpath); \
+ if (ms == 1) \
+ D_AddFile(musicpath); \
+ else if (ms == 0) \
+ I_Error("File "str" has been modified with non-music/sound lumps"); \
+ }
-#ifdef DEVELOP // This section can be deleted when music_new is merged with music.dta
- {
- const char *musicfile = "music_new.dta";
- const char *musicpath = va(pandf,srb2waddir,musicfile);
- int ms = W_VerifyNMUSlumps(musicpath); // Don't forget the music!
- if (ms == 1)
- D_AddFile(musicpath);
- else if (ms == 0)
- I_Error("File %s has been modified with non-music lumps",musicfile);
+ MUSICTEST("music.dta")
+#ifdef DEVELOP // remove when music_new.dta is merged into music.dta
+ MUSICTEST("music_new.dta")
+#endif
}
#endif
}
@@ -977,6 +971,20 @@ void D_SRB2Main(void)
INT32 pstartmap = 1;
boolean autostart = false;
+ // Print GPL notice for our console users (Linux)
+ CONS_Printf(
+ "\n\nSonic Robo Blast 2\n"
+ "Copyright (C) 1998-2018 by Sonic Team Junior\n\n"
+ "This program comes with ABSOLUTELY NO WARRANTY.\n\n"
+ "This is free software, and you are welcome to redistribute it\n"
+ "and/or modify it under the terms of the GNU General Public License\n"
+ "as published by the Free Software Foundation; either version 2 of\n"
+ "the License, or (at your option) any later version.\n"
+ "See the 'LICENSE.txt' file for details.\n\n"
+ "Sonic the Hedgehog and related characters are trademarks of SEGA.\n"
+ "We do not claim ownership of SEGA's intellectual property used\n"
+ "in this program.\n\n");
+
// keep error messages until the final flush(stderr)
#if !defined (PC_DOS) && !defined(NOTERMIOS)
if (setvbuf(stderr, NULL, _IOFBF, 1000))
@@ -1145,12 +1153,39 @@ void D_SRB2Main(void)
// Make backups of some SOCcable tables.
P_BackupTables();
- // Setup default unlockable conditions
- M_SetupDefaultConditionSets();
+ // Setup character tables
+ // Have to be done here before files are loaded
+ M_InitCharacterTables();
+
+ mainwads = 0;
+
+#ifndef DEVELOP // md5s last updated 12/14/14
+
+ // Check MD5s of autoloaded files
+ W_VerifyFileMD5(mainwads++, ASSET_HASH_SRB2_PK3); // srb2.pk3
+ W_VerifyFileMD5(mainwads++, ASSET_HASH_ZONES_DTA); // zones.dta
+ W_VerifyFileMD5(mainwads++, ASSET_HASH_PLAYER_DTA); // player.dta
+#ifdef USE_PATCH_DTA
+ W_VerifyFileMD5(mainwads++, ASSET_HASH_PATCH_DTA); // patch.dta
+#endif
+ // don't check music.dta because people like to modify it, and it doesn't matter if they do
+ // ...except it does if they slip maps in there, and that's what W_VerifyNMUSlumps is for.
+ //mainwads++; // music.dta does not increment mainwads (see <= 2.1.21)
+ //mainwads++; // neither does music_new.dta
+#else
+
+ mainwads++; // srb2.pk3
+ mainwads++; // zones.dta
+ mainwads++; // player.dta
+#ifdef USE_PATCH_DTA
+ mainwads++; // patch.dta
+#endif
+ //mainwads++; // music.dta does not increment mainwads (see <= 2.1.21)
+ //mainwads++; // neither does music_new.dta
// load wad, including the main wad file
CONS_Printf("W_InitMultipleFiles(): Adding IWAD and main PWADs.\n");
- if (!W_InitMultipleFiles(startupwadfiles))
+ if (!W_InitMultipleFiles(startupwadfiles, mainwads))
#ifdef _DEBUG
CONS_Error("A WAD file was not found or not valid.\nCheck the log to see which ones.\n");
#else
@@ -1158,27 +1193,9 @@ void D_SRB2Main(void)
#endif
D_CleanFile();
-#ifndef DEVELOP // md5s last updated 12/14/14
-
- // Check MD5s of autoloaded files
- //W_VerifyFileMD5(0, ASSET_HASH_SRB2_PK3); // srb2.pk3
- //W_VerifyFileMD5(1, ASSET_HASH_ZONES_DTA); // zones.dta
- //W_VerifyFileMD5(2, ASSET_HASH_PLAYER_DTA); // player.dta
-#ifdef USE_PATCH_DTA
- //W_VerifyFileMD5(3, ASSET_HASH_PATCH_PK3); // patch.pk3
-#endif
-
- // don't check music.dta because people like to modify it, and it doesn't matter if they do
- // ...except it does if they slip maps in there, and that's what W_VerifyNMUSlumps is for.
#endif //ifndef DEVELOP
- mainwads = 3; // there are 3 wads not to unload
-#ifdef USE_PATCH_DTA
- ++mainwads; // patch.pk3 adds one more
-#endif
-#ifdef DEVELOP
- ++mainwads; // music_new, too
-#endif
+ mainwadstally = packetsizetally;
mainwadstally = packetsizetally;
@@ -1256,7 +1273,7 @@ void D_SRB2Main(void)
else
{
if (M_CheckParm("-nomidimusic"))
- midi_disabled = true; ; // WARNING: DOS version initmusic in I_StartupSound
+ midi_disabled = true; // WARNING: DOS version initmusic in I_StartupSound
if (M_CheckParm("-nodigmusic"))
digital_disabled = true; // WARNING: DOS version initmusic in I_StartupSound
}
@@ -1413,14 +1430,14 @@ void D_SRB2Main(void)
}
else if (M_CheckParm("-skipintro"))
{
- CON_ToggleOff();
- CON_ClearHUD();
F_InitMenuPresValues();
F_StartTitleScreen();
}
else
F_StartIntro(); // Tails 03-03-2002
+ CON_ToggleOff();
+
if (dedicated && server)
{
levelstarttic = gametic;
diff --git a/src/d_main.h b/src/d_main.h
index 4c9c99ea5..d67a5bb49 100644
--- a/src/d_main.h
+++ b/src/d_main.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/d_net.c b/src/d_net.c
index 2c0a8a329..9f68c187c 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -27,6 +27,7 @@
#include "d_clisrv.h"
#include "z_zone.h"
#include "i_tcp.h"
+#include "d_main.h" // srb2home
//
// NETWORKING
@@ -1369,12 +1370,12 @@ boolean D_CheckNetGame(void)
{
k++;
sprintf(filename, "debug%d.txt", k);
- debugfile = fopen(filename, "w");
+ debugfile = fopen(va("%s" PATHSEP "%s", srb2home, filename), "w");
}
if (debugfile)
- CONS_Printf(M_GetText("debug output to: %s\n"), filename);
+ CONS_Printf(M_GetText("debug output to: %s\n"), va("%s" PATHSEP "%s", srb2home, filename));
else
- CONS_Alert(CONS_WARNING, M_GetText("cannot debug output to file %s!\n"), filename);
+ CONS_Alert(CONS_WARNING, M_GetText("cannot debug output to file %s!\n"), va("%s" PATHSEP "%s", srb2home, filename));
}
#endif
diff --git a/src/d_net.h b/src/d_net.h
index 84814ce39..3d1058702 100644
--- a/src/d_net.h
+++ b/src/d_net.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -19,9 +19,10 @@
#define __D_NET__
// Max computers in a game
-#define MAXNETNODES 32
+#define MAXNETNODES (MAXPLAYERS+4)
#define BROADCASTADDR MAXNETNODES
#define MAXSPLITSCREENPLAYERS 2 // Max number of players on a single computer
+//#define NETSPLITSCREEN // Kart's splitscreen netgame feature
#define STATLENGTH (TICRATE*2)
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index d17860243..392f0e5af 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -142,7 +142,9 @@ static void Command_Changepassword_f(void);
static void Command_Login_f(void);
static void Got_Login(UINT8 **cp, INT32 playernum);
static void Got_Verification(UINT8 **cp, INT32 playernum);
+static void Got_Removal(UINT8 **cp, INT32 playernum);
static void Command_Verify_f(void);
+static void Command_RemoveAdmin_f(void);
static void Command_MotD_f(void);
static void Got_MotD_f(UINT8 **cp, INT32 playernum);
@@ -244,9 +246,9 @@ INT32 cv_debug;
consvar_t cv_usemouse = {"use_mouse", "On", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_usemouse2 = {"use_mouse2", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse2, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_usejoystick = {"use_joystick", "0", CV_SAVE|CV_CALL, usejoystick_cons_t,
+consvar_t cv_usejoystick = {"use_joystick", "1", CV_SAVE|CV_CALL, usejoystick_cons_t,
I_InitJoystick, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_usejoystick2 = {"use_joystick2", "0", CV_SAVE|CV_CALL, usejoystick_cons_t,
+consvar_t cv_usejoystick2 = {"use_joystick2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t,
I_InitJoystick2, 0, NULL, NULL, 0, 0, NULL};
#if (defined (LJOYSTICK) || defined (HAVE_SDL))
#ifdef LJOYSTICK
@@ -366,7 +368,7 @@ consvar_t cv_sleep = {"cpusleep", "-1", CV_SAVE, sleeping_cons_t, NULL, -1, NULL
INT16 gametype = GT_COOP;
boolean splitscreen = false;
boolean circuitmap = false;
-INT32 adminplayer = -1;
+INT32 adminplayers[MAXPLAYERS];
/// \warning Keep this up-to-date if you add/remove/rename net text commands
const char *netxcmdnames[MAXNETXCMD - 1] =
@@ -435,8 +437,10 @@ void D_RegisterServerCommands(void)
COM_AddCommand("password", Command_Changepassword_f);
RegisterNetXCmd(XD_LOGIN, Got_Login);
COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
- COM_AddCommand("verify", Command_Verify_f);
+ COM_AddCommand("promote", Command_Verify_f);
RegisterNetXCmd(XD_VERIFIED, Got_Verification);
+ COM_AddCommand("demote", Command_RemoveAdmin_f);
+ RegisterNetXCmd(XD_DEMOTED, Got_Removal);
COM_AddCommand("motd", Command_MotD_f);
RegisterNetXCmd(XD_SETMOTD, Got_MotD_f); // For remote admin
@@ -708,10 +712,20 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_msaturation);
// m_menu.c
+ CV_RegisterVar(&cv_compactscoreboard);
+ CV_RegisterVar(&cv_chatheight);
+ CV_RegisterVar(&cv_chatwidth);
+ CV_RegisterVar(&cv_chattime);
+ CV_RegisterVar(&cv_chatspamprotection);
+ CV_RegisterVar(&cv_chatbacktint);
+ CV_RegisterVar(&cv_consolechat);
+ CV_RegisterVar(&cv_chatnotifications);
CV_RegisterVar(&cv_crosshair);
CV_RegisterVar(&cv_crosshair2);
CV_RegisterVar(&cv_alwaysfreelook);
CV_RegisterVar(&cv_alwaysfreelook2);
+ CV_RegisterVar(&cv_chasefreelook);
+ CV_RegisterVar(&cv_chasefreelook2);
CV_RegisterVar(&cv_tutorialprompt);
// g_input.c
@@ -1038,8 +1052,8 @@ static void SetPlayerName(INT32 playernum, char *newname)
if (strcasecmp(newname, player_names[playernum]) != 0)
{
if (netgame)
- CONS_Printf(M_GetText("%s renamed to %s\n"),
- player_names[playernum], newname);
+ HU_AddChatText(va("\x82*%s renamed to %s", player_names[playernum], newname), false);
+
strcpy(player_names[playernum], newname);
}
}
@@ -1217,7 +1231,7 @@ static void SendNameAndColor(void)
snacpending++;
// Don't change name if muted
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
+ if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
else // Cleanup name if changing it
CleanupPlayerName(consoleplayer, cv_playername.zstring);
@@ -1653,7 +1667,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
mapchangepending = 0;
// spawn the server if needed
// reset players if there is a new one
- if (!(adminplayer == consoleplayer))
+ if (!IsPlayerAdmin(consoleplayer))
{
if (SV_SpawnServer())
buf[0] &= ~(1<<1);
@@ -1711,7 +1725,7 @@ static void Command_Map_f(void)
return;
}
- if (client && !(adminplayer == consoleplayer))
+ if (client && !IsPlayerAdmin(consoleplayer))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -1835,8 +1849,11 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
UINT8 flags;
INT32 resetplayer = 1, lastgametype;
UINT8 skipprecutscene, FLS;
+#ifdef HAVE_BLUA
+ INT16 mapnumber;
+#endif
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal map change received from %s\n"), player_names[playernum]);
if (server)
@@ -1882,6 +1899,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
mapname, resetplayer, lastgametype, gametype, chmappending));
CONS_Printf(M_GetText("Speeding off to level...\n"));
}
+
if (demoplayback && !timingdemo)
precache = false;
@@ -1896,13 +1914,13 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
}
#ifdef HAVE_BLUA
- LUAh_MapChange();
+ mapnumber = M_MapNumber(mapname[3], mapname[4]);
+ LUAh_MapChange(mapnumber);
#endif
G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene, FLS);
if (demoplayback && !timingdemo)
precache = true;
- CON_ToggleOff();
if (timingdemo)
G_DoneLevelLoad();
@@ -1928,7 +1946,7 @@ static void Command_Pause(void)
else
WRITEUINT8(cp, 0);
- if (cv_pause.value || server || (adminplayer == consoleplayer))
+ if (cv_pause.value || server || (IsPlayerAdmin(consoleplayer)))
{
if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
{
@@ -1946,7 +1964,7 @@ static void Got_Pause(UINT8 **cp, INT32 playernum)
UINT8 dedicatedpause = false;
const char *playername;
- if (netgame && !cv_pause.value && playernum != serverplayer && playernum != adminplayer)
+ if (netgame && !cv_pause.value && playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal pause command received from %s\n"), player_names[playernum]);
if (server)
@@ -2075,7 +2093,7 @@ static void Got_RandomSeed(UINT8 **cp, INT32 playernum)
*/
static void Command_Clearscores_f(void)
{
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
return;
SendNetXCmd(XD_CLEARSCORES, NULL, 1);
@@ -2095,7 +2113,7 @@ static void Got_Clearscores(UINT8 **cp, INT32 playernum)
INT32 i;
(void)cp;
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal clear scores command received from %s\n"), player_names[playernum]);
if (server)
@@ -2316,7 +2334,7 @@ static void Command_ServerTeamChange_f(void)
UINT16 usvalue;
NetPacket.value.l = NetPacket.value.b = 0;
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -2463,7 +2481,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
if (NetPacket.packet.verification) // Special marker that the server sent the request
{
- if (playernum != serverplayer && (playernum != adminplayer))
+ if (playernum != serverplayer && (!IsPlayerAdmin(playernum)))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
if (server)
@@ -2502,7 +2520,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
}
else
{
- if (playernum != serverplayer && (playernum != adminplayer))
+ if (playernum != serverplayer && (!IsPlayerAdmin(playernum)))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal team change received from player %s\n"), player_names[playernum]);
if (server)
@@ -2824,13 +2842,56 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
if (!memcmp(sentmd5, finalmd5, 16))
{
CONS_Printf(M_GetText("%s passed authentication.\n"), player_names[playernum]);
- COM_BufInsertText(va("verify %d\n", playernum)); // do this immediately
+ COM_BufInsertText(va("promote %d\n", playernum)); // do this immediately
}
else
CONS_Printf(M_GetText("Password from %s failed.\n"), player_names[playernum]);
#endif
}
+boolean IsPlayerAdmin(INT32 playernum)
+{
+ INT32 i;
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playernum == adminplayers[i])
+ return true;
+
+ return false;
+}
+
+void SetAdminPlayer(INT32 playernum)
+{
+ INT32 i;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playernum == adminplayers[i])
+ return; // Player is already admin
+
+ if (adminplayers[i] == -1)
+ {
+ adminplayers[i] = playernum; // Set the player to a free spot
+ break; // End the loop now. If it keeps going, the same player might get assigned to two slots.
+ }
+
+
+ }
+}
+
+void ClearAdminPlayers(void)
+{
+ INT32 i;
+ for (i = 0; i < MAXPLAYERS; i++)
+ adminplayers[i] = -1;
+}
+
+void RemoveAdminPlayer(INT32 playernum)
+{
+ INT32 i;
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playernum == adminplayers[i])
+ adminplayers[i] = -1;
+}
+
static void Command_Verify_f(void)
{
char buf[8]; // Should be plenty
@@ -2851,7 +2912,7 @@ static void Command_Verify_f(void)
if (COM_Argc() != 2)
{
- CONS_Printf(M_GetText("verify : give admin privileges to a node\n"));
+ CONS_Printf(M_GetText("promote : give admin privileges to a node\n"));
return;
}
@@ -2885,7 +2946,7 @@ static void Got_Verification(UINT8 **cp, INT32 playernum)
return;
}
- adminplayer = num;
+ SetAdminPlayer(num);
if (num != consoleplayer)
return;
@@ -2893,6 +2954,62 @@ static void Got_Verification(UINT8 **cp, INT32 playernum)
CONS_Printf(M_GetText("You are now a server administrator.\n"));
}
+static void Command_RemoveAdmin_f(void)
+{
+ char buf[8]; // Should be plenty
+ char *temp;
+ INT32 playernum;
+
+ if (client)
+ {
+ CONS_Printf(M_GetText("Only the server can use this.\n"));
+ return;
+ }
+
+ if (COM_Argc() != 2)
+ {
+ CONS_Printf(M_GetText("demote : remove admin privileges from a node\n"));
+ return;
+ }
+
+ strlcpy(buf, COM_Argv(1), sizeof(buf));
+
+ playernum = atoi(buf);
+
+ temp = buf;
+
+ WRITEUINT8(temp, playernum);
+
+ if (playeringame[playernum])
+ SendNetXCmd(XD_DEMOTED, buf, 1);
+}
+
+static void Got_Removal(UINT8 **cp, INT32 playernum)
+{
+ INT16 num = READUINT8(*cp);
+
+ if (playernum != serverplayer) // it's not from the server (hacker or bug)
+ {
+ CONS_Alert(CONS_WARNING, M_GetText("Illegal demotion received from %s (serverplayer is %s)\n"), player_names[playernum], player_names[serverplayer]);
+ if (server)
+ {
+ UINT8 buf[2];
+
+ buf[0] = (UINT8)playernum;
+ buf[1] = KICK_MSG_CON_FAIL;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ }
+ return;
+ }
+
+ RemoveAdminPlayer(num);
+
+ if (num != consoleplayer)
+ return;
+
+ CONS_Printf(M_GetText("You are no longer a server administrator.\n"));
+}
+
static void Command_MotD_f(void)
{
size_t i, j;
@@ -2904,7 +3021,7 @@ static void Command_MotD_f(void)
return;
}
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -2928,7 +3045,7 @@ static void Command_MotD_f(void)
}
if ((netgame || multiplayer) && client)
- SendNetXCmd(XD_SETMOTD, mymotd, sizeof(motd));
+ SendNetXCmd(XD_SETMOTD, mymotd, i); // send the actual size of the motd string, not the full buffer's size
else
{
strcpy(motd, mymotd);
@@ -2951,7 +3068,7 @@ static void Got_MotD_f(UINT8 **cp, INT32 playernum)
if (!isprint(mymotd[i]) || mymotd[i] == ';')
kick = true;
- if ((playernum != serverplayer && playernum != adminplayer) || kick)
+ if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick)
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal motd change received from %s\n"), player_names[playernum]);
if (server)
@@ -2988,7 +3105,7 @@ static void Command_RunSOC(void)
else
fn = COM_Argv(1);
- if (netgame && !(server || consoleplayer == adminplayer))
+ if (netgame && !(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -3014,7 +3131,7 @@ static void Got_RunSOCcmd(UINT8 **cp, INT32 playernum)
char filename[256];
filestatus_t ncs = FS_NOTFOUND;
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal runsoc command received from %s\n"), player_names[playernum]);
if (server)
@@ -3085,7 +3202,7 @@ static void Command_Addfile(void)
if (!musiconly)
{
// ... But only so long as they contain nothing more then music and sprites.
- if (netgame && !(server || adminplayer == consoleplayer))
+ if (netgame && !(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -3148,7 +3265,7 @@ static void Command_Addfile(void)
WRITEMEM(buf_p, md5sum, 16);
}
- if (adminplayer == consoleplayer && (!server)) // Request to add file
+ if (IsPlayerAdmin(consoleplayer) && (!server)) // Request to add file
SendNetXCmd(XD_REQADDFILE, buf, buf_p - buf);
else
SendNetXCmd(XD_ADDFILE, buf, buf_p - buf);
@@ -3161,7 +3278,11 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
UINT8 md5sum[16];
boolean kick = false;
boolean toomany = false;
- INT32 i;
+ INT32 i,j;
+ serverinfo_pak *dummycheck = NULL;
+
+ // Shut the compiler up.
+ (void)dummycheck;
READSTRINGN(*cp, filename, 240);
READMEM(*cp, md5sum, 16);
@@ -3175,7 +3296,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
if (!isprint(filename[i]) || filename[i] == ';')
kick = true;
- if ((playernum != serverplayer && playernum != adminplayer) || kick)
+ if ((playernum != serverplayer && !IsPlayerAdmin(playernum)) || kick)
{
UINT8 buf[2];
@@ -3209,8 +3330,9 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
CONS_Printf("%s",message);
- if (adminplayer)
- COM_BufAddText(va("sayto %d %s", adminplayer, message));
+ for (j = 0; j < MAXPLAYERS; j++)
+ if (adminplayers[j])
+ COM_BufAddText(va("sayto %d %s", adminplayers[j], message));
return;
}
@@ -3300,10 +3422,56 @@ static void Command_ListWADS_f(void)
static void Command_Version_f(void)
{
#ifdef DEVELOP
- CONS_Printf("Sonic Robo Blast 2 %s-%s (%s %s)\n", compbranch, comprevision, compdate, comptime);
+ CONS_Printf("Sonic Robo Blast 2 %s-%s (%s %s) ", compbranch, comprevision, compdate, comptime);
#else
- CONS_Printf("Sonic Robo Blast 2 %s (%s %s %s)\n", VERSIONSTRING, compdate, comptime, comprevision);
+ CONS_Printf("Sonic Robo Blast 2 %s (%s %s %s) ", VERSIONSTRING, compdate, comptime, comprevision);
#endif
+
+ // Base library
+#if defined( HAVE_SDL)
+ CONS_Printf("SDL ");
+#elif defined(_WINDOWS)
+ CONS_Printf("DD ");
+#endif
+
+ // OS
+ // Would be nice to use SDL_GetPlatform for this
+#if defined (_WIN32) || defined (_WIN64)
+ CONS_Printf("Windows ");
+#elif defined(__linux__)
+ CONS_Printf("Linux ");
+#elif defined(MACOSX)
+ CONS_Printf("macOS ");
+#elif defined(UNIXCOMMON)
+ CONS_Printf("Unix (Common) ");
+#else
+ CONS_Printf("Other OS ");
+#endif
+
+ // Bitness
+ if (sizeof(void*) == 4)
+ CONS_Printf("32-bit ");
+ else if (sizeof(void*) == 8)
+ CONS_Printf("64-bit ");
+ else // 16-bit? 128-bit?
+ CONS_Printf("Bits Unknown ");
+
+ // No ASM?
+#ifdef NOASM
+ CONS_Printf("\x85" "NOASM " "\x80");
+#endif
+
+ // Debug build
+#ifdef _DEBUG
+ CONS_Printf("\x85" "DEBUG " "\x80");
+#endif
+
+ // DEVELOP build
+#ifdef DEVELOP
+ CONS_Printf("\x87" "DEVELOP " "\x80");
+#endif
+
+ CONS_Printf("\n");
}
#ifdef UPDATE_ALERT
@@ -3671,7 +3839,7 @@ void D_GameTypeChanged(INT32 lastgametype)
if (playeringame[i])
players[i].ctfteam = 0;
- if (server || (adminplayer == consoleplayer))
+ if (server || (IsPlayerAdmin(consoleplayer)))
{
CV_StealthSetValue(&cv_teamscramble, 0);
teamscramble = 0;
@@ -3754,7 +3922,7 @@ static void TeamScramble_OnChange(void)
if (!cv_teamscramble.value)
teamscramble = 0;
- if (!G_GametypeHasTeams() && (server || consoleplayer == adminplayer))
+ if (!G_GametypeHasTeams() && (server || IsPlayerAdmin(consoleplayer)))
{
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
CV_StealthSetValue(&cv_teamscramble, 0);
@@ -3930,7 +4098,7 @@ static void Command_ExitLevel_f(void)
{
if (!(netgame || (multiplayer && gametype != GT_COOP)) && !cv_debug)
CONS_Printf(M_GetText("This only works in a netgame.\n"));
- else if (!(server || (adminplayer == consoleplayer)))
+ else if (!(server || (IsPlayerAdmin(consoleplayer))))
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else if (gamestate != GS_LEVEL || demoplayback)
CONS_Printf(M_GetText("You must be in a level to use this.\n"));
@@ -3946,7 +4114,7 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum)
if (gameaction == ga_completed)
return;
- if (playernum != serverplayer && playernum != adminplayer)
+ if (playernum != serverplayer && !IsPlayerAdmin(playernum))
{
CONS_Alert(CONS_WARNING, M_GetText("Illegal exitlevel command received from %s\n"), player_names[playernum]);
if (server)
@@ -4054,7 +4222,7 @@ static void Command_Cheats_f(void)
{
if (COM_CheckParm("off"))
{
- if (!(server || (adminplayer == consoleplayer)))
+ if (!(server || (IsPlayerAdmin(consoleplayer))))
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
else
CV_ResetCheatNetVars();
@@ -4064,7 +4232,7 @@ static void Command_Cheats_f(void)
if (CV_CheatsEnabled())
{
CONS_Printf(M_GetText("At least one CHEAT-marked variable has been changed -- Cheats are enabled.\n"));
- if (server || (adminplayer == consoleplayer))
+ if (server || (IsPlayerAdmin(consoleplayer)))
CONS_Printf(M_GetText("Type CHEATS OFF to reset all cheat variables to default.\n"));
}
else
@@ -4133,6 +4301,20 @@ static void Command_Archivetest_f(void)
*/
static void ForceSkin_OnChange(void)
{
+ if ((server || IsPlayerAdmin(consoleplayer)) && (cv_forceskin.value < -1 || cv_forceskin.value >= numskins))
+ {
+ if (cv_forceskin.value == -2)
+ CV_SetValue(&cv_forceskin, numskins-1);
+ else
+ {
+ // hack because I can't restrict this and still allow added skins to be used with forceskin.
+ if (!menuactive)
+ CONS_Printf(M_GetText("Valid skin numbers are 0 to %d (-1 disables)\n"), numskins - 1);
+ CV_SetValue(&cv_forceskin, -1);
+ }
+ return;
+ }
+
// NOT in SP, silly!
if (!(netgame || multiplayer))
return;
@@ -4149,7 +4331,7 @@ static void ForceSkin_OnChange(void)
//Allows the player's name to be changed if cv_mute is off.
static void Name_OnChange(void)
{
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
+ if (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer)))
{
CONS_Alert(CONS_NOTICE, M_GetText("You may not change your name when chat is muted.\n"));
CV_StealthSet(&cv_playername, player_names[consoleplayer]);
@@ -4272,7 +4454,7 @@ static void Color2_OnChange(void)
*/
static void Mute_OnChange(void)
{
- if (server || (adminplayer == consoleplayer))
+ if (server || (IsPlayerAdmin(consoleplayer)))
return;
if (cv_mute.value)
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index 3ffa3f3f1..d9889d979 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -137,9 +137,10 @@ typedef enum
XD_DELFILE, // 18 - replace next time we add an XD
XD_SETMOTD, // 19
XD_SUICIDE, // 20
+ XD_DEMOTED, // 21
#ifdef HAVE_BLUA
- XD_LUACMD, // 21
- XD_LUAVAR, // 22
+ XD_LUACMD, // 22
+ XD_LUAVAR, // 23
#endif
MAXNETXCMD
} netxcmd_t;
@@ -194,6 +195,10 @@ void Command_ExitGame_f(void);
void Command_Retry_f(void);
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore
void D_MapChange(INT32 pmapnum, INT32 pgametype, boolean pultmode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pfromlevelselect);
+boolean IsPlayerAdmin(INT32 playernum);
+void SetAdminPlayer(INT32 playernum);
+void ClearAdminPlayers(void);
+void RemoveAdminPlayer(INT32 playernum);
void ItemFinder_OnChange(void);
void D_SetPassword(const char *pw);
diff --git a/src/d_netfil.c b/src/d_netfil.c
index 92ecab950..db9fa12e4 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/d_netfil.h b/src/d_netfil.h
index 28d9d5a63..3d7c2ed59 100644
--- a/src/d_netfil.h
+++ b/src/d_netfil.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/d_player.h b/src/d_player.h
index 0b271ec2f..e68992e15 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/d_think.h b/src/d_think.h
index 2831b9f2b..b907c17fd 100644
--- a/src/d_think.h
+++ b/src/d_think.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h
index 83f684b6f..1ea015439 100644
--- a/src/d_ticcmd.h
+++ b/src/d_ticcmd.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/dehacked.c b/src/dehacked.c
index 6f4fb59e6..4f68c5062 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -305,11 +305,11 @@ static void clear_levels(void)
static boolean findFreeSlot(INT32 *num)
{
// Send the character select entry to a free slot.
- while (*num < 32 && (description[*num].used))
+ while (*num < MAXSKINS && (description[*num].used))
*num = *num+1;
// No more free slots. :(
- if (*num >= 32)
+ if (*num >= MAXSKINS)
return false;
description[*num].picname[0] = '\0'; // Redesign your logo. (See M_DrawSetupChoosePlayerMenu in m_menu.c...)
@@ -1161,6 +1161,13 @@ static void readlevelheader(MYFILE *f, INT32 num)
#endif
else if (fastcmp(word, "MUSICTRACK"))
mapheaderinfo[num-1]->mustrack = ((UINT16)i - 1);
+ else if (fastcmp(word, "MUSICPOS"))
+ mapheaderinfo[num-1]->muspos = (UINT32)get_number(word2);
+ else if (fastcmp(word, "MUSICINTERFADEOUT"))
+ mapheaderinfo[num-1]->musinterfadeout = (UINT32)get_number(word2);
+ else if (fastcmp(word, "MUSICINTER"))
+ deh_strlcpy(mapheaderinfo[num-1]->musintername, word2,
+ sizeof(mapheaderinfo[num-1]->musintername), va("Level header %d: intermission music", num));
else if (fastcmp(word, "FORCECHARACTER"))
{
strlcpy(mapheaderinfo[num-1]->forcecharacter, word2, SKINNAMESIZE+1);
@@ -1265,6 +1272,13 @@ static void readlevelheader(MYFILE *f, INT32 num)
else
mapheaderinfo[num-1]->levelflags &= ~LF_SAVEGAME;
}
+ else if (fastcmp(word, "MIXNIGHTSCOUNTDOWN"))
+ {
+ if (i || word2[0] == 'T' || word2[0] == 'Y')
+ mapheaderinfo[num-1]->levelflags |= LF_MIXNIGHTSCOUNTDOWN;
+ else
+ mapheaderinfo[num-1]->levelflags &= ~LF_MIXNIGHTSCOUNTDOWN;
+ }
// Individual triggers for menu flags
else if (fastcmp(word, "HIDDEN"))
@@ -1461,6 +1475,10 @@ static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
{
cutscenes[num]->scene[scenenum].musswitchflags = ((UINT16)i) & MUSIC_TRACKMASK;
}
+ else if (fastcmp(word, "MUSICPOS"))
+ {
+ cutscenes[num]->scene[scenenum].musswitchposition = (UINT32)get_number(word2);
+ }
else if (fastcmp(word, "MUSICLOOP"))
{
cutscenes[num]->scene[scenenum].musicloop = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
@@ -2647,12 +2665,8 @@ static void reademblemdata(MYFILE *f, INT32 num)
else
emblemlocations[num-1].type = (UINT8)value;
}
- else if (fastcmp(word, "X"))
- emblemlocations[num-1].x = (INT16)value;
- else if (fastcmp(word, "Y"))
- emblemlocations[num-1].y = (INT16)value;
- else if (fastcmp(word, "Z"))
- emblemlocations[num-1].z = (INT16)value;
+ else if (fastcmp(word, "TAG"))
+ emblemlocations[num-1].tag = (INT16)value;
else if (fastcmp(word, "MAPNUM"))
{
// Support using the actual map name,
@@ -3294,7 +3308,7 @@ static void readmaincfg(MYFILE *f)
}
else if (fastcmp(word, "LOOPTITLE"))
{
- looptitle = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y');
+ looptitle = (value || word2[0] == 'T' || word2[0] == 'Y');
titlechanged = true;
}
else if (fastcmp(word, "TITLEMAP"))
@@ -3335,7 +3349,7 @@ static void readmaincfg(MYFILE *f)
}
else if (fastcmp(word, "DISABLESPEEDADJUST"))
{
- disableSpeedAdjust = (UINT8)get_number(word2);
+ disableSpeedAdjust = (value || word2[0] == 'T' || word2[0] == 'Y');
}
else if (fastcmp(word, "NUMDEMOS"))
{
@@ -3380,7 +3394,7 @@ static void readmaincfg(MYFILE *f)
strncpy(timeattackfolder, gamedatafilename, min(filenamelen, sizeof (timeattackfolder)));
timeattackfolder[min(filenamelen, sizeof (timeattackfolder) - 1)] = '\0';
- strncpy(savegamename, timeattackfolder, strlen(timeattackfolder));
+ strcpy(savegamename, timeattackfolder);
strlcat(savegamename, "%u.ssg", sizeof(savegamename));
// can't use sprintf since there is %u in savegamename
strcatbf(savegamename, srb2home, PATHSEP);
@@ -3616,7 +3630,7 @@ static void ignorelines(MYFILE *f)
Z_Free(s);
}
-static void DEH_LoadDehackedFile(MYFILE *f)
+static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word;
@@ -3676,15 +3690,16 @@ static void DEH_LoadDehackedFile(MYFILE *f)
continue;
}
word2 = strtok(NULL, " ");
+ if (word2) {
+ strupr(word2);
+ if (word2[strlen(word2) - 1] == '\n')
+ word2[strlen(word2) - 1] = '\0';
+ i = atoi(word2);
+ }
+ else
+ i = 0;
if (fastcmp(word, "CHARACTER"))
{
- if (word2) {
- strupr(word2);
- if (word2[strlen(word2)-1] == '\n')
- word2[strlen(word2)-1] = '\0';
- i = atoi(word2);
- } else
- i = 0;
if (i >= 0 && i < 32)
readPlayer(f, i);
else
@@ -3694,13 +3709,60 @@ static void DEH_LoadDehackedFile(MYFILE *f)
}
continue;
}
+ else if (fastcmp(word, "EMBLEM"))
+ {
+ if (!mainfile && !gamedataadded)
+ {
+ deh_warning("You must define a custom gamedata to use \"%s\"", word);
+ ignorelines(f);
+ }
+ else
+ {
+ if (!word2)
+ i = numemblems + 1;
+
+ if (i > 0 && i <= MAXEMBLEMS)
+ {
+ if (numemblems < i)
+ numemblems = i;
+ reademblemdata(f, i);
+ }
+ else
+ {
+ deh_warning("Emblem number %d out of range (1 - %d)", i, MAXEMBLEMS);
+ ignorelines(f);
+ }
+ }
+ continue;
+ }
+ else if (fastcmp(word, "EXTRAEMBLEM"))
+ {
+ if (!mainfile && !gamedataadded)
+ {
+ deh_warning("You must define a custom gamedata to use \"%s\"", word);
+ ignorelines(f);
+ }
+ else
+ {
+ if (!word2)
+ i = numextraemblems + 1;
+
+ if (i > 0 && i <= MAXEXTRAEMBLEMS)
+ {
+ if (numextraemblems < i)
+ numextraemblems = i;
+ readextraemblemdata(f, i);
+ }
+ else
+ {
+ deh_warning("Extra emblem number %d out of range (1 - %d)", i, MAXEXTRAEMBLEMS);
+ ignorelines(f);
+ }
+ }
+ continue;
+ }
if (word2)
{
- strupr(word2);
- if (word2[strlen(word2)-1] == '\n')
- word2[strlen(word2)-1] = '\0';
- i = atoi(word2);
-
if (fastcmp(word, "THING") || fastcmp(word, "MOBJ") || fastcmp(word, "OBJECT"))
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
@@ -3836,47 +3898,9 @@ static void DEH_LoadDehackedFile(MYFILE *f)
ignorelines(f);
}
}
- else if (fastcmp(word, "EMBLEM"))
- {
- if (!gamedataadded)
- {
- deh_warning("You must define a custom gamedata to use \"%s\"", word);
- ignorelines(f);
- }
- else if (i > 0 && i <= MAXEMBLEMS)
- {
- if (numemblems < i)
- numemblems = i;
- reademblemdata(f, i);
- }
- else
- {
- deh_warning("Emblem number %d out of range (1 - %d)", i, MAXEMBLEMS);
- ignorelines(f);
- }
- }
- else if (fastcmp(word, "EXTRAEMBLEM"))
- {
- if (!gamedataadded)
- {
- deh_warning("You must define a custom gamedata to use \"%s\"", word);
- ignorelines(f);
- }
- else if (i > 0 && i <= MAXEXTRAEMBLEMS)
- {
- if (numextraemblems < i)
- numextraemblems = i;
- readextraemblemdata(f, i);
- }
- else
- {
- deh_warning("Extra emblem number %d out of range (1 - %d)", i, MAXEXTRAEMBLEMS);
- ignorelines(f);
- }
- }
else if (fastcmp(word, "UNLOCKABLE"))
{
- if (!gamedataadded)
+ if (!mainfile && !gamedataadded)
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
ignorelines(f);
@@ -3891,7 +3915,7 @@ static void DEH_LoadDehackedFile(MYFILE *f)
}
else if (fastcmp(word, "CONDITIONSET"))
{
- if (!gamedataadded)
+ if (!mainfile && !gamedataadded)
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
ignorelines(f);
@@ -3922,7 +3946,7 @@ static void DEH_LoadDehackedFile(MYFILE *f)
{
boolean clearall = (fastcmp(word2, "ALL"));
- if (!gamedataadded)
+ if (!mainfile && !gamedataadded)
{
deh_warning("You must define a custom gamedata to use \"%s\"", word);
continue;
@@ -3993,7 +4017,7 @@ static void DEH_LoadDehackedFile(MYFILE *f)
// read dehacked lump in a wad (there is special trick for for deh
// file that are converted to wad in w_wad.c)
-void DEH_LoadDehackedLumpPwad(UINT16 wad, UINT16 lump)
+void DEH_LoadDehackedLumpPwad(UINT16 wad, UINT16 lump, boolean mainfile)
{
MYFILE f;
f.wad = wad;
@@ -4002,13 +4026,13 @@ void DEH_LoadDehackedLumpPwad(UINT16 wad, UINT16 lump)
W_ReadLumpPwad(wad, lump, f.data);
f.curpos = f.data;
f.data[f.size] = 0;
- DEH_LoadDehackedFile(&f);
+ DEH_LoadDehackedFile(&f, mainfile);
Z_Free(f.data);
}
void DEH_LoadDehackedLump(lumpnum_t lumpnum)
{
- DEH_LoadDehackedLumpPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum));
+ DEH_LoadDehackedLumpPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum), false);
}
////////////////////////////////////////////////////////////////////////////////
@@ -8051,6 +8075,7 @@ struct {
// doomdef.h constants
{"TICRATE",TICRATE},
+ {"MUSICRATE",MUSICRATE},
{"RING_DIST",RING_DIST},
{"PUSHACCEL",PUSHACCEL},
{"MODID",MODID}, // I don't know, I just thought it would be cool for a wad to potentially know what mod it was loaded into.
@@ -8137,6 +8162,7 @@ struct {
{"LF_NORELOAD",LF_NORELOAD},
{"LF_NOZONE",LF_NOZONE},
{"LF_SAVEGAME",LF_SAVEGAME},
+ {"LF_MIXNIGHTSCOUNTDOWN",LF_MIXNIGHTSCOUNTDOWN},
// And map flags
{"LF2_HIDEINMENU",LF2_HIDEINMENU},
{"LF2_HIDEINSTATS",LF2_HIDEINSTATS},
@@ -9536,15 +9562,19 @@ static inline int lib_getenum(lua_State *L)
} else if (fastcmp(word,"mapmusflags")) {
lua_pushinteger(L, mapmusflags);
return 1;
+ } else if (fastcmp(word,"mapmusposition")) {
+ lua_pushinteger(L, mapmusposition);
+ return 1;
} else if (fastcmp(word,"server")) {
if ((!multiplayer || !netgame) && !playeringame[serverplayer])
return 0;
LUA_PushUserdata(L, &players[serverplayer], META_PLAYER);
return 1;
- } else if (fastcmp(word,"admin")) {
- if (!playeringame[adminplayer] || adminplayer == serverplayer)
+ } else if (fastcmp(word,"admin")) { // BACKWARDS COMPATIBILITY HACK: This was replaced with IsPlayerAdmin(), but some 2.1 Lua scripts still use the admin variable. It now points to the first admin player in the array.
+ LUA_Deprecated(L, "admin", "IsPlayerAdmin(player)");
+ if (!playeringame[adminplayers[0]] || IsPlayerAdmin(serverplayer))
return 0;
- LUA_PushUserdata(L, &players[adminplayer], META_PLAYER);
+ LUA_PushUserdata(L, &players[adminplayers[0]], META_PLAYER);
return 1;
} else if (fastcmp(word,"emeralds")) {
lua_pushinteger(L, emeralds);
@@ -9559,7 +9589,6 @@ static inline int lib_getenum(lua_State *L)
lua_pushinteger(L, token);
return 1;
}
-
return 0;
}
diff --git a/src/dehacked.h b/src/dehacked.h
index dfce996a2..31f7f220d 100644
--- a/src/dehacked.h
+++ b/src/dehacked.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -28,7 +28,7 @@ typedef enum
} undotype_f;
void DEH_LoadDehackedLump(lumpnum_t lumpnum);
-void DEH_LoadDehackedLumpPwad(UINT16 wad, UINT16 lump);
+void DEH_LoadDehackedLumpPwad(UINT16 wad, UINT16 lump, boolean mainfile);
void DEH_Check(void);
diff --git a/src/djgppdos/i_sound.c b/src/djgppdos/i_sound.c
index 52c90aac2..88b598622 100644
--- a/src/djgppdos/i_sound.c
+++ b/src/djgppdos/i_sound.c
@@ -438,6 +438,37 @@ boolean I_SetSongSpeed(float speed)
return false;
}
+/// ------------------------
+// MUSIC SEEKING
+/// ------------------------
+
+UINT32 I_GetSongLength(void)
+{
+ return 0;
+}
+
+boolean I_SetSongLoopPoint(UINT32 looppoint)
+{
+ (void)looppoint;
+ return false;
+}
+
+UINT32 I_GetSongLoopPoint(void)
+{
+ return 0;
+}
+
+boolean I_SetSongPosition(UINT32 position)
+{
+ (void)position;
+ return false;
+}
+
+UINT32 I_GetSongPosition(void)
+{
+ return 0;
+}
+
/// ------------------------
// MUSIC PLAYBACK
/// ------------------------
@@ -545,3 +576,44 @@ int I_QrySongPlaying(int handle)
return (midi_pos==-1);
}
#endif
+
+/// ------------------------
+// MUSIC FADING
+/// ------------------------
+
+void I_SetInternalMusicVolume(UINT8 volume)
+{
+ (void)volume;
+}
+
+void I_StopFadingSong(void)
+{
+}
+
+boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void));
+{
+ (void)target_volume;
+ (void)source_volume;
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void));
+{
+ (void)target_volume;
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeOutStopSong(UINT32 ms)
+{
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeInPlaySong(UINT32 ms, boolean looping)
+{
+ (void)ms;
+ (void)looping;
+ return false;
+}
diff --git a/src/djgppdos/rdb-s.h b/src/djgppdos/rdb-s.h
index 6202dacfa..7a6d8be3c 100644
--- a/src/djgppdos/rdb-s.h
+++ b/src/djgppdos/rdb-s.h
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// Copyright (C) 2005 by Sonic Team Jr.
+// Copyright (C) 2005-2018 by Sonic Team Jr.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
diff --git a/src/doomdata.h b/src/doomdata.h
index 5ee39c5a8..2b9472569 100644
--- a/src/doomdata.h
+++ b/src/doomdata.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/doomdef.h b/src/doomdef.h
index 2981fecf6..512c90f86 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -150,6 +150,9 @@ extern FILE *logstream;
// Comment or uncomment this as necessary.
#define USE_PATCH_DTA
+// Use .kart extension addons
+//#define USE_KART
+
// Modification options
// If you want to take advantage of the Master Server's ability to force clients to update
// to the latest version, fill these out. Otherwise, just comment out UPDATE_ALERT and leave
@@ -203,7 +206,21 @@ extern FILE *logstream;
// it's only for detection of the version the player is using so the MS can alert them of an update.
// Only set it higher, not lower, obviously.
// Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
-#define MODVERSION 25
+#define MODVERSION 28
+
+// To version config.cfg, MAJOREXECVERSION is set equal to MODVERSION automatically.
+// Increment MINOREXECVERSION whenever a config change is needed that does not correspond
+// to an increment in MODVERSION. This might never happen in practice.
+// If MODVERSION increases, set MINOREXECVERSION to 0.
+#define MAJOREXECVERSION MODVERSION
+#define MINOREXECVERSION 0
+// (It would have been nice to use VERSION and SUBVERSION but those are zero'd out for DEVELOP builds)
+
+// Macros
+#define GETMAJOREXECVERSION(v) (v & 0xFFFF)
+#define GETMINOREXECVERSION(v) (v >> 16)
+#define GETEXECVERSION(major,minor) (major + (minor << 16))
+#define EXECVERSION GETEXECVERSION(MAJOREXECVERSION, MINOREXECVERSION)
// =========================================================================
@@ -356,6 +373,8 @@ typedef enum
#define NEWTICRATERATIO 1 // try 4 for 140 fps :)
#define NEWTICRATE (TICRATE*NEWTICRATERATIO)
+#define MUSICRATE 1000 // sound timing is calculated by milliseconds
+
#define RING_DIST 512*FRACUNIT // how close you need to be to a ring to attract it
#define PUSHACCEL (2*FRACUNIT) // Acceleration for MF2_SLIDEPUSH items.
@@ -464,6 +483,7 @@ extern INT32 cv_debug;
// Modifier key variables, accessible anywhere
extern UINT8 shiftdown, ctrldown, altdown;
+extern boolean capslock;
// if we ever make our alloc stuff...
#define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)
@@ -478,6 +498,15 @@ INT32 I_GetKey(void);
#define max(x, y) (((x) > (y)) ? (x) : (y))
#endif
+// Floating point comparison epsilons from float.h
+#ifndef FLT_EPSILON
+#define FLT_EPSILON 1.1920928955078125e-7f
+#endif
+
+#ifndef DBL_EPSILON
+#define DBL_EPSILON 2.2204460492503131e-16
+#endif
+
// An assert-type mechanism.
#ifdef PARANOIA
#define I_Assert(e) ((e) ? (void)0 : I_Error("assert failed: %s, file %s, line %d", #e, __FILE__, __LINE__))
diff --git a/src/doomstat.h b/src/doomstat.h
index 1ff261d64..efd347dbe 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -33,8 +33,10 @@
extern INT16 gamemap;
extern char mapmusname[7];
extern UINT16 mapmusflags;
+extern UINT32 mapmusposition;
#define MUSIC_TRACKMASK 0x0FFF // ----************
#define MUSIC_RELOADRESET 0x8000 // *---------------
+#define MUSIC_FORCERESET 0x4000 // -*--------------
// Use other bits if necessary.
extern INT16 maptol;
@@ -160,6 +162,7 @@ typedef struct
char musswitch[7];
UINT16 musswitchflags;
+ UINT32 musswitchposition;
UINT8 fadecolor; // Color number for fade, 0 means don't do the first fade
UINT8 fadeinid; // ID of the first fade, to a color -- ignored if fadecolor is 0
@@ -284,6 +287,7 @@ typedef struct
INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end.
char musname[7]; ///< Music track to play. "" for no music.
UINT16 mustrack; ///< Subsong to play. Only really relevant for music modules and specific formats supported by GME. 0 to ignore.
+ UINT32 muspos; ///< Music position to jump to.
char forcecharacter[17]; ///< (SKINNAMESIZE+1) Skin to switch to or "" to disable.
UINT8 weather; ///< 0 = sunny day, 1 = storm, 2 = snow, 3 = rain, 4 = blank, 5 = thunder w/o rain, 6 = rain w/o lightning, 7 = heat wave.
INT16 skynum; ///< Sky number to use.
@@ -305,7 +309,7 @@ typedef struct
SINT8 bonustype; ///< What type of bonus does this level have? (-1 for null.)
SINT8 maxbonuslives; ///< How many bonus lives to award at Intermission? (-1 for unlimited.)
- UINT8 levelflags; ///< LF_flags: merged eight booleans into one UINT8 for space, see below
+ UINT8 levelflags; ///< LF_flags: merged booleans into one UINT8 for space, see below
UINT8 menuflags; ///< LF2_flags: options that affect record attack / nights mode menus
char selectheading[22]; ///< Level select heading. Allows for controllable grouping.
@@ -318,6 +322,10 @@ typedef struct
UINT8 numGradedMares; ///< Internal. For grade support.
nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful.
+ // Music stuff.
+ UINT32 musinterfadeout; ///< Fade out level music on intermission screen in milliseconds
+ char musintername[7]; ///< Intermission screen music.
+
// Lua stuff.
// (This is not ifdeffed so the map header structure can stay identical, just in case.)
UINT8 numCustomOptions; ///< Internal. For Lua custom value support.
@@ -331,6 +339,7 @@ typedef struct
#define LF_NORELOAD 8 ///< Don't reload level on death
#define LF_NOZONE 16 ///< Don't include "ZONE" on level title
#define LF_SAVEGAME 32 ///< Save the game upon loading this level
+#define LF_MIXNIGHTSCOUNTDOWN 64 ///< Play sfx_timeup instead of music change for NiGHTS countdown
#define LF2_HIDEINMENU 1 ///< Hide in the multiplayer menu
#define LF2_HIDEINSTATS 2 ///< Hide in the statistics screen
@@ -564,7 +573,8 @@ extern consvar_t cv_showinputjoy; // display joystick in time attack
extern consvar_t cv_forceskin; // force clients to use the server's skin
extern consvar_t cv_downloading; // allow clients to downloading WADs.
extern ticcmd_t netcmds[BACKUPTICS][MAXPLAYERS];
-extern INT32 adminplayer, serverplayer;
+extern INT32 serverplayer;
+extern INT32 adminplayers[MAXPLAYERS];
/// \note put these in d_clisrv outright?
diff --git a/src/doomtype.h b/src/doomtype.h
index 741b68c21..48a10a19a 100644
--- a/src/doomtype.h
+++ b/src/doomtype.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/dummy/i_sound.c b/src/dummy/i_sound.c
index 7275bb1ae..f09158e01 100644
--- a/src/dummy/i_sound.c
+++ b/src/dummy/i_sound.c
@@ -95,6 +95,37 @@ boolean I_SetSongSpeed(float speed)
return false;
}
+/// ------------------------
+// MUSIC SEEKING
+/// ------------------------
+
+UINT32 I_GetSongLength(void)
+{
+ return 0;
+}
+
+boolean I_SetSongLoopPoint(UINT32 looppoint)
+{
+ (void)looppoint;
+ return false;
+}
+
+UINT32 I_GetSongLoopPoint(void)
+{
+ return 0;
+}
+
+boolean I_SetSongPosition(UINT32 position)
+{
+ (void)position;
+ return false;
+}
+
+UINT32 I_GetSongPosition(void)
+{
+ return 0;
+}
+
/// ------------------------
// MUSIC PLAYBACK
/// ------------------------
@@ -142,4 +173,45 @@ boolean I_SetSongTrack(int track)
{
(void)track;
return false;
-}
\ No newline at end of file
+}
+
+/// ------------------------
+// MUSIC FADING
+/// ------------------------
+
+void I_SetInternalMusicVolume(UINT8 volume)
+{
+ (void)volume;
+}
+
+void I_StopFadingSong(void)
+{
+}
+
+boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void));
+{
+ (void)target_volume;
+ (void)source_volume;
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void));
+{
+ (void)target_volume;
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeOutStopSong(UINT32 ms)
+{
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeInPlaySong(UINT32 ms, boolean looping)
+{
+ (void)ms;
+ (void)looping;
+ return false;
+}
diff --git a/src/endian.h b/src/endian.h
index 2b876e7cb..d3c1cb18b 100644
--- a/src/endian.h
+++ b/src/endian.h
@@ -1,6 +1,6 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
-// Copyright (C) 2014-2016 by Sonic Team Junior.
+// Copyright (C) 2014-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/f_finale.c b/src/f_finale.c
index 10d564c12..df8d7b683 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -389,7 +389,6 @@ void F_StartIntro(void)
gameaction = ga_nothing;
paused = false;
CON_ToggleOff();
- CON_ClearHUD();
F_NewCutscene(introtext[0]);
intro_scenenum = 0;
@@ -933,6 +932,8 @@ static const char *credits[] = {
"Callum Dickinson",
"Scott \"Graue\" Feeney",
"Nathan \"Jazz\" Giroux",
+ "Vivian \"toaster\" Grannell",
+ "Kepa \"Nev3r\" Iceta",
"Thomas \"Shadow Hog\" Igoe",
"Iestyn \"Monster Iestyn\" Jealous",
"Ronald \"Furyhunter\" Kinard", // The SDL2 port
@@ -940,6 +941,7 @@ static const char *credits[] = {
"Ehab \"Wolfy\" Saeed",
"\"Kaito Sinclaire\"",
"\"SSNTails\"",
+ "Marco \"mazmazz\" Zafra",
"",
"\1Programming",
"\1Assistance",
@@ -947,15 +949,21 @@ static const char *credits[] = {
"Andrew \"orospakr\" Clunis",
"Gregor \"Oogaland\" Dick",
"Louis-Antoine \"LJSonic\" de Moulins", // for fixing 2.1's netcode (de Rochefort doesn't quite fit on the screen sorry lol)
- "Vivian \"toaster\" Grannell",
+ "Victor \"Steel Titanium\" Fuentes",
"Julio \"Chaos Zero 64\" Guir",
+ "\"Jimita\"",
"\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog
+ "\"Lat'\"", // SRB2-CHAT, the chat window from Kart
"Matthew \"Shuffle\" Marsalko",
"Steven \"StroggOnMeth\" McGranahan",
"\"Morph\"", // For SRB2Morphed stuff
"Colin \"Sonict\" Pfaff",
"Sean \"Sryder13\" Ryder",
+ "Tasos \"tatokis\" Sahanidis", // Corrected C FixedMul, making 64-bit builds netplay compatible
"Ben \"Cue\" Woodford",
+ // Git contributors with 5+ approved merges of substantive quality,
+ // or contributors with at least one groundbreaking merge, may be named.
+ // Everyone else is acknowledged under "Special Thanks > SRB2 Community Contributors".
"",
"\1Sprite Artists",
"Odi \"Iceman404\" Atunzu",
@@ -1002,15 +1010,15 @@ static const char *credits[] = {
"Dan \"Blitzzo\" Hagerstrand",
"Kepa \"Nev3r\" Iceta",
"Thomas \"Shadow Hog\" Igoe",
- "Erik \"Torgo\" Nielsen",
"\"Kaito Sinclaire\"",
- "Wessel \"Spherallic\" Smit",
+ "Wessel \"sphere\" Smit",
"\"Spazzo\"",
"\"SSNTails\"",
"Rob Tisdell",
+ "\"Torgo\"",
"Jarrett \"JEV3\" Voight",
"Johnny \"Sonikku\" Wallbank",
- "Marco \"Digiku\" Zafra",
+ "Marco \"mazmazz\" Zafra",
"",
"\1Boss Design",
"Ben \"Mystic\" Geyer",
@@ -1031,11 +1039,17 @@ static const char *credits[] = {
"Bill \"Tets\" Reed",
"",
"\1Special Thanks",
- "Doom Legacy Project",
"iD Software",
- "Alex \"MistaED\" Fuller",
+ "Doom Legacy Project",
"FreeDoom Project", // Used some of the mancubus and rocket launcher sprites for Brak
+ "Alex \"MistaED\" Fuller",
+ "Pascal \"CodeImp\" vd Heiden", // Doom Builder developer
"Randi Heit ()", // For their MSPaint sprite that we nicked
+ "Simon \"sirjuddington\" Judd", // SLADE developer
+ // Acknowledged here are the following:
+ // Minor merge request authors, see guideline above
+ // Golden - Expanded thin font
+ "SRB2 Community Contributors",
"",
"\1Produced By",
"Sonic Team Junior",
@@ -1087,7 +1101,6 @@ void F_StartCredits(void)
gameaction = ga_nothing;
paused = false;
CON_ToggleOff();
- CON_ClearHUD();
S_StopMusic();
S_ChangeMusicInternal("_creds", false);
@@ -1108,6 +1121,9 @@ void F_CreditDrawer(void)
for (i = 0; credits_pics[i].patch; i++)
V_DrawSciencePatch(credits_pics[i].x<>1);
+ // Dim the background
+ V_DrawFadeScreen(0xFF00, 16);
+
// Draw credits text on top
for (i = 0; credits[i]; i++)
{
@@ -1130,16 +1146,34 @@ void F_CreditDrawer(void)
if (FixedMul(y,vid.dupy) > vid.height)
break;
}
+}
+void F_CreditTicker(void)
+{
+ // "Simulate" the drawing of the credits so that dedicated mode doesn't get stuck
+ UINT16 i;
+ fixed_t y = (80< vid.height)
+ break;
+ }
+
+ // Do this here rather than in the drawer you doofus! (this is why dedicated mode broke at credits)
if (!credits[i] && y <= 120<scene[scenenum].ycoord[picnum];
if (cutscenes[cutnum]->scene[scenenum].musswitch[0])
- S_ChangeMusic(cutscenes[cutnum]->scene[scenenum].musswitch,
+ S_ChangeMusicEx(cutscenes[cutnum]->scene[scenenum].musswitch,
cutscenes[cutnum]->scene[scenenum].musswitchflags,
- cutscenes[cutnum]->scene[scenenum].musicloop);
+ cutscenes[cutnum]->scene[scenenum].musicloop,
+ cutscenes[cutnum]->scene[scenenum].musswitchposition, 0, 0);
// Fade to the next
dofadenow = true;
@@ -1945,8 +1977,6 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
F_NewCutscene(cutscenes[cutscenenum]->scene[0].text);
- CON_ClearHUD();
-
cutsceneover = false;
runningprecutscene = precutscene;
precutresetplayer = resetplayer;
@@ -1967,9 +1997,10 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
stoptimer = 0;
if (cutscenes[cutnum]->scene[0].musswitch[0])
- S_ChangeMusic(cutscenes[cutnum]->scene[0].musswitch,
+ S_ChangeMusicEx(cutscenes[cutnum]->scene[0].musswitch,
cutscenes[cutnum]->scene[0].musswitchflags,
- cutscenes[cutnum]->scene[0].musicloop);
+ cutscenes[cutnum]->scene[0].musicloop,
+ cutscenes[cutnum]->scene[scenenum].musswitchposition, 0, 0);
else
S_StopMusic();
}
@@ -2032,7 +2063,7 @@ void F_CutsceneTicker(void)
for (i = 0; i < MAXPLAYERS; i++)
{
- if (netgame && i != serverplayer && i != adminplayer)
+ if (netgame && i != serverplayer && !IsPlayerAdmin(i))
continue;
if (players[i].cmd.buttons & BT_USE)
@@ -2569,7 +2600,7 @@ void F_TextPromptTicker(void)
{
for (i = 0; i < MAXPLAYERS; i++)
{
- if (netgame && i != serverplayer && i != adminplayer)
+ if (netgame && i != serverplayer && !IsPlayerAdmin(i))
continue;
else if (splitscreen) {
// Both players' controls are locked,
@@ -2600,7 +2631,7 @@ void F_TextPromptTicker(void)
{
for (i = 0; i < MAXPLAYERS; i++)
{
- if (netgame && i != serverplayer && i != adminplayer)
+ if (netgame && i != serverplayer && !IsPlayerAdmin(i))
continue;
else if (splitscreen) {
// Both players' controls are locked,
diff --git a/src/f_finale.h b/src/f_finale.h
index 18c032ba6..c0c6360c3 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/f_wipe.c b/src/f_wipe.c
index 3e0d474c6..26c65ad91 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -3,7 +3,7 @@
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2013-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/filesrch.c b/src/filesrch.c
index c1abccde3..8f157bdd5 100644
--- a/src/filesrch.c
+++ b/src/filesrch.c
@@ -445,9 +445,13 @@ filestatus_t filesearch(char *filename, const char *startpath, const UINT8 *want
return retval;
}
-char exttable[NUM_EXT_TABLE][5] = {
- ".txt", ".cfg", // exec
- ".wad", ".pk3", ".soc", ".lua"}; // addfile
+char exttable[NUM_EXT_TABLE][7] = { // maximum extension length (currently 4) plus 3 (null terminator, stop, and length including previous two)
+ "\5.txt", "\5.cfg", // exec
+ "\5.wad",
+#ifdef USE_KART
+ "\6.kart",
+#endif
+ "\5.pk3", "\5.soc", "\5.lua"}; // addfile
char filenamebuf[MAX_WADFILES][MAX_WADPATH];
@@ -458,9 +462,9 @@ static boolean filemenucmp(char *haystack, char *needle)
strlcpy(localhaystack, haystack, 128);
if (!cv_addons_search_case.value)
strupr(localhaystack);
- return ((cv_addons_search_type.value)
- ? (strstr(localhaystack, needle) != 0)
- : (!strncmp(localhaystack, needle, menusearch[0])));
+ if (cv_addons_search_type.value)
+ return (strstr(localhaystack, needle) != 0);
+ return (!strncmp(localhaystack, needle, menusearch[0]));
}
void closefilemenu(boolean validsize)
@@ -568,7 +572,7 @@ void searchfilemenu(char *tempname)
{
if ((!(dirmenu = Z_Realloc(dirmenu, sizeof(char *), PU_STATIC, NULL)))
|| !(dirmenu[0] = Z_StrDup(va("%c\13No results...", EXT_NORESULTS))))
- I_Error("Ran out of memory whilst preparing add-ons menu");
+ I_Error("searchfilemenu(): could not create \"No results...\".");
sizedirmenu = 1;
dir_on[menudepthleft] = 0;
if (tempname)
@@ -577,7 +581,7 @@ void searchfilemenu(char *tempname)
}
if (!(dirmenu = Z_Realloc(dirmenu, sizedirmenu*sizeof(char *), PU_STATIC, NULL)))
- I_Error("Ran out of memory whilst preparing add-ons menu");
+ I_Error("searchfilemenu(): could not reallocate dirmenu.");
sizedirmenu = 0;
for (i = first; i < sizecoredirmenu; i++)
@@ -596,7 +600,7 @@ void searchfilemenu(char *tempname)
if (tempname)
{
- dir_on[menudepthleft] = 0;
+ dir_on[menudepthleft] = 0; //first; -- can't be first, causes problems
Z_Free(tempname);
}
}
@@ -655,7 +659,7 @@ boolean preparefilemenu(boolean samedepth)
size_t len = strlen(dent->d_name)+1;
UINT8 ext;
for (ext = 0; ext < NUM_EXT_TABLE; ext++)
- if (!strcasecmp(exttable[ext], dent->d_name+len-5)) break; // extension comparison
+ if (!strcasecmp(exttable[ext]+1, dent->d_name+len-(exttable[ext][0]))) break; // extension comparison
if (ext == NUM_EXT_TABLE) continue; // not an addfile-able (or exec-able) file
}
}
@@ -688,7 +692,7 @@ boolean preparefilemenu(boolean samedepth)
if (!(coredirmenu = Z_Realloc(coredirmenu, sizecoredirmenu*sizeof(char *), PU_STATIC, NULL)))
{
closedir(dirhandle); // just in case
- I_Error("Ran out of memory whilst preparing add-ons menu");
+ I_Error("preparefilemenu(): could not reallocate coredirmenu.");
}
rewinddir(dirhandle);
@@ -721,7 +725,7 @@ boolean preparefilemenu(boolean samedepth)
{
if (!((numfolders+pos) < sizecoredirmenu)) continue; // crash prevention
for (; ext < NUM_EXT_TABLE; ext++)
- if (!strcasecmp(exttable[ext], dent->d_name+len-5)) break; // extension comparison
+ if (!strcasecmp(exttable[ext]+1, dent->d_name+len-(exttable[ext][0]))) break; // extension comparison
if (ext == NUM_EXT_TABLE && !cv_addons_showall.value) continue; // not an addfile-able (or exec-able) file
ext += EXT_START; // moving to be appropriate position
@@ -763,7 +767,7 @@ boolean preparefilemenu(boolean samedepth)
len = 255;
if (!(temp = Z_Malloc((len+DIR_STRING+folder) * sizeof (char), PU_STATIC, NULL)))
- I_Error("Ran out of memory whilst preparing add-ons menu");
+ I_Error("preparefilemenu(): could not create file entry.");
temp[DIR_TYPE] = ext;
temp[DIR_LEN] = (UINT8)(len);
strlcpy(temp+DIR_STRING, dent->d_name, len);
@@ -781,7 +785,7 @@ boolean preparefilemenu(boolean samedepth)
if ((menudepthleft != menudepth-1) // now for UP... entry
&& !(coredirmenu[0] = Z_StrDup(va("%c\5UP...", EXT_UP))))
- I_Error("Ran out of memory whilst preparing add-ons menu");
+ I_Error("preparefilemenu(): could not create \"UP...\".");
menupath[menupathindex[menudepthleft]] = 0;
sizecoredirmenu = (numfolders+pos); // just in case things shrink between opening and rewind
diff --git a/src/filesrch.h b/src/filesrch.h
index 41dc80d13..4186271b0 100644
--- a/src/filesrch.h
+++ b/src/filesrch.h
@@ -55,6 +55,9 @@ typedef enum
EXT_CFG,
EXT_LOADSTART,
EXT_WAD = EXT_LOADSTART,
+#ifdef USE_KART
+ EXT_KART,
+#endif
EXT_PK3,
EXT_SOC,
EXT_LUA, // allowed even if not HAVE_BLUA so that we can yell on load attempt
diff --git a/src/g_game.c b/src/g_game.c
index 96ca98989..af17b2b28 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -71,6 +71,7 @@ static void G_DoWorldDone(void);
char mapmusname[7]; // Music name
UINT16 mapmusflags; // Track and reset bit
+UINT32 mapmusposition; // Position to jump to
INT16 gamemap = 1;
INT16 maptol;
@@ -325,12 +326,45 @@ static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"},
"More Axis Sets"
#endif
+// don't mind me putting these here, I was lazy to figure out where else I could put those without blowing up the compiler.
+
+// it automatically becomes compact with 20+ players, but if you like it, I guess you can turn that on!
+consvar_t cv_compactscoreboard= {"compactscoreboard", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// chat timer thingy
+static CV_PossibleValue_t chattime_cons_t[] = {{5, "MIN"}, {999, "MAX"}, {0, NULL}};
+consvar_t cv_chattime = {"chattime", "8", CV_SAVE, chattime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// chatwidth
+static CV_PossibleValue_t chatwidth_cons_t[] = {{64, "MIN"}, {150, "MAX"}, {0, NULL}};
+consvar_t cv_chatwidth = {"chatwidth", "128", CV_SAVE, chatwidth_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// chatheight
+static CV_PossibleValue_t chatheight_cons_t[] = {{6, "MIN"}, {22, "MAX"}, {0, NULL}};
+consvar_t cv_chatheight= {"chatheight", "8", CV_SAVE, chatheight_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// chat notifications (do you want to hear beeps? I'd understand if you didn't.)
+consvar_t cv_chatnotifications= {"chatnotifications", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// chat spam protection (why would you want to disable that???)
+consvar_t cv_chatspamprotection= {"chatspamprotection", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// minichat text background
+consvar_t cv_chatbacktint = {"chatbacktint", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// old shit console chat. (mostly exists for stuff like terminal, not because I cared if anyone liked the old chat.)
+static CV_PossibleValue_t consolechat_cons_t[] = {{0, "Window"}, {1, "Console"}, {2, "Window (Hidden)"}, {0, NULL}};
+consvar_t cv_consolechat = {"chatmode", "Window", CV_SAVE, consolechat_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+
consvar_t cv_crosshair = {"crosshair", "Cross", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_crosshair2 = {"crosshair2", "Cross", CV_SAVE, crosshair_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_invertmouse = {"invertmouse", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_alwaysfreelook = {"alwaysmlook", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_alwaysfreelook = {"alwaysmlook", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_invertmouse2 = {"invertmouse2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_alwaysfreelook2 = {"alwaysmlook2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_alwaysfreelook2 = {"alwaysmlook2", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_chasefreelook = {"chasemlook", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_chasefreelook2 = {"chasemlook2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mousemove = {"mousemove", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_mousemove2 = {"mousemove2", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -362,23 +396,23 @@ typedef enum
AXISFIRENORMAL,
} axis_input_e;
-consvar_t cv_turnaxis = {"joyaxis_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_turnaxis = {"joyaxis_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_moveaxis = {"joyaxis_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_sideaxis = {"joyaxis_side", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_lookaxis = {"joyaxis_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_fireaxis = {"joyaxis_fire", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_firenaxis = {"joyaxis_firenormal", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_sideaxis = {"joyaxis_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_lookaxis = {"joyaxis_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_jumpaxis = {"joyaxis_jump", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_spinaxis = {"joyaxis_spin", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_fireaxis = {"joyaxis_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_firenaxis = {"joyaxis_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_turnaxis2 = {"joyaxis2_turn", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_turnaxis2 = {"joyaxis2_turn", "X-Rudder", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_moveaxis2 = {"joyaxis2_move", "Y-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_sideaxis2 = {"joyaxis2_side", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_lookaxis2 = {"joyaxis2_look", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_fireaxis2 = {"joyaxis2_fire", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_firenaxis2 = {"joyaxis2_firenormal", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_sideaxis2 = {"joyaxis2_side", "X-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_lookaxis2 = {"joyaxis2_look", "Y-Rudder-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_jumpaxis2 = {"joyaxis2_jump", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_spinaxis2 = {"joyaxis2_spin", "None", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_fireaxis2 = {"joyaxis2_fire", "Z-Axis-", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_firenaxis2 = {"joyaxis2_firenormal", "Z-Axis", CV_SAVE, joyaxis_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
#if MAXPLAYERS > 32
#error "please update player_name table using the new value for MAXPLAYERS"
@@ -883,16 +917,17 @@ static fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn
void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
{
boolean forcestrafe = false;
- INT32 tspeed, forward, side, axis, i;
+ INT32 tspeed, forward, side, axis, altaxis, i;
const INT32 speed = 1;
// these ones used for multiple conditions
- boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove;
+ boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming;
player_t *player = &players[consoleplayer];
camera_t *thiscam = &camera;
static INT32 turnheld; // for accelerative turning
static boolean keyboard_look; // true if lookup/down using keyboard
static boolean resetdown; // don't cam reset every frame
+ static boolean joyaiming; // check the last frame's value if we need to reset the camera
G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
@@ -915,10 +950,18 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
movefkey = PLAYER1INPUTDOWN(gc_forward);
movebkey = PLAYER1INPUTDOWN(gc_backward);
- mouseaiming = (PLAYER1INPUTDOWN(gc_mouseaiming)) ^ cv_alwaysfreelook.value;
+ mouseaiming = (PLAYER1INPUTDOWN(gc_mouseaiming)) ^
+ (cv_chasecam.value ? cv_chasefreelook.value : cv_alwaysfreelook.value);
analogjoystickmove = cv_usejoystick.value && !Joystick.bGamepadStyle;
gamepadjoystickmove = cv_usejoystick.value && Joystick.bGamepadStyle;
+ thisjoyaiming = (cv_chasecam.value) ? cv_chasefreelook.value : cv_alwaysfreelook.value;
+
+ // Reset the vertical look if we're no longer joyaiming
+ if (!thisjoyaiming && joyaiming)
+ localaiming = 0;
+ joyaiming = thisjoyaiming;
+
axis = JoyAxis(AXISTURN);
if (gamepadjoystickmove && axis != 0)
{
@@ -1003,9 +1046,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
// forward with key or button
axis = JoyAxis(AXISMOVE);
- if (movefkey || (gamepadjoystickmove && axis < 0))
+ altaxis = JoyAxis(AXISLOOK);
+ if (movefkey || (gamepadjoystickmove && axis < 0)
+ || ((player->powers[pw_carry] == CR_NIGHTSMODE)
+ && (PLAYER1INPUTDOWN(gc_lookup) || (gamepadjoystickmove && altaxis < 0))))
forward = forwardmove[speed];
- if (movebkey || (gamepadjoystickmove && axis > 0))
+ if (movebkey || (gamepadjoystickmove && axis > 0)
+ || ((player->powers[pw_carry] == CR_NIGHTSMODE)
+ && (PLAYER1INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && altaxis > 0))))
forward -= forwardmove[speed];
if (analogjoystickmove && axis != 0)
@@ -1093,25 +1141,28 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
}
axis = JoyAxis(AXISLOOK);
- if (analogjoystickmove && axis != 0 && cv_lookaxis.value != 0)
+ if (analogjoystickmove && joyaiming && axis != 0 && cv_lookaxis.value != 0)
localaiming += (axis<<16) * screen_invert;
// spring back if not using keyboard neither mouselookin'
- if (!keyboard_look && cv_lookaxis.value == 0 && !mouseaiming)
+ if (!keyboard_look && cv_lookaxis.value == 0 && !joyaiming && !mouseaiming)
localaiming = 0;
- if (PLAYER1INPUTDOWN(gc_lookup) || (gamepadjoystickmove && axis < 0))
+ if (!(player->powers[pw_carry] == CR_NIGHTSMODE))
{
- localaiming += KB_LOOKSPEED * screen_invert;
- keyboard_look = true;
+ if (PLAYER1INPUTDOWN(gc_lookup) || (gamepadjoystickmove && axis < 0))
+ {
+ localaiming += KB_LOOKSPEED * screen_invert;
+ keyboard_look = true;
+ }
+ else if (PLAYER1INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && axis > 0))
+ {
+ localaiming -= KB_LOOKSPEED * screen_invert;
+ keyboard_look = true;
+ }
+ else if (PLAYER1INPUTDOWN(gc_centerview))
+ localaiming = 0;
}
- else if (PLAYER1INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && axis > 0))
- {
- localaiming -= KB_LOOKSPEED * screen_invert;
- keyboard_look = true;
- }
- else if (PLAYER1INPUTDOWN(gc_centerview))
- localaiming = 0;
// accept no mlook for network games
if (!cv_allowmlook.value)
@@ -1192,16 +1243,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
{
boolean forcestrafe = false;
- INT32 tspeed, forward, side, axis, i;
+ INT32 tspeed, forward, side, axis, altaxis, i;
const INT32 speed = 1;
// these ones used for multiple conditions
- boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove;
+ boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming;
player_t *player = &players[secondarydisplayplayer];
camera_t *thiscam = (player->bot == 2 ? &camera : &camera2);
static INT32 turnheld; // for accelerative turning
static boolean keyboard_look; // true if lookup/down using keyboard
static boolean resetdown; // don't cam reset every frame
+ static boolean joyaiming; // check the last frame's value if we need to reset the camera
G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver
@@ -1222,10 +1274,18 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
movefkey = PLAYER2INPUTDOWN(gc_forward);
movebkey = PLAYER2INPUTDOWN(gc_backward);
- mouseaiming = (PLAYER2INPUTDOWN(gc_mouseaiming)) ^ cv_alwaysfreelook2.value;
+ mouseaiming = (PLAYER2INPUTDOWN(gc_mouseaiming)) ^
+ (cv_chasecam2.value ? cv_chasefreelook2.value : cv_alwaysfreelook2.value);
analogjoystickmove = cv_usejoystick2.value && !Joystick2.bGamepadStyle;
gamepadjoystickmove = cv_usejoystick2.value && Joystick2.bGamepadStyle;
+ thisjoyaiming = (cv_chasecam2.value) ? cv_chasefreelook2.value : cv_alwaysfreelook2.value;
+
+ // Reset the vertical look if we're no longer joyaiming
+ if (!thisjoyaiming && joyaiming)
+ localaiming2 = 0;
+ joyaiming = thisjoyaiming;
+
axis = Joy2Axis(AXISTURN);
if (gamepadjoystickmove && axis != 0)
{
@@ -1310,9 +1370,14 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
// forward with key or button
axis = Joy2Axis(AXISMOVE);
- if (movefkey || (gamepadjoystickmove && axis < 0))
+ altaxis = Joy2Axis(AXISLOOK);
+ if (movefkey || (gamepadjoystickmove && axis < 0)
+ || ((player->powers[pw_carry] == CR_NIGHTSMODE)
+ && (PLAYER2INPUTDOWN(gc_lookup) || (gamepadjoystickmove && altaxis < 0))))
forward = forwardmove[speed];
- if (movebkey || (gamepadjoystickmove && axis > 0))
+ if (movebkey || (gamepadjoystickmove && axis > 0)
+ || ((player->powers[pw_carry] == CR_NIGHTSMODE)
+ && (PLAYER2INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && altaxis > 0))))
forward -= forwardmove[speed];
if (analogjoystickmove && axis != 0)
@@ -1397,25 +1462,28 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
}
axis = Joy2Axis(AXISLOOK);
- if (analogjoystickmove && axis != 0 && cv_lookaxis2.value != 0)
+ if (analogjoystickmove && joyaiming && axis != 0 && cv_lookaxis2.value != 0)
localaiming2 += (axis<<16) * screen_invert;
// spring back if not using keyboard neither mouselookin'
- if (!keyboard_look && cv_lookaxis2.value == 0 && !mouseaiming)
+ if (!keyboard_look && cv_lookaxis2.value == 0 && !joyaiming && !mouseaiming)
localaiming2 = 0;
- if (PLAYER2INPUTDOWN(gc_lookup) || (gamepadjoystickmove && axis < 0))
+ if (!(player->powers[pw_carry] == CR_NIGHTSMODE))
{
- localaiming2 += KB_LOOKSPEED * screen_invert;
- keyboard_look = true;
+ if (PLAYER2INPUTDOWN(gc_lookup) || (gamepadjoystickmove && axis < 0))
+ {
+ localaiming2 += KB_LOOKSPEED * screen_invert;
+ keyboard_look = true;
+ }
+ else if (PLAYER2INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && axis > 0))
+ {
+ localaiming2 -= KB_LOOKSPEED * screen_invert;
+ keyboard_look = true;
+ }
+ else if (PLAYER2INPUTDOWN(gc_centerview))
+ localaiming2 = 0;
}
- else if (PLAYER2INPUTDOWN(gc_lookdown) || (gamepadjoystickmove && axis > 0))
- {
- localaiming2 -= KB_LOOKSPEED * screen_invert;
- keyboard_look = true;
- }
- else if (PLAYER2INPUTDOWN(gc_centerview))
- localaiming2 = 0;
// accept no mlook for network games
if (!cv_allowmlook.value)
@@ -1663,7 +1731,8 @@ static INT32 camtoggledelay, camtoggledelay2 = 0;
boolean G_Responder(event_t *ev)
{
// allow spy mode changes even during the demo
- if (gamestate == GS_LEVEL && ev->type == ev_keydown && ev->data1 == KEY_F12)
+ if (gamestate == GS_LEVEL && ev->type == ev_keydown
+ && (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1]))
{
if (splitscreen || !netgame)
displayplayer = consoleplayer;
@@ -2226,9 +2295,14 @@ void G_PlayerReborn(INT32 player)
{
strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname, 7);
mapmusname[6] = 0;
- mapmusflags = mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK;
+ mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK);
+ mapmusposition = mapheaderinfo[gamemap-1]->muspos;
}
- S_ChangeMusic(mapmusname, mapmusflags, true);
+
+ // This is in S_Start, but this was not here previously.
+ // if (cv_resetmusic.value)
+ // S_StopMusic();
+ S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
}
if (gametype == GT_COOP)
@@ -2670,7 +2744,7 @@ void G_DoReborn(INT32 playernum)
else
{
#ifdef HAVE_BLUA
- LUAh_MapChange();
+ LUAh_MapChange(gamemap);
#endif
G_DoLoadLevel(true);
return;
@@ -3830,7 +3904,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
unlocktriggers = 0;
// clear itemfinder, just in case
- if (!dedicated) // except in dedicated servers, where it is not registered and can actually I_Error debug builds
+ if (!dedicated) // except in dedicated servers, where it is not registered and can actually I_Error debug builds
CV_StealthSetValue(&cv_itemfinder, 0);
}
@@ -5464,7 +5538,7 @@ void G_DoPlayDemo(char *defdemoname)
SetPlayerSkin(0, skin);
#ifdef HAVE_BLUA
- LUAh_MapChange();
+ LUAh_MapChange(gamemap);
#endif
displayplayer = consoleplayer = 0;
memset(playeringame,0,sizeof(playeringame));
diff --git a/src/g_game.h b/src/g_game.h
index 1e30831d8..3cbde9a3c 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -58,9 +58,10 @@ extern boolean pausebreakkey;
// used in game menu
extern consvar_t cv_tutorialprompt;
+extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection, cv_compactscoreboard;
extern consvar_t cv_crosshair, cv_crosshair2;
-extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_mousemove;
-extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_mousemove2;
+extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_chasefreelook, cv_mousemove;
+extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_chasefreelook2, cv_mousemove2;
extern consvar_t cv_useranalog, cv_useranalog2;
extern consvar_t cv_analog, cv_analog2;
extern consvar_t cv_directionchar, cv_directionchar2;
diff --git a/src/g_input.c b/src/g_input.c
index 4686f57ce..45c517e1a 100644
--- a/src/g_input.c
+++ b/src/g_input.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -25,10 +25,10 @@ static CV_PossibleValue_t mousesens_cons_t[] = {{1, "MIN"}, {MAXMOUSESENSITIVITY
static CV_PossibleValue_t onecontrolperkey_cons_t[] = {{1, "One"}, {2, "Several"}, {0, NULL}};
// mouse values are used once
-consvar_t cv_mousesens = {"mousesens", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_mousesens2 = {"mousesens2", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_mouseysens = {"mouseysens", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_mouseysens2 = {"mouseysens2", "12", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mousesens = {"mousesens", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mousesens2 = {"mousesens2", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mouseysens = {"mouseysens", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mouseysens2 = {"mouseysens2", "20", CV_SAVE, mousesens_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_controlperkey = {"controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
INT32 mousex, mousey;
@@ -46,6 +46,7 @@ UINT8 gamekeydown[NUMINPUTS];
INT32 gamecontrol[num_gamecontrols][2];
INT32 gamecontrolbis[num_gamecontrols][2]; // secondary splitscreen player
INT32 gamecontroldefault[num_gamecontrolschemes][num_gamecontrols][2]; // default control storage, use 0 (gcs_custom) for memory retention
+INT32 gamecontrolbisdefault[num_gamecontrolschemes][num_gamecontrols][2];
// lists of GC codes for selective operation
const INT32 gcl_tutorial_check[num_gcl_tutorial_check] = {
@@ -139,6 +140,8 @@ void G_MapEventsToControls(event_t *ev)
break;
case ev_mouse: // buttons are virtual keys
+ if (menuactive || CON_Ready() || chat_on)
+ break;
mousex = (INT32)(ev->data2*((cv_mousesens.value*cv_mousesens.value)/110.0f + 0.1f));
mousey = (INT32)(ev->data3*((cv_mousesens.value*cv_mousesens.value)/110.0f + 0.1f));
mlooky = (INT32)(ev->data3*((cv_mouseysens.value*cv_mousesens.value)/110.0f + 0.1f));
@@ -146,7 +149,7 @@ void G_MapEventsToControls(event_t *ev)
case ev_joystick: // buttons are virtual keys
i = ev->data1;
- if (i >= JOYAXISSET)
+ if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on)
break;
if (ev->data2 != INT32_MAX) joyxmove[i] = ev->data2;
if (ev->data3 != INT32_MAX) joyymove[i] = ev->data3;
@@ -154,13 +157,15 @@ void G_MapEventsToControls(event_t *ev)
case ev_joystick2: // buttons are virtual keys
i = ev->data1;
- if (i >= JOYAXISSET)
+ if (i >= JOYAXISSET || menuactive || CON_Ready() || chat_on)
break;
if (ev->data2 != INT32_MAX) joy2xmove[i] = ev->data2;
if (ev->data3 != INT32_MAX) joy2ymove[i] = ev->data3;
break;
case ev_mouse2: // buttons are virtual keys
+ if (menuactive || CON_Ready() || chat_on)
+ break;
mouse2x = (INT32)(ev->data2*((cv_mousesens2.value*cv_mousesens2.value)/110.0f + 0.1f));
mouse2y = (INT32)(ev->data3*((cv_mousesens2.value*cv_mousesens2.value)/110.0f + 0.1f));
mlook2y = (INT32)(ev->data3*((cv_mouseysens2.value*cv_mousesens2.value)/110.0f + 0.1f));
@@ -591,6 +596,10 @@ static const char *gamecontrolname[num_gamecontrols] =
"jump",
"console",
"pause",
+ "systemmenu",
+ "screenshot",
+ "recordgif",
+ "viewpoint",
"custom1",
"custom2",
"custom3",
@@ -607,6 +616,16 @@ void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control)
setupcontrols[control][1] = KEY_NULL;
}
+void G_ClearAllControlKeys(void)
+{
+ INT32 i;
+ for (i = 0; i < num_gamecontrols; i++)
+ {
+ G_ClearControlKeys(gamecontrol, i);
+ G_ClearControlKeys(gamecontrolbis, i);
+ }
+}
+
//
// Returns the name of a key (or virtual key for mouse and joy)
// the input value being an keynum
@@ -710,6 +729,39 @@ void G_DefineDefaultControls(void)
gamecontroldefault[i][gc_scores ][0] = KEY_TAB;
gamecontroldefault[i][gc_console ][0] = KEY_CONSOLE;
gamecontroldefault[i][gc_pause ][0] = 'p';
+ gamecontroldefault[i][gc_screenshot ][0] = KEY_F8;
+ gamecontroldefault[i][gc_recordgif ][0] = KEY_F9;
+ gamecontroldefault[i][gc_viewpoint ][0] = KEY_F12;
+
+ // Gamepad controls -- same for both schemes
+ gamecontroldefault[i][gc_weaponnext ][1] = KEY_JOY1+1; // B
+ gamecontroldefault[i][gc_weaponprev ][1] = KEY_JOY1+2; // X
+ gamecontroldefault[i][gc_tossflag ][1] = KEY_JOY1+0; // A
+ gamecontroldefault[i][gc_use ][1] = KEY_JOY1+4; // LB
+ gamecontroldefault[i][gc_camtoggle ][1] = KEY_HAT1+0; // D-Pad Up
+ gamecontroldefault[i][gc_camreset ][1] = KEY_JOY1+3; // Y
+ gamecontroldefault[i][gc_centerview ][1] = KEY_JOY1+9; // Right Stick
+ gamecontroldefault[i][gc_talkkey ][1] = KEY_HAT1+2; // D-Pad Left
+ gamecontroldefault[i][gc_scores ][1] = KEY_HAT1+3; // D-Pad Right
+ gamecontroldefault[i][gc_jump ][1] = KEY_JOY1+5; // RB
+ gamecontroldefault[i][gc_pause ][1] = KEY_JOY1+6; // Back
+ gamecontroldefault[i][gc_screenshot ][1] = KEY_HAT1+1; // D-Pad Down
+ gamecontroldefault[i][gc_systemmenu ][0] = KEY_JOY1+7; // Start
+
+ // Second player controls only have joypad defaults
+ gamecontrolbisdefault[i][gc_weaponnext][0] = KEY_2JOY1+1; // B
+ gamecontrolbisdefault[i][gc_weaponprev][0] = KEY_2JOY1+2; // X
+ gamecontrolbisdefault[i][gc_tossflag ][0] = KEY_2JOY1+0; // A
+ gamecontrolbisdefault[i][gc_use ][0] = KEY_2JOY1+4; // LB
+ gamecontrolbisdefault[i][gc_camreset ][0] = KEY_2JOY1+3; // Y
+ gamecontrolbisdefault[i][gc_centerview][0] = KEY_2JOY1+9; // Right Stick
+ gamecontrolbisdefault[i][gc_jump ][0] = KEY_2JOY1+5; // RB
+ //gamecontrolbisdefault[i][gc_pause ][0] = KEY_2JOY1+6; // Back
+ //gamecontrolbisdefault[i][gc_systemmenu][0] = KEY_2JOY1+7; // Start
+ gamecontrolbisdefault[i][gc_camtoggle ][0] = KEY_2HAT1+0; // D-Pad Up
+ gamecontrolbisdefault[i][gc_screenshot][0] = KEY_2HAT1+1; // D-Pad Down
+ //gamecontrolbisdefault[i][gc_talkkey ][0] = KEY_2HAT1+2; // D-Pad Left
+ //gamecontrolbisdefault[i][gc_scores ][0] = KEY_2HAT1+3; // D-Pad Right
}
}
@@ -779,30 +831,164 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis
}
}
-void G_CheckDoubleUsage(INT32 keynum)
+INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify)
{
+ INT32 result = gc_null;
if (cv_controlperkey.value == 1)
{
INT32 i;
for (i = 0; i < num_gamecontrols; i++)
{
if (gamecontrol[i][0] == keynum)
- gamecontrol[i][0] = KEY_NULL;
+ {
+ result = i;
+ if (modify) gamecontrol[i][0] = KEY_NULL;
+ }
if (gamecontrol[i][1] == keynum)
- gamecontrol[i][1] = KEY_NULL;
+ {
+ result = i;
+ if (modify) gamecontrol[i][1] = KEY_NULL;
+ }
if (gamecontrolbis[i][0] == keynum)
- gamecontrolbis[i][0] = KEY_NULL;
+ {
+ result = i;
+ if (modify) gamecontrolbis[i][0] = KEY_NULL;
+ }
if (gamecontrolbis[i][1] == keynum)
- gamecontrolbis[i][1] = KEY_NULL;
+ {
+ result = i;
+ if (modify) gamecontrolbis[i][1] = KEY_NULL;
+ }
+ if (result && !modify)
+ return result;
}
}
+ return result;
}
-static void setcontrol(INT32 (*gc)[2], INT32 na)
+static INT32 G_FilterKeyByVersion(INT32 numctrl, INT32 keyidx, INT32 player, INT32 *keynum1, INT32 *keynum2, boolean *nestedoverride)
+{
+ // Special case: ignore KEY_PAUSE because it's hardcoded
+ if (keyidx == 0 && *keynum1 == KEY_PAUSE)
+ {
+ if (*keynum2 != KEY_PAUSE)
+ {
+ *keynum1 = *keynum2; // shift down keynum2 and continue
+ *keynum2 = 0;
+ }
+ else
+ return -1; // skip setting control
+ }
+ else if (keyidx == 1 && *keynum2 == KEY_PAUSE)
+ return -1; // skip setting control
+
+ if (GETMAJOREXECVERSION(cv_execversion.value) < 27 && ( // v2.1.22
+ numctrl == gc_weaponnext || numctrl == gc_weaponprev || numctrl == gc_tossflag ||
+ numctrl == gc_use || numctrl == gc_camreset || numctrl == gc_jump ||
+ numctrl == gc_pause || numctrl == gc_systemmenu || numctrl == gc_camtoggle ||
+ numctrl == gc_screenshot || numctrl == gc_talkkey || numctrl == gc_scores ||
+ numctrl == gc_centerview
+ ))
+ {
+ INT32 keynum = 0, existingctrl = 0;
+ INT32 defaultkey;
+ boolean defaultoverride = false;
+
+ // get the default gamecontrol
+ if (player == 0 && numctrl == gc_systemmenu)
+ defaultkey = gamecontrol[numctrl][0];
+ else
+ defaultkey = (player == 1 ? gamecontrolbis[numctrl][0] : gamecontrol[numctrl][1]);
+
+ // Assign joypad button defaults if there is an open slot.
+ // At this point, gamecontrol/bis should have the default controls
+ // (unless LOADCONFIG is being run)
+ //
+ // If the player runs SETCONTROL in-game, this block should not be reached
+ // because EXECVERSION is locked onto the latest version.
+ if (keyidx == 0 && !*keynum1)
+ {
+ if (*keynum2) // push keynum2 down; this is an edge case
+ {
+ *keynum1 = *keynum2;
+ *keynum2 = 0;
+ keynum = *keynum1;
+ }
+ else
+ {
+ keynum = defaultkey;
+ defaultoverride = true;
+ }
+ }
+ else if (keyidx == 1 && (!*keynum2 || (!*keynum1 && *keynum2))) // last one is the same edge case as above
+ {
+ keynum = defaultkey;
+ defaultoverride = true;
+ }
+ else // default to the specified keynum
+ keynum = (keyidx == 1 ? *keynum2 : *keynum1);
+
+ // Did our last call override keynum2?
+ if (*nestedoverride)
+ {
+ defaultoverride = true;
+ *nestedoverride = false;
+ }
+
+ // Fill keynum2 with the default control
+ if (keyidx == 0 && !*keynum2)
+ {
+ *keynum2 = defaultkey;
+ // Tell the next call that this is an override
+ *nestedoverride = true;
+
+ // if keynum2 already matches keynum1, we probably recursed
+ // so unset it
+ if (*keynum1 == *keynum2)
+ {
+ *keynum2 = 0;
+ *nestedoverride = false;
+ }
+ }
+
+ // check if the key is being used somewhere else before passing it
+ // pass it through if it's the same numctrl. This is an edge case -- when using
+ // LOADCONFIG, gamecontrol is not reset with default.
+ //
+ // Also, only check if we're actually overriding, to preserve behavior where
+ // config'd keys overwrite default keys.
+ if (defaultoverride)
+ existingctrl = G_CheckDoubleUsage(keynum, false);
+
+ if (keynum && (!existingctrl || existingctrl == numctrl))
+ return keynum;
+ else if (keyidx == 0 && *keynum2)
+ {
+ // try it again and push down keynum2
+ *keynum1 = *keynum2;
+ *keynum2 = 0;
+ return G_FilterKeyByVersion(numctrl, keyidx, player, keynum1, keynum2, nestedoverride);
+ // recursion *should* be safe because we only assign keynum2 to a joy default
+ // and then clear it if we find that keynum1 already has the joy default.
+ }
+ else
+ return 0;
+ }
+
+ // All's good, so pass the keynum as-is
+ if (keyidx == 1)
+ return *keynum2;
+ else //if (keyidx == 0)
+ return *keynum1;
+}
+
+static void setcontrol(INT32 (*gc)[2])
{
INT32 numctrl;
const char *namectrl;
- INT32 keynum;
+ INT32 keynum, keynum1, keynum2;
+ INT32 player = ((void*)gc == (void*)&gamecontrolbis ? 1 : 0);
+ boolean nestedoverride = false;
namectrl = COM_Argv(1);
for (numctrl = 0; numctrl < num_gamecontrols && stricmp(namectrl, gamecontrolname[numctrl]);
@@ -813,31 +999,38 @@ static void setcontrol(INT32 (*gc)[2], INT32 na)
CONS_Printf(M_GetText("Control '%s' unknown\n"), namectrl);
return;
}
- keynum = G_KeyStringtoNum(COM_Argv(2));
+ keynum1 = G_KeyStringtoNum(COM_Argv(2));
+ keynum2 = G_KeyStringtoNum(COM_Argv(3));
+ keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride);
- if (keynum == KEY_PAUSE) // fail silently; pause is hardcoded
+ if (keynum >= 0)
{
- if (na == 4)
+ (void)G_CheckDoubleUsage(keynum, true);
+
+ // if keynum was rejected, try it again with keynum2
+ if (!keynum && keynum2)
{
- na--;
- keynum = G_KeyStringtoNum(COM_Argv(3));
- if (keynum == KEY_PAUSE)
- return;
+ keynum1 = keynum2; // push down keynum2
+ keynum2 = 0;
+ keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride);
+ if (keynum >= 0)
+ (void)G_CheckDoubleUsage(keynum, true);
}
- else
- return;
}
- G_CheckDoubleUsage(keynum);
- gc[numctrl][0] = keynum;
+ if (keynum >= 0)
+ gc[numctrl][0] = keynum;
- if (na == 4)
+ if (keynum2)
{
- keynum = G_KeyStringtoNum(COM_Argv(3));
- if (keynum != KEY_PAUSE)
- gc[numctrl][1] = keynum;
- else
- gc[numctrl][1] = 0;
+ keynum = G_FilterKeyByVersion(numctrl, 1, player, &keynum1, &keynum2, &nestedoverride);
+ if (keynum >= 0)
+ {
+ if (keynum != gc[numctrl][0])
+ gc[numctrl][1] = keynum;
+ else
+ gc[numctrl][1] = 0;
+ }
}
else
gc[numctrl][1] = 0;
@@ -855,7 +1048,7 @@ void Command_Setcontrol_f(void)
return;
}
- setcontrol(gamecontrol, na);
+ setcontrol(gamecontrol);
}
void Command_Setcontrol2_f(void)
@@ -870,5 +1063,5 @@ void Command_Setcontrol2_f(void)
return;
}
- setcontrol(gamecontrolbis, na);
+ setcontrol(gamecontrolbis);
}
diff --git a/src/g_input.h b/src/g_input.h
index 4bf67ebd8..d3c693781 100644
--- a/src/g_input.h
+++ b/src/g_input.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -93,6 +93,10 @@ typedef enum
gc_jump,
gc_console,
gc_pause,
+ gc_systemmenu,
+ gc_screenshot,
+ gc_recordgif,
+ gc_viewpoint,
gc_custom1, // Lua scriptable
gc_custom2, // Lua scriptable
gc_custom3, // Lua scriptable
@@ -125,6 +129,7 @@ extern UINT8 gamekeydown[NUMINPUTS];
extern INT32 gamecontrol[num_gamecontrols][2];
extern INT32 gamecontrolbis[num_gamecontrols][2]; // secondary splitscreen player
extern INT32 gamecontroldefault[num_gamecontrolschemes][num_gamecontrols][2]; // default control storage, use 0 (gcs_custom) for memory retention
+extern INT32 gamecontrolbisdefault[num_gamecontrolschemes][num_gamecontrols][2];
#define PLAYER1INPUTDOWN(gc) (gamekeydown[gamecontrol[gc][0]] || gamekeydown[gamecontrol[gc][1]])
#define PLAYER2INPUTDOWN(gc) (gamekeydown[gamecontrolbis[gc][0]] || gamekeydown[gamecontrolbis[gc][1]])
@@ -160,12 +165,13 @@ INT32 G_KeyStringtoNum(const char *keystr);
// detach any keys associated to the given game control
void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control);
+void G_ClearAllControlKeys(void);
void Command_Setcontrol_f(void);
void Command_Setcontrol2_f(void);
void G_DefineDefaultControls(void);
INT32 G_GetControlScheme(INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen);
void G_CopyControls(INT32 (*setupcontrols)[2], INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen);
void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis)[2]);
-void G_CheckDoubleUsage(INT32 keynum);
+INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify);
#endif
diff --git a/src/g_state.h b/src/g_state.h
index 81548b7ce..76c9bd16f 100644
--- a/src/g_state.h
+++ b/src/g_state.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/hardware/hw_bsp.c b/src/hardware/hw_bsp.c
index 04afb9be9..483932492 100644
--- a/src/hardware/hw_bsp.c
+++ b/src/hardware/hw_bsp.c
@@ -194,14 +194,14 @@ static polyvertex_t *fracdivline(fdivline_t *bsp, polyvertex_t *v1,
v2dy = bsp->dy;
den = v2dy*v1dx - v2dx*v1dy;
- if (den == 0)
+ if (fabsf((float)den) < 1.0E-36f) // avoid checking exactly for 0.0
return NULL; // parallel
// first check the frac along the polygon segment,
// (do not accept hit with the extensions)
num = (v2x - v1x)*v2dy + (v1y - v2y)*v2dx;
frac = num / den;
- if (frac < 0 || frac > 1)
+ if (frac < 0.0 || frac > 1.0)
return NULL;
// now get the frac along the BSP line
@@ -218,29 +218,6 @@ static polyvertex_t *fracdivline(fdivline_t *bsp, polyvertex_t *v1,
return &pt;
}
-#if 0
-//Hurdler: it's not used anymore
-static boolean NearVertice (polyvertex_t *p1, polyvertex_t *p2)
-{
-#if 1
- float diff;
- diff = p2->x - p1->x;
- if (diff < -1.5f || diff > 1.5f)
- return false;
- diff = p2->y - p1->y;
- if (diff < -1.5f || diff > 1.5f)
- return false;
-#else
- if (p1->x != p2->x)
- return false;
- if (p1->y != p2->y)
- return false;
-#endif
- // p1 and p2 are considered the same vertex
- return true;
-}
-#endif
-
// if two vertice coords have a x and/or y difference
// of less or equal than 1 FRACUNIT, they are considered the same
// point. Note: hardcoded value, 1.0f could be anything else.
@@ -254,11 +231,23 @@ static boolean SameVertice (polyvertex_t *p1, polyvertex_t *p2)
diff = p2->y - p1->y;
if (diff < -1.5f || diff > 1.5f)
return false;
-#else
+#elif 0
if (p1->x != p2->x)
return false;
if (p1->y != p2->y)
return false;
+#elif 0
+ if (fabsf( p2->x - p1->x ) > 1.0E-36f )
+ return false;
+ if (fabsf( p2->y - p1->y ) > 1.0E-36f )
+ return false;
+#else
+#define DIVLINE_VERTEX_DIFF 0.45f
+ float ep = DIVLINE_VERTEX_DIFF;
+ if (fabsf( p2->x - p1->x ) > ep )
+ return false;
+ if (fabsf( p2->y - p1->y ) > ep )
+ return false;
#endif
// p1 and p2 are considered the same vertex
return true;
@@ -295,57 +284,57 @@ static void SplitPoly (fdivline_t *bsp, //splitting parametric line
// start & end points
pv = fracdivline(bsp, &poly->pts[i], &poly->pts[j]);
- if (pv)
+ if (pv == NULL)
+ continue;
+
+ if (ps < 0)
{
- if (ps < 0)
+ // first point
+ ps = i;
+ vs = *pv;
+ fracs = bspfrac;
+ }
+ else
+ {
+ //the partition line traverse a junction between two segments
+ // or the two points are so close, they can be considered as one
+ // thus, don't accept, since split 2 must be another vertex
+ if (SameVertice(pv, &lastpv))
{
- // first point
- ps = i;
- vs = *pv;
- fracs = bspfrac;
- }
- else
- {
- //the partition line traverse a junction between two segments
- // or the two points are so close, they can be considered as one
- // thus, don't accept, since split 2 must be another vertex
- if (SameVertice(pv, &lastpv))
+ if (pe < 0)
{
- if (pe < 0)
- {
- ps = i;
- psonline = 1;
- }
- else
- {
- pe = i;
- peonline = 1;
- }
+ ps = i;
+ psonline = 1;
}
else
{
- if (pe < 0)
- {
- pe = i;
- ve = *pv;
- frace = bspfrac;
- }
- else
- {
- // a frac, not same vertice as last one
- // we already got pt2 so pt 2 is not on the line,
- // so we probably got back to the start point
- // which is on the line
- if (SameVertice(pv, &vs))
- psonline = 1;
- break;
- }
+ pe = i;
+ peonline = 1;
+ }
+ }
+ else
+ {
+ if (pe < 0)
+ {
+ pe = i;
+ ve = *pv;
+ frace = bspfrac;
+ }
+ else
+ {
+ // a frac, not same vertice as last one
+ // we already got pt2 so pt 2 is not on the line,
+ // so we probably got back to the start point
+ // which is on the line
+ if (SameVertice(pv, &vs))
+ psonline = 1;
+ break;
}
}
-
- // remember last point intercept to detect identical points
- lastpv = *pv;
}
+
+ // remember last point intercept to detect identical points
+ lastpv = *pv;
}
// no split: the partition line is either parallel and
@@ -369,7 +358,7 @@ static void SplitPoly (fdivline_t *bsp, //splitting parametric line
return;
}
- if (ps >= 0 && pe < 0)
+ if (pe < 0)
{
//I_Error("SplitPoly: only one point for split line (%d %d)", ps, pe);
*frontpoly = poly;
@@ -388,7 +377,7 @@ static void SplitPoly (fdivline_t *bsp, //splitting parametric line
*backpoly = HWR_AllocPoly(2 + nptback);
else
*backpoly = NULL;
- if (nptfront)
+ if (nptfront > 0)
*frontpoly = HWR_AllocPoly(2 + nptfront);
else
*frontpoly = NULL;
@@ -483,42 +472,42 @@ static poly_t *CutOutSubsecPoly(seg_t *lseg, INT32 count, poly_t *poly)
pv = fracdivline(&cutseg, &poly->pts[i], &poly->pts[j]);
- if (pv)
+ if (pv == NULL)
+ continue;
+
+ if (ps < 0)
{
- if (ps < 0)
+ ps = i;
+ vs = *pv;
+ fracs = bspfrac;
+ }
+ else
+ {
+ //frac 1 on previous segment,
+ // 0 on the next,
+ //the split line goes through one of the convex poly
+ // vertices, happens quite often since the convex
+ // poly is already adjacent to the subsector segs
+ // on most borders
+ if (SameVertice(pv, &vs))
+ continue;
+
+ if (fracs <= bspfrac)
{
+ nump = 2 + poly->numpts - (i-ps);
+ pe = ps;
ps = i;
- vs = *pv;
- fracs = bspfrac;
+ ve = *pv;
}
else
{
- //frac 1 on previous segment,
- // 0 on the next,
- //the split line goes through one of the convex poly
- // vertices, happens quite often since the convex
- // poly is already adjacent to the subsector segs
- // on most borders
- if (SameVertice(pv, &vs))
- continue;
-
- if (fracs <= bspfrac)
- {
- nump = 2 + poly->numpts - (i-ps);
- pe = ps;
- ps = i;
- ve = *pv;
- }
- else
- {
- nump = 2 + (i-ps);
- pe = i;
- ve = vs;
- vs = *pv;
- }
- //found 2nd point
- break;
+ nump = 2 + (i-ps);
+ pe = i;
+ ve = vs;
+ vs = *pv;
}
+ //found 2nd point
+ break;
}
}
@@ -582,18 +571,42 @@ static inline void HWR_SubsecPoly(INT32 num, poly_t *poly)
// search for the segs source of this divline
static inline void SearchDivline(node_t *bsp, fdivline_t *divline)
{
-#if 0 // MAR - If you don't use the same partition line that the BSP uses, the front/back polys won't match the subsectors in the BSP!
-#endif
divline->x = FIXED_TO_FLOAT(bsp->x);
divline->y = FIXED_TO_FLOAT(bsp->y);
divline->dx = FIXED_TO_FLOAT(bsp->dx);
divline->dy = FIXED_TO_FLOAT(bsp->dy);
}
+#ifdef HWR_LOADING_SCREEN
//Hurdler: implement a loading status
static size_t ls_count = 0;
static UINT8 ls_percent = 0;
+static void loading_status(void)
+{
+ char s[16];
+ int x, y;
+
+ I_OsPolling();
+ CON_Drawer();
+ sprintf(s, "%d%%", (++ls_percent)<<1);
+ x = BASEVIDWIDTH/2;
+ y = BASEVIDHEIGHT/2;
+ V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); // Black background to match fade in effect
+ //V_DrawPatchFill(W_CachePatchName("SRB2BACK",PU_CACHE)); // SRB2 background, ehhh too bright.
+ M_DrawTextBox(x-58, y-8, 13, 1);
+ V_DrawString(x-50, y, V_YELLOWMAP, "Loading...");
+ V_DrawRightAlignedString(x+50, y, V_YELLOWMAP, s);
+
+ // Is this really necessary at this point..?
+ V_DrawCenteredString(BASEVIDWIDTH/2, 40, V_YELLOWMAP, "OPENGL MODE IS INCOMPLETE AND MAY");
+ V_DrawCenteredString(BASEVIDWIDTH/2, 50, V_YELLOWMAP, "NOT DISPLAY SOME SURFACES.");
+ V_DrawCenteredString(BASEVIDWIDTH/2, 70, V_YELLOWMAP, "USE AT SONIC'S RISK.");
+
+ I_UpdateNoVsync();
+}
+#endif
+
// poly : the convex polygon that encloses all child subsectors
static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *bbox)
{
@@ -631,36 +644,19 @@ static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *b
}
else
{
- HWR_SubsecPoly(bspnum&(~NF_SUBSECTOR), poly);
- //Hurdler: implement a loading status
+ HWR_SubsecPoly(bspnum & ~NF_SUBSECTOR, poly);
+ //Hurdler: implement a loading status
+#ifdef HWR_LOADING_SCREEN
if (ls_count-- <= 0)
{
- char s[16];
- int x, y;
-
- I_OsPolling();
ls_count = numsubsectors/50;
- CON_Drawer();
- sprintf(s, "%d%%", (++ls_percent)<<1);
- x = BASEVIDWIDTH/2;
- y = BASEVIDHEIGHT/2;
- V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); // Background to match fade in effect
- //V_DrawPatchFill(W_CachePatchName("SRB2BACK",PU_CACHE)); // SRB2 background, ehhh too bright.
- M_DrawTextBox(x-58, y-8, 13, 1);
- V_DrawString(x-50, y, V_YELLOWMAP, "Loading...");
- V_DrawRightAlignedString(x+50, y, V_YELLOWMAP, s);
-
- // Is this really necessary at this point..?
- V_DrawCenteredString(BASEVIDWIDTH/2, 40, V_YELLOWMAP, "OPENGL MODE IS INCOMPLETE AND MAY");
- V_DrawCenteredString(BASEVIDWIDTH/2, 50, V_YELLOWMAP, "NOT DISPLAY SOME SURFACES.");
- V_DrawCenteredString(BASEVIDWIDTH/2, 70, V_YELLOWMAP, "USE AT SONIC'S RISK.");
-
- I_UpdateNoVsync();
+ loading_status();
}
+#endif
}
M_ClearBox(bbox);
- poly = extrasubsectors[bspnum&~NF_SUBSECTOR].planepoly;
+ poly = extrasubsectors[bspnum & ~NF_SUBSECTOR].planepoly;
for (i = 0, pt = poly->pts; i < poly->numpts; i++,pt++)
M_AddToBox(bbox, FLOAT_TO_FIXED(pt->x), FLOAT_TO_FIXED(pt->y));
@@ -692,14 +688,13 @@ static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *b
if (backpoly)
{
// Correct back bbox to include floor/ceiling convex polygon
- WalkBSPNode(bsp->children[1], backpoly, &bsp->children[1],
- bsp->bbox[1]);
+ WalkBSPNode(bsp->children[1], backpoly, &bsp->children[1], bsp->bbox[1]);
- // enlarge bbox with seconde child
+ // enlarge bbox with second child
M_AddToBox(bbox, bsp->bbox[1][BOXLEFT ],
- bsp->bbox[1][BOXTOP ]);
+ bsp->bbox[1][BOXTOP ]);
M_AddToBox(bbox, bsp->bbox[1][BOXRIGHT ],
- bsp->bbox[1][BOXBOTTOM]);
+ bsp->bbox[1][BOXBOTTOM]);
}
}
@@ -779,9 +774,9 @@ static void SearchSegInBSP(INT32 bspnum,polyvertex_t *p,poly_t *poly)
if (bspnum & NF_SUBSECTOR)
{
- if (bspnum!=-1)
+ if (bspnum != -1)
{
- bspnum&=~NF_SUBSECTOR;
+ bspnum &= ~NF_SUBSECTOR;
q = extrasubsectors[bspnum].planepoly;
if (poly == q || !q)
return;
@@ -967,7 +962,9 @@ void HWR_CreatePlanePolygons(INT32 bspnum)
fixed_t rootbbox[4];
CONS_Debug(DBG_RENDER, "Creating polygons, please wait...\n");
+#ifdef HWR_LOADING_SCREEN
ls_count = ls_percent = 0; // reset the loading status
+#endif
CON_Drawer(); //let the user know what we are doing
I_FinishUpdate(); // page flip or blit buffer
diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 395fc2e4b..6bc2c712e 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -41,37 +41,316 @@ static INT32 blocksize, blockwidth, blockheight;
INT32 patchformat = GR_TEXFMT_AP_88; // use alpha for holes
INT32 textureformat = GR_TEXFMT_P_8; // use chromakey for hole
-// sprite, use alpha and chroma key for hole
+static const INT32 format2bpp[16] =
+{
+ 0, //0
+ 0, //1
+ 1, //2 GR_TEXFMT_ALPHA_8
+ 1, //3 GR_TEXFMT_INTENSITY_8
+ 1, //4 GR_TEXFMT_ALPHA_INTENSITY_44
+ 1, //5 GR_TEXFMT_P_8
+ 4, //6 GR_RGBA
+ 0, //7
+ 0, //8
+ 0, //9
+ 2, //10 GR_TEXFMT_RGB_565
+ 2, //11 GR_TEXFMT_ARGB_1555
+ 2, //12 GR_TEXFMT_ARGB_4444
+ 2, //13 GR_TEXFMT_ALPHA_INTENSITY_88
+ 2, //14 GR_TEXFMT_AP_88
+};
+
+
+// This code was originally placed directly in HWR_DrawPatchInCache.
+// It is now split from it for my sanity! (and the sanity of others)
+// -- Monster Iestyn (13/02/19)
+static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipmap_t *mipmap,
+ INT32 pblockheight, INT32 blockmodulo,
+ fixed_t yfracstep, fixed_t scale_y,
+ texpatch_t *originPatch, INT32 patchheight,
+ INT32 bpp)
+{
+ fixed_t yfrac, position, count;
+ UINT8 *dest;
+ const UINT8 *source;
+ INT32 topdelta, prevdelta = -1;
+ INT32 originy = 0;
+
+ // for writing a pixel to dest
+ RGBA_t colortemp;
+ UINT8 alpha;
+ UINT8 texel;
+ UINT16 texelu16;
+
+ (void)patchheight; // This parameter is unused
+
+ if (originPatch) // originPatch can be NULL here, unlike in the software version
+ originy = originPatch->originy;
+
+ while (patchcol->topdelta != 0xff)
+ {
+ topdelta = patchcol->topdelta;
+ if (topdelta <= prevdelta)
+ topdelta += prevdelta;
+ prevdelta = topdelta;
+ source = (const UINT8 *)patchcol + 3;
+ count = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
+ position = originy + topdelta;
+
+ yfrac = 0;
+ //yfracstep = (patchcol->length << FRACBITS) / count;
+ if (position < 0)
+ {
+ yfrac = -position<> FRACBITS);
+ position = 0;
+ }
+
+ position = ((position * scale_y) + (FRACUNIT/2)) >> FRACBITS;
+
+ if (position < 0)
+ position = 0;
+
+ if (position + count >= pblockheight)
+ count = pblockheight - position;
+
+ dest = block + (position*blockmodulo);
+ while (count > 0)
+ {
+ count--;
+
+ texel = source[yfrac>>FRACBITS];
+
+ if (firetranslucent && (transtables[(texel<<8)+0x40000]!=texel))
+ alpha = 0x80;
+ else
+ alpha = 0xff;
+
+ //Hurdler: not perfect, but better than holes
+ if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX && (mipmap->flags & TF_CHROMAKEYED))
+ texel = HWR_CHROMAKEY_EQUIVALENTCOLORINDEX;
+ //Hurdler: 25/04/2000: now support colormap in hardware mode
+ else if (mipmap->colormap)
+ texel = mipmap->colormap[texel];
+
+ // hope compiler will get this switch out of the loops (dreams...)
+ // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
+ // Alam: SRB2 uses Mingw, HUGS
+ switch (bpp)
+ {
+ case 2 : texelu16 = (UINT16)((alpha<<8) | texel);
+ memcpy(dest, &texelu16, sizeof(UINT16));
+ break;
+ case 3 : colortemp = V_GetColor(texel);
+ memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
+ break;
+ case 4 : colortemp = V_GetColor(texel);
+ colortemp.s.alpha = alpha;
+ memcpy(dest, &colortemp, sizeof(RGBA_t));
+ break;
+ // default is 1
+ default: *dest = texel;
+ break;
+ }
+
+ dest += blockmodulo;
+ yfrac += yfracstep;
+ }
+ patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4);
+ }
+}
+
+static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, GLMipmap_t *mipmap,
+ INT32 pblockheight, INT32 blockmodulo,
+ fixed_t yfracstep, fixed_t scale_y,
+ texpatch_t *originPatch, INT32 patchheight,
+ INT32 bpp)
+{
+ fixed_t yfrac, position, count;
+ UINT8 *dest;
+ const UINT8 *source;
+ INT32 topdelta, prevdelta = -1;
+ INT32 originy = 0;
+
+ // for writing a pixel to dest
+ RGBA_t colortemp;
+ UINT8 alpha;
+ UINT8 texel;
+ UINT16 texelu16;
+
+ if (originPatch) // originPatch can be NULL here, unlike in the software version
+ originy = originPatch->originy;
+
+ while (patchcol->topdelta != 0xff)
+ {
+ topdelta = patchcol->topdelta;
+ if (topdelta <= prevdelta)
+ topdelta += prevdelta;
+ prevdelta = topdelta;
+ topdelta = patchheight-patchcol->length-topdelta;
+ source = (const UINT8 *)patchcol + 3;
+ count = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
+ position = originy + topdelta;
+
+ yfrac = (patchcol->length-1) << FRACBITS;
+
+ if (position < 0)
+ {
+ yfrac += position<> FRACBITS);
+ position = 0;
+ }
+
+ position = ((position * scale_y) + (FRACUNIT/2)) >> FRACBITS;
+
+ if (position < 0)
+ position = 0;
+
+ if (position + count >= pblockheight)
+ count = pblockheight - position;
+
+ dest = block + (position*blockmodulo);
+ while (count > 0)
+ {
+ count--;
+
+ texel = source[yfrac>>FRACBITS];
+
+ if (firetranslucent && (transtables[(texel<<8)+0x40000]!=texel))
+ alpha = 0x80;
+ else
+ alpha = 0xff;
+
+ //Hurdler: not perfect, but better than holes
+ if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX && (mipmap->flags & TF_CHROMAKEYED))
+ texel = HWR_CHROMAKEY_EQUIVALENTCOLORINDEX;
+ //Hurdler: 25/04/2000: now support colormap in hardware mode
+ else if (mipmap->colormap)
+ texel = mipmap->colormap[texel];
+
+ // hope compiler will get this switch out of the loops (dreams...)
+ // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
+ // Alam: SRB2 uses Mingw, HUGS
+ switch (bpp)
+ {
+ case 2 : texelu16 = (UINT16)((alpha<<8) | texel);
+ memcpy(dest, &texelu16, sizeof(UINT16));
+ break;
+ case 3 : colortemp = V_GetColor(texel);
+ memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
+ break;
+ case 4 : colortemp = V_GetColor(texel);
+ colortemp.s.alpha = alpha;
+ memcpy(dest, &colortemp, sizeof(RGBA_t));
+ break;
+ // default is 1
+ default: *dest = texel;
+ break;
+ }
+
+ dest += blockmodulo;
+ yfrac -= yfracstep;
+ }
+ patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4);
+ }
+}
+
+
+// Simplified patch caching function
+// for use by sprites and other patches that are not part of a wall texture
+// no alpha or flipping should be present since we do not want non-texture graphics to have them
+// no offsets are used either
+// -- Monster Iestyn (13/02/19)
static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
- INT32 pblockwidth, INT32 pblockheight, INT32 blockmodulo,
- INT32 ptexturewidth, INT32 ptextureheight,
- INT32 originx, INT32 originy, // where to draw patch in surface block
- const patch_t *realpatch, INT32 bpp)
+ INT32 pblockwidth, INT32 pblockheight,
+ INT32 pwidth, INT32 pheight,
+ const patch_t *realpatch)
+{
+ INT32 ncols;
+ fixed_t xfrac, xfracstep;
+ fixed_t yfracstep, scale_y;
+ const column_t *patchcol;
+ UINT8 *block = mipmap->grInfo.data;
+ INT32 bpp;
+ INT32 blockmodulo;
+
+ if (pwidth <= 0 || pheight <= 0)
+ return;
+
+ ncols = (pwidth * pblockwidth) / pwidth;
+
+ // source advance
+ xfrac = 0;
+ xfracstep = (pwidth << FRACBITS) / pblockwidth;
+ yfracstep = (pheight << FRACBITS) / pblockheight;
+ scale_y = (pblockheight << FRACBITS) / pheight;
+
+ bpp = format2bpp[mipmap->grInfo.format];
+
+ if (bpp < 1 || bpp > 4)
+ I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp);
+
+ // NOTE: should this actually be pblockwidth*bpp?
+ blockmodulo = blockwidth*bpp;
+
+ // Draw each column to the block cache
+ for (; ncols--; block += bpp, xfrac += xfracstep)
+ {
+ patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[xfrac>>FRACBITS]));
+
+ HWR_DrawColumnInCache(patchcol, block, mipmap,
+ pblockheight, blockmodulo,
+ yfracstep, scale_y,
+ NULL, pheight, // not that pheight is going to get used anyway...
+ bpp);
+ }
+}
+
+// This function we use for caching patches that belong to textures
+static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
+ INT32 pblockwidth, INT32 pblockheight,
+ texture_t *texture, texpatch_t *patch,
+ const patch_t *realpatch)
{
INT32 x, x1, x2;
INT32 col, ncols;
fixed_t xfrac, xfracstep;
- fixed_t yfrac, yfracstep, position, count;
- fixed_t scale_y;
- RGBA_t colortemp;
- UINT8 *dest;
- const UINT8 *source;
+ fixed_t yfracstep, scale_y;
const column_t *patchcol;
- UINT8 alpha;
UINT8 *block = mipmap->grInfo.data;
- UINT8 texel;
- UINT16 texelu16;
+ INT32 bpp;
+ INT32 blockmodulo;
+ INT32 width, height;
+ // Column drawing function pointer.
+ static void (*ColumnDrawerPointer)(const column_t *patchcol, UINT8 *block, GLMipmap_t *mipmap,
+ INT32 pblockheight, INT32 blockmodulo,
+ fixed_t yfracstep, fixed_t scale_y,
+ texpatch_t *originPatch, INT32 patchheight,
+ INT32 bpp);
- if (!ptexturewidth)
+ if (texture->width <= 0 || texture->height <= 0)
return;
- x1 = originx;
- x2 = x1 + SHORT(realpatch->width);
+ /*if ((patch->style == AST_TRANSLUCENT) && (patch->alpha <= (10*255/11))) // Alpha style set to translucent? Is the alpha small enough for translucency?
+ {
+ if (patch->alpha < 255/11) // Is the patch way too translucent? Don't render then.
+ continue;
+ ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawTransFlippedColumnInCache : HWR_DrawTransColumnInCache;
+ }
+ else*/
+ {
+ ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawFlippedColumnInCache : HWR_DrawColumnInCache;
+ }
- if (x1 > ptexturewidth || x2 < 0)
+ x1 = patch->originx;
+ width = SHORT(realpatch->width);
+ height = SHORT(realpatch->height);
+ x2 = x1 + width;
+
+ if (x1 > texture->width || x2 < 0)
return; // patch not located within texture's x bounds, ignore
- if (originy > ptextureheight || (originy + SHORT(realpatch->height)) < 0)
+ if (patch->originy > texture->height || (patch->originy + height) < 0)
return; // patch not located within texture's y bounds, ignore
// patch is actually inside the texture!
@@ -84,19 +363,18 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
x = x1;
// right edge
- if (x2 > ptexturewidth)
- x2 = ptexturewidth;
+ if (x2 > texture->width)
+ x2 = texture->width;
- col = x * pblockwidth / ptexturewidth;
- ncols = ((x2 - x) * pblockwidth) / ptexturewidth;
+ col = x * pblockwidth / texture->width;
+ ncols = ((x2 - x) * pblockwidth) / texture->width;
/*
- CONS_Debug(DBG_RENDER, "patch %dx%d texture %dx%d block %dx%d\n", SHORT(realpatch->width),
- SHORT(realpatch->height),
- ptexturewidth,
- textureheight,
- pblockwidth,pblockheight);
+ CONS_Debug(DBG_RENDER, "patch %dx%d texture %dx%d block %dx%d\n",
+ width, height,
+ texture->width, texture->height,
+ pblockwidth, pblockheight);
CONS_Debug(DBG_RENDER, " col %d ncols %d x %d\n", col, ncols, x);
*/
@@ -105,90 +383,31 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
if (x1 < 0)
xfrac = -x1<width << FRACBITS) / pblockwidth;
+ yfracstep = (texture->height<< FRACBITS) / pblockheight;
+ scale_y = (pblockheight << FRACBITS) / texture->height;
+
+ bpp = format2bpp[mipmap->grInfo.format];
+
if (bpp < 1 || bpp > 4)
I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp);
+ // NOTE: should this actually be pblockwidth*bpp?
+ blockmodulo = blockwidth*bpp;
+
+ // Draw each column to the block cache
for (block += col*bpp; ncols--; block += bpp, xfrac += xfracstep)
{
- INT32 topdelta, prevdelta = -1;
- patchcol = (const column_t *)((const UINT8 *)realpatch
- + LONG(realpatch->columnofs[xfrac>>FRACBITS]));
+ if (patch->flip & 1)
+ patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[(width-1)-(xfrac>>FRACBITS)]));
+ else
+ patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[xfrac>>FRACBITS]));
- scale_y = (pblockheight << FRACBITS) / ptextureheight;
-
- while (patchcol->topdelta != 0xff)
- {
- topdelta = patchcol->topdelta;
- if (topdelta <= prevdelta)
- topdelta += prevdelta;
- prevdelta = topdelta;
- source = (const UINT8 *)patchcol + 3;
- count = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
- position = originy + topdelta;
-
- yfrac = 0;
- //yfracstep = (patchcol->length << FRACBITS) / count;
- if (position < 0)
- {
- yfrac = -position<> FRACBITS);
- position = 0;
- }
-
- position = ((position * scale_y) + (FRACUNIT/2)) >> FRACBITS;
-
- if (position < 0)
- position = 0;
-
- if (position + count >= pblockheight)
- count = pblockheight - position;
-
- dest = block + (position*blockmodulo);
- while (count > 0)
- {
- count--;
-
- texel = source[yfrac>>FRACBITS];
-
- if (firetranslucent && (transtables[(texel<<8)+0x40000]!=texel))
- alpha = 0x80;
- else
- alpha = 0xff;
-
- //Hurdler: not perfect, but better than holes
- if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX && (mipmap->flags & TF_CHROMAKEYED))
- texel = HWR_CHROMAKEY_EQUIVALENTCOLORINDEX;
- //Hurdler: 25/04/2000: now support colormap in hardware mode
- else if (mipmap->colormap)
- texel = mipmap->colormap[texel];
-
- // hope compiler will get this switch out of the loops (dreams...)
- // gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
- // Alam: SRB2 uses Mingw, HUGS
- switch (bpp)
- {
- case 2 : texelu16 = (UINT16)((alpha<<8) | texel);
- memcpy(dest, &texelu16, sizeof(UINT16));
- break;
- case 3 : colortemp = V_GetColor(texel);
- memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
- break;
- case 4 : colortemp = V_GetColor(texel);
- colortemp.s.alpha = alpha;
- memcpy(dest, &colortemp, sizeof(RGBA_t));
- break;
- // default is 1
- default: *dest = texel;
- break;
- }
-
- dest += blockmodulo;
- yfrac += yfracstep;
- }
- patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4);
- }
+ ColumnDrawerPointer(patchcol, block, mipmap,
+ pblockheight, blockmodulo,
+ yfracstep, scale_y,
+ patch, height,
+ bpp);
}
}
@@ -349,26 +568,6 @@ static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight,
//CONS_Debug(DBG_RENDER, "Width is %d, Height is %d\n", blockwidth, blockheight);
}
-
-static const INT32 format2bpp[16] =
-{
- 0, //0
- 0, //1
- 1, //2 GR_TEXFMT_ALPHA_8
- 1, //3 GR_TEXFMT_INTENSITY_8
- 1, //4 GR_TEXFMT_ALPHA_INTENSITY_44
- 1, //5 GR_TEXFMT_P_8
- 4, //6 GR_RGBA
- 0, //7
- 0, //8
- 0, //9
- 2, //10 GR_TEXFMT_RGB_565
- 2, //11 GR_TEXFMT_ARGB_1555
- 2, //12 GR_TEXFMT_ARGB_4444
- 2, //13 GR_TEXFMT_ALPHA_INTENSITY_88
- 2, //14 GR_TEXFMT_AP_88
-};
-
static UINT8 *MakeBlock(GLMipmap_t *grMipmap)
{
UINT8 *block;
@@ -452,13 +651,10 @@ static void HWR_GenerateTexture(INT32 texnum, GLTexture_t *grtex)
for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
{
realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
- HWR_DrawPatchInCache(&grtex->mipmap,
+ HWR_DrawTexturePatchInCache(&grtex->mipmap,
blockwidth, blockheight,
- blockwidth*format2bpp[grtex->mipmap.grInfo.format],
- texture->width, texture->height,
- patch->originx, patch->originy,
- realpatch,
- format2bpp[grtex->mipmap.grInfo.format]);
+ texture, patch,
+ realpatch);
Z_Unlock(realpatch);
}
//Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :(
@@ -545,11 +741,8 @@ void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipm
HWR_DrawPatchInCache(grMipmap,
newwidth, newheight,
- blockwidth*format2bpp[grMipmap->grInfo.format],
grPatch->width, grPatch->height,
- 0, 0,
- patch,
- format2bpp[grMipmap->grInfo.format]);
+ patch);
}
grPatch->max_s = (float)newwidth / (float)blockwidth;
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 100bb1180..c43cfe82a 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -296,7 +296,7 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
Z_Free(realpatch);
}
// centre screen
- if ((float)vid.width != (float)BASEVIDWIDTH * dupx)
+ if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
{
if (option & V_SNAPTORIGHT)
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx));
@@ -307,7 +307,7 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
else if (perplayershuffle & 8)
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/4;
}
- if ((float)vid.height != (float)BASEVIDHEIGHT * dupy)
+ if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f)
{
if (option & V_SNAPTOBOTTOM)
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy));
@@ -452,14 +452,14 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
Z_Free(realpatch);
}
// centre screen
- if ((float)vid.width != (float)BASEVIDWIDTH * dupx)
+ if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
{
if (option & V_SNAPTORIGHT)
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx));
else if (!(option & V_SNAPTOLEFT))
cx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx))/2;
}
- if ((float)vid.height != (float)BASEVIDHEIGHT * dupy)
+ if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f)
{
if (option & V_SNAPTOBOTTOM)
cy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy));
@@ -876,18 +876,6 @@ void HWR_DrawViewBorder(INT32 clearlines)
// AM_MAP.C DRAWING STUFF
// ==========================================================================
-// Clear the automap part of the screen
-void HWR_clearAutomap(void)
-{
- FRGBAFloat fColor = {0, 0, 0, 1};
-
- // minx,miny,maxx,maxy
- HWD.pfnGClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
- HWD.pfnClearBuffer(true, true, &fColor);
- HWD.pfnGClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE);
-}
-
-
// -----------------+
// HWR_drawAMline : draw a line of the automap (the clipping is already done in automap code)
// Arg : color is a RGB 888 value
@@ -908,6 +896,110 @@ void HWR_drawAMline(const fline_t *fl, INT32 color)
HWD.pfnDraw2DLine(&v1, &v2, color_rgba);
}
+// -------------------+
+// HWR_DrawConsoleFill : draw flat coloured transparent rectangle because that's cool, and hw sucks less than sw for that.
+// -------------------+
+void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, UINT32 color, INT32 options)
+{
+ FOutVector v[4];
+ FSurfaceInfo Surf;
+ float fx, fy, fw, fh;
+
+ if (w < 0 || h < 0)
+ return; // consistency w/ software
+
+// 3--2
+// | /|
+// |/ |
+// 0--1
+
+ fx = (float)x;
+ fy = (float)y;
+ fw = (float)w;
+ fh = (float)h;
+
+ if (!(options & V_NOSCALESTART))
+ {
+ float dupx = (float)vid.dupx, dupy = (float)vid.dupy;
+
+ if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
+ {
+ RGBA_t rgbaColour = V_GetColor(color);
+ FRGBAFloat clearColour;
+ clearColour.red = (float)rgbaColour.s.red / 255;
+ clearColour.green = (float)rgbaColour.s.green / 255;
+ clearColour.blue = (float)rgbaColour.s.blue / 255;
+ clearColour.alpha = 1;
+ HWD.pfnClearBuffer(true, false, &clearColour);
+ return;
+ }
+
+ fx *= dupx;
+ fy *= dupy;
+ fw *= dupx;
+ fh *= dupy;
+
+ if (fabsf((float)vid.width - ((float)BASEVIDWIDTH * dupx)) > 1.0E-36f)
+ {
+ if (options & V_SNAPTORIGHT)
+ fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx));
+ else if (!(options & V_SNAPTOLEFT))
+ fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 2;
+ }
+ if (fabsf((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) > 1.0E-36f)
+ {
+ // same thing here
+ if (options & V_SNAPTOBOTTOM)
+ fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy));
+ else if (!(options & V_SNAPTOTOP))
+ fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 2;
+ }
+ }
+
+ if (fx >= vid.width || fy >= vid.height)
+ return;
+ if (fx < 0)
+ {
+ fw += fx;
+ fx = 0;
+ }
+ if (fy < 0)
+ {
+ fh += fy;
+ fy = 0;
+ }
+
+ if (fw <= 0 || fh <= 0)
+ return;
+ if (fx + fw > vid.width)
+ fw = (float)vid.width - fx;
+ if (fy + fh > vid.height)
+ fh = (float)vid.height - fy;
+
+ fx = -1 + fx / (vid.width / 2);
+ fy = 1 - fy / (vid.height / 2);
+ fw = fw / (vid.width / 2);
+ fh = fh / (vid.height / 2);
+
+ v[0].x = v[3].x = fx;
+ v[2].x = v[1].x = fx + fw;
+ v[0].y = v[1].y = fy;
+ v[2].y = v[3].y = fy - fh;
+
+ //Hurdler: do we still use this argb color? if not, we should remove it
+ v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //;
+ v[0].z = v[1].z = v[2].z = v[3].z = 1.0f;
+
+ v[0].sow = v[3].sow = 0.0f;
+ v[2].sow = v[1].sow = 1.0f;
+ v[0].tow = v[1].tow = 0.0f;
+ v[2].tow = v[3].tow = 1.0f;
+
+ Surf.FlatColor.rgba = UINT2RGBA(color);
+ Surf.FlatColor.s.alpha = 0x80;
+
+ HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest);
+}
// -----------------+
// HWR_DrawFill : draw flat coloured rectangle, with no texture
@@ -1022,7 +1114,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
fw *= dupx;
fh *= dupy;
- if ((float)vid.width != (float)BASEVIDWIDTH * dupx)
+ if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f)
{
if (color & V_SNAPTORIGHT)
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx));
@@ -1033,7 +1125,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
else if (perplayershuffle & 8)
fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4;
}
- if ((float)vid.height != (float)BASEVIDHEIGHT * dupy)
+ if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f)
{
// same thing here
if (color & V_SNAPTOBOTTOM)
diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h
index 5d1a81d4f..9656e54e9 100644
--- a/src/hardware/hw_glob.h
+++ b/src/hardware/hw_glob.h
@@ -27,6 +27,9 @@
// the original aspect ratio of Doom graphics isn't square
#define ORIGINAL_ASPECT (320.0f/200.0f)
+// Uncomment this to enable the OpenGL loading screen
+//#define HWR_LOADING_SCREEN
+
// -----------
// structures
// -----------
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 3a152562b..2b32d8ff8 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -3576,9 +3576,7 @@ static void HWR_Subsector(size_t num)
#ifndef POLYSKY
// Moved here because before, when above the ceiling and the floor does not have the sky flat, it doesn't draw the sky
if (gr_frontsector->ceilingpic == skyflatnum || gr_frontsector->floorpic == skyflatnum)
- {
drawsky = true;
- }
#endif
#ifdef R_FAKEFLOORS
@@ -4158,7 +4156,7 @@ static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float t
swallVerts[0].z = swallVerts[3].z = spr->z1;
swallVerts[2].z = swallVerts[1].z = spr->z2;
- if (spr->mobj && this_scale != 1.0f)
+ if (spr->mobj && fabsf(this_scale - 1.0f) > 1.0E-36f)
{
// Always a pixel above the floor, perfectly flat.
swallVerts[0].y = swallVerts[1].y = swallVerts[2].y = swallVerts[3].y = spr->ty - gpatch->topoffset * this_scale - (floorheight+3);
@@ -4326,7 +4324,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr)
wallVerts[1].z = wallVerts[2].z = spr->z2;
wallVerts[2].y = wallVerts[3].y = spr->ty;
- if (spr->mobj && this_scale != 1.0f)
+ if (spr->mobj && fabsf(this_scale - 1.0f) > 1.0E-36f)
wallVerts[0].y = wallVerts[1].y = spr->ty - gpatch->height * this_scale;
else
wallVerts[0].y = wallVerts[1].y = spr->ty - gpatch->height;
@@ -4355,6 +4353,16 @@ static void HWR_SplitSprite(gr_vissprite_t *spr)
wallVerts[0].tow = wallVerts[1].tow = gpatch->max_t;
}
+ // if it has a dispoffset, push it a little towards the camera
+ if (spr->dispoffset) {
+ float co = -gr_viewcos*(0.05f*spr->dispoffset);
+ float si = -gr_viewsin*(0.05f*spr->dispoffset);
+ wallVerts[0].z = wallVerts[3].z = wallVerts[0].z+si;
+ wallVerts[1].z = wallVerts[2].z = wallVerts[1].z+si;
+ wallVerts[0].x = wallVerts[3].x = wallVerts[0].x+co;
+ wallVerts[1].x = wallVerts[2].x = wallVerts[1].x+co;
+ }
+
realtop = top = wallVerts[3].y;
realbot = bot = wallVerts[0].y;
towtop = wallVerts[3].tow;
@@ -4607,7 +4615,7 @@ static void HWR_DrawSprite(gr_vissprite_t *spr)
wallVerts[0].x = wallVerts[3].x = spr->x1;
wallVerts[2].x = wallVerts[1].x = spr->x2;
wallVerts[2].y = wallVerts[3].y = spr->ty;
- if (spr->mobj && this_scale != 1.0f)
+ if (spr->mobj && fabsf(this_scale - 1.0f) > 1.0E-36f)
wallVerts[0].y = wallVerts[1].y = spr->ty - gpatch->height * this_scale;
else
wallVerts[0].y = wallVerts[1].y = spr->ty - gpatch->height;
@@ -4657,6 +4665,16 @@ static void HWR_DrawSprite(gr_vissprite_t *spr)
HWR_DrawSpriteShadow(spr, gpatch, this_scale);
}
+ // if it has a dispoffset, push it a little towards the camera
+ if (spr->dispoffset) {
+ float co = -gr_viewcos*(0.05f*spr->dispoffset);
+ float si = -gr_viewsin*(0.05f*spr->dispoffset);
+ wallVerts[0].z = wallVerts[3].z = wallVerts[0].z+si;
+ wallVerts[1].z = wallVerts[2].z = wallVerts[1].z+si;
+ wallVerts[0].x = wallVerts[3].x = wallVerts[0].x+co;
+ wallVerts[1].x = wallVerts[2].x = wallVerts[1].x+co;
+ }
+
// This needs to be AFTER the shadows so that the regular sprites aren't drawn completely black.
// sprite lighting by modulating the RGB components
/// \todo coloured
@@ -4853,7 +4871,7 @@ static void HWR_SortVisSprites(void)
best = ds;
}
// order visprites of same scale by dispoffset, smallest first
- else if (ds->tz == bestdist && ds->dispoffset < bestdispoffset)
+ else if (fabsf(ds->tz - bestdist) < 1.0E-36f && ds->dispoffset < bestdispoffset)
{
bestdispoffset = ds->dispoffset;
best = ds;
@@ -5694,7 +5712,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing)
// ==========================================================================
//
// ==========================================================================
-static void HWR_DrawSkyBackground(player_t *player)
+static void HWR_DrawSkyBackground(void)
{
FOutVector v[4];
angle_t angle;
@@ -5702,18 +5720,18 @@ static void HWR_DrawSkyBackground(player_t *player)
float aspectratio;
float angleturn;
-// 3--2
-// | /|
-// |/ |
-// 0--1
-
- (void)player;
HWR_GetTexture(texturetranslation[skytexture]);
+ aspectratio = (float)vid.width/(float)vid.height;
//Hurdler: the sky is the only texture who need 4.0f instead of 1.0
// because it's called just after clearing the screen
// and thus, the near clipping plane is set to 3.99
// Sryder: Just use the near clipping plane value then
+
+ // 3--2
+ // | /|
+ // |/ |
+ // 0--1
v[0].x = v[3].x = -ZCLIP_PLANE-1;
v[1].x = v[2].x = ZCLIP_PLANE+1;
v[0].y = v[1].y = -ZCLIP_PLANE-1;
@@ -5737,10 +5755,13 @@ static void HWR_DrawSkyBackground(player_t *player)
// Y
angle = aimingangle;
-
- aspectratio = (float)vid.width/(float)vid.height;
dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->height/(128.0f*aspectratio));
- angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply;
+
+ if (splitscreen)
+ {
+ dimensionmultiply *= 2;
+ angle *= 2;
+ }
// Middle of the sky should always be at angle 0
// need to keep correct aspect ratio with X
@@ -5756,6 +5777,8 @@ static void HWR_DrawSkyBackground(player_t *player)
v[3].tow = v[2].tow = v[0].tow - (1.0f/dimensionmultiply); // top (or bottom - 1.0f)
}
+ angleturn = (((float)ANGLE_45-1.0f)*aspectratio)*dimensionmultiply;
+
if (angle > ANGLE_180) // Do this because we don't want the sky to suddenly teleport when crossing over 0 to 360 and vice versa
{
angle = InvAngle(angle);
@@ -5814,7 +5837,7 @@ void HWR_SetViewSize(void)
gr_viewwindowx = (vid.width - gr_viewwidth) / 2;
gr_windowcenterx = (float)(vid.width / 2);
- if (gr_viewwidth == vid.width)
+ if (fabsf(gr_viewwidth - vid.width) < 1.0E-36f)
{
gr_baseviewwindowy = 0;
gr_basewindowcentery = gr_viewheight / 2; // window top left corner at 0,0
@@ -5920,7 +5943,7 @@ if (0)
}
if (drawsky)
- HWR_DrawSkyBackground(player);
+ HWR_DrawSkyBackground();
//Hurdler: it doesn't work in splitscreen mode
drawsky = splitscreen;
@@ -6137,7 +6160,7 @@ if (0)
}
if (!skybox && drawsky) // Don't draw the regular sky if there's a skybox
- HWR_DrawSkyBackground(player);
+ HWR_DrawSkyBackground();
//Hurdler: it doesn't work in splitscreen mode
drawsky = splitscreen;
diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h
index f25720d1e..642e440d6 100644
--- a/src/hardware/hw_main.h
+++ b/src/hardware/hw_main.h
@@ -31,7 +31,6 @@
void HWR_Startup(void);
void HWR_Shutdown(void);
-void HWR_clearAutomap(void);
void HWR_drawAMline(const fline_t *fl, INT32 color);
void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength);
void HWR_DrawConsoleBack(UINT32 color, INT32 height);
@@ -52,6 +51,7 @@ void HWR_CreatePlanePolygons(INT32 bspnum);
void HWR_CreateStaticLightmaps(INT32 bspnum);
void HWR_PrepLevelCache(size_t pnumtextures);
void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color);
+void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, UINT32 color, INT32 options); // Lat: separate flags from color since color needs to be an uint to work right.
void HWR_DrawPic(INT32 x,INT32 y,lumpnum_t lumpnum);
void HWR_AddCommands(void);
diff --git a/src/hardware/r_opengl/r_opengl-vc10.vcxproj b/src/hardware/r_opengl/r_opengl-vc10.vcxproj
index f04ae320b..d1f856e96 100644
--- a/src/hardware/r_opengl/r_opengl-vc10.vcxproj
+++ b/src/hardware/r_opengl/r_opengl-vc10.vcxproj
@@ -1,6 +1,14 @@

+
+ Debug
+ ARM
+
+
+ Debug
+ ARM64
+
Debug
Win32
@@ -9,6 +17,14 @@
Debug
x64
+
+ Release
+ ARM
+
+
+ Release
+ ARM64
+
Release
Win32
@@ -22,7 +38,7 @@
r_opengl
{51137D5C-4E81-4955-AACF-EA3092006051}
r_opengl
- 8.1
+ 10.0.16299.0
@@ -30,21 +46,45 @@
false
v140
+
+ DynamicLibrary
+ false
+ v141
+ true
+
DynamicLibrary
false
v140
+
+ DynamicLibrary
+ false
+ v141
+ true
+
DynamicLibrary
false
v140
+
+ DynamicLibrary
+ false
+ v141
+ true
+
DynamicLibrary
false
v140
+
+ DynamicLibrary
+ false
+ v141
+ true
+
@@ -52,37 +92,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<_ProjectFileVersion>10.0.30319.1
.\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
.\..\..\..\objs\VC10\$(Platform)\$(Configuration)\r_opengl\
+ .\..\..\..\objs\VC10\$(Platform)\$(Configuration)\r_opengl\
true
+ true
true
+ true
.\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
.\..\..\..\objs\VC10\$(Platform)\$(Configuration)\r_opengl\
+ .\..\..\..\objs\VC10\$(Platform)\$(Configuration)\r_opengl\
true
+ true
true
+ true
.\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
.\..\..\..\objs\VC10\$(Platform)\$(Configuration)\r_opengl\
+ .\..\..\..\objs\VC10\$(Platform)\$(Configuration)\r_opengl\
true
+ true
false
+ false
.\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
.\..\..\..\objs\VC10\$(Platform)\$(Configuration)\r_opengl\
+ .\..\..\..\objs\VC10\$(Platform)\$(Configuration)\r_opengl\
true
+ true
false
+ false
@@ -123,6 +195,49 @@
$(IntDir)r_opengl.lib
MachineX86
Windows
+ gdi32.lib;%(AdditionalDependencies)
+
+
+ true
+ $(OutDir)r_opengl.bsc
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\r_opengl\r_opengl.tlb
+
+
+
+
+ Disabled
+ _DEBUG;WIN32;_WINDOWS;__WIN32__;__MSC__;USE_WGL_SWAP;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebug
+ $(IntDir)
+ $(IntDir)r_opengl.pdb
+ true
+ Level4
+ true
+ ProgramDatabase
+ CompileAsC
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(OutDir)r_opengl.dll
+ true
+ true
+ $(OutDir)r_opengl.pdb
+
+
+ $(IntDir)r_opengl.lib
+ Windows
+ gdi32.lib;%(AdditionalDependencies)
true
@@ -168,6 +283,49 @@
$(IntDir)r_opengl.lib
MachineX64
Windows
+ gdi32.lib;%(AdditionalDependencies)
+
+
+ true
+ $(OutDir)r_opengl.bsc
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\r_opengl\r_opengl.tlb
+
+
+
+
+ Disabled
+ _DEBUG;WIN32;_WINDOWS;__WIN32__;__MSC__;USE_WGL_SWAP;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebug
+ $(IntDir)
+ $(IntDir)r_opengl.pdb
+ true
+ Level4
+ true
+ ProgramDatabase
+ CompileAsC
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(OutDir)r_opengl.dll
+ true
+ true
+ $(OutDir)r_opengl.pdb
+
+
+ $(IntDir)r_opengl.lib
+ Windows
+ gdi32.lib;%(AdditionalDependencies)
true
@@ -215,6 +373,52 @@
$(IntDir)r_opengl.lib
MachineX86
Windows
+ gdi32.lib;%(AdditionalDependencies)
+
+
+ true
+ $(OutDir)r_opengl.bsc
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\r_opengl\r_opengl.tlb
+
+
+
+
+ /MP %(AdditionalOptions)
+ MaxSpeed
+ OnlyExplicitInline
+ NDEBUG;WIN32;_WINDOWS;__WIN32__;__MSC__;USE_WGL_SWAP;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ $(IntDir)
+ $(IntDir)r_opengl.pdb
+ true
+ Level3
+ true
+ ProgramDatabase
+ CompileAsC
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(OutDir)r_opengl.dll
+ true
+ true
+ $(OutDir)r_opengl.pdb
+
+
+ $(IntDir)r_opengl.lib
+ Windows
+ gdi32.lib;%(AdditionalDependencies)
true
@@ -262,6 +466,52 @@
$(IntDir)r_opengl.lib
MachineX64
Windows
+ gdi32.lib;%(AdditionalDependencies)
+
+
+ true
+ $(OutDir)r_opengl.bsc
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\r_opengl\r_opengl.tlb
+
+
+
+
+ /MP %(AdditionalOptions)
+ MaxSpeed
+ OnlyExplicitInline
+ NDEBUG;WIN32;_WINDOWS;__WIN32__;__MSC__;USE_WGL_SWAP;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ $(IntDir)
+ $(IntDir)r_opengl.pdb
+ true
+ Level3
+ true
+ ProgramDatabase
+ CompileAsC
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ $(OutDir)r_opengl.dll
+ true
+ true
+ $(OutDir)r_opengl.pdb
+
+
+ $(IntDir)r_opengl.lib
+ Windows
+ gdi32.lib;%(AdditionalDependencies)
true
@@ -271,15 +521,23 @@
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index 6ab268a85..dfee19857 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// Copyright (C) 1998-2006 by Sonic Team Junior.
+// Copyright (C) 1998-2018 by Sonic Team Junior.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
@@ -551,7 +551,8 @@ static void GLPerspective(GLdouble fovy, GLdouble aspect)
const GLdouble deltaZ = zFar - zNear;
GLdouble cotangent;
- if ((deltaZ == 0.0f) || (sine == 0.0f) || (aspect == 0.0f)) {
+ if ((fabsf((float)deltaZ) < 1.0E-36f) || fpclassify(sine) == FP_ZERO || fpclassify(aspect) == FP_ZERO)
+ {
return;
}
cotangent = cos(radians) / sine;
@@ -585,7 +586,7 @@ static void GLProject(GLdouble objX, GLdouble objY, GLdouble objZ,
out[2] * projMatrix[2*4+i] +
out[3] * projMatrix[3*4+i];
}
- if (in[3] == 0.0f) return;
+ if (fpclassify(in[3]) == FP_ZERO) return;
in[0] /= in[3];
in[1] /= in[3];
in[2] /= in[3];
@@ -1660,7 +1661,7 @@ static void DrawMD2Ex(INT32 *gl_cmd_buffer, md2_frame_t *frame, INT32 duration,
pglTexCoord2f(s, t);
- if (!nextframe || pol == 0.0f)
+ if (!nextframe || fpclassify(pol) == FP_ZERO)
{
pglNormal3f(frame->vertices[pindex].normal[0],
frame->vertices[pindex].normal[1],
@@ -1729,6 +1730,7 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
pglLoadIdentity();
if (stransform)
{
+ boolean fovx90;
// keep a trace of the transformation for md2
memcpy(&md2_transform, stransform, sizeof (md2_transform));
@@ -1743,7 +1745,8 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform)
pglMatrixMode(GL_PROJECTION);
pglLoadIdentity();
- special_splitscreen = (stransform->splitscreen && stransform->fovxangle == 90.0f);
+ fovx90 = stransform->fovxangle > 0.0f && fabsf(stransform->fovxangle - 90.0f) < 0.5f;
+ special_splitscreen = (stransform->splitscreen && fovx90);
if (special_splitscreen)
GLPerspective(53.13l, 2*ASPECT_RATIO); // 53.13 = 2*atan(0.5)
else
diff --git a/src/hardware/s_openal/s_openal-vc10.vcxproj b/src/hardware/s_openal/s_openal-vc10.vcxproj
index 8b4f6cbbe..5039cd006 100644
--- a/src/hardware/s_openal/s_openal-vc10.vcxproj
+++ b/src/hardware/s_openal/s_openal-vc10.vcxproj
@@ -1,6 +1,14 @@

+
+ Debug
+ ARM
+
+
+ Debug
+ ARM64
+
Debug
Win32
@@ -9,6 +17,14 @@
Debug
x64
+
+ Release
+ ARM
+
+
+ Release
+ ARM64
+
Release
Win32
@@ -22,7 +38,7 @@
s_openal
{E662D0B3-412D-4D55-A5EC-8CBD680DDCBE}
s_openal
- 8.1
+ 10.0.16299.0
@@ -31,22 +47,44 @@
MultiByte
v140
+
+ DynamicLibrary
+ false
+ MultiByte
+ v141
+
DynamicLibrary
false
v140
+
+ DynamicLibrary
+ false
+ v141
+
DynamicLibrary
false
MultiByte
v140
+
+ DynamicLibrary
+ false
+ MultiByte
+ v141
+
DynamicLibrary
false
v140
+
+ DynamicLibrary
+ false
+ v141
+
@@ -54,37 +92,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<_ProjectFileVersion>10.0.30319.1
.\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
.\..\..\..\objs\VC10\$(Platform)\$(Configuration)\s_openal\
+ .\..\..\..\objs\VC10\$(Platform)\$(Configuration)\s_openal\
true
+ true
false
+ false
.\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
.\..\..\..\objs\VC10\$(Platform)\$(Configuration)\s_openal\
+ .\..\..\..\objs\VC10\$(Platform)\$(Configuration)\s_openal\
true
+ true
false
+ false
.\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
.\..\..\..\objs\VC10\$(Platform)\$(Configuration)\s_openal\
+ .\..\..\..\objs\VC10\$(Platform)\$(Configuration)\s_openal\
true
+ true
true
+ true
.\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\
.\..\..\..\objs\VC10\$(Platform)\$(Configuration)\s_openal\
+ .\..\..\..\objs\VC10\$(Platform)\$(Configuration)\s_openal\
true
+ true
true
+ true
@@ -132,6 +202,50 @@
$(OutDir)s_openal.bsc
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\s_openal\s_openal.tlb
+
+
+
+
+ /MP %(AdditionalOptions)
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;_WINDOWS;__WIN32__;__MSC__;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ $(IntDir)
+ $(IntDir)s_openal.pdb
+ true
+ Level3
+ true
+ CompileAsC
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ OpenAL32.lib;%(AdditionalDependencies)
+ $(OutDir)s_openal.dll
+ true
+ true
+ $(OutDir)s_openal.pdb
+ false
+
+
+ $(IntDir)s_openal.lib
+ Windows
+
+
+ true
+ $(OutDir)s_openal.bsc
+
+
NDEBUG;%(PreprocessorDefinitions)
@@ -178,6 +292,50 @@
$(OutDir)s_openal.bsc
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\s_openal\s_openal.tlb
+
+
+
+
+ /MP %(AdditionalOptions)
+ MaxSpeed
+ OnlyExplicitInline
+ WIN32;NDEBUG;_WINDOWS;__WIN32__;__MSC__;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ true
+ MultiThreaded
+ true
+ $(IntDir)
+ $(IntDir)s_openal.pdb
+ true
+ Level3
+ true
+ CompileAsC
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ OpenAL32.lib;%(AdditionalDependencies)
+ $(OutDir)s_openal.dll
+ true
+ true
+ $(OutDir)s_openal.pdb
+ false
+
+
+ $(IntDir)s_openal.lib
+ Windows
+
+
+ true
+ $(OutDir)s_openal.bsc
+
+
_DEBUG;%(PreprocessorDefinitions)
@@ -225,6 +383,50 @@
$(OutDir)s_openal.bsc
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\s_openal\s_openal.tlb
+
+
+
+
+ /MP /SAFESEH:OFF %(AdditionalOptions)
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;__WIN32__;__MSC__;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebug
+ $(IntDir)
+ $(IntDir)s_openal.pdb
+ true
+ Level4
+ true
+ ProgramDatabase
+ CompileAsC
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ OpenAL32.lib;%(AdditionalDependencies)
+ $(OutDir)s_openal.dll
+ true
+ true
+ $(OutDir)s_openal.pdb
+ false
+
+
+ $(IntDir)s_openal.lib
+ Windows
+
+
+ true
+ $(OutDir)s_openal.bsc
+
+
_DEBUG;%(PreprocessorDefinitions)
@@ -272,12 +474,60 @@
$(OutDir)s_openal.bsc
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\..\..\..\bin\VC10\$(Platform)\$(Configuration)\s_openal\s_openal.tlb
+
+
+
+
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;__WIN32__;__MSC__;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+ EnableFastChecks
+ MultiThreadedDebug
+ $(IntDir)
+ $(IntDir)s_openal.pdb
+ true
+ Level4
+ true
+ ProgramDatabase
+ CompileAsC
+ /MP /SAFESEH:OFF %(AdditionalOptions)
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ OpenAL32.lib;%(AdditionalDependencies)
+ $(OutDir)s_openal.dll
+ true
+ true
+ $(OutDir)s_openal.pdb
+ false
+
+
+ $(IntDir)s_openal.lib
+ Windows
+
+
+ true
+ $(OutDir)s_openal.bsc
+
+
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
%(PreprocessorDefinitions)
+ %(PreprocessorDefinitions)
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 6e17b939d..87b56adb5 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -74,6 +74,7 @@ patch_t *ttlnum[20]; // act numbers (0-19)
static player_t *plr;
boolean chat_on; // entering a chat message?
static char w_chat[HU_MAXMSGLEN];
+static size_t c_input = 0; // let's try to make the chat input less shitty.
static boolean headsupactive = false;
boolean hu_showscores; // draw rankings
static char hu_tick;
@@ -327,6 +328,88 @@ void HU_Start(void)
//======================================================================
#ifndef NONET
+
+// EVERY CHANGE IN THIS SCRIPT IS LOL XD! BY VINCYTM
+
+static UINT32 chat_nummsg_log = 0;
+static UINT32 chat_nummsg_min = 0;
+static UINT32 chat_scroll = 0;
+static tic_t chat_scrolltime = 0;
+
+static UINT32 chat_maxscroll = 0; // how far can we scroll?
+
+//static chatmsg_t chat_mini[CHAT_BUFSIZE]; // Display the last few messages sent.
+//static chatmsg_t chat_log[CHAT_BUFSIZE]; // Keep every message sent to us in memory so we can scroll n shit, it's cool.
+
+static char chat_log[CHAT_BUFSIZE][255]; // hold the last 48 or so messages in that log.
+static char chat_mini[8][255]; // display up to 8 messages that will fade away / get overwritten
+static tic_t chat_timers[8];
+
+static boolean chat_scrollmedown = false; // force instant scroll down on the chat log. Happens when you open it / send a message.
+
+// remove text from minichat table
+
+static INT16 addy = 0; // use this to make the messages scroll smoothly when one fades away
+
+static void HU_removeChatText_Mini(void)
+{
+ // MPC: Don't create new arrays, just iterate through an existing one
+ size_t i;
+ for(i=0;i= CHAT_BUFSIZE) // too many messages!
+ HU_removeChatText_Log();
+
+ strcpy(chat_log[chat_nummsg_log], text);
+ chat_nummsg_log++;
+
+ if (chat_nummsg_min >= 8)
+ HU_removeChatText_Mini();
+
+ strcpy(chat_mini[chat_nummsg_min], text);
+ chat_timers[chat_nummsg_min] = TICRATE*cv_chattime.value;
+ chat_nummsg_min++;
+
+ if (OLDCHAT) // if we're using oldchat, print directly in console
+ CONS_Printf("%s\n", text);
+ else // if we aren't, still save the message to log.txt
+ CON_LogMessage(va("%s\n", text));
+#else
+ (void)playsound;
+ CONS_Printf("%s\n", text);
+#endif
+}
+
+#ifndef NONET
+
/** Runs a say command, sending an ::XD_SAY message.
* A say command consists of a signed 8-bit integer for the target, an
* unsigned 8-bit flag variable, and then the message itself.
@@ -345,6 +428,8 @@ void HU_Start(void)
* \sa Command_Say_f, Command_Sayteam_f, Command_Sayto_f, Got_Saycmd
* \author Graue
*/
+
+
static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
{
char buf[254];
@@ -355,14 +440,14 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
numwords = COM_Argc() - usedargs;
I_Assert(numwords > 0);
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
+ if (CHAT_MUTE) // TODO: Per Player mute.
{
- CONS_Alert(CONS_NOTICE, M_GetText("The chat is muted. You can't say anything at the moment.\n"));
+ HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"), false);
return;
}
// Only servers/admins can CSAY.
- if(!server && adminplayer != consoleplayer)
+ if(!server && !(IsPlayerAdmin(consoleplayer)))
flags &= ~HU_CSAY;
// We handle HU_SERVER_SAY, not the caller.
@@ -381,6 +466,57 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
strlcat(msg, COM_Argv(ix + usedargs), msgspace);
}
+ if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0) // used /pm
+ {
+ // what we're gonna do now is check if the node exists
+ // with that logic, characters 4 and 5 are our numbers:
+ const char *newmsg;
+ char *nodenum = (char*) malloc(3);
+ INT32 spc = 1; // used if nodenum[1] is a space.
+
+ strncpy(nodenum, msg+3, 3);
+ // check for undesirable characters in our "number"
+ if (((nodenum[0] < '0') || (nodenum[0] > '9')) || ((nodenum[1] < '0') || (nodenum[1] > '9')))
+ {
+ // check if nodenum[1] is a space
+ if (nodenum[1] == ' ')
+ spc = 0;
+ // let it slide
+ else
+ {
+ HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false);
+ free(nodenum);
+ return;
+ }
+ }
+ // I'm very bad at C, I swear I am, additional checks eww!
+ if (spc != 0)
+ {
+ if (msg[5] != ' ')
+ {
+ HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false);
+ free(nodenum);
+ return;
+ }
+ }
+
+ target = atoi((const char*) nodenum); // turn that into a number
+ free(nodenum);
+ //CONS_Printf("%d\n", target);
+
+ // check for target player, if it doesn't exist then we can't send the message!
+ if (target < MAXPLAYERS && playeringame[target]) // player exists
+ target++; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
+ else
+ {
+ HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target), false); // same
+ return;
+ }
+ buf[0] = target;
+ newmsg = msg+5+spc;
+ strlcpy(msg, newmsg, 252);
+ }
+
SendNetXCmd(XD_SAY, buf, strlen(msg) + 1 + msg-buf);
}
@@ -456,7 +592,7 @@ static void Command_CSay_f(void)
return;
}
- if(!server && adminplayer != consoleplayer)
+ if(!server && !IsPlayerAdmin(consoleplayer))
{
CONS_Alert(CONS_NOTICE, M_GetText("Only servers and admins can use csay.\n"));
return;
@@ -464,6 +600,7 @@ static void Command_CSay_f(void)
DoSayCommand(0, 1, HU_CSAY);
}
+static tic_t stop_spamming[MAXPLAYERS];
/** Receives a message, processing an ::XD_SAY command.
* \sa DoSayCommand
@@ -477,6 +614,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
char *msg;
boolean action = false;
char *ptr;
+ INT32 spam_eatmsg = 0;
CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]);
@@ -485,7 +623,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
msg = (char *)*p;
SKIPSTRING(*p);
- if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && playernum != adminplayer)
+ if ((cv_mute.value || flags & (HU_CSAY|HU_SERVER_SAY)) && playernum != serverplayer && !(IsPlayerAdmin(playernum)))
{
CONS_Alert(CONS_WARNING, cv_mute.value ?
M_GetText("Illegal say command received from %s while muted\n") : M_GetText("Illegal csay command received from non-admin %s\n"),
@@ -523,11 +661,28 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
}
}
+ // before we do anything, let's verify the guy isn't spamming, get this easier on us.
+
+ //if (stop_spamming[playernum] != 0 && cv_chatspamprotection.value && !(flags & HU_CSAY))
+ if (stop_spamming[playernum] != 0 && consoleplayer != playernum && cv_chatspamprotection.value && !(flags & HU_CSAY))
+ {
+ CONS_Debug(DBG_NETPLAY,"Received SAY cmd too quickly from Player %d (%s), assuming as spam and blocking message.\n", playernum+1, player_names[playernum]);
+ stop_spamming[playernum] = 4;
+ spam_eatmsg = 1;
+ }
+ else
+ stop_spamming[playernum] = 4; // you can hold off for 4 tics, can you?
+
+ // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first.
+
#ifdef HAVE_BLUA
if (LUAh_PlayerMsg(playernum, target, flags, msg))
return;
#endif
+ if (spam_eatmsg)
+ return; // don't proceed if we were supposed to eat the message.
+
// If it's a CSAY, just CECHO and be done with it.
if (flags & HU_CSAY)
{
@@ -567,31 +722,84 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
|| target == 0 // To everyone
|| consoleplayer == target-1) // To you
{
- const char *cstart = "", *cend = "", *adminchar = "~", *remotechar = "@", *fmt;
+ const char *prefix = "", *cstart = "", *cend = "", *adminchar = "\x82~\x83", *remotechar = "\x82@\x83", *fmt2, *textcolor = "\x80";
char *tempchar = NULL;
- // In CTF and team match, color the player's name.
- if (G_GametypeHasTeams())
+ // player is a spectator?
+ if (players[playernum].spectator)
{
- cend = "\x80";
- if (players[playernum].ctfteam == 1) // red
- cstart = "\x85";
- else if (players[playernum].ctfteam == 2) // blue
- cstart = "\x84";
+ cstart = "\x86"; // grey name
+ textcolor = "\x86";
}
+ else if (target == -1) // say team
+ {
+ if (players[playernum].ctfteam == 1) // red
+ {
+ cstart = "\x85";
+ textcolor = "\x85";
+ }
+ else // blue
+ {
+ cstart = "\x84";
+ textcolor = "\x84";
+ }
+ }
+ else
+ {
+ const UINT8 color = players[playernum].skincolor;
+
+ cstart = "\x83";
+
+ // Follow palette order at r_draw.c Color_Names
+ if (color <= SKINCOLOR_SILVER
+ || color == SKINCOLOR_AETHER)
+ cstart = "\x80"; // White
+ else if (color <= SKINCOLOR_BLACK
+ || color == SKINCOLOR_SLATE)
+ cstart = "\x86"; // Grey
+ else if (color <= SKINCOLOR_YOGURT)
+ cstart = "\x85"; // Red
+ else if (color <= SKINCOLOR_BEIGE)
+ cstart = "\x86"; // Grey
+ else if (color <= SKINCOLOR_LAVENDER)
+ cstart = "\x81"; // Purple
+ else if (color <= SKINCOLOR_PEACHY)
+ cstart = "\x85"; // Red
+ else if (color <= SKINCOLOR_RUST)
+ cstart = "\x87"; // Orange
+ else if (color == SKINCOLOR_GOLD
+ || color == SKINCOLOR_YELLOW)
+ cstart = "\x82"; // Yellow
+ else if (color == SKINCOLOR_SANDY
+ || color == SKINCOLOR_OLIVE)
+ cstart = "\x81"; // Purple
+ else if (color <= SKINCOLOR_MINT)
+ cstart = "\x83"; // Green
+ else if (color <= SKINCOLOR_DUSK)
+ cstart = "\x84"; // Blue
+ else if (color == SKINCOLOR_PINK
+ || color == SKINCOLOR_PASTEL
+ || color == SKINCOLOR_BUBBLEGUM
+ || color == SKINCOLOR_MAGENTA
+ || color == SKINCOLOR_ROSY)
+ cstart = "\x85"; // Red
+ else if (color <= SKINCOLOR_PLUM)
+ cstart = "\x81"; // Purple
+ }
+ prefix = cstart;
// Give admins and remote admins their symbols.
if (playernum == serverplayer)
tempchar = (char *)Z_Calloc(strlen(cstart) + strlen(adminchar) + 1, PU_STATIC, NULL);
- else if (playernum == adminplayer)
+ else if (IsPlayerAdmin(playernum))
tempchar = (char *)Z_Calloc(strlen(cstart) + strlen(remotechar) + 1, PU_STATIC, NULL);
if (tempchar)
{
- strcat(tempchar, cstart);
if (playernum == serverplayer)
strcat(tempchar, adminchar);
else
strcat(tempchar, remotechar);
+ strcat(tempchar, cstart);
cstart = tempchar;
}
@@ -600,21 +808,39 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
// name, color end, and the message itself.
// '\4' makes the message yellow and beeps; '\3' just beeps.
if (action)
- fmt = "\4* %s%s%s \x82%s\n";
- else if (target == 0) // To everyone
- fmt = "\3<%s%s%s> %s\n";
+ fmt2 = "* %s%s%s%s \x82%s%s";
else if (target-1 == consoleplayer) // To you
- fmt = "\3*%s%s%s* %s\n";
+ {
+ prefix = "\x82[PM]";
+ cstart = "\x82";
+ textcolor = "\x82";
+ fmt2 = "%s<%s%s>%s\x80 %s%s";
+ }
else if (target > 0) // By you, to another player
{
// Use target's name.
dispname = player_names[target-1];
- fmt = "\3->*%s%s%s* %s\n";
- }
- else // To your team
- fmt = "\3>>%s%s%s<< (team) %s\n";
+ prefix = "\x82[TO]";
+ cstart = "\x82";
+ fmt2 = "%s<%s%s>%s\x80 %s%s";
+
+ }
+ else if (target == 0) // To everyone
+ fmt2 = "%s<%s%s%s>\x80 %s%s";
+ else // To your team
+ {
+ if (players[playernum].ctfteam == 1) // red
+ prefix = "\x85[TEAM]";
+ else if (players[playernum].ctfteam == 2) // blue
+ prefix = "\x84[TEAM]";
+ else
+ prefix = "\x83"; // makes sure this doesn't implode if you sayteam on non-team gamemodes
+
+ fmt2 = "%s<%s%s>\x80%s %s%s";
+ }
+
+ HU_AddChatText(va(fmt2, prefix, cstart, dispname, cend, textcolor, msg), cv_chatnotifications.value); // add to chat
- CONS_Printf(fmt, cstart, dispname, cend, msg);
if (tempchar)
Z_Free(tempchar);
}
@@ -627,7 +853,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
CONS_Printf("Dropped chat: %d %d %s\n", playernum, target, msg);
#endif
}
-#endif
// Handles key input and string input
//
@@ -641,19 +866,53 @@ static inline boolean HU_keyInChatString(char *s, char ch)
l = strlen(s);
if (l < HU_MAXMSGLEN - 1)
{
- s[l++] = ch;
- s[l]=0;
+ if (c_input >= strlen(s)) // don't do anything complicated
+ {
+ s[l++] = ch;
+ s[l]=0;
+ }
+ else
+ {
+
+ // move everything past c_input for new characters:
+ size_t m = HU_MAXMSGLEN-1;
+ while (m>=c_input)
+ {
+ if (s[m])
+ s[m+1] = (s[m]);
+ if (m == 0) // prevent overflow
+ break;
+ m--;
+ }
+ s[c_input] = ch; // and replace this.
+ }
+ c_input++;
return true;
}
return false;
}
else if (ch == KEY_BACKSPACE)
{
- l = strlen(s);
- if (l)
- s[--l] = 0;
- else
+ size_t i = c_input;
+
+ if (c_input <= 0)
return false;
+
+ if (!s[i-1])
+ return false;
+
+ if (i >= strlen(s)-1)
+ {
+ s[strlen(s)-1] = 0;
+ c_input--;
+ return false;
+ }
+
+ for (; (i < HU_MAXMSGLEN); i++)
+ {
+ s[i-1] = s[i];
+ }
+ c_input--;
}
else if (ch != KEY_ENTER)
return false; // did not eat key
@@ -661,6 +920,8 @@ static inline boolean HU_keyInChatString(char *s, char ch)
return true; // ate the key
}
+#endif
+
//
//
void HU_Ticker(void)
@@ -677,28 +938,32 @@ void HU_Ticker(void)
hu_showscores = false;
}
-#define QUEUESIZE 256
+#ifndef NONET
static boolean teamtalk = false;
-static char chatchars[QUEUESIZE];
-static INT32 head = 0, tail = 0;
+/*static char chatchars[QUEUESIZE];
+static INT32 head = 0, tail = 0;*/
+// WHY DO YOU OVERCOMPLICATE EVERYTHING?????????
-//
-// HU_dequeueChatChar
-//
-char HU_dequeueChatChar(void)
+// Clear spaces so we don't end up with messages only made out of emptiness
+static boolean HU_clearChatSpaces(void)
{
- char c;
+ size_t i = 0; // Used to just check our message
+ char c; // current character we're iterating.
+ boolean nothingbutspaces = true;
- if (head != tail)
+ for (; i < strlen(w_chat); i++) // iterate through message and eradicate all spaces that don't belong.
{
- c = chatchars[tail];
- tail = (tail + 1) & (QUEUESIZE-1);
- }
- else
- c = 0;
+ c = w_chat[i];
+ if (!c)
+ break; // if there's nothing, it's safe to assume our message has ended, so let's not waste any more time here.
- return c;
+ if (c != ' ') // Isn't a space
+ {
+ nothingbutspaces = false;
+ }
+ }
+ return nothingbutspaces;
}
//
@@ -709,95 +974,194 @@ static void HU_queueChatChar(char c)
if (c == KEY_ENTER)
{
char buf[2+256];
+ char *msg = &buf[2];
+ size_t i = 0;
size_t ci = 2;
+ INT32 target = 0;
+
+ if (HU_clearChatSpaces()) // Avoids being able to send empty messages, or something.
+ return; // If this returns true, that means our message was NOTHING but spaces, so don't send it period.
do {
- c = HU_dequeueChatChar();
+ c = w_chat[-2+ci++];
if (!c || (c >= ' ' && !(c & 0x80))) // copy printable characters and terminating '\0' only.
- buf[ci++]=c;
+ buf[ci-1]=c;
} while (c);
+ for (;(iERROR: The chat is muted. You can't say anything.", "\x85"), false);
return;
}
+ if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0) // used /pm
+ {
+ INT32 spc = 1; // used if nodenum[1] is a space.
+ char *nodenum = (char*) malloc(3);
+ const char *newmsg;
+
+ // what we're gonna do now is check if the node exists
+ // with that logic, characters 4 and 5 are our numbers:
+
+ // teamtalk can't send PMs, just don't send it, else everyone would be able to see it, and no one wants to see your sex RP sicko.
+ if (teamtalk)
+ {
+ HU_AddChatText(va("%sCannot send sayto in Say-Team.", "\x85"), false);
+ return;
+ }
+
+ strncpy(nodenum, msg+3, 3);
+ // check for undesirable characters in our "number"
+ if (((nodenum[0] < '0') || (nodenum[0] > '9')) || ((nodenum[1] < '0') || (nodenum[1] > '9')))
+ {
+ // check if nodenum[1] is a space
+ if (nodenum[1] == ' ')
+ spc = 0;
+ // let it slide
+ else
+ {
+ HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false);
+ free(nodenum);
+ return;
+ }
+ }
+ // I'm very bad at C, I swear I am, additional checks eww!
+ if (spc != 0)
+ {
+ if (msg[5] != ' ')
+ {
+ HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm \'.", false);
+ free(nodenum);
+ return;
+ }
+ }
+
+ target = atoi((const char*) nodenum); // turn that into a number
+ free(nodenum);
+ //CONS_Printf("%d\n", target);
+
+ // check for target player, if it doesn't exist then we can't send the message!
+ if (target < MAXPLAYERS && playeringame[target]) // player exists
+ target++; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
+ else
+ {
+ HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target), false); // same
+ return;
+ }
+
+ // we need to get rid of the /pm
+ newmsg = msg+5+spc;
+ strlcpy(msg, newmsg, 255);
+ }
if (ci > 3) // don't send target+flags+empty message.
{
if (teamtalk)
buf[0] = -1; // target
else
- buf[0] = 0; // target
+ buf[0] = target;
+
buf[1] = 0; // flags
SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1);
}
return;
}
-
- if (((head + 1) & (QUEUESIZE-1)) == tail)
- CONS_Printf(M_GetText("[Message unsent]\n")); // message not sent
- else
- {
- if (c == KEY_BACKSPACE)
- {
- if (tail != head)
- head = (head - 1) & (QUEUESIZE-1);
- }
- else
- {
- chatchars[head] = c;
- head = (head + 1) & (QUEUESIZE-1);
- }
- }
}
+#endif
void HU_clearChatChars(void)
{
- while (tail != head)
- HU_queueChatChar(KEY_BACKSPACE);
+ size_t i = 0;
+ for (;itype != ev_keydown)
return false;
// only KeyDown events now...
+ /*// Shoot, to prevent P1 chatting from ruining the game for everyone else, it's either:
+ // A. completely disallow opening chat entirely in online splitscreen
+ // or B. iterate through all controls to make sure it's bound to player 1 before eating
+ // You can see which one I chose.
+ // (Unless if you're sharing a keyboard, since you probably establish when you start chatting that you have dibs on it...)
+ // (Ahhh, the good ol days when I was a kid who couldn't afford an extra USB controller...)
+
+ if (ev->data1 >= KEY_MOUSE1)
+ {
+ INT32 i;
+ for (i = 0; i < num_gamecontrols; i++)
+ {
+ if (gamecontrol[i][0] == ev->data1 || gamecontrol[i][1] == ev->data1)
+ break;
+ }
+
+ if (i == num_gamecontrols)
+ return false;
+ }*/ //We don't actually care about that unless we get splitscreen netgames. :V
+
+ c = (INT32)ev->data1;
+
+ // capslock (now handled outside of chat on so that it works everytime......)
+ if (c && c == KEY_CAPSLOCK) // it's a toggle.
+ {
+ if (capslock)
+ capslock = false;
+ else
+ capslock = true;
+ return true;
+ }
+
+#ifndef NONET
if (!chat_on)
{
// enter chat mode
if ((ev->data1 == gamecontrol[gc_talkkey][0] || ev->data1 == gamecontrol[gc_talkkey][1])
- && netgame && (!cv_mute.value || server || (adminplayer == consoleplayer)))
+ && netgame && !OLD_MUTE) // check for old chat mute, still let the players open the chat incase they want to scroll otherwise.
{
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
- return false;
chat_on = true;
w_chat[0] = 0;
teamtalk = false;
+ chat_scrollmedown = true;
+ typelines = 1;
return true;
}
if ((ev->data1 == gamecontrol[gc_teamkey][0] || ev->data1 == gamecontrol[gc_teamkey][1])
- && netgame && (!cv_mute.value || server || (adminplayer == consoleplayer)))
+ && netgame && !OLD_MUTE)
{
- if (cv_mute.value && !(server || adminplayer == consoleplayer))
- return false;
chat_on = true;
w_chat[0] = 0;
- teamtalk = true;
+ teamtalk = G_GametypeHasTeams(); // Don't teamtalk if we don't have teams.
+ chat_scrollmedown = true;
+ typelines = 1;
return true;
}
}
else // if chat_on
{
+
// Ignore modifier keys
// Note that we do this here so users can still set
// their chat keys to one of these, if they so desire.
@@ -806,34 +1170,626 @@ boolean HU_Responder(event_t *ev)
|| ev->data1 == KEY_LALT || ev->data1 == KEY_RALT)
return true;
- c = (UINT8)ev->data1;
+ c = (INT32)ev->data1;
- // use console translations
- if (shiftdown)
- c = shiftxform[c];
+ // I know this looks very messy but this works. If it ain't broke, don't fix it!
+ // shift LETTERS to uppercase if we have capslock or are holding shift
+ if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
+ {
+ if (shiftdown ^ capslock)
+ c = shiftxform[c];
+ }
+ else // if we're holding shift we should still shift non letter symbols
+ {
+ if (shiftdown)
+ c = shiftxform[c];
+ }
- if (HU_keyInChatString(w_chat,c))
+ // pasting. pasting is cool. chat is a bit limited, though :(
+ if (((c == 'v' || c == 'V') && ctrldown) && !CHAT_MUTE)
+ {
+ const char *paste = I_ClipboardPaste();
+ size_t chatlen;
+ size_t pastelen;
+
+ // create a dummy string real quickly
+
+ if (paste == NULL)
+ return true;
+
+ chatlen = strlen(w_chat);
+ pastelen = strlen(paste);
+ if (chatlen+pastelen > HU_MAXMSGLEN)
+ return true; // we can't paste this!!
+
+ if (c_input >= strlen(w_chat)) // add it at the end of the string.
+ {
+ memcpy(&w_chat[chatlen], paste, pastelen); // copy all of that.
+ c_input += pastelen;
+ /*size_t i = 0;
+ for (;i= c_input)
+ {
+ if (w_chat[i])
+ w_chat[i+pastelen] = w_chat[i];
+ if (i == 0) // prevent overflow
+ break;
+ i--;
+ }
+ memcpy(&w_chat[c_input], paste, pastelen); // copy all of that.
+ c_input += pastelen;
+ return true;
+ }
+ }
+
+ if (!CHAT_MUTE && HU_keyInChatString(w_chat,c))
+ {
HU_queueChatChar(c);
+ }
if (c == KEY_ENTER)
+ {
chat_on = false;
- else if (c == KEY_ESCAPE)
+ c_input = 0; // reset input cursor
+ chat_scrollmedown = true; // you hit enter, so you might wanna autoscroll to see what you just sent. :)
+ }
+ else if (c == KEY_ESCAPE
+ || ((c == gamecontrol[gc_talkkey][0] || c == gamecontrol[gc_talkkey][1]
+ || c == gamecontrol[gc_teamkey][0] || c == gamecontrol[gc_teamkey][1])
+ && c >= KEY_MOUSE1)) // If it's not a keyboard key, then the chat button is used as a toggle.
+ {
chat_on = false;
-
+ c_input = 0; // reset input cursor
+ }
+ else if ((c == KEY_UPARROW || c == KEY_MOUSEWHEELUP) && chat_scroll > 0 && !OLDCHAT) // CHAT SCROLLING YAYS!
+ {
+ chat_scroll--;
+ justscrolledup = true;
+ chat_scrolltime = 4;
+ }
+ else if ((c == KEY_DOWNARROW || c == KEY_MOUSEWHEELDOWN) && chat_scroll < chat_maxscroll && chat_maxscroll > 0 && !OLDCHAT)
+ {
+ chat_scroll++;
+ justscrolleddown = true;
+ chat_scrolltime = 4;
+ }
+ else if (c == KEY_LEFTARROW && c_input != 0 && !OLDCHAT) // i said go back
+ c_input--;
+ else if (c == KEY_RIGHTARROW && c_input < strlen(w_chat) && !OLDCHAT) // don't need to check for admin or w/e here since the chat won't ever contain anything if it's muted.
+ c_input++;
return true;
}
+#endif
+
return false;
}
+
//======================================================================
// HEADS UP DRAWING
//======================================================================
+#ifndef NONET
+
+// Precompile a wordwrapped string to any given width.
+// This is a muuuch better method than V_WORDWRAP.
+// again stolen and modified a bit from video.c, don't mind me, will need to rearrange this one day.
+// this one is simplified for the chat drawer.
+static char *CHAT_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
+{
+ INT32 c;
+ size_t chw, i, lastusablespace = 0;
+ size_t slen;
+ char *newstring = Z_StrDup(string);
+ INT32 spacewidth = (vid.width < 640) ? 8 : 4, charwidth = (vid.width < 640) ? 8 : 4;
+
+ slen = strlen(string);
+ x = 0;
+
+ for (i = 0; i < slen; ++i)
+ {
+ c = newstring[i];
+ if ((UINT8)c >= 0x80 && (UINT8)c <= 0x89) //color parsing! -Inuyasha 2.16.09
+ continue;
+
+ if (c == '\n')
+ {
+ x = 0;
+ lastusablespace = 0;
+ continue;
+ }
+
+ if (!(option & V_ALLOWLOWERCASE))
+ c = toupper(c);
+ c -= HU_FONTSTART;
+
+ if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
+ {
+ chw = spacewidth;
+ lastusablespace = i;
+ }
+ else
+ chw = charwidth;
+
+ x += chw;
+
+ if (lastusablespace != 0 && x > w)
+ {
+ //CONS_Printf("Wrap at index %d\n", i);
+ newstring[lastusablespace] = '\n';
+ i = lastusablespace+1;
+ lastusablespace = 0;
+ x = 0;
+ }
+ }
+ return newstring;
+}
+
+
+// 30/7/18: chaty is now the distance at which the lowest point of the chat will be drawn if that makes any sense.
+
+INT16 chatx = 13, chaty = 169; // let's use this as our coordinates, shh
+
+// chat stuff by VincyTM LOL XD!
+
+// HU_DrawMiniChat
+
+static void HU_drawMiniChat(void)
+{
+ INT32 x = chatx+2;
+ INT32 charwidth = 4, charheight = 6;
+ INT32 boxw = cv_chatwidth.value;
+ INT32 dx = 0, dy = 0;
+ size_t i = chat_nummsg_min;
+ boolean prev_linereturn = false; // a hack to prevent double \n while I have no idea why they happen in the first place.
+
+ INT32 msglines = 0;
+ // process all messages once without rendering anything or doing anything fancy so that we know how many lines each message has...
+ INT32 y;
+
+ if (!chat_nummsg_min)
+ return; // needless to say it's useless to do anything if we don't have anything to draw.
+
+ /*if (splitscreen > 1)
+ boxw = max(64, boxw/2);*/
+
+ for (; i>0; i--)
+ {
+ const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i-1]);
+ size_t j = 0;
+ INT32 linescount = 0;
+
+ while(msg[j]) // iterate through msg
+ {
+ if (msg[j] < HU_FONTSTART) // don't draw
+ {
+ if (msg[j] == '\n') // get back down.
+ {
+ ++j;
+ if (!prev_linereturn)
+ {
+ linescount += 1;
+ dx = 0;
+ }
+ prev_linereturn = true;
+ continue;
+ }
+ else if (msg[j] & 0x80) // stolen from video.c, nice.
+ {
+ ++j;
+ continue;
+ }
+
+ ++j;
+ }
+ else
+ {
+ j++;
+ }
+ prev_linereturn = false;
+ dx += charwidth;
+ if (dx >= boxw)
+ {
+ dx = 0;
+ linescount += 1;
+ }
+ }
+ dy = 0;
+ dx = 0;
+ msglines += linescount+1;
+ }
+
+ y = chaty - charheight*(msglines+1);
+
+ /*if (splitscreen)
+ {
+ y -= BASEVIDHEIGHT/2;
+ if (splitscreen > 1)
+ y += 16;
+ }*/
+ y -= (G_RingSlingerGametype() ? 16 : 0);
+
+ dx = 0;
+ dy = 0;
+ i = 0;
+ prev_linereturn = false;
+
+ for (; i<=(chat_nummsg_min-1); i++) // iterate through our hot messages
+ {
+ INT32 clrflag = 0;
+ INT32 timer = ((cv_chattime.value*TICRATE)-chat_timers[i]) - cv_chattime.value*TICRATE+9; // see below...
+ INT32 transflag = (timer >= 0 && timer <= 9) ? (timer*V_10TRANS) : 0; // you can make bad jokes out of this one.
+ size_t j = 0;
+ const char *msg = CHAT_WordWrap(x+2, boxw-(charwidth*2), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, chat_mini[i]); // get the current message, and word wrap it.
+ UINT8 *colormap = NULL;
+
+ while(msg[j]) // iterate through msg
+ {
+ if (msg[j] < HU_FONTSTART) // don't draw
+ {
+ if (msg[j] == '\n') // get back down.
+ {
+ ++j;
+ if (!prev_linereturn)
+ {
+ dy += charheight;
+ dx = 0;
+ }
+ prev_linereturn = true;
+ continue;
+ }
+ else if (msg[j] & 0x80) // stolen from video.c, nice.
+ {
+ clrflag = ((msg[j] & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK;
+ colormap = V_GetStringColormap(clrflag);
+ ++j;
+ continue;
+ }
+
+ ++j;
+ }
+ else
+ {
+ if (cv_chatbacktint.value) // on request of wolfy
+ V_DrawFillConsoleMap(x + dx + 2, y+dy, charwidth, charheight, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT);
+
+ V_DrawChatCharacter(x + dx + 2, y+dy, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|transflag, !cv_allcaps.value, colormap);
+ }
+
+ dx += charwidth;
+ prev_linereturn = false;
+ if (dx >= boxw)
+ {
+ dx = 0;
+ dy += charheight;
+ }
+ }
+ dy += charheight;
+ dx = 0;
+ }
+
+ // decrement addy and make that shit smooth:
+ addy /= 2;
+
+}
+
+// HU_DrawChatLog
+
+static void HU_drawChatLog(INT32 offset)
+{
+ INT32 charwidth = 4, charheight = 6;
+ INT32 boxw = cv_chatwidth.value, boxh = cv_chatheight.value;
+ INT32 x = chatx+2, y, dx = 0, dy = 0;
+ UINT32 i = 0;
+ INT32 chat_topy, chat_bottomy;
+ boolean atbottom = false;
+
+ // make sure that our scroll position isn't "illegal";
+ if (chat_scroll > chat_maxscroll)
+ chat_scroll = chat_maxscroll;
+
+#ifdef NETSPLITSCREEN
+ if (splitscreen)
+ {
+ boxh = max(6, boxh/2);
+ if (splitscreen > 1)
+ boxw = max(64, boxw/2);
+ }
+#endif
+
+ y = chaty - offset*charheight - (chat_scroll*charheight) - boxh*charheight - 12;
+
+#ifdef NETSPLITSCREEN
+ if (splitscreen)
+ {
+ y -= BASEVIDHEIGHT/2;
+ if (splitscreen > 1)
+ y += 16;
+ }
+#endif
+ y -= (G_RingSlingerGametype() ? 16 : 0);
+
+ chat_topy = y + chat_scroll*charheight;
+ chat_bottomy = chat_topy + boxh*charheight;
+
+ V_DrawFillConsoleMap(chatx, chat_topy, boxw, boxh*charheight +2, 239|V_SNAPTOBOTTOM|V_SNAPTOLEFT); // log box
+
+ for (i=0; i= chat_topy) && (y+dy < (chat_bottomy)))
+ V_DrawChatCharacter(x + dx + 2, y+dy+2, msg[j++] |V_SNAPTOBOTTOM|V_SNAPTOLEFT, !cv_allcaps.value, colormap);
+ else
+ j++; // don't forget to increment this or we'll get stuck in the limbo.
+ }
+
+ dx += charwidth;
+ if (dx >= boxw-charwidth-2 && i= HU_FONTSTART) // end of message shouldn't count, nor should invisible characters!!!!
+ {
+ dx = 0;
+ dy += charheight;
+ }
+ }
+ dy += charheight;
+ dx = 0;
+ }
+
+
+ if (((chat_scroll >= chat_maxscroll) || (chat_scrollmedown)) && !(justscrolleddown || justscrolledup || chat_scrolltime)) // was already at the bottom of the page before new maxscroll calculation and was NOT scrolling.
+ {
+ atbottom = true; // we should scroll
+ }
+ chat_scrollmedown = false;
+
+ // getmaxscroll through a lazy hack. We do all these loops, so let's not do more loops that are gonna lag the game more. :P
+ chat_maxscroll = (dy/charheight); // welcome to C, we don't know what min() and max() are.
+ if (chat_maxscroll <= (UINT32)cv_chatheight.value)
+ chat_maxscroll = 0;
+ else
+ chat_maxscroll -= cv_chatheight.value;
+
+ // if we're not bound by the time, autoscroll for next frame:
+ if (atbottom)
+ chat_scroll = chat_maxscroll;
+
+ // draw arrows to indicate that we can (or not) scroll.
+ // account for Y = -1 offset in tinyfont
+ if (chat_scroll > 0)
+ V_DrawThinString(chatx-8, ((justscrolledup) ? (chat_topy-1) : (chat_topy)) - 1, V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_YELLOWMAP, "\x1A"); // up arrow
+ if (chat_scroll < chat_maxscroll)
+ V_DrawThinString(chatx-8, chat_bottomy-((justscrolleddown) ? 5 : 6) - 1, V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_YELLOWMAP, "\x1B"); // down arrow
+
+ justscrolleddown = false;
+ justscrolledup = false;
+}
+
//
// HU_DrawChat
//
// Draw chat input
//
+
static void HU_DrawChat(void)
+{
+ INT32 charwidth = 4, charheight = 6;
+ INT32 boxw = cv_chatwidth.value;
+ INT32 t = 0, c = 0, y = chaty - (typelines*charheight);
+ UINT32 i = 0, saylen = strlen(w_chat); // You learn new things everyday!
+ INT32 cflag = 0;
+ const char *ntalk = "Say: ", *ttalk = "Team: ";
+ const char *talk = ntalk;
+ const char *mute = "Chat has been muted.";
+
+#ifdef NETSPLITSCREEN
+ if (splitscreen)
+ {
+ y -= BASEVIDHEIGHT/2;
+ if (splitscreen > 1)
+ {
+ y += 16;
+ boxw = max(64, boxw/2);
+ }
+ }
+#endif
+ y -= (G_RingSlingerGametype() ? 16 : 0);
+
+ if (teamtalk)
+ {
+ talk = ttalk;
+#if 0
+ if (players[consoleplayer].ctfteam == 1)
+ t = 0x500; // Red
+ else if (players[consoleplayer].ctfteam == 2)
+ t = 0x400; // Blue
+#endif
+ }
+
+ if (CHAT_MUTE)
+ {
+ talk = mute;
+ typelines = 1;
+ cflag = V_GRAYMAP; // set text in gray if chat is muted.
+ }
+
+ V_DrawFillConsoleMap(chatx, y-1, boxw, (typelines*charheight), 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT);
+
+ while (talk[i])
+ {
+ if (talk[i] < HU_FONTSTART)
+ ++i;
+ else
+ {
+ V_DrawChatCharacter(chatx + c + 2, y, talk[i] |V_SNAPTOBOTTOM|V_SNAPTOLEFT|cflag, !cv_allcaps.value, V_GetStringColormap(talk[i]|cflag));
+ i++;
+ }
+
+ c += charwidth;
+ }
+
+ // if chat is muted, just draw the log and get it over with, no need to draw anything else.
+ if (CHAT_MUTE)
+ {
+ HU_drawChatLog(0);
+ return;
+ }
+
+ i = 0;
+ typelines = 1;
+
+ if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4)
+ V_DrawChatCharacter(chatx + 2 + c, y+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
+
+ while (w_chat[i])
+ {
+ boolean skippedline = false;
+ if (c_input == (i+1))
+ {
+ INT32 cursorx = (c+charwidth < boxw-charwidth) ? (chatx + 2 + c+charwidth) : (chatx+1); // we may have to go down.
+ INT32 cursory = (cursorx != chatx+1) ? (y) : (y+charheight);
+ if (hu_tick < 4)
+ V_DrawChatCharacter(cursorx, cursory+1, '_' |V_SNAPTOBOTTOM|V_SNAPTOLEFT|t, !cv_allcaps.value, NULL);
+
+ if (cursorx == chatx+1 && saylen == i) // a weirdo hack
+ {
+ typelines += 1;
+ skippedline = true;
+ }
+ }
+
+ //Hurdler: isn't it better like that?
+ if (w_chat[i] < HU_FONTSTART)
+ ++i;
+ else
+ V_DrawChatCharacter(chatx + c + 2, y, w_chat[i++] | V_SNAPTOBOTTOM|V_SNAPTOLEFT | t, !cv_allcaps.value, NULL);
+
+ c += charwidth;
+ if (c > boxw-(charwidth*2) && !skippedline)
+ {
+ c = 0;
+ y += charheight;
+ typelines += 1;
+ }
+ }
+
+ // handle /pm list. It's messy, horrible and I don't care.
+ if (strnicmp(w_chat, "/pm", 3) == 0 && vid.width >= 400 && !teamtalk) // 320x200 unsupported kthxbai
+ {
+ INT32 count = 0;
+ INT32 p_dispy = chaty - charheight -1;
+#ifdef NETSPLITSCREEN
+ if (splitscreen)
+ {
+ p_dispy -= BASEVIDHEIGHT/2;
+ if (splitscreen > 1)
+ p_dispy += 16;
+ }
+#endif
+ p_dispy -= (G_RingSlingerGametype() ? 16 : 0);
+
+ i = 0;
+ for(i=0; (i '9'))) || ((w_chat[4] != 0) && (((w_chat[4] < '0') || (w_chat[4] > '9'))))) && (w_chat[4] != ' '))
+ break;
+
+
+ nodenum = (char*) malloc(3);
+ strncpy(nodenum, w_chat+3, 3);
+ n = atoi((const char*) nodenum); // turn that into a number
+ free(nodenum);
+ // special cases:
+
+ if ((n == 0) && !(w_chat[4] == '0'))
+ {
+ if (!(i<10))
+ continue;
+ }
+ else if ((n == 1) && !(w_chat[3] == '0'))
+ {
+ if (!((i == 1) || ((i >= 10) && (i <= 19))))
+ continue;
+ }
+ else if ((n == 2) && !(w_chat[3] == '0'))
+ {
+ if (!((i == 2) || ((i >= 20) && (i <= 29))))
+ continue;
+ }
+ else if ((n == 3) && !(w_chat[3] == '0'))
+ {
+ if (!((i == 3) || ((i >= 30) && (i <= 31))))
+ continue;
+ }
+ else // general case.
+ {
+ if (i != n)
+ continue;
+ }
+ }
+
+ if (playeringame[i])
+ {
+ char name[MAXPLAYERNAME+1];
+ strlcpy(name, player_names[i], 7); // shorten name to 7 characters.
+ V_DrawFillConsoleMap(chatx+ boxw + 2, p_dispy- (6*count), 48, 6, 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); // fill it like the chat so the text doesn't become hard to read because of the hud.
+ V_DrawSmallString(chatx+ boxw + 4, p_dispy- (6*count), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, va("\x82%d\x80 - %s", i, name));
+ count++;
+ }
+ }
+ if (count == 0) // no results.
+ {
+ V_DrawFillConsoleMap(chatx+boxw+2, p_dispy- (6*count), 48, 6, 239 | V_SNAPTOBOTTOM | V_SNAPTOLEFT); // fill it like the chat so the text doesn't become hard to read because of the hud.
+ V_DrawSmallString(chatx+boxw+4, p_dispy- (6*count), V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_ALLOWLOWERCASE, "NO RESULT.");
+ }
+ }
+
+ HU_drawChatLog(typelines-1); // typelines is the # of lines we're typing. If there's more than 1 then the log should scroll up to give us more space.
+
+}
+
+
+// For anyone who, for some godforsaken reason, likes oldchat.
+
+static void HU_DrawChat_Old(void)
{
INT32 t = 0, c = 0, y = HU_INPUTY;
size_t i = 0;
@@ -841,7 +1797,6 @@ static void HU_DrawChat(void)
const char *talk = ntalk;
INT32 charwidth = 8 * con_scalefactor; //SHORT(hu_font['A'-HU_FONTSTART]->width) * con_scalefactor;
INT32 charheight = 8 * con_scalefactor; //SHORT(hu_font['A'-HU_FONTSTART]->height) * con_scalefactor;
-
if (teamtalk)
{
talk = ttalk;
@@ -868,9 +1823,20 @@ static void HU_DrawChat(void)
c += charwidth;
}
+ if ((strlen(w_chat) == 0 || c_input == 0) && hu_tick < 4)
+ V_DrawCharacter(HU_INPUTX+c, y+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value);
+
i = 0;
while (w_chat[i])
{
+
+ if (c_input == (i+1) && hu_tick < 4)
+ {
+ INT32 cursorx = (HU_INPUTX+c+charwidth < vid.width) ? (HU_INPUTX + c + charwidth) : (HU_INPUTX); // we may have to go down.
+ INT32 cursory = (cursorx != HU_INPUTX) ? (y) : (y+charheight);
+ V_DrawCharacter(cursorx, cursory+2*con_scalefactor, '_' |cv_constextsize.value | V_NOSCALESTART|t, !cv_allcaps.value);
+ }
+
//Hurdler: isn't it better like that?
if (w_chat[i] < HU_FONTSTART)
{
@@ -894,7 +1860,7 @@ static void HU_DrawChat(void)
if (hu_tick < 4)
V_DrawCharacter(HU_INPUTX + c, y, '_' | cv_constextsize.value |V_NOSCALESTART|t, true);
}
-
+#endif
// draw the Crosshair, at the exact center of the view.
//
@@ -1071,9 +2037,47 @@ static void HU_DrawDemoInfo(void)
//
void HU_Drawer(void)
{
+#ifndef NONET
// draw chat string plus cursor
if (chat_on)
- HU_DrawChat();
+ {
+ // count down the scroll timer.
+ if (chat_scrolltime > 0)
+ chat_scrolltime--;
+ if (!OLDCHAT)
+ HU_DrawChat();
+ else
+ HU_DrawChat_Old();
+ }
+ else
+ {
+ typelines = 1;
+ chat_scrolltime = 0;
+ if (!OLDCHAT && cv_consolechat.value < 2 && netgame) // Don't display minimized chat if you set the mode to Window (Hidden)
+ HU_drawMiniChat(); // draw messages in a cool fashion.
+ }
+
+ if (netgame) // would handle that in hu_drawminichat, but it's actually kinda awkward when you're typing a lot of messages. (only handle that in netgames duh)
+ {
+ size_t i = 0;
+
+ // handle spam while we're at it:
+ for(; (i 0)
+ stop_spamming[i]--;
+ }
+
+ // handle chat timers
+ for (i=0; (i 0)
+ chat_timers[i]--;
+ else
+ HU_removeChatText_Mini();
+ }
+ }
+#endif
if (cechotimer)
HU_DrawCEcho();
@@ -1194,9 +2198,9 @@ void HU_Erase(void)
// clear the message lines that go away, so use _oldclearlines_
bottomline = oldclearlines;
oldclearlines = con_clearlines;
- if (chat_on)
+ if (chat_on && OLDCHAT)
if (bottomline < 8)
- bottomline = 8;
+ bottomline = 8; // only do it for consolechat. consolechat is gay.
if (automapactive || viewwindowx == 0) // hud msgs don't need to be cleared
return;
@@ -1233,7 +2237,42 @@ void HU_Erase(void)
//======================================================================
#define supercheckdef ((players[tab[i].num].powers[pw_super] && players[tab[i].num].mo && (players[tab[i].num].mo->state < &states[S_PLAY_SUPER_TRANS1] || players[tab[i].num].mo->state >= &states[S_PLAY_SUPER_TRANS6])) || (players[tab[i].num].powers[pw_carry] == CR_NIGHTSMODE && skins[players[tab[i].num].skin].flags & SF_SUPER))
-#define greycheckdef ((players[tab[i].num].mo && players[tab[i].num].mo->health <= 0) || players[tab[i].num].spectator)
+#define greycheckdef ((players[tab[i].num].mo && ((players[tab[i].num].rings <= 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres <= 0 && (maptol & TOL_NIGHTS)))) || players[tab[i].num].spectator)
+
+//
+// HU_drawPing
+//
+void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext)
+{
+ UINT8 numbars = 1; // how many ping bars do we draw?
+ UINT8 barcolor = 128; // color we use for the bars (green, yellow or red)
+ SINT8 i = 0;
+ SINT8 yoffset = 6;
+ INT32 dx = x+1 - (V_SmallStringWidth(va("%dms", ping), V_ALLOWLOWERCASE)/2);
+
+ if (ping < 128)
+ {
+ numbars = 3;
+ barcolor = 184;
+ }
+ else if (ping < 256)
+ {
+ numbars = 2; // Apparently ternaries w/ multiple statements don't look good in C so I decided against it.
+ barcolor = 103;
+ }
+
+ if (!notext || vid.width >= 640) // how sad, we're using a shit resolution.
+ V_DrawSmallString(dx, y+4, V_ALLOWLOWERCASE, va("%dms", ping));
+
+ for (i=0; (i<3); i++) // Draw the ping bar
+ {
+ V_DrawFill(x+2 *(i-1), y+yoffset-4, 2, 8-yoffset, 31);
+ if (i < numbars)
+ V_DrawFill(x+2 *(i-1), y+yoffset-3, 1, 8-yoffset-1, barcolor);
+
+ yoffset -= 2;
+ }
+}
//
// HU_DrawTabRankings
@@ -1257,6 +2296,14 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
greycheck = greycheckdef;
supercheck = supercheckdef;
+ if (!splitscreen) // don't draw it on splitscreen,
+ {
+ if (!(tab[i].num == serverplayer))
+ HU_drawPing(x+ 253, y+2, playerpingtable[tab[i].num], false);
+ //else
+ // V_DrawSmallString(x+ 246, y+4, V_YELLOWMAP, "SERVER");
+ }
+
V_DrawString(x + 20, y,
((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
| (greycheck ? V_60TRANS : 0)
@@ -1336,6 +2383,113 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
}
}
+//
+// HU_Draw32Emeralds
+//
+static void HU_Draw32Emeralds(INT32 x, INT32 y, INT32 pemeralds)
+{
+ //Draw the emeralds, in the CORRECT order, using tiny emerald sprites.
+ if (pemeralds & EMERALD1)
+ V_DrawSmallScaledPatch(x , y, 0, emeraldpics[1][0]);
+
+ if (pemeralds & EMERALD2)
+ V_DrawSmallScaledPatch(x+4, y, 0, emeraldpics[1][1]);
+
+ if (pemeralds & EMERALD3)
+ V_DrawSmallScaledPatch(x+8, y, 0, emeraldpics[1][2]);
+
+ if (pemeralds & EMERALD4)
+ V_DrawSmallScaledPatch(x+12 , y, 0, emeraldpics[1][3]);
+
+ if (pemeralds & EMERALD5)
+ V_DrawSmallScaledPatch(x+16, y, 0, emeraldpics[1][4]);
+
+ if (pemeralds & EMERALD6)
+ V_DrawSmallScaledPatch(x+20, y, 0, emeraldpics[1][5]);
+
+ if (pemeralds & EMERALD7)
+ V_DrawSmallScaledPatch(x+24, y, 0, emeraldpics[1][6]);
+}
+
+//
+// HU_Draw32TeamTabRankings
+//
+static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer)
+{
+ INT32 i,x,y;
+ INT32 redplayers = 0, blueplayers = 0;
+ const UINT8 *colormap;
+ char name[MAXPLAYERNAME+1];
+
+ V_DrawFill(160, 26, 1, 154, 0); //Draw a vertical line to separate the two teams.
+ V_DrawFill(1, 26, 318, 1, 0); //And a horizontal line to make a T.
+ V_DrawFill(1, 180, 318, 1, 0); //And a horizontal line near the bottom.
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (players[tab[i].num].spectator)
+ continue; //ignore them.
+
+ if (tab[i].color == skincolor_redteam) //red
+ {
+ redplayers++;
+ x = 14 + (BASEVIDWIDTH/2);
+ y = (redplayers * 9) + 20;
+ }
+ else if (tab[i].color == skincolor_blueteam) //blue
+ {
+ blueplayers++;
+ x = 14;
+ y = (blueplayers * 9) + 20;
+ }
+ else //er? not on red or blue, so ignore them
+ continue;
+
+ strlcpy(name, tab[i].name, 8);
+ V_DrawString(x + 10, y,
+ ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
+ | (((players[tab[i].num].rings > 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres > 0 && (maptol & TOL_NIGHTS))) ? 0 : V_TRANSLUCENT)
+ | V_ALLOWLOWERCASE, name);
+
+ if (gametype == GT_CTF)
+ {
+ if (players[tab[i].num].gotflag & GF_REDFLAG) // Red
+ V_DrawFixedPatch((x-10)*FRACUNIT, (y)*FRACUNIT, FRACUNIT/4, 0, rflagico, 0);
+ else if (players[tab[i].num].gotflag & GF_BLUEFLAG) // Blue
+ V_DrawFixedPatch((x-10)*FRACUNIT, (y)*FRACUNIT, FRACUNIT/4, 0, bflagico, 0);
+ }
+
+ // Draw emeralds
+ if (!players[tab[i].num].powers[pw_super]
+ || ((leveltime/7) & 1))
+ {
+ HU_Draw32Emeralds(x+60, y+2, tab[i].emeralds);
+ }
+
+ if (players[tab[i].num].powers[pw_super])
+ {
+ colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
+ V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/4, 0, superprefix[players[tab[i].num].skin], colormap);
+ }
+ else
+ {
+ colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
+ if ((players[tab[i].num].rings <= 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres <= 0 && (maptol & TOL_NIGHTS)))
+ V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/4, V_HUDTRANSHALF, faceprefix[players[tab[i].num].skin], colormap);
+ else
+ V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/4, 0, faceprefix[players[tab[i].num].skin], colormap);
+ }
+ V_DrawRightAlignedThinString(x+128, y, (((players[tab[i].num].rings > 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres > 0 && (maptol & TOL_NIGHTS))) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+ if (!splitscreen)
+ {
+ if (!(tab[i].num == serverplayer))
+ HU_drawPing(x+ 135, y+3, playerpingtable[tab[i].num], true);
+ //else
+ //V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST");
+ }
+ }
+}
+
//
// HU_DrawTeamTabRankings
//
@@ -1343,14 +2497,51 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
{
INT32 i,x,y;
INT32 redplayers = 0, blueplayers = 0;
+ boolean smol = false;
const UINT8 *colormap;
char name[MAXPLAYERNAME+1];
boolean greycheck, supercheck;
+ // before we draw, we must count how many players are in each team. It makes an additional loop, but we need to know if we have to draw a big or a small ranking.
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (players[tab[i].num].spectator)
+ continue; //ignore them.
+
+ if (tab[i].color == skincolor_redteam) //red
+ {
+ if (redplayers++ > 8)
+ {
+ smol = true;
+ break; // don't make more loops than we need to.
+ }
+ }
+ else if (tab[i].color == skincolor_blueteam) //blue
+ {
+ if (blueplayers++ > 8)
+ {
+ smol = true;
+ break;
+ }
+ }
+ else //er? not on red or blue, so ignore them
+ continue;
+
+ }
+
+ // I'll be blunt with you, this may add more lines, but I'm not adding weird cases for this, so we're executing a separate function.
+ if (smol == true || cv_compactscoreboard.value)
+ {
+ HU_Draw32TeamTabRankings(tab, whiteplayer);
+ return;
+ }
+
V_DrawFill(160, 26, 1, 154, 0); //Draw a vertical line to separate the two teams.
V_DrawFill(1, 26, 318, 1, 0); //And a horizontal line to make a T.
V_DrawFill(1, 180, 318, 1, 0); //And a horizontal line near the bottom.
+ i=0, redplayers=0, blueplayers=0;
+
for (i = 0; i < MAXPLAYERS; i++)
{
if (players[tab[i].num].spectator)
@@ -1376,7 +2567,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
greycheck = greycheckdef;
supercheck = supercheckdef;
- strlcpy(name, tab[i].name, 9);
+ strlcpy(name, tab[i].name, 7);
V_DrawString(x + 20, y,
((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
| (greycheck ? V_TRANSLUCENT : 0)
@@ -1410,7 +2601,14 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
else
V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
}
- V_DrawRightAlignedThinString(x+120, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
+ V_DrawRightAlignedThinString(x+100, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
+ if (!splitscreen)
+ {
+ if (!(tab[i].num == serverplayer))
+ HU_drawPing(x+ 113, y+2, playerpingtable[tab[i].num], false);
+ //else
+ // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER");
+ }
}
}
@@ -1436,7 +2634,12 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
greycheck = greycheckdef;
supercheck = supercheckdef;
- strlcpy(name, tab[i].name, 9);
+ strlcpy(name, tab[i].name, 7);
+ if (!(tab[i].num == serverplayer))
+ HU_drawPing(x+ 113, y+2, playerpingtable[tab[i].num], false);
+ //else
+ // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER");
+
V_DrawString(x + 20, y,
((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
| (greycheck ? V_TRANSLUCENT : 0)
@@ -1494,15 +2697,15 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
if (circuitmap)
{
if (players[tab[i].num].exiting)
- V_DrawRightAlignedThinString(x+156, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
+ V_DrawRightAlignedThinString(x+146, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
else
- V_DrawRightAlignedThinString(x+156, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
+ V_DrawRightAlignedThinString(x+146, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
}
else
- V_DrawRightAlignedThinString(x+156, y, (greycheck ? V_TRANSLUCENT : 0), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
+ V_DrawRightAlignedThinString(x+146, y, (greycheck ? V_TRANSLUCENT : 0), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
}
else
- V_DrawRightAlignedThinString(x+120, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
+ V_DrawRightAlignedThinString(x+100, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
y += 16;
if (y > 160)
@@ -1513,6 +2716,107 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
}
}
+//
+// HU_Draw32TabRankings
+//
+static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer)
+{
+ INT32 i;
+ const UINT8 *colormap;
+ char name[MAXPLAYERNAME+1];
+
+ V_DrawFill(160, 26, 1, 154, 0); //Draw a vertical line to separate the two sides.
+ V_DrawFill(1, 26, 318, 1, 0); //And a horizontal line to make a T.
+ V_DrawFill(1, 180, 318, 1, 0); //And a horizontal line near the bottom.
+
+ for (i = 0; i < scorelines; i++)
+ {
+ if (players[tab[i].num].spectator)
+ continue; //ignore them.
+
+ strlcpy(name, tab[i].name, 7);
+ if (!splitscreen) // don't draw it on splitscreen,
+ {
+ if (!(tab[i].num == serverplayer))
+ HU_drawPing(x+ 135, y+3, playerpingtable[tab[i].num], true);
+ //else
+ // V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST");
+ }
+
+ V_DrawString(x + 10, y,
+ ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
+ | (((players[tab[i].num].rings > 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres > 0 && (maptol & TOL_NIGHTS))) ? 0 : V_TRANSLUCENT)
+ | V_ALLOWLOWERCASE, name);
+
+ if (G_GametypeUsesLives()) //show lives
+ V_DrawRightAlignedThinString(x-1, y, V_ALLOWLOWERCASE, va("%d", players[tab[i].num].lives));
+ else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
+ V_DrawFixedPatch((x-10)*FRACUNIT, (y)*FRACUNIT, FRACUNIT/4, 0, tagico, 0);
+
+ // Draw emeralds
+ if (!players[tab[i].num].powers[pw_super]
+ || ((leveltime/7) & 1))
+ {
+ HU_Draw32Emeralds(x+60, y+2, tab[i].emeralds);
+ //HU_DrawEmeralds(x-12,y+2,tab[i].emeralds);
+ }
+
+ //V_DrawSmallScaledPatch (x, y-4, 0, livesback);
+ if (tab[i].color == 0)
+ {
+ colormap = colormaps;
+ if (players[tab[i].num].powers[pw_super])
+ V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/4, 0, superprefix[players[tab[i].num].skin], 0);
+ else
+ {
+ if ((players[tab[i].num].rings <= 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres <= 0 && (maptol & TOL_NIGHTS)))
+ V_DrawFixedPatch(x*FRACUNIT, (y)*FRACUNIT, FRACUNIT/4, V_HUDTRANSHALF, faceprefix[players[tab[i].num].skin], 0);
+ else
+ V_DrawFixedPatch(x*FRACUNIT, (y)*FRACUNIT, FRACUNIT/4, 0, faceprefix[players[tab[i].num].skin], 0);
+ }
+ }
+ else
+ {
+ if (players[tab[i].num].powers[pw_super])
+ {
+ colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
+ V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/4, 0, superprefix[players[tab[i].num].skin], colormap);
+ }
+ else
+ {
+ colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
+ if ((players[tab[i].num].rings <= 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres <= 0 && (maptol & TOL_NIGHTS)))
+ V_DrawFixedPatch(x*FRACUNIT, (y)*FRACUNIT, FRACUNIT/4, V_HUDTRANSHALF, faceprefix[players[tab[i].num].skin], colormap);
+ else
+ V_DrawFixedPatch(x*FRACUNIT, (y)*FRACUNIT, FRACUNIT/4, 0, faceprefix[players[tab[i].num].skin], colormap);
+ }
+ }
+
+ // All data drawn with thin string for space.
+ if (gametype == GT_RACE)
+ {
+ if (circuitmap)
+ {
+ if (players[tab[i].num].exiting)
+ V_DrawRightAlignedThinString(x+128, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
+ else
+ V_DrawRightAlignedThinString(x+128, y, (((players[tab[i].num].rings > 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres > 0 && (maptol & TOL_NIGHTS))) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+ }
+ else
+ V_DrawRightAlignedThinString(x+128, y, (((players[tab[i].num].rings > 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres > 0 && (maptol & TOL_NIGHTS))) ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
+ }
+ else
+ V_DrawRightAlignedThinString(x+128, y, (((players[tab[i].num].rings > 0 && !(maptol & TOL_NIGHTS)) || (players[tab[i].num].spheres > 0 && (maptol & TOL_NIGHTS))) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+
+ y += 9;
+ if (i == 16)
+ {
+ y = 32;
+ x += BASEVIDWIDTH/2;
+ }
+ }
+}
+
//
// HU_DrawEmeralds
//
@@ -1733,15 +3037,18 @@ static void HU_DrawRankings(void)
scorelines++;
}
- if (scorelines > 20)
- scorelines = 20; //dont draw past bottom of screen, show the best only
+ //if (scorelines > 20)
+ // scorelines = 20; //dont draw past bottom of screen, show the best only
+ // shush, we'll do it anyway.
if (G_GametypeHasTeams())
HU_DrawTeamTabRankings(tab, whiteplayer); //separate function for Spazzo's silly request
- else if (scorelines <= 9)
+ else if (scorelines <= 9 && !cv_compactscoreboard.value)
HU_DrawTabRankings(40, 32, tab, scorelines, whiteplayer);
- else
+ else if (scorelines <= 20 && !cv_compactscoreboard.value)
HU_DrawDualTabRankings(32, 32, tab, scorelines, whiteplayer);
+ else
+ HU_Draw32TabRankings(14, 28, tab, scorelines, whiteplayer);
// draw spectators in a ticker across the bottom
if (!splitscreen && G_GametypeHasSpectators())
diff --git a/src/hu_stuff.h b/src/hu_stuff.h
index 46906dd73..ab77e67b6 100644
--- a/src/hu_stuff.h
+++ b/src/hu_stuff.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -57,6 +57,20 @@ typedef struct
// chat stuff
//------------------------------------
#define HU_MAXMSGLEN 224
+#define CHAT_BUFSIZE 64 // that's enough messages, right? We'll delete the older ones when that gets out of hand.
+#ifdef NETSPLITSCREEN
+#define OLDCHAT (cv_consolechat.value == 1 || dedicated || vid.width < 640)
+#else
+#define OLDCHAT (cv_consolechat.value == 1 || dedicated || vid.width < 640 || splitscreen)
+#endif
+#define CHAT_MUTE (cv_mute.value && !(server || IsPlayerAdmin(consoleplayer))) // this still allows to open the chat but not to type. That's used for scrolling and whatnot.
+#define OLD_MUTE (OLDCHAT && cv_mute.value && !(server || IsPlayerAdmin(consoleplayer))) // this is used to prevent oldchat from opening when muted.
+
+// some functions
+void HU_AddChatText(const char *text, boolean playsound);
+
+// set true when entering a chat message
+extern boolean chat_on;
extern patch_t *hu_font[HU_FONTSIZE], *tny_font[HU_FONTSIZE];
extern patch_t *tallnum[10];
@@ -74,9 +88,6 @@ extern patch_t *tallminus;
extern patch_t *tallinfin;
extern patch_t *tokenicon;
-// set true when entering a chat message
-extern boolean chat_on;
-
// set true whenever the tab rankings are being shown for any reason
extern boolean hu_showscores;
@@ -89,12 +100,12 @@ void HU_LoadGraphics(void);
void HU_Start(void);
boolean HU_Responder(event_t *ev);
-
void HU_Ticker(void);
void HU_Drawer(void);
char HU_dequeueChatChar(void);
void HU_Erase(void);
void HU_clearChatChars(void);
+void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext); // Lat': Ping drawer for scoreboard.
void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer);
void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer);
void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer);
diff --git a/src/i_addrinfo.c b/src/i_addrinfo.c
index 208f470f4..375fd4845 100644
--- a/src/i_addrinfo.c
+++ b/src/i_addrinfo.c
@@ -1,6 +1,6 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
-// Copyright (C) 2011-2016 by Sonic Team Junior.
+// Copyright (C) 2011-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/i_addrinfo.h b/src/i_addrinfo.h
index 4cda8968b..7c3afc614 100644
--- a/src/i_addrinfo.h
+++ b/src/i_addrinfo.h
@@ -1,6 +1,6 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
-// Copyright (C) 2011-2016 by Sonic Team Junior.
+// Copyright (C) 2011-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/i_joy.h b/src/i_joy.h
index 5cba1af0a..27013f011 100644
--- a/src/i_joy.h
+++ b/src/i_joy.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/i_net.h b/src/i_net.h
index 2bfa5eac7..0e17077bb 100644
--- a/src/i_net.h
+++ b/src/i_net.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/i_sound.h b/src/i_sound.h
index bc9829fdd..9a5c2930a 100644
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -146,6 +146,18 @@ boolean I_SongPaused(void);
boolean I_SetSongSpeed(float speed);
+/// ------------------------
+// MUSIC SEEKING
+/// ------------------------
+
+UINT32 I_GetSongLength(void);
+
+boolean I_SetSongLoopPoint(UINT32 looppoint);
+UINT32 I_GetSongLoopPoint(void);
+
+boolean I_SetSongPosition(UINT32 position);
+UINT32 I_GetSongPosition(void);
+
/// ------------------------
// MUSIC PLAYBACK
/// ------------------------
@@ -216,6 +228,17 @@ void I_SetMusicVolume(UINT8 volume);
boolean I_SetSongTrack(INT32 track);
+/// ------------------------
+/// MUSIC FADING
+/// ------------------------
+
+void I_SetInternalMusicVolume(UINT8 volume);
+void I_StopFadingSong(void);
+boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void));
+boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void));
+boolean I_FadeOutStopSong(UINT32 ms);
+boolean I_FadeInPlaySong(UINT32 ms, boolean looping);
+
/// ------------------------
// CD MUSIC I/O
/// ------------------------
diff --git a/src/i_system.h b/src/i_system.h
index d61f2d16e..a8d707d16 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/i_tcp.c b/src/i_tcp.c
index c62adab06..f2b4336dc 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -575,7 +575,7 @@ static boolean SOCK_Get(void)
if (c != ERRSOCKET)
{
// find remote node number
- for (j = 0; j <= MAXNETNODES; j++) //include LAN
+ for (j = 1; j <= MAXNETNODES; j++) //include LAN
{
if (SOCK_cmpaddr(&fromaddress, &clientaddress[j], 0))
{
@@ -1256,7 +1256,7 @@ void I_ShutdownTcpDriver(void)
static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
{
SINT8 newnode = -1;
- struct my_addrinfo *ai, *runp, hints;
+ struct my_addrinfo *ai = NULL, *runp, hints;
int gaie;
if (!port || !port[0])
@@ -1286,8 +1286,12 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
while (runp != NULL)
{
// find ip of the server
- memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
- runp = NULL;
+ if (sendto(mysockets[0], NULL, 0, 0, runp->ai_addr, runp->ai_addrlen) == 0)
+ {
+ memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
+ break;
+ }
+ runp = runp->ai_next;
}
I_freeaddrinfo(ai);
return newnode;
diff --git a/src/i_tcp.h b/src/i_tcp.h
index a084abb84..06680cd9b 100644
--- a/src/i_tcp.h
+++ b/src/i_tcp.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/i_video.h b/src/i_video.h
index 7ee07f000..4bb2c5829 100644
--- a/src/i_video.h
+++ b/src/i_video.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/info.c b/src/info.c
index 769d27e88..86e40c388 100644
--- a/src/info.c
+++ b/src/info.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -6033,7 +6033,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
},
{ // MT_EMBLEM
- -1, // doomednum
+ 322, // doomednum
S_EMBLEM1, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
@@ -6270,7 +6270,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
},
{ // MT_EMERALDSPAWN
- 323, // doomednum
+ 321, // doomednum
S_INVISIBLE, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
@@ -18026,7 +18026,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
10, // mass
0, // damage
sfx_None, // activesound
- MF_NOTHINK|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
+ MF_SCENERY|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags
S_NULL // raisestate
},
diff --git a/src/info.h b/src/info.h
index 8baafbd87..962a6be29 100644
--- a/src/info.h
+++ b/src/info.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/keys.h b/src/keys.h
index 654aa1d9d..cf3a0d060 100644
--- a/src/keys.h
+++ b/src/keys.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/locale/en.po b/src/locale/en.po
index 069930b81..5f4592079 100644
--- a/src/locale/en.po
+++ b/src/locale/en.po
@@ -3624,7 +3624,7 @@ msgid "another castle!"
msgstr ""
#: st_stuff.c:2328 st_stuff.c:2334 st_stuff.c:2356
-msgid "Press F12 to watch another player."
+msgid "Press Viewpoint Key to watch a player."
msgstr ""
#: st_stuff.c:2333
diff --git a/src/locale/srb2.pot b/src/locale/srb2.pot
index ced13bbe6..552e9edb1 100644
--- a/src/locale/srb2.pot
+++ b/src/locale/srb2.pot
@@ -3815,7 +3815,7 @@ msgid "another castle!"
msgstr ""
#: st_stuff.c:2092 st_stuff.c:2098 st_stuff.c:2120
-msgid "Press F12 to watch another player."
+msgid "Press Viewpoint Key to watch a player."
msgstr ""
#: st_stuff.c:2097
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index bb9e93edf..755b76835 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -23,6 +23,9 @@
#include "m_random.h"
#include "s_sound.h"
#include "g_game.h"
+#include "hu_stuff.h" // HU_AddChatText
+#include "console.h"
+#include "d_netcmd.h" // IsPlayerAdmin
#include "lua_script.h"
#include "lua_libs.h"
@@ -90,6 +93,51 @@ static int lib_print(lua_State *L)
return 0;
}
+// Print stuff in the chat, or in the console if we can't.
+static int lib_chatprint(lua_State *L)
+{
+ const char *str = luaL_checkstring(L, 1); // retrieve string
+ boolean sound = lua_optboolean(L, 2); // retrieve sound boolean
+ int len = strlen(str);
+
+ if (str == NULL) // error if we don't have a string!
+ return luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("chatprint"));
+
+ if (len > 255) // string is too long!!!
+ return luaL_error(L, "String exceeds the 255 characters limit of the chat buffer.");
+
+ HU_AddChatText(str, sound);
+ return 0;
+}
+
+// Same as above, but do it for only one player.
+static int lib_chatprintf(lua_State *L)
+{
+ int n = lua_gettop(L); /* number of arguments */
+ const char *str = luaL_checkstring(L, 2); // retrieve string
+ boolean sound = lua_optboolean(L, 3); // sound?
+ int len = strlen(str);
+ player_t *plr;
+
+ if (n < 2)
+ return luaL_error(L, "chatprintf requires at least two arguments: player and text.");
+
+ plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); // retrieve player
+ if (!plr)
+ return LUA_ErrInvalid(L, "player_t");
+ if (plr != &players[consoleplayer])
+ return 0;
+
+ if (str == NULL) // error if we don't have a string!
+ return luaL_error(L, LUA_QL("tostring") " must return a string to " LUA_QL("chatprintf"));
+
+ if (len > 255) // string is too long!!!
+ return luaL_error(L, "String exceeds the 255 characters limit of the chat buffer.");
+
+ HU_AddChatText(str, sound);
+ return 0;
+}
+
static const struct {
const char *meta;
const char *utype;
@@ -172,6 +220,16 @@ static int lib_userdataType(lua_State *L)
return 1;
}
+static int lib_isPlayerAdmin(lua_State *L)
+{
+ player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+ //HUDSAFE
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ lua_pushboolean(L, IsPlayerAdmin(player-players));
+ return 1;
+}
+
// M_RANDOM
//////////////
@@ -957,7 +1015,8 @@ static int lib_pRestoreMusic(lua_State *L)
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
- P_RestoreMusic(player);
+ if (P_IsLocalPlayer(player))
+ P_RestoreMusic(player);
return 0;
}
@@ -2132,6 +2191,25 @@ static int lib_rSetPlayerSkin(lua_State *L)
return 0;
}
+// R_DATA
+////////////
+
+static int lib_rCheckTextureNumForName(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ //HUDSAFE
+ lua_pushinteger(L, R_CheckTextureNumForName(name));
+ return 1;
+}
+
+static int lib_rTextureNumForName(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ //HUDSAFE
+ lua_pushinteger(L, R_TextureNumForName(name));
+ return 1;
+}
+
// S_SOUND
////////////
@@ -2140,7 +2218,7 @@ static int lib_sStartSound(lua_State *L)
const void *origin = NULL;
sfxenum_t sound_id = luaL_checkinteger(L, 2);
player_t *player = NULL;
- NOHUD
+ //NOHUD // kys @whoever did this.
if (sound_id >= NUMSFX)
return luaL_error(L, "sfx %d out of range (0 - %d)", sound_id, NUMSFX-1);
if (!lua_isnil(L, 1))
@@ -2156,7 +2234,12 @@ static int lib_sStartSound(lua_State *L)
return LUA_ErrInvalid(L, "player_t");
}
if (!player || P_IsLocalPlayer(player))
+ {
+ if (hud_running)
+ origin = NULL; // HUD rendering startsound shouldn't have an origin, just remove it instead of having a retarded error.
+
S_StartSound(origin, sound_id);
+ }
return 0;
}
@@ -2201,7 +2284,7 @@ static int lib_sChangeMusic(lua_State *L)
{
#ifdef MUSICSLOT_COMPATIBILITY
const char *music_name;
- UINT32 music_num;
+ UINT32 music_num, position, prefadems, fadeinms;
char music_compat_name[7];
boolean looping;
@@ -2229,7 +2312,6 @@ static int lib_sChangeMusic(lua_State *L)
music_name = luaL_checkstring(L, 1);
}
-
looping = (boolean)lua_opttrueboolean(L, 2);
#else
@@ -2254,8 +2336,12 @@ static int lib_sChangeMusic(lua_State *L)
#endif
music_flags = (UINT16)luaL_optinteger(L, 4, 0);
+ position = (UINT32)luaL_optinteger(L, 5, 0);
+ prefadems = (UINT32)luaL_optinteger(L, 6, 0);
+ fadeinms = (UINT32)luaL_optinteger(L, 7, 0);
+
if (!player || P_IsLocalPlayer(player))
- S_ChangeMusic(music_name, music_flags, looping);
+ S_ChangeMusicEx(music_name, music_flags, looping, position, prefadems, fadeinms);
return 0;
}
@@ -2272,10 +2358,8 @@ static int lib_sSpeedMusic(lua_State *L)
return LUA_ErrInvalid(L, "player_t");
}
if (!player || P_IsLocalPlayer(player))
- lua_pushboolean(L, S_SpeedMusic(speed));
- else
- lua_pushboolean(L, false);
- return 1;
+ S_SpeedMusic(speed);
+ return 0;
}
static int lib_sStopMusic(lua_State *L)
@@ -2293,6 +2377,110 @@ static int lib_sStopMusic(lua_State *L)
return 0;
}
+static int lib_sSetInternalMusicVolume(lua_State *L)
+{
+ UINT32 volume = (UINT32)luaL_checkinteger(L, 1);
+ player_t *player = NULL;
+ NOHUD
+ if (!lua_isnone(L, 2) && lua_isuserdata(L, 2))
+ {
+ player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER));
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ }
+ if (!player || P_IsLocalPlayer(player))
+ {
+ S_SetInternalMusicVolume(volume);
+ lua_pushboolean(L, true);
+ }
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static int lib_sStopFadingMusic(lua_State *L)
+{
+ player_t *player = NULL;
+ NOHUD
+ if (!lua_isnone(L, 1) && lua_isuserdata(L, 1))
+ {
+ player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ }
+ if (!player || P_IsLocalPlayer(player))
+ {
+ S_StopFadingMusic();
+ lua_pushboolean(L, true);
+ }
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static int lib_sFadeMusic(lua_State *L)
+{
+ UINT32 target_volume = (UINT32)luaL_checkinteger(L, 1);
+ UINT32 ms;
+ INT32 source_volume;
+ player_t *player = NULL;
+ NOHUD
+ if (!lua_isnone(L, 3) && lua_isuserdata(L, 3))
+ {
+ player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER));
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ ms = (UINT32)luaL_checkinteger(L, 2);
+ source_volume = -1;
+ }
+ else if (!lua_isnone(L, 4) && lua_isuserdata(L, 4))
+ {
+ player = *((player_t **)luaL_checkudata(L, 4, META_PLAYER));
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ source_volume = (INT32)luaL_checkinteger(L, 2);
+ ms = (UINT32)luaL_checkinteger(L, 3);
+ }
+ else if (luaL_optinteger(L, 3, INT32_MAX) == INT32_MAX)
+ {
+ ms = (UINT32)luaL_checkinteger(L, 2);
+ source_volume = -1;
+ }
+ else
+ {
+ source_volume = (INT32)luaL_checkinteger(L, 2);
+ ms = (UINT32)luaL_checkinteger(L, 3);
+ }
+
+ NOHUD
+
+ if (!player || P_IsLocalPlayer(player))
+ lua_pushboolean(L, S_FadeMusicFromVolume(target_volume, source_volume, ms));
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static int lib_sFadeOutStopMusic(lua_State *L)
+{
+ UINT32 ms = (UINT32)luaL_checkinteger(L, 1);
+ player_t *player = NULL;
+ NOHUD
+ if (!lua_isnone(L, 2) && lua_isuserdata(L, 2))
+ {
+ player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER));
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ }
+ if (!player || P_IsLocalPlayer(player))
+ {
+ lua_pushboolean(L, S_FadeOutStopMusic(ms));
+ }
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
static int lib_sOriginPlaying(lua_State *L)
{
void *origin = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@@ -2376,7 +2564,9 @@ static int lib_gDoReborn(lua_State *L)
return 0;
}
-static int lib_gExitLevel(lua_State *L)
+// Another Lua function that doesn't actually exist!
+// Sets nextmapoverride & skipstats without instantly ending the level, for instances where other sources should be exiting the level, like normal signposts.
+static int lib_gSetCustomExitVars(lua_State *L)
{
int n = lua_gettop(L); // Num arguments
NOHUD
@@ -2384,21 +2574,36 @@ static int lib_gExitLevel(lua_State *L)
// LUA EXTENSION: Custom exit like support
// Supported:
- // G_ExitLevel(); [no modifications]
- // G_ExitLevel(int) [nextmap override only]
- // G_ExitLevel(bool) [skipstats only]
- // G_ExitLevel(int, bool) [both of the above]
+ // G_SetCustomExitVars(); [reset to defaults]
+ // G_SetCustomExitVars(int) [nextmap override only]
+ // G_SetCustomExitVars(bool) [skipstats only]
+ // G_SetCustomExitVars(int, bool) [both of the above]
if (n >= 1)
{
if (lua_isnumber(L, 1) || n >= 2)
{
nextmapoverride = (INT16)luaL_checknumber(L, 1);
- lua_pop(L, 1); // pop nextmapoverride; skipstats now 1 if available
+ lua_remove(L, 1); // remove nextmapoverride; skipstats now 1 if available
}
skipstats = lua_optboolean(L, 1);
}
+ else
+ {
+ nextmapoverride = 0;
+ skipstats = false;
+ }
// ---
+ return 0;
+}
+
+static int lib_gExitLevel(lua_State *L)
+{
+ int n = lua_gettop(L); // Num arguments
+ NOHUD
+ // Moved this bit to G_SetCustomExitVars
+ if (n >= 1) // Don't run the reset to defaults option
+ lib_gSetCustomExitVars(L);
G_ExitLevel();
return 0;
}
@@ -2503,7 +2708,10 @@ static int lib_gTicsToMilliseconds(lua_State *L)
static luaL_Reg lib[] = {
{"print", lib_print},
+ {"chatprint", lib_chatprint},
+ {"chatprintf", lib_chatprintf},
{"userdataType", lib_userdataType},
+ {"IsPlayerAdmin", lib_isPlayerAdmin},
// m_random
{"P_RandomFixed",lib_pRandomFixed},
@@ -2669,6 +2877,10 @@ static luaL_Reg lib[] = {
{"R_Frame2Char",lib_rFrame2Char},
{"R_SetPlayerSkin",lib_rSetPlayerSkin},
+ // r_data
+ {"R_CheckTextureNumForName",lib_rCheckTextureNumForName},
+ {"R_TextureNumForName",lib_rTextureNumForName},
+
// s_sound
{"S_StartSound",lib_sStartSound},
{"S_StartSoundAtVolume",lib_sStartSoundAtVolume},
@@ -2676,6 +2888,10 @@ static luaL_Reg lib[] = {
{"S_ChangeMusic",lib_sChangeMusic},
{"S_SpeedMusic",lib_sSpeedMusic},
{"S_StopMusic",lib_sStopMusic},
+ {"S_SetInternalMusicVolume", lib_sSetInternalMusicVolume},
+ {"S_StopFadingMusic",lib_sStopFadingMusic},
+ {"S_FadeMusic",lib_sFadeMusic},
+ {"S_FadeOutStopMusic",lib_sFadeOutStopMusic},
{"S_OriginPlaying",lib_sOriginPlaying},
{"S_IdPlaying",lib_sIdPlaying},
{"S_SoundPlaying",lib_sSoundPlaying},
@@ -2684,6 +2900,7 @@ static luaL_Reg lib[] = {
// g_game
{"G_BuildMapName",lib_gBuildMapName},
{"G_DoReborn",lib_gDoReborn},
+ {"G_SetCustomExitVars",lib_gSetCustomExitVars},
{"G_ExitLevel",lib_gExitLevel},
{"G_IsSpecialStage",lib_gIsSpecialStage},
{"G_GametypeUsesLives",lib_gGametypeUsesLives},
diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c
index ebfd6a2df..dabbdd9f6 100644
--- a/src/lua_blockmaplib.c
+++ b/src/lua_blockmaplib.c
@@ -186,9 +186,6 @@ static int lib_searchBlockmap(lua_State *L)
UINT8 funcret = 0;
blockmap_func searchFunc;
- if (gamestate != GS_LEVEL)
- return luaL_error(L, "This function can only be used in a level!");
-
lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2]
luaL_checktype(L, 1, LUA_TFUNCTION);
diff --git a/src/lua_consolelib.c b/src/lua_consolelib.c
index d576efb9a..98d18d8db 100644
--- a/src/lua_consolelib.c
+++ b/src/lua_consolelib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -63,7 +63,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum)
lua_pop(gL, 1); // pop flags
// requires server/admin and the player is not one of them
- if ((flags & 1) && playernum != serverplayer && playernum != adminplayer)
+ if ((flags & 1) && playernum != serverplayer && !IsPlayerAdmin(playernum))
goto deny;
lua_rawgeti(gL, -1, 1); // push function from command info table
@@ -141,7 +141,7 @@ void COM_Lua_f(void)
UINT8 argc;
lua_pop(gL, 1); // pop command info table
- if (flags & 1 && !server && adminplayer != playernum) // flag 1: only server/admin can use this command.
+ if (flags & 1 && !server && !IsPlayerAdmin(playernum)) // flag 1: only server/admin can use this command.
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 2b113077c..9fcc36594 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -54,7 +54,7 @@ enum hook {
};
extern const char *const hookNames[];
-void LUAh_MapChange(void); // Hook for map change (before load)
+void LUAh_MapChange(INT16 mapnumber); // Hook for map change (before load)
void LUAh_MapLoad(void); // Hook for map load
void LUAh_PlayerJoin(int playernum); // Hook for Got_AddPlayer
void LUAh_ThinkFrame(void); // Hook for frame (after mobj and player thinkers)
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index bb1f59729..d605499e2 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -200,6 +200,8 @@ static int lib_addHook(lua_State *L)
case hook_JumpSpinSpecial:
case hook_PlayerSpawn:
case hook_FollowMobj:
+ case hook_ShieldSpawn:
+ case hook_ShieldSpecial:
lastp = &playerhooks;
break;
case hook_LinedefExecute:
@@ -325,14 +327,14 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which)
}
// Hook for map change (before load)
-void LUAh_MapChange(void)
+void LUAh_MapChange(INT16 mapnumber)
{
hook_p hookp;
if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8))))
return;
lua_settop(gL, 0);
- lua_pushinteger(gL, gamemap);
+ lua_pushinteger(gL, mapnumber);
for (hookp = roothook; hookp; hookp = hookp->next)
if (hookp->type == hook_MapChange)
@@ -968,7 +970,7 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
return hooked;
}
-// Hook for player chat
+
boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
{
hook_p hookp;
@@ -1021,6 +1023,7 @@ boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
return hooked;
}
+
// Hook for hurt messages
boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{
diff --git a/src/lua_hud.h b/src/lua_hud.h
index c1479d5ef..7f928f7c4 100644
--- a/src/lua_hud.h
+++ b/src/lua_hud.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2014-2016 by John "JTE" Muniz.
-// Copyright (C) 2014-2016 by Sonic Team Junior.
+// Copyright (C) 2014-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 9c5a21ec9..f3a1ba210 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2014-2016 by John "JTE" Muniz.
-// Copyright (C) 2014-2016 by Sonic Team Junior.
+// Copyright (C) 2014-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_infolib.c b/src/lua_infolib.c
index 7d7aab750..55afa3874 100644
--- a/src/lua_infolib.c
+++ b/src/lua_infolib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_libs.h b/src/lua_libs.h
index 3ffdd8078..827c1d798 100644
--- a/src/lua_libs.h
+++ b/src/lua_libs.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 66fbb22b3..27b97204f 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -905,16 +905,16 @@ static int side_set(lua_State *L)
side->rowoffset = luaL_checkfixed(L, 3);
break;
case side_toptexture:
- side->toptexture = luaL_checkinteger(L, 3);
+ side->toptexture = luaL_checkinteger(L, 3);
break;
case side_bottomtexture:
- side->bottomtexture = luaL_checkinteger(L, 3);
+ side->bottomtexture = luaL_checkinteger(L, 3);
break;
case side_midtexture:
- side->midtexture = luaL_checkinteger(L, 3);
+ side->midtexture = luaL_checkinteger(L, 3);
break;
case side_repeatcnt:
- side->repeatcnt = luaL_checkinteger(L, 3);
+ side->repeatcnt = luaL_checkinteger(L, 3);
break;
}
return 0;
@@ -1637,6 +1637,7 @@ static int ffloor_get(lua_State *L)
{
ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
enum ffloor_e field = luaL_checkoption(L, 2, ffloor_opt[0], ffloor_opt);
+ INT16 i;
if (!ffloor)
{
@@ -1656,11 +1657,11 @@ static int ffloor_get(lua_State *L)
lua_pushfixed(L, *ffloor->topheight);
return 1;
case ffloor_toppic: { // toppic
- levelflat_t *levelflat;
- INT16 i;
- for (i = 0, levelflat = levelflats; i != *ffloor->toppic; i++, levelflat++)
- ;
- lua_pushlstring(L, levelflat->name, 8);
+ levelflat_t *levelflat = &levelflats[*ffloor->toppic];
+ for (i = 0; i < 8; i++)
+ if (!levelflat->name[i])
+ break;
+ lua_pushlstring(L, levelflat->name, i);
return 1;
}
case ffloor_toplightlevel:
@@ -1670,11 +1671,11 @@ static int ffloor_get(lua_State *L)
lua_pushfixed(L, *ffloor->bottomheight);
return 1;
case ffloor_bottompic: { // bottompic
- levelflat_t *levelflat;
- INT16 i;
- for (i = 0, levelflat = levelflats; i != *ffloor->bottompic; i++, levelflat++)
- ;
- lua_pushlstring(L, levelflat->name, 8);
+ levelflat_t *levelflat = &levelflats[*ffloor->bottompic];
+ for (i = 0; i < 8; i++)
+ if (!levelflat->name[i])
+ break;
+ lua_pushlstring(L, levelflat->name, i);
return 1;
}
#ifdef ESLOPE
@@ -2030,6 +2031,12 @@ static int mapheaderinfo_get(lua_State *L)
lua_pushstring(L, header->musname);
else if (fastcmp(field,"mustrack"))
lua_pushinteger(L, header->mustrack);
+ else if (fastcmp(field,"muspos"))
+ lua_pushinteger(L, header->muspos);
+ else if (fastcmp(field,"musinterfadeout"))
+ lua_pushinteger(L, header->musinterfadeout);
+ else if (fastcmp(field,"musintername"))
+ lua_pushstring(L, header->musintername);
else if (fastcmp(field,"forcecharacter"))
lua_pushstring(L, header->forcecharacter);
else if (fastcmp(field,"weather"))
diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c
index dd526a169..1f0d6e287 100644
--- a/src/lua_mathlib.c
+++ b/src/lua_mathlib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index a9db95d4b..0835e5cc0 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 4919883c4..f3eb978f6 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_script.c b/src/lua_script.c
index 69e275712..04aa55cf0 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_script.h b/src/lua_script.h
index 51f1eaeaa..b690e4fa7 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c
index 1a70a82d9..a8f785c5a 100644
--- a/src/lua_skinlib.c
+++ b/src/lua_skinlib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2014-2016 by John "JTE" Muniz.
-// Copyright (C) 2014-2016 by Sonic Team Junior.
+// Copyright (C) 2014-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/lua_thinkerlib.c b/src/lua_thinkerlib.c
index a661aaf04..ae648613a 100644
--- a/src/lua_thinkerlib.c
+++ b/src/lua_thinkerlib.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by John "JTE" Muniz.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_aatree.c b/src/m_aatree.c
index 6cb3a32cb..f19fa1dc0 100644
--- a/src/m_aatree.c
+++ b/src/m_aatree.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_aatree.h b/src/m_aatree.h
index eeaebca3b..e7f1eba05 100644
--- a/src/m_aatree.h
+++ b/src/m_aatree.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_anigif.c b/src/m_anigif.c
index 4ebf2aff3..901927fa1 100644
--- a/src/m_anigif.c
+++ b/src/m_anigif.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 2013-2016 by Matthew "Inuyasha" Walsh.
// Copyright (C) 2013 by "Ninji".
-// Copyright (C) 2013-2016 by Sonic Team Junior.
+// Copyright (C) 2013-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -497,8 +497,8 @@ static void GIF_framewrite(void)
// screen regions are handled in GIF_lzw
{
- int d1 = (int)((100.0/NEWTICRATE)*(gif_frames+1));
- int d2 = (int)((100.0/NEWTICRATE)*(gif_frames));
+ int d1 = (int)((100.0f/NEWTICRATE)*(gif_frames+1));
+ int d2 = (int)((100.0f/NEWTICRATE)*(gif_frames));
UINT16 delay = d1-d2;
INT32 startline;
diff --git a/src/m_anigif.h b/src/m_anigif.h
index dbf7b10a2..6d94ecf8c 100644
--- a/src/m_anigif.h
+++ b/src/m_anigif.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2013-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 2013-2016 by Sonic Team Junior.
+// Copyright (C) 2013-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_argv.c b/src/m_argv.c
index 213577e76..117ec7833 100644
--- a/src/m_argv.c
+++ b/src/m_argv.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -25,6 +25,10 @@ INT32 myargc;
*/
char **myargv;
+/** \brief did we alloc myargv ourselves?
+*/
+boolean myargmalloc = false;
+
/** \brief founded the parm
*/
static INT32 found;
@@ -171,6 +175,7 @@ void M_FindResponseFile(void)
free(file);
I_Error("Not enough memory to read response file");
}
+ myargmalloc = true; // mark as having been allocated by us
memset(myargv, 0, sizeof (char *) * MAXARGVS);
myargv[0] = firstargv;
@@ -193,14 +198,12 @@ void M_FindResponseFile(void)
k++;
} while (k < size);
- free(file);
-
for (k = 0; k < pindex; k++)
myargv[indexinfile++] = moreargs[k];
myargc = indexinfile;
// display arguments
- CONS_Printf(M_GetText("%d command-line args:\n"), myargc);
+ CONS_Printf(M_GetText("%d command-line args:\n"), myargc-1); // -1 so @ don't actually get counted for
for (k = 1; k < myargc; k++)
CONS_Printf("%s\n", myargv[k]);
diff --git a/src/m_argv.h b/src/m_argv.h
index 46ef9a2cf..d654dcc19 100644
--- a/src/m_argv.h
+++ b/src/m_argv.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -19,6 +19,7 @@
//
extern INT32 myargc;
extern char **myargv;
+extern boolean myargmalloc;
// Returns the position of the given parameter in the arg list (0 if not found).
INT32 M_CheckParm(const char *check);
diff --git a/src/m_bbox.c b/src/m_bbox.c
index fb44b853e..0c57de43e 100644
--- a/src/m_bbox.c
+++ b/src/m_bbox.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_bbox.h b/src/m_bbox.h
index a11257b8a..db5c2b4a7 100644
--- a/src/m_bbox.h
+++ b/src/m_bbox.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 79a52fe9f..bee60087f 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_cheat.h b/src/m_cheat.h
index d50ddc119..aa9b0bfeb 100644
--- a/src/m_cheat.h
+++ b/src/m_cheat.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_cond.c b/src/m_cond.c
index e9d655694..e03542bf3 100644
--- a/src/m_cond.c
+++ b/src/m_cond.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -28,649 +28,18 @@ UINT32 unlocktriggers;
// The meat of this system lies in condition sets
conditionset_t conditionSets[MAXCONDITIONSETS];
-// Default Emblem locations
-emblem_t emblemlocations[MAXEMBLEMS] =
-{
- // GREEN FLOWER 1
- // ---
- {0, 8156, 6936, 129, 1, 'A', SKINCOLOR_BLUE, 0,
- "Go get your feet wet\n"
- "to find this, the first emblem.\n"
- "Yes, it's very deep.", 0},
- {0, 3184, 1812, 928, 1, 'B', SKINCOLOR_LAVENDER, 0,
- "There are many rings,\n"
- "but this one's not what you think.\n"
- "There lies the emblem.", 0},
- {0, 9024, 6716, 769, 1, 'C', SKINCOLOR_RED, 0,
- "Right next to a lake,\n"
- "a ledge has been constructed.\n"
- "Near there is the goal.", 0},
- {0, 2080, -384, 512, 1, 'D', SKINCOLOR_ORANGE, 0,
- "Streams come to an end\n"
- "where they can no longer fall.\n"
- "But if you went up...", 0},
- {0, -336, 2064, 195, 1, 'E', SKINCOLOR_EMERALD, 0,
- "This one's in plain sight.\n"
- "Why haven't you claimed it?\n"
- "Surely you saw it.", 0},
+// Emblem locations
+emblem_t emblemlocations[MAXEMBLEMS];
- {ET_SCORE, 0,0,0, 1, 'S', SKINCOLOR_BROWN, 125000, "", 0},
- {ET_TIME, 0,0,0, 1, 'T', SKINCOLOR_GREY, 20*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 1, 'R', SKINCOLOR_GOLD, 200, "", 0},
+// Extra emblems
+extraemblem_t extraemblems[MAXEXTRAEMBLEMS];
+// Unlockables
+unlockable_t unlockables[MAXUNLOCKABLES];
- // GREEN FLOWER 2
- // ---
- {0, 2624, -6816, 1332, 2, 'A', SKINCOLOR_BLUE, 0,
- "Near the giant lake\n"
- "lies a cave with a 1-Up.\n"
- "An emblem's there, too!", 0},
- {0, -5728, -2848, 2848, 2, 'B', SKINCOLOR_LAVENDER, 0,
- "Near the final lake,\n"
- "a higher lake falls on in.\n"
- "Three platforms await.", 0},
- {0, 3648, 6464, 2576, 2, 'C', SKINCOLOR_RED, 0,
- "Near the level's start,\n"
- "a bridge crosses a river.\n"
- "What's that river's source?", 0},
- {0, -2032,-10048, 986, 2, 'D', SKINCOLOR_ORANGE, 0,
- "Near the level's end,\n"
- "another bridge spans a lake.\n"
- "What could be under...?", 0},
- {0, -170, 491, 3821, 2, 'E', SKINCOLOR_EMERALD, 0,
- "An ivied tunnel\n"
- "has a corner that's sunlit.\n"
- "Go reach for the sky!", 0},
-
- {ET_SCORE, 0,0,0, 2, 'S', SKINCOLOR_BROWN, 150000, "", 0},
- {ET_TIME, 0,0,0, 2, 'T', SKINCOLOR_GREY, 40*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 2, 'R', SKINCOLOR_GOLD, 200, "", 0},
-
-
- // GREEN FLOWER 3
- // ---
- {ET_TIME, 0,0,0, 3, 'T', SKINCOLOR_GREY, 30*TICRATE, "", 0},
-
-
- // TECHNO HILL 1
- // ---
- {0, -5664, -5072, 2396, 4, 'A', SKINCOLOR_BLUE, 0,
- "Three pipes reside near\n"
- "where our heroes' paths split off.\n"
- "You'll have to look up!", 0},
- {0, -784,-13968, 2888, 4, 'B', SKINCOLOR_LAVENDER, 0,
- "Climbing yields great range.\n"
- "Yet, on a path for climbers,\n"
- "flying is the key.", 0},
- {0, 4160, -5824, 3776, 4, 'C', SKINCOLOR_RED, 0,
- "That's sure lots of slime.\n"
- "Say, do you ever wonder\n"
- "what's dumping it all?", 0},
- {0, 6400, -8352, 1764, 4, 'D', SKINCOLOR_ORANGE, 0,
- "Spinning through small gaps\n"
- "can slip you into a cave.\n"
- "In that cave's first stretch...", 0},
- {0, 2848, -9088, 488, 4, 'E', SKINCOLOR_EMERALD, 0,
- "The slime lake is deep,\n"
- "but reaching the floor takes height.\n"
- "Scream \"Geronimo!\"...", 0},
-
- {ET_SCORE, 0,0,0, 4, 'S', SKINCOLOR_BROWN, 75000, "", 0},
- {ET_TIME, 0,0,0, 4, 'T', SKINCOLOR_GREY, 75*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 4, 'R', SKINCOLOR_GOLD, 300, "", 0},
-
-
- // TECHNO HILL 2
- // ---
- {0,-19138, -2692, 688, 5, 'A', SKINCOLOR_BLUE, 0,
- "Near the first checkpoint,\n"
- "a bridge crosses a slime pool.\n"
- "(Sensing a pattern?)", 0},
- {0,-13120, 8062, 1248, 5, 'B', SKINCOLOR_LAVENDER, 0,
- "Behind the windows,\n"
- "near crushers, ever smashing\n"
- "a conveyor belt.", 0},
- {0, 580, 4552, 1344, 5, 'C', SKINCOLOR_RED, 0,
- "A pipe drops onto\n"
- "a half-outdoors conveyor.\n"
- "But is it empty?", 0},
- {0, 192, -8768, 24, 5, 'D', SKINCOLOR_ORANGE, 0,
- "There is a hallway\n"
- "that a button floods with slime.\n"
- "Go through it again!", 0},
- {0, -2468,-12128, 1312, 5, 'E', SKINCOLOR_EMERALD, 0,
- "Jumping on turtles\n"
- "will send you springing skyward.\n"
- "Now, do that six times...", 0},
-
- {ET_SCORE, 0,0,0, 5, 'S', SKINCOLOR_BROWN, 100000, "", 0},
- {ET_TIME, 0,0,0, 5, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 5, 'R', SKINCOLOR_GOLD, 600, "", 0},
-
-
- // TECHNO HILL 3
- // ---
- {ET_TIME, 0,0,0, 6, 'T', SKINCOLOR_GREY, 40*TICRATE, "", 0},
-
-
- // DEEP SEA 1
- // ---
- {0,-16224, -2880, 3530, 7, 'A', SKINCOLOR_BLUE, 0,
- "Climb up two maze walls.\n"
- "Break the roof, then a corner.\n"
- "There, glide, but stay dry.", 0},
- {0, -8224, 896, 1056, 7, 'B', SKINCOLOR_LAVENDER, 0,
- "Follow the left path.\n"
- "A square green button lurks deep.\n"
- "Weight it down, somehow.", 0},
- {0, 4992, -5072, 4136, 7, 'C', SKINCOLOR_RED, 0,
- "A certain path holds\n"
- "many gargoyle puzzles.\n"
- "Victors reach a \"V\".", 0},
- {0, 4576, 5168, 2660, 7, 'D', SKINCOLOR_ORANGE, 0,
- "A caved-in hallway?\n"
- "The floor falls; the path goes down.\n"
- "But those rocks looked weak...", 0},
- {0, 12576, 16096, -992, 7, 'E', SKINCOLOR_EMERALD, 0,
- "The end is quite dry.\n"
- "Some rocks dam the water in.\n"
- "Knuckles can fix that...", 0},
-
- {ET_SCORE, 0,0,0, 7, 'S', SKINCOLOR_BROWN, 75000, "", 0},
- {ET_TIME, 0,0,0, 7, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 7, 'R', SKINCOLOR_GOLD, 400, "", 0},
-
-
- // DEEP SEA 2
- // ---
- {0,-15040, 6976, 2016, 8, 'A', SKINCOLOR_BLUE, 0,
- "A waterfall lands\n"
- "near a starpost in a cave.\n"
- "It's dark up there, but...", 0},
- {0, 4288, 2912, 544, 8, 'B', SKINCOLOR_LAVENDER, 0,
- "So many blocks here!\n"
- "Take five; bathe in the fountain.\n"
- "Hmmm? A hidden path...?", 0},
- {0, -5696, 16992, 791, 8, 'C', SKINCOLOR_RED, 0,
- "An ornate dragon\n"
- "faces a secret passage.\n"
- "Knuckles! Don't get crushed!", 0},
- {0,-13344, 18688, 1034, 8, 'D', SKINCOLOR_ORANGE, 0,
- "In the current maze\n"
- "hides a dark room of columns.\n"
- "Find it, then look up.", 0},
- {0, 3104, 16192, 2408, 8, 'E', SKINCOLOR_EMERALD, 0,
- "That same dragon's eye\n"
- "hides another secret room.\n"
- "There, solve its riddle.", 0},
-
- {ET_SCORE, 0,0,0, 8, 'S', SKINCOLOR_BROWN, 50000, "", 0},
- {ET_TIME, 0,0,0, 8, 'T', SKINCOLOR_GREY, 150*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 8, 'R', SKINCOLOR_GOLD, 250, "", 0},
-
-
- // DEEP SEA 3
- // ---
- {ET_TIME, 0,0,0, 9, 'T', SKINCOLOR_GREY, 90*TICRATE, "", 0},
-
-
- // CASTLE EGGMAN 1
- // ---
- {0, -6176, -5184, -128, 10, 'A', SKINCOLOR_BLUE, 0,
- "A drain feeds the lake.\n"
- "Water rushes quickly through.\n"
- "Go against the flow.", 0},
- {0, 3648,-15296, -992, 10, 'B', SKINCOLOR_LAVENDER, 0,
- "The left starting path\n"
- "goes atop a large wood deck.\n"
- "Checked underneath yet?", 0},
- {0, 11712, 21312, 5472, 10, 'C', SKINCOLOR_RED, 0,
- "At last, the castle!\n"
- "Hold up! Don't just barge right in!\n"
- "What's the facade hold...?", 0},
- {0, 20224, 13344, 3104, 10, 'D', SKINCOLOR_ORANGE, 0,
- "The final approach!\n"
- "A tower holds the emblem\n"
- "near a ring arrow.", 0},
- {0, 9472, -5890, 710, 10, 'E', SKINCOLOR_EMERALD, 0,
- "The right starting path\n"
- "hides this near a canopy,\n"
- "high, where two trees meet.", 0},
-
- {ET_SCORE, 0,0,0, 10, 'S', SKINCOLOR_BROWN, 50000, "", 0},
- {ET_TIME, 0,0,0, 10, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 10, 'R', SKINCOLOR_GOLD, 200, "", 0},
-
-
- // CASTLE EGGMAN 2
- // ---
- {0, 832,-15168, 7808, 11, 'A', SKINCOLOR_BLUE, 0,
- "Find a trick bookcase\n"
- "that hides a darkened hallway.\n"
- "There, climb a tower.", 0},
- {0,-18460,-22180, 2416, 11, 'B', SKINCOLOR_LAVENDER, 0,
- "Down in the dungeon,\n"
- "a cracked wall hides secret paths.\n"
- "Echidnas only!", 0},
- {0, -6144,-11792, 3232, 11, 'C', SKINCOLOR_RED, 0,
- "A room you can flood!\n"
- "A brown grate's near its exit.\n"
- "Knuckles can break it...", 0},
- {0, 4608, -7024, 4256, 11, 'D', SKINCOLOR_ORANGE, 0,
- "Some of these bookshelves\n"
- "are not flush against the walls.\n"
- "Wonder why that is?", 0},
- {0, 12708,-13536, 4768, 11, 'E', SKINCOLOR_EMERALD, 0,
- "The ending's towers\n"
- "are hiding a small alcove.\n"
- "Check around outside.", 0},
-
- {ET_SCORE, 0,0,0, 11, 'S', SKINCOLOR_BROWN, 400000, "", 0},
- {ET_TIME, 0,0,0, 11, 'T', SKINCOLOR_GREY, 210*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 11, 'R', SKINCOLOR_GOLD, 600, "", 0},
-
-
- // CASTLE EGGMAN 3
- // ---
- {ET_TIME, 0,0,0, 12, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0},
-
-
- // ARID CANYON 1
- // ---
- {0, 3488, 2208, 3072, 13, 'A', SKINCOLOR_BLUE, 0,
- "A rather large gap\n"
- "must be crossed by way of tram.\n"
- "At its end, jump left.", 0},
- {0, -7552, 10464, 4094, 13, 'B', SKINCOLOR_LAVENDER, 0,
- "Crushers that go up!\n"
- "Mind your step; if they're triggered,\n"
- "they'll block this emblem.", 0},
- {0,-12093, 14575, 5752, 13, 'C', SKINCOLOR_RED, 0,
- "There's an oil lake\n"
- "that you can sink deep into.\n"
- "Drain it, and explore.", 0},
- {0, 512, -7136, 4640, 13, 'D', SKINCOLOR_ORANGE, 0,
- "Not far from the start,\n"
- "if you climb toward the sky,\n"
- "the cliffs hide something.", 0},
- {0, 12504, 6848, 3424, 13, 'E', SKINCOLOR_EMERALD, 0,
- "Right by the exit,\n"
- "an emblem lies on a cliff.\n"
- "Ride ropes to reach it.", 0},
-
- {ET_SCORE, 0,0,0, 13, 'S', SKINCOLOR_BROWN, 50000, "", 0},
- {ET_TIME, 0,0,0, 13, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 13, 'R', SKINCOLOR_GOLD, 300, "", 0},
-
-
- // RED VOLCANO 1
- // ---
- {0,-13184, 11424, 3080, 16, 'A', SKINCOLOR_BLUE, 0,
- "Look around the room,\n"
- "just before you clear the stage;\n"
- "something's hidden there!", 0},
- {0, -2816, 3120, 3044, 16, 'B', SKINCOLOR_LAVENDER, 0,
- "Ever look upwards\n"
- "when you're traversing across\n"
- "collapsing platforms?", 0},
- {0, 6720, 6784, 1452, 16, 'C', SKINCOLOR_RED, 0,
- "Check out a corner\n"
- "of a lake of magma near\n"
- "spinning jets of flame.", 0},
- {0, -5504, 9824, 800, 16, 'D', SKINCOLOR_ORANGE, 0,
- "Where once a bridge stood,\n"
- "now magma falls from above.\n"
- "The bridge dropped something...", 0},
- {0, 8287,-11043, 1328, 16, 'E', SKINCOLOR_EMERALD, 0,
- "A lake of magma\n"
- "ebbs and flows unendingly.\n"
- "Wait for its nadir.", 0},
-
- {ET_SCORE, 0,0,0, 16, 'S', SKINCOLOR_BROWN, 30000, "", 0},
- {ET_TIME, 0,0,0, 16, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 16, 'R', SKINCOLOR_GOLD, 100, "", 0},
-
-
- // EGG ROCK 1
- // ---
- {0,-10976, -7328, 1584, 22, 'A', SKINCOLOR_BLUE, 0,
- "Vanishing platforms,\n"
- "then collapsing ones herald\n"
- "a last-second jump.", 0},
- {0, -6592,-11200, 2208, 22, 'B', SKINCOLOR_LAVENDER, 0,
- "What is this red stuff?\n"
- "You can't breathe it in, but look!\n"
- "It can't reach up there...", 0},
- {0, 6816, 832, 936, 22, 'C', SKINCOLOR_RED, 0,
- "The team's paths diverge.\n"
- "Should Tails run the crusher path?\n"
- "No! Fly outside it!", 0},
- {0, 6942, -8902, 2080, 22, 'D', SKINCOLOR_ORANGE, 0,
- "Don't jump too high here!\n"
- "No conveyor will catch you;\n"
- "you'd fall to your death.", 0},
- {0, -6432, -6192, 584, 22, 'E', SKINCOLOR_EMERALD, 0,
- "Conveyors! Magma!\n"
- "What an intense room this is!\n"
- "But, what brought you here?", 0},
-
- {ET_SCORE, 0,0,0, 22, 'S', SKINCOLOR_BROWN, 25000, "", 0},
- {ET_TIME, 0,0,0, 22, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 22, 'R', SKINCOLOR_GOLD, 150, "", 0},
-
-
- // EGG ROCK 2
- // ---
- {0, -6672, 7792, 352, 23, 'A', SKINCOLOR_BLUE, 0,
- "Walk on the ceiling;\n"
- "resist the urge to flip back!\n"
- "Find the cyan path...", 0},
- {0,-12256, 15136, -288, 23, 'B', SKINCOLOR_LAVENDER, 0,
- "X marks the spot? Nope!\n"
- "Try standing further away\n"
- "when the timer flips.", 0},
- {0, 1536, 16224, 1144, 23, 'C', SKINCOLOR_RED, 0,
- "There is more than one\n"
- "elevator inside the\n"
- "elevator shaft...", 0},
- {0,-15968, 14192, 3152, 23, 'D', SKINCOLOR_ORANGE, 0,
- "Gears with missing teeth\n"
- "can hide a clever secret!\n"
- "Think Green Hill Zone boss.", 0},
- {0, 1920, 20608, 1064, 23, 'E', SKINCOLOR_EMERALD, 0,
- "Just before you reach\n"
- "the defective cargo bay,\n"
- "fly under a bridge.", 0},
-
- {ET_SCORE, 0,0,0, 23, 'S', SKINCOLOR_BROWN, 60000, "", 0},
- {ET_TIME, 0,0,0, 23, 'T', SKINCOLOR_GREY, 300*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 23, 'R', SKINCOLOR_GOLD, 250, "", 0},
-
-
- // EGG ROCK 3
- // ---
-/* Just in case, I'll leave these here in the source.
- {0, 848, -3584, 592, 24, 'A', SKINCOLOR_BLUE, 0,
- "[PH] Hiding at the end of the first hallway.", 0},
- {0,-10368, -2816, 144, 24, 'B', SKINCOLOR_LAVENDER, 0,
- "Directions are meaningless.", 0},
- {0, -8160, -5952, 560, 24, 'C', SKINCOLOR_RED, 0,
- "[PH] In the ceiling of the conveyor belt + laser hallway.", 0},
- {0,-13728,-13728, 1552, 24, 'D', SKINCOLOR_ORANGE, 0,
- "[PH] On top of the platform with rows of spikes in reverse gravity.", 0},
- {0,-14944, 768, 1232, 24, 'E', SKINCOLOR_EMERALD, 0,
- "Follow the leader.", 0},
-*/
-
- {ET_SCORE, 0,0,0, 24, 'S', SKINCOLOR_BROWN, 14000, "", 0},
- {ET_TIME, 0,0,0, 24, 'T', SKINCOLOR_GREY, 210*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 24, 'R', SKINCOLOR_GOLD, 100, "", 0},
-
-
- // EGG ROCK CORE
- // ---
- {ET_TIME, 0,0,0, 25, 'T', SKINCOLOR_GREY, 100*TICRATE, "", 0},
-
-
- // PIPE TOWERS
- // ---
- {0, 3182, 5040, 3008, 30, 'A', SKINCOLOR_BLUE, 0,
- "A pipe in the roof\n"
- "eternally drops water.\n"
- "Something's stuck up there.", 0},
- {0, -2400, 5984, 2752, 30, 'B', SKINCOLOR_LAVENDER, 0,
- "Pushing a red switch\n"
- "raises the water level;\n"
- "from there, can't miss it.", 0},
- {0, 6112, 7008, 4032, 30, 'C', SKINCOLOR_RED, 0,
- "A high-up passage\n"
- "hides near the second checkpoint.\n"
- "Climb in; then, climb more.", 0},
- {0, 11424, -4832, 1376, 30, 'D', SKINCOLOR_ORANGE, 0,
- "The underground room\n"
- "with platforms that fall and rise\n"
- "only LOOKS empty...", 0},
- {0 , 4960, -6112, 1312, 30, 'E', SKINCOLOR_EMERALD, 0,
- "This one's straightforward.\n"
- "What comes to mind when I say:\n"
- "\"WELCOME TO WARP ZONE!\"?", 0},
-
- {ET_SCORE, 0,0,0, 30, 'S', SKINCOLOR_BROWN, 75000, "", 0},
- {ET_TIME, 0,0,0, 30, 'T', SKINCOLOR_GREY, 100*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 30, 'R', SKINCOLOR_GOLD, 300, "", 0},
-
-
- // AERIAL GARDEN
- // ---
- {0, 10176,-14304, 1796, 40, 'A', SKINCOLOR_BLUE, 0,
- "A central tower,\n"
- "one with many waterfalls,\n"
- "hides a secret room.", 0},
- {0, 480, 17696, 6496, 40, 'B', SKINCOLOR_LAVENDER, 0,
- "Hidden off the path\n"
- "lies a skyscraping tower.\n"
- "A lake's at the top.", 0},
- {0, -8896, 13248, 3362, 40, 'C', SKINCOLOR_RED, 0,
- "Find all four buttons\n"
- "that sink when you stand on them.\n"
- "They'll open a door...", 0},
- {0, -8896, -9952, 2480, 40, 'D', SKINCOLOR_ORANGE, 0,
- "Much like the last one,\n"
- "you need to find some switches.\n"
- "Only two, this time.", 0},
- {0, 13184, 18880, 6672, 40, 'E', SKINCOLOR_EMERALD, 0,
- "The inner sanctum!\n"
- "Teleport to its switches;\n"
- "then, check near the goal.", 0},
-
- {ET_SCORE, 0,0,0, 40, 'S', SKINCOLOR_BROWN, 300000, "", 0},
- {ET_TIME, 0,0,0, 40, 'T', SKINCOLOR_GREY, 240*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 40, 'R', SKINCOLOR_GOLD, 1200, "", 0},
-
-
- // AZURE TEMPLE
- // ---
- {0, -2400, 7552, 1120, 41, 'A', SKINCOLOR_BLUE, 0,
- "For those who can swim,\n"
- "a long tunnel hides rewards.\n"
- "Do mind the Buzzes!", 0},
- {0, -64, 14016, 2072, 41, 'B', SKINCOLOR_LAVENDER, 0,
- "So many skylights!\n"
- "A markedly large one hides\n"
- "behind a starpost...", 0},
- {0, 2976, 13920, -32, 41, 'C', SKINCOLOR_RED, 0,
- "When you reach gauntlets\n"
- "of diagonal fire,\n"
- "check out the corners.", 0},
- {0, 2176, 22592, 1376, 41, 'D', SKINCOLOR_ORANGE, 0,
- "A room of currents;\n"
- "most of them are marked by spikes.\n"
- "This one? A corner.", 0},
- {0, -4128, 21344, 1120, 41, 'E', SKINCOLOR_EMERALD, 0,
- "The only way to hit\n"
- "all those gems at once is with\n"
- "a radial blast.", 0},
-
- {ET_SCORE, 0,0,0, 41, 'S', SKINCOLOR_BROWN, 425000, "", 0},
- {ET_TIME, 0,0,0, 41, 'T', SKINCOLOR_GREY, 240*TICRATE, "", 0},
- {ET_RINGS, 0,0,0, 41, 'R', SKINCOLOR_GOLD, 300, "", 0},
-
-
- // FLORAL FIELD
- // ---
- {0, 5394, -996, 160, 50, 'N', SKINCOLOR_RUST, 0, "", 0},
- {ET_NGRADE, 0,0,0, 50, 'Q', SKINCOLOR_CYAN, GRADE_A, "", 0},
- {ET_NTIME, 0,0,0, 50, 'T', SKINCOLOR_GREY, 40*TICRATE, "", 0},
-
-
- // TOXIC PLATEAU
- // ---
- {0, 780, -1664, 32, 51, 'N', SKINCOLOR_RUST, 0, "", 0},
- {ET_NGRADE, 0,0,0, 51, 'Q', SKINCOLOR_CYAN, GRADE_A, "", 0},
- {ET_NTIME, 0,0,0, 51, 'T', SKINCOLOR_GREY, 50*TICRATE, "", 0},
-
-
- // FLOODED COVE
- // ---
- {0, 1824, -1888, 2448, 52, 'N', SKINCOLOR_RUST, 0, "", 0},
- {ET_NGRADE, 0,0,0, 52, 'Q', SKINCOLOR_CYAN, GRADE_A, "", 0},
- {ET_NTIME, 0,0,0, 52, 'T', SKINCOLOR_GREY, 90*TICRATE, "", 0},
-
-
- // CAVERN FORTRESS
- // ---
- {0, -3089, -431, 1328, 53, 'N', SKINCOLOR_RUST, 0, "", 0},
- {ET_NGRADE, 0,0,0, 53, 'Q', SKINCOLOR_CYAN, GRADE_A, "", 0},
- {ET_NTIME, 0,0,0, 53, 'T', SKINCOLOR_GREY, 75*TICRATE, "", 0},
-
-
- // DUSTY WASTELAND
- // ---
- {0, 957, 924, 2956, 54, 'N', SKINCOLOR_RUST, 0, "", 0},
- {ET_NGRADE, 0,0,0, 54, 'Q', SKINCOLOR_CYAN, GRADE_A, "", 0},
- {ET_NTIME, 0,0,0, 54, 'T', SKINCOLOR_GREY, 65*TICRATE, "", 0},
-
-
- // MAGMA CAVES
- // ---
- {0, -2752, 3104, 1800, 55, 'N', SKINCOLOR_RUST, 0, "", 0},
- {ET_NGRADE, 0,0,0, 55, 'Q', SKINCOLOR_CYAN, GRADE_A, "", 0},
- {ET_NTIME, 0,0,0, 55, 'T', SKINCOLOR_GREY, 80*TICRATE, "", 0},
-
-
- // EGG SATELLITE
- // ---
- {0, 5334, -609, 3426, 56, 'N', SKINCOLOR_RUST, 0, "", 0},
- {ET_NGRADE, 0,0,0, 56, 'Q', SKINCOLOR_CYAN, GRADE_A, "", 0},
- {ET_NTIME, 0,0,0, 56, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0},
-
-
- // BLACK HOLE
- // ---
- {0, 2108, 3776, 32, 57, 'N', SKINCOLOR_RUST, 0, "", 0},
- {ET_NGRADE, 0,0,0, 57, 'Q', SKINCOLOR_CYAN, GRADE_A, "", 0},
- {ET_NTIME, 0,0,0, 57, 'T', SKINCOLOR_GREY, 150*TICRATE, "", 0},
-
-
- // SPRING HILL
- // ---
- {0, -1840, -1024, 1644, 58, 'N', SKINCOLOR_RUST, 0, "", 0},
- {ET_NGRADE, 0,0,0, 58, 'Q', SKINCOLOR_CYAN, GRADE_A, "", 0},
- {ET_NTIME, 0,0,0, 58, 'T', SKINCOLOR_GREY, 60*TICRATE, "", 0},
-};
-
-// Default Extra Emblems
-extraemblem_t extraemblems[MAXEXTRAEMBLEMS] =
-{
- {"Game Complete", "Complete 1P Mode", 10, 'X', SKINCOLOR_BLUE, 0},
- {"All Emeralds", "Complete 1P Mode with all Emeralds", 11, 'V', SKINCOLOR_GREY, 0},
- {"Perfect Bonus", "Perfect Bonus on a non-secret stage", 30, 'P', SKINCOLOR_GOLD, 0},
- {"PLACEHOLDER", "PLACEHOLDER", 0, 'O', SKINCOLOR_RUST, 0},
- {"NiGHTS Mastery", "Show your mastery of NiGHTS!", 22, 'W', SKINCOLOR_CYAN, 0},
-};
-
-// Default Unlockables
-unlockable_t unlockables[MAXUNLOCKABLES] =
-{
- // Name, Objective, Menu Height, ConditionSet, Unlock Type, Variable, NoCecho, NoChecklist
- /* 01 */ {"Record Attack", "/", 0, 1, SECRET_RECORDATTACK, 0, true, true, 0},
- /* 02 */ {"NiGHTS Mode", "/", 0, 2, SECRET_NIGHTSMODE, 0, true, true, 0},
-
- /* 03 */ {"Play Credits", "/", 30, 10, SECRET_CREDITS, 0, true, true, 0},
- /* 04 */ {"Sound Test", "/", 40, 10, SECRET_SOUNDTEST, 0, false, false, 0},
-
- /* 05 */ {"EXTRA LEVELS", "/", 58, 0, SECRET_HEADER, 0, true, true, 0},
-
- /* 06 */ {"Aerial Garden Zone", "/", 70, 11, SECRET_WARP, 40, false, false, 0},
- /* 07 */ {"Azure Temple Zone", "/", 80, 20, SECRET_WARP, 41, false, false, 0},
-
- /* 08 */ {"BONUS LEVELS", "/", 98, 0, SECRET_HEADER, 0, true, true, 0},
-
- /* 09 */ {"PLACEHOLDER", "/", 0, 0, SECRET_NONE, 0, true, true, 0},
- /* 10 */ {"Mario Koopa Blast", "/", 110, 42, SECRET_WARP, 30, false, false, 0},
- /* 11 */ {"PLACEHOLDER", "/", 0, 0, SECRET_NONE, 0, true, true, 0},
-
- /* 12 */ {"Spring Hill Zone", "/", 0, 44, SECRET_NONE, 0, false, false, 0},
- /* 13 */ {"Black Hole", "Get grade A in all Special Stages", 0, 50, SECRET_NONE, 0, false, true, 0},
-
- /* 14 */ {"Emblem Hints", "/", 0, 41, SECRET_EMBLEMHINTS, 0, false, true, 0},
- /* 15 */ {"Emblem Radar", "/", 0, 43, SECRET_ITEMFINDER, 0, false, true, 0},
-
- /* 16 */ {"Pandora's Box", "/", 0, 45, SECRET_PANDORA, 0, false, false, 0},
- /* 17 */ {"Level Select", "/", 20, 45, SECRET_LEVELSELECT, 1, false, true, 0},
-};
-
-// Default number of emblems and extra emblems
-INT32 numemblems = 155;
-INT32 numextraemblems = 5;
-
-// DEFAULT CONDITION SETS FOR SRB2 2.1:
-void M_SetupDefaultConditionSets(void)
-{
- memset(conditionSets, 0, sizeof(conditionSets));
-
- // -- 1: Complete GFZ1
- M_AddRawCondition(1, 1, UC_MAPBEATEN, 1, 0, 0);
-
- // -- 2: Complete SS1
- M_AddRawCondition(2, 1, UC_MAPBEATEN, 50, 0, 0);
-
- // -- 10: Complete the game
- M_AddRawCondition(10, 1, UC_GAMECLEAR, 1, 0, 0);
-
- // -- 11: Complete the game with all emeralds
- M_AddRawCondition(11, 1, UC_ALLEMERALDS, 1, 0, 0);
-
- // -- 20: Beat AGZ
- M_AddRawCondition(20, 1, UC_MAPBEATEN, 40, 0, 0);
-
- // -- 22: Beat Black Hole
- M_AddRawCondition(22, 1, UC_MAPBEATEN, 57, 0, 0);
-
- // -- 30: Perfect Bonus
- M_AddRawCondition(30, 1, UC_MAPPERFECT, 1, 0, 0);
- M_AddRawCondition(30, 2, UC_MAPPERFECT, 2, 0, 0);
- M_AddRawCondition(30, 3, UC_MAPPERFECT, 4, 0, 0);
- M_AddRawCondition(30, 4, UC_MAPPERFECT, 5, 0, 0);
- M_AddRawCondition(30, 5, UC_MAPPERFECT, 7, 0, 0);
- M_AddRawCondition(30, 6, UC_MAPPERFECT, 8, 0, 0);
- M_AddRawCondition(30, 7, UC_MAPPERFECT, 10, 0, 0);
- M_AddRawCondition(30, 8, UC_MAPPERFECT, 11, 0, 0);
- M_AddRawCondition(30, 9, UC_MAPPERFECT, 13, 0, 0);
- M_AddRawCondition(30, 10, UC_MAPPERFECT, 16, 0, 0);
- M_AddRawCondition(30, 11, UC_MAPPERFECT, 22, 0, 0);
- M_AddRawCondition(30, 12, UC_MAPPERFECT, 23, 0, 0);
- M_AddRawCondition(30, 13, UC_MAPPERFECT, 24, 0, 0);
- M_AddRawCondition(30, 14, UC_MAPPERFECT, 40, 0, 0);
- M_AddRawCondition(30, 15, UC_MAPPERFECT, 41, 0, 0);
-
- // -- 40: Find 20 emblems
- M_AddRawCondition(40, 1, UC_TOTALEMBLEMS, 20, 0, 0);
-
- // -- 41: Find 40 emblems
- M_AddRawCondition(41, 1, UC_TOTALEMBLEMS, 40, 0, 0);
-
- // -- 42: Find 60 emblems
- M_AddRawCondition(42, 1, UC_TOTALEMBLEMS, 60, 0, 0);
-
- // -- 43: Find 80 emblems
- M_AddRawCondition(43, 1, UC_TOTALEMBLEMS, 80, 0, 0);
-
- // -- 44: Find 100 emblems
- M_AddRawCondition(44, 1, UC_TOTALEMBLEMS, 100, 0, 0);
-
- // -- 45: Find 160 (all) emblems
- M_AddRawCondition(45, 1, UC_TOTALEMBLEMS, 160, 0, 0);
-
- // -- 50: A rank all NiGHTS special stages
- M_AddRawCondition(50, 1, UC_NIGHTSGRADE, GRADE_A, 50, 0);
- M_AddRawCondition(50, 1, UC_NIGHTSGRADE, GRADE_A, 51, 0);
- M_AddRawCondition(50, 1, UC_NIGHTSGRADE, GRADE_A, 52, 0);
- M_AddRawCondition(50, 1, UC_NIGHTSGRADE, GRADE_A, 53, 0);
- M_AddRawCondition(50, 1, UC_NIGHTSGRADE, GRADE_A, 54, 0);
- M_AddRawCondition(50, 1, UC_NIGHTSGRADE, GRADE_A, 55, 0);
- M_AddRawCondition(50, 1, UC_NIGHTSGRADE, GRADE_A, 56, 0);
-}
+// Number of emblems and extra emblems
+INT32 numemblems = 0;
+INT32 numextraemblems = 0;
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2)
{
diff --git a/src/m_cond.h b/src/m_cond.h
index 00f633a8d..f82e49372 100644
--- a/src/m_cond.h
+++ b/src/m_cond.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2012-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 2012-2016 by Sonic Team Junior.
+// Copyright (C) 2012-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -87,9 +87,7 @@ typedef struct
typedef struct
{
UINT8 type; ///< Emblem type
- INT16 x; ///< X coordinate.
- INT16 y; ///< Y coordinate.
- INT16 z; ///< Z coordinate.
+ INT16 tag; ///< Tag of emblem mapthing
INT16 level; ///< Level on which this emblem can be found.
UINT8 sprite; ///< emblem sprite to use, 0 - 25
UINT8 color; ///< skincolor to use
@@ -150,8 +148,7 @@ extern INT32 numextraemblems;
extern UINT32 unlocktriggers;
-// Condition Set Setup
-void M_SetupDefaultConditionSets(void);
+// Condition set setup
void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2);
// Clearing secrets
diff --git a/src/m_dllist.h b/src/m_dllist.h
index f19659cca..18f351144 100644
--- a/src/m_dllist.h
+++ b/src/m_dllist.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2005 by James Haley
-// Copyright (C) 2005-2016 by Sonic Team Junior.
+// Copyright (C) 2005-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_fixed.c b/src/m_fixed.c
index 014457386..d45bb70bf 100644
--- a/src/m_fixed.c
+++ b/src/m_fixed.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -432,7 +432,7 @@ vector3_t *FV3_Cross(const vector3_t *a_1, const vector3_t *a_2, vector3_t *a_o)
//
vector3_t *FV3_ClosestPointOnLine(const vector3_t *Line, const vector3_t *p, vector3_t *out)
{
- // Determine t (the length of the vector from ‘Line[0]’ to ‘p’)
+ // Determine t (the length of the vector from �Line[0]� to �p�)
vector3_t c, V;
fixed_t t, d = 0;
FV3_SubEx(p, &Line[0], &c);
@@ -442,7 +442,7 @@ vector3_t *FV3_ClosestPointOnLine(const vector3_t *Line, const vector3_t *p, vec
d = FV3_Distance(&Line[0], &Line[1]);
t = FV3_Dot(&V, &c);
- // Check to see if ‘t’ is beyond the extents of the line segment
+ // Check to see if �t� is beyond the extents of the line segment
if (t < 0)
{
return FV3_Copy(out, &Line[0]);
@@ -452,7 +452,7 @@ vector3_t *FV3_ClosestPointOnLine(const vector3_t *Line, const vector3_t *p, vec
return FV3_Copy(out, &Line[1]);
}
- // Return the point between ‘Line[0]’ and ‘Line[1]’
+ // Return the point between �Line[0]� and �Line[1]�
FV3_Mul(&V, t);
return FV3_AddEx(&Line[0], &V, out);
diff --git a/src/m_fixed.h b/src/m_fixed.h
index 8050324db..d8e722b13 100644
--- a/src/m_fixed.h
+++ b/src/m_fixed.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -70,7 +70,7 @@ typedef INT32 fixed_t;
:"=a" (ret) // eax is always the result and the first operand (%0,%1)
:"0" (a), "r" (b) // and %2 is what we use imull on with what in %1
, "I" (FRACBITS) // %3 holds FRACBITS (normally 16)
- :"%cc", "%edx" // edx and condition codes clobbered
+ :"cc", "%edx" // edx and condition codes clobbered
);
return ret;
}
diff --git a/src/m_menu.c b/src/m_menu.c
index 7526e17a2..4f78d0adc 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -3,7 +3,7 @@
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2011-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -58,6 +58,8 @@
#include "i_sound.h"
#include "fastcmp.h"
+#include "i_joy.h" // for joystick menu controls
+
// Condition Sets
#include "m_cond.h"
@@ -115,45 +117,7 @@ typedef enum
const char *quitmsg[NUM_QUITMESSAGES];
// Stuff for customizing the player select screen Tails 09-22-2003
-// A rare case.
-// External files modify this menu, so we can't call it static.
-// And I'm too lazy to go through and rename it everywhere. ARRGH!
-description_t description[32] =
-{
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0},
- {false, "???", "", "", 0, 0}
-};
-
+description_t description[MAXSKINS];
INT16 char_on = -1, startchar = 1;
static char *char_notes = NULL;
static fixed_t char_scroll = 0;
@@ -172,7 +136,7 @@ typedef enum
levellist_mode_t levellistmode = LLM_CREATESERVER;
UINT8 maplistoption = 0;
-static char joystickInfo[8][25];
+static char joystickInfo[8][29];
#ifndef NONET
static UINT32 serverlistpage;
#endif
@@ -315,7 +279,6 @@ menu_t OP_MPControlsDef, OP_CameraControlsDef, OP_MiscControlsDef;
menu_t OP_P1ControlsDef, OP_P2ControlsDef, OP_MouseOptionsDef;
menu_t OP_Mouse2OptionsDef, OP_Joystick1Def, OP_Joystick2Def;
static void M_VideoModeMenu(INT32 choice);
-static void M_SoundMenu(INT32 choice);
static void M_Setup1PControlsMenu(INT32 choice);
static void M_Setup2PControlsMenu(INT32 choice);
static void M_Setup1PJoystickMenu(INT32 choice);
@@ -329,9 +292,6 @@ menu_t OP_VideoOptionsDef, OP_VideoModeDef, OP_ColorOptionsDef;
menu_t OP_OpenGLOptionsDef, OP_OpenGLFogDef, OP_OpenGLColorDef;
#endif
menu_t OP_SoundOptionsDef;
-static void M_ToggleSFX(INT32 choice);
-static void M_ToggleDigital(INT32 choice);
-static void M_ToggleMIDI(INT32 choice);
//Misc
menu_t OP_DataOptionsDef, OP_ScreenshotOptionsDef, OP_EraseDataDef;
@@ -342,7 +302,7 @@ static void M_EraseData(INT32 choice);
static void M_Addons(INT32 choice);
static void M_AddonsOptions(INT32 choice);
-static patch_t *addonsp[NUM_EXT+6];
+static patch_t *addonsp[NUM_EXT+5];
#define numaddonsshown 4
@@ -369,7 +329,6 @@ static void M_DrawControl(void);
static void M_DrawMainVideoMenu(void);
static void M_DrawVideoMode(void);
static void M_DrawColorMenu(void);
-static void M_DrawSoundMenu(void);
static void M_DrawScreenshotMenu(void);
static void M_DrawMonitorToggles(void);
#ifdef HWRENDER
@@ -1027,7 +986,7 @@ static menuitem_t OP_MainMenu[] =
{IT_CVAR | IT_STRING, NULL, "Controls per key", &cv_controlperkey, 30},
{IT_SUBMENU | IT_STRING, NULL, "Video Options...", &OP_VideoOptionsDef, 50},
- {IT_CALL | IT_STRING, NULL, "Sound Options...", M_SoundMenu, 60},
+ {IT_SUBMENU | IT_STRING, NULL, "Sound Options...", &OP_SoundOptionsDef, 60},
{IT_CALL | IT_STRING, NULL, "Server Options...", M_ServerOptions, 80},
@@ -1088,7 +1047,11 @@ static menuitem_t OP_ChangeControlsMenu[] =
{IT_SPACE, NULL, NULL, NULL, 0}, // padding
{IT_CALL | IT_STRING2, NULL, "Game Status",
M_ChangeControl, gc_scores },
- {IT_CALL | IT_STRING2, NULL, "Pause / Run Retry", M_ChangeControl, gc_pause },
+ {IT_CALL | IT_STRING2, NULL, "Pause / Run Retry", M_ChangeControl, gc_pause },
+ {IT_CALL | IT_STRING2, NULL, "Screenshot", M_ChangeControl, gc_screenshot },
+ {IT_CALL | IT_STRING2, NULL, "Toggle GIF Recording", M_ChangeControl, gc_recordgif },
+ {IT_CALL | IT_STRING2, NULL, "Open/Close Menu (ESC)", M_ChangeControl, gc_systemmenu },
+ {IT_CALL | IT_STRING2, NULL, "Change Viewpoint", M_ChangeControl, gc_viewpoint },
{IT_CALL | IT_STRING2, NULL, "Console", M_ChangeControl, gc_console },
{IT_HEADER, NULL, "Multiplayer", NULL, 0},
{IT_SPACE, NULL, NULL, NULL, 0}, // padding
@@ -1126,6 +1089,9 @@ static menuitem_t OP_Joystick1Menu[] =
{IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis , 80},
{IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis , 90},
{IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis ,100},
+
+ {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook, 120},
+ {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook, 130},
};
static menuitem_t OP_Joystick2Menu[] =
@@ -1139,6 +1105,9 @@ static menuitem_t OP_Joystick2Menu[] =
{IT_STRING | IT_CVAR, NULL, "Spin Axis" , &cv_spinaxis2 , 80},
{IT_STRING | IT_CVAR, NULL, "Fire Axis" , &cv_fireaxis2 , 90},
{IT_STRING | IT_CVAR, NULL, "Fire Normal Axis" , &cv_firenaxis2 ,100},
+
+ {IT_STRING | IT_CVAR, NULL, "First-Person Vert-Look", &cv_alwaysfreelook2,120},
+ {IT_STRING | IT_CVAR, NULL, "Third-Person Vert-Look", &cv_chasefreelook2, 130},
};
static menuitem_t OP_JoystickSetMenu[] =
@@ -1155,13 +1124,14 @@ static menuitem_t OP_MouseOptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Use Mouse", &cv_usemouse, 10},
- {IT_STRING | IT_CVAR, NULL, "Always Mouselook", &cv_alwaysfreelook, 30},
- {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove, 40},
- {IT_STRING | IT_CVAR, NULL, "Invert Y Axis", &cv_invertmouse, 50},
+ {IT_STRING | IT_CVAR, NULL, "First-Person MouseLook", &cv_alwaysfreelook, 30},
+ {IT_STRING | IT_CVAR, NULL, "Third-Person MouseLook", &cv_chasefreelook, 40},
+ {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove, 50},
+ {IT_STRING | IT_CVAR, NULL, "Invert Y Axis", &cv_invertmouse, 60},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
- NULL, "Mouse X Sensitivity", &cv_mousesens, 60},
+ NULL, "Mouse X Sensitivity", &cv_mousesens, 70},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
- NULL, "Mouse Y Sensitivity", &cv_mouseysens, 70},
+ NULL, "Mouse Y Sensitivity", &cv_mouseysens, 80},
};
static menuitem_t OP_Mouse2OptionsMenu[] =
@@ -1169,13 +1139,14 @@ static menuitem_t OP_Mouse2OptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Use Mouse 2", &cv_usemouse2, 10},
{IT_STRING | IT_CVAR, NULL, "Second Mouse Serial Port",
&cv_mouse2port, 20},
- {IT_STRING | IT_CVAR, NULL, "Always Mouselook", &cv_alwaysfreelook2, 30},
- {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove2, 40},
- {IT_STRING | IT_CVAR, NULL, "Invert Y Axis", &cv_invertmouse2, 50},
+ {IT_STRING | IT_CVAR, NULL, "First-Person MouseLook", &cv_alwaysfreelook2, 30},
+ {IT_STRING | IT_CVAR, NULL, "Third-Person MouseLook", &cv_chasefreelook2, 40},
+ {IT_STRING | IT_CVAR, NULL, "Mouse Move", &cv_mousemove2, 50},
+ {IT_STRING | IT_CVAR, NULL, "Invert Y Axis", &cv_invertmouse2, 60},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
- NULL, "Mouse X Sensitivity", &cv_mousesens2, 60},
+ NULL, "Mouse X Sensitivity", &cv_mousesens2, 70},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
- NULL, "Mouse Y Sensitivity", &cv_mouseysens2, 70},
+ NULL, "Mouse Y Sensitivity", &cv_mouseysens2, 80},
};
static menuitem_t OP_VideoOptionsMenu[] =
@@ -1211,15 +1182,24 @@ static menuitem_t OP_VideoOptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Background color", &cons_backcolor, 96},
{IT_STRING | IT_CVAR, NULL, "Text Size", &cv_constextsize, 101},
- {IT_HEADER, NULL, "Level", NULL, 110},
- {IT_STRING | IT_CVAR, NULL, "Draw Distance", &cv_drawdist, 116},
- {IT_STRING | IT_CVAR, NULL, "NiGHTS Draw Dist.", &cv_drawdist_nights, 121},
- {IT_STRING | IT_CVAR, NULL, "Weather Draw Dist.", &cv_drawdist_precip, 126},
- {IT_STRING | IT_CVAR, NULL, "Weather Density", &cv_precipdensity, 131},
+ {IT_HEADER, NULL, "Chat", NULL, 110},
+ {IT_STRING | IT_CVAR, NULL, "Chat Mode", &cv_consolechat, 116},
+ {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Width", &cv_chatwidth, 121},
+ {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Height", &cv_chatheight, 126},
+ {IT_STRING | IT_CVAR, NULL, "Message Fadeout Time", &cv_chattime, 131},
+ {IT_STRING | IT_CVAR, NULL, "Chat Notifications", &cv_chatnotifications, 136},
+ {IT_STRING | IT_CVAR, NULL, "Spam Protection", &cv_chatspamprotection, 141},
+ {IT_STRING | IT_CVAR, NULL, "Chat background tint", &cv_chatbacktint, 146},
- {IT_HEADER, NULL, "Diagnostic", NULL, 140},
- {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 146},
- {IT_STRING | IT_CVAR, NULL, "Clear Before Redraw", &cv_homremoval, 151},
+ {IT_HEADER, NULL, "Level", NULL, 155},
+ {IT_STRING | IT_CVAR, NULL, "Draw Distance", &cv_drawdist, 161},
+ {IT_STRING | IT_CVAR, NULL, "NiGHTS Draw Dist.", &cv_drawdist_nights, 166},
+ {IT_STRING | IT_CVAR, NULL, "Weather Draw Dist.", &cv_drawdist_precip, 171},
+ {IT_STRING | IT_CVAR, NULL, "Weather Density", &cv_precipdensity, 176},
+
+ {IT_HEADER, NULL, "Diagnostic", NULL, 185},
+ {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 191},
+ {IT_STRING | IT_CVAR, NULL, "Clear Before Redraw", &cv_homremoval, 196},
};
static menuitem_t OP_VideoModeMenu[] =
@@ -1313,16 +1293,18 @@ static menuitem_t OP_OpenGLColorMenu[] =
static menuitem_t OP_SoundOptionsMenu[] =
{
- {IT_STRING | IT_KEYHANDLER, NULL, "Sound Effects", M_ToggleSFX, 10},
- {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Sound Volume", &cv_soundvolume, 20},
+ {IT_HEADER, NULL, "Game Audio", NULL, 0}, // 0 // ScrollMenu offsets
+ {IT_STRING | IT_CVAR, NULL, "Sound Effects", &cv_gamesounds, 13}, // 6
+ {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Sound Volume", &cv_soundvolume, 23}, // 11
- {IT_STRING | IT_KEYHANDLER, NULL, "Digital Music", M_ToggleDigital, 40},
- {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Digital Music Volume", &cv_digmusicvolume, 50},
+ {IT_STRING | IT_CVAR, NULL, "Digital Music", &cv_gamedigimusic, 43}, // 21
+ {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Digital Music Volume", &cv_digmusicvolume, 53}, // 26
- {IT_STRING | IT_KEYHANDLER, NULL, "MIDI Music", M_ToggleMIDI, 70},
- {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "MIDI Music Volume", &cv_midimusicvolume, 80},
+ {IT_STRING | IT_CVAR, NULL, "MIDI Music", &cv_gamemidimusic, 73}, // 36
+ {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "MIDI Music Volume", &cv_midimusicvolume, 83}, // 41
- {IT_STRING | IT_CVAR, NULL, "Closed Captioning", &cv_closedcaptioning, 100},
+ {IT_HEADER, NULL, "Advanced", NULL, 103}, // 50
+ {IT_STRING | IT_CVAR, NULL, "Closed Captioning", &cv_closedcaptioning, 115}, // 56
};
static menuitem_t OP_DataOptionsMenu[] =
@@ -1511,6 +1493,9 @@ menu_t MISC_ChangeLevelDef =
menu_t MISC_HelpDef = IMAGEDEF(MISC_HelpMenu);
+static INT32 highlightflags, recommendedflags, warningflags;
+
+
// Sky Room
menu_t SR_PandoraDef =
{
@@ -1902,7 +1887,7 @@ menu_t OP_SoundOptionsDef =
sizeof (OP_SoundOptionsMenu)/sizeof (menuitem_t),
&OP_MainDef,
OP_SoundOptionsMenu,
- M_DrawSoundMenu,
+ M_DrawGenericMenu,
30, 30,
0,
NULL
@@ -2787,6 +2772,7 @@ boolean M_Responder(event_t *ev)
INT32 ch = -1;
// INT32 i;
static tic_t joywait = 0, mousewait = 0;
+ static INT32 pjoyx = 0, pjoyy = 0;
static INT32 pmousex = 0, pmousey = 0;
static INT32 lastx = 0, lasty = 0;
void (*routine)(INT32 choice); // for some casting problem
@@ -2802,63 +2788,84 @@ boolean M_Responder(event_t *ev)
// (but still allow shift keyup so caps doesn't get stuck)
return false;
}
- else if (ev->type == ev_keydown)
- {
- ch = ev->data1;
-
- // added 5-2-98 remap virtual keys (mouse & joystick buttons)
- switch (ch)
- {
- case KEY_MOUSE1:
- case KEY_JOY1:
- case KEY_JOY1 + 2:
- ch = KEY_ENTER;
- break;
- case KEY_JOY1 + 3:
- ch = 'n';
- break;
- case KEY_MOUSE1 + 1:
- case KEY_JOY1 + 1:
- ch = KEY_BACKSPACE;
- break;
- case KEY_HAT1:
- ch = KEY_UPARROW;
- break;
- case KEY_HAT1 + 1:
- ch = KEY_DOWNARROW;
- break;
- case KEY_HAT1 + 2:
- ch = KEY_LEFTARROW;
- break;
- case KEY_HAT1 + 3:
- ch = KEY_RIGHTARROW;
- break;
- }
- }
else if (menuactive)
{
- if (ev->type == ev_joystick && ev->data1 == 0 && joywait < I_GetTime())
+ if (ev->type == ev_keydown)
{
- if (ev->data3 == -1)
+ ch = ev->data1;
+
+ // added 5-2-98 remap virtual keys (mouse & joystick buttons)
+ switch (ch)
{
- ch = KEY_UPARROW;
- joywait = I_GetTime() + NEWTICRATE/7;
+ case KEY_MOUSE1:
+ case KEY_JOY1:
+ ch = KEY_ENTER;
+ break;
+ case KEY_JOY1 + 3:
+ ch = 'n';
+ break;
+ case KEY_MOUSE1 + 1:
+ case KEY_JOY1 + 1:
+ ch = KEY_ESCAPE;
+ break;
+ case KEY_JOY1 + 2:
+ ch = KEY_BACKSPACE;
+ break;
+ case KEY_HAT1:
+ ch = KEY_UPARROW;
+ break;
+ case KEY_HAT1 + 1:
+ ch = KEY_DOWNARROW;
+ break;
+ case KEY_HAT1 + 2:
+ ch = KEY_LEFTARROW;
+ break;
+ case KEY_HAT1 + 3:
+ ch = KEY_RIGHTARROW;
+ break;
}
- else if (ev->data3 == 1)
+ }
+ else if (ev->type == ev_joystick && ev->data1 == 0 && joywait < I_GetTime())
+ {
+ const INT32 jdeadzone = JOYAXISRANGE/4;
+ if (ev->data3 != INT32_MAX)
{
- ch = KEY_DOWNARROW;
- joywait = I_GetTime() + NEWTICRATE/7;
+ if (Joystick.bGamepadStyle || abs(ev->data3) > jdeadzone)
+ {
+ if (ev->data3 < 0 && pjoyy >= 0)
+ {
+ ch = KEY_UPARROW;
+ joywait = I_GetTime() + NEWTICRATE/7;
+ }
+ else if (ev->data3 > 0 && pjoyy <= 0)
+ {
+ ch = KEY_DOWNARROW;
+ joywait = I_GetTime() + NEWTICRATE/7;
+ }
+ pjoyy = ev->data3;
+ }
+ else
+ pjoyy = 0;
}
- if (ev->data2 == -1)
+ if (ev->data2 != INT32_MAX)
{
- ch = KEY_LEFTARROW;
- joywait = I_GetTime() + NEWTICRATE/17;
- }
- else if (ev->data2 == 1)
- {
- ch = KEY_RIGHTARROW;
- joywait = I_GetTime() + NEWTICRATE/17;
+ if (Joystick.bGamepadStyle || abs(ev->data2) > jdeadzone)
+ {
+ if (ev->data2 < 0 && pjoyx >= 0)
+ {
+ ch = KEY_LEFTARROW;
+ joywait = I_GetTime() + NEWTICRATE/17;
+ }
+ else if (ev->data2 > 0 && pjoyx <= 0)
+ {
+ ch = KEY_RIGHTARROW;
+ joywait = I_GetTime() + NEWTICRATE/17;
+ }
+ pjoyx = ev->data2;
+ }
+ else
+ pjoyx = 0;
}
}
else if (ev->type == ev_mouse && mousewait < I_GetTime())
@@ -2892,9 +2899,13 @@ boolean M_Responder(event_t *ev)
}
}
}
+ else if (ev->type == ev_keydown) // Preserve event for other responders
+ ch = ev->data1;
if (ch == -1)
return false;
+ else if (ch == gamecontrol[gc_systemmenu][0] || ch == gamecontrol[gc_systemmenu][1]) // allow remappable ESC key
+ ch = KEY_ESCAPE;
// F-Keys
if (!menuactive)
@@ -3261,7 +3272,7 @@ void M_StartControlPanel(void)
MPauseMenu[mpause_switchteam].status = IT_DISABLED;
MPauseMenu[mpause_psetup].status = IT_DISABLED;
- if ((server || adminplayer == consoleplayer))
+ if ((server || IsPlayerAdmin(consoleplayer)))
{
MPauseMenu[mpause_switchmap].status = IT_STRING | IT_CALL;
MPauseMenu[mpause_addons].status = IT_STRING | IT_CALL;
@@ -3438,6 +3449,21 @@ void M_Init(void)
#endif
}
+void M_InitCharacterTables(void)
+{
+ UINT8 i;
+
+ // Setup description table
+ for (i = 0; i < MAXSKINS; i++)
+ {
+ description[i].used = false;
+ strcpy(description[i].notes, "???");
+ strcpy(description[i].picname, "");
+ strcpy(description[i].skinname, "");
+ description[i].prev = description[i].next = 0;
+ }
+}
+
// ==========================================================================
// SPECIAL MENU OPTION DRAW ROUTINES GO HERE
// ==========================================================================
@@ -3636,6 +3662,7 @@ static void M_DrawStaticBox(fixed_t x, fixed_t y, INT32 flags, fixed_t w, fixed_
//
// Draw border for the savegame description
//
+#if 0 // once used for joysticks and savegames, now no longer
static void M_DrawSaveLoadBorder(INT32 x,INT32 y)
{
INT32 i;
@@ -3650,6 +3677,7 @@ static void M_DrawSaveLoadBorder(INT32 x,INT32 y)
V_DrawScaledPatch (x,y+7,0,W_CachePatchName("M_LSRGHT",PU_CACHE));
}
+#endif
// horizontally centered text
static void M_CentreText(INT32 y, const char *string)
@@ -4355,7 +4383,7 @@ static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
if (mapnum+1 == spstage_start)
return true;
- // intentional fallthrough
+ /* FALLTHRU */
case LLM_RECORDATTACK:
case LLM_NIGHTSATTACK:
if (mapvisited[mapnum] & MV_MAX)
@@ -4765,7 +4793,7 @@ static void M_HandleLevelPlatter(INT32 choice)
}
break;
}
- // intentionall fallthrough
+ /* FALLTHRU */
case KEY_RIGHTARROW:
if (levellistmode == LLM_CREATESERVER && !lsrow)
{
@@ -5320,7 +5348,8 @@ static void M_AddonsOptions(INT32 choice)
M_SetupNextMenu(&OP_AddonsOptionsDef);
}
-#define LOCATIONSTRING "Visit \x83SRB2.ORG/MODS\x80 to get & make add-ons!"
+#define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make add-ons!"
+//#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make add-ons!"
static void M_Addons(INT32 choice)
{
@@ -5328,6 +5357,11 @@ static void M_Addons(INT32 choice)
(void)choice;
+ // If M_GetGameypeColor() is ever ported from Kart, then remove this.
+ highlightflags = V_YELLOWMAP;
+ recommendedflags = V_GREENMAP;
+ warningflags = V_REDMAP;
+
#if 1
if (cv_addons_option.value == 0)
pathname = usehome ? srb2home : srb2path;
@@ -5353,7 +5387,8 @@ static void M_Addons(INT32 choice)
if (!preparefilemenu(false))
{
- M_StartMessage(M_GetText("No files/folders found.\n\n"LOCATIONSTRING"\n\n(Press a key)\n"),NULL,MM_NOTHING);
+ M_StartMessage(va("No files/folders found.\n\n%s\n\n(Press a key)\n",LOCATIONSTRING1),NULL,MM_NOTHING);
+ // (recommendedflags == V_SKYMAP ? LOCATIONSTRING2 : LOCATIONSTRING1))
return;
}
else
@@ -5362,7 +5397,7 @@ static void M_Addons(INT32 choice)
if (addonsp[0]) // never going to have some provided but not all, saves individually checking
{
size_t i;
- for (i = 0; i < NUM_EXT+6; i++)
+ for (i = 0; i < NUM_EXT+5; i++)
W_UnlockCachedPatch(addonsp[i]);
}
@@ -5372,15 +5407,17 @@ static void M_Addons(INT32 choice)
addonsp[EXT_TXT] = W_CachePatchName("M_FTXT", PU_STATIC);
addonsp[EXT_CFG] = W_CachePatchName("M_FCFG", PU_STATIC);
addonsp[EXT_WAD] = W_CachePatchName("M_FWAD", PU_STATIC);
+#ifdef USE_KART
+ addonsp[EXT_KART] = W_CachePatchName("M_FKART", PU_STATIC);
+#endif
addonsp[EXT_PK3] = W_CachePatchName("M_FPK3", PU_STATIC);
addonsp[EXT_SOC] = W_CachePatchName("M_FSOC", PU_STATIC);
addonsp[EXT_LUA] = W_CachePatchName("M_FLUA", PU_STATIC);
addonsp[NUM_EXT] = W_CachePatchName("M_FUNKN", PU_STATIC);
- addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL1", PU_STATIC);
- addonsp[NUM_EXT+2] = W_CachePatchName("M_FSEL2", PU_STATIC);
- addonsp[NUM_EXT+3] = W_CachePatchName("M_FLOAD", PU_STATIC);
- addonsp[NUM_EXT+4] = W_CachePatchName("M_FSRCH", PU_STATIC);
- addonsp[NUM_EXT+5] = W_CachePatchName("M_FSAVE", PU_STATIC);
+ addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL", PU_STATIC);
+ addonsp[NUM_EXT+2] = W_CachePatchName("M_FLOAD", PU_STATIC);
+ addonsp[NUM_EXT+3] = W_CachePatchName("M_FSRCH", PU_STATIC);
+ addonsp[NUM_EXT+4] = W_CachePatchName("M_FSAVE", PU_STATIC);
MISC_AddonsDef.prevMenu = currentMenu;
M_SetupNextMenu(&MISC_AddonsDef);
@@ -5482,14 +5519,14 @@ static boolean M_AddonsRefresh(void)
{
S_StartSound(NULL, sfx_lose);
if (refreshdirmenu & REFRESHDIR_MAX)
- message = va("\x82%s\x80\nMaximum number of add-ons reached.\nA file could not be loaded.\nIf you want to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", refreshdirname);
+ message = va("%c%s\x80\nMaximum number of add-ons reached.\nA file could not be loaded.\nIf you want to play with this add-on, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
else
- message = va("\x82%s\x80\nA file was not loaded.\nCheck the console log for more information.\n\n(Press a key)\n", refreshdirname);
+ message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
}
else if (refreshdirmenu & (REFRESHDIR_WARNING|REFRESHDIR_ERROR))
{
S_StartSound(NULL, sfx_skid);
- message = va("\x82%s\x80\nA file was loaded with %s.\nCheck the console log for more information.\n\n(Press a key)\n", refreshdirname, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings"));
+ message = va("%c%s\x80\nA file was loaded with %s.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings"));
}
if (message)
@@ -5505,15 +5542,12 @@ static boolean M_AddonsRefresh(void)
return false;
}
-#ifdef FIXUPO0
-#pragma GCC optimize ("0")
-#endif
-
static void M_DrawAddons(void)
{
INT32 x, y;
- ssize_t i, max;
- const char* topstr;
+ ssize_t i, m;
+ const UINT8 *flashcol = NULL;
+ UINT8 hilicol;
// hack - need to refresh at end of frame to handle addfile...
if (refreshdirmenu & M_AddonsRefresh())
@@ -5523,15 +5557,10 @@ static void M_DrawAddons(void)
}
if (Playing())
- topstr = "\x85""Adding files mid-game may cause problems.";
- else if (savemoddata)
- topstr = "\x83""Add-on has its own data, saving enabled.";
- else if (modifiedgame)
- topstr = "\x87""Game is modified, saving is disabled.";
+ V_DrawCenteredString(BASEVIDWIDTH/2, 5, warningflags, "Adding files mid-game may cause problems.");
else
- topstr = LOCATIONSTRING;
-
- V_DrawCenteredString(BASEVIDWIDTH/2, 5, 0, topstr);
+ V_DrawCenteredString(BASEVIDWIDTH/2, 5, 0, LOCATIONSTRING1);
+ // (recommendedflags == V_SKYMAP ? LOCATIONSTRING2 : LOCATIONSTRING1)
if (numwadfiles <= mainwads+1)
y = 0;
@@ -5539,8 +5568,8 @@ static void M_DrawAddons(void)
y = FRACUNIT;
else
{
- x = FixedDiv((numwadfiles - mainwads+1)< y)
y = x;
if (y > FRACUNIT) // happens because of how we're shrinkin' it a little
@@ -5553,34 +5582,54 @@ static void M_DrawAddons(void)
x = currentMenu->x;
y = currentMenu->y + 1;
- //M_DrawLevelPlatterHeader(y - 16, M_AddonsHeaderPath(), true, true); -- wanted different width
- V_DrawString(x-21, (y - 16) + (lsheadingheight - 12), V_YELLOWMAP|V_ALLOWLOWERCASE, M_AddonsHeaderPath());
- V_DrawFill(x-21, (y - 16) + (lsheadingheight - 3), (MAXSTRINGLENGTH*8+6 - 1), 1, yellowmap[3]);
- V_DrawFill(x-21 + (MAXSTRINGLENGTH*8+6 - 1), (y - 16) + (lsheadingheight - 3), 1, 1, 26);
- V_DrawFill(x-21, (y - 16) + (lsheadingheight - 2), MAXSTRINGLENGTH*8+6, 1, 26);
+ hilicol = 0; // white
- V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, (BASEVIDHEIGHT - currentMenu->y + 2) - (y - 1), 159);
+ V_DrawString(x-21, (y - 16) + (lsheadingheight - 12), highlightflags|V_ALLOWLOWERCASE, M_AddonsHeaderPath());
+ V_DrawFill(x-21, (y - 16) + (lsheadingheight - 3), MAXSTRINGLENGTH*8+6, 1, hilicol);
+ V_DrawFill(x-21, (y - 16) + (lsheadingheight - 2), MAXSTRINGLENGTH*8+6, 1, 30);
- // get bottom...
- max = dir_on[menudepthleft] + numaddonsshown + 1;
- if (max > (ssize_t)sizedirmenu)
- max = sizedirmenu;
+ m = (BASEVIDHEIGHT - currentMenu->y + 2) - (y - 1);
+ // addons menu back color
+ V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, m, 159);
- // then top...
- i = max - (2*numaddonsshown + 1);
-
- // then adjust!
- if (i < 0)
- {
- if ((max -= i) > (ssize_t)sizedirmenu)
- max = sizedirmenu;
+ // scrollbar!
+ if (sizedirmenu <= (2*numaddonsshown + 1))
i = 0;
+ else
+ {
+ ssize_t q = m;
+ m = ((2*numaddonsshown + 1) * m)/sizedirmenu;
+ if (dir_on[menudepthleft] <= numaddonsshown) // all the way up
+ i = 0;
+ else if (sizedirmenu <= (dir_on[menudepthleft] + numaddonsshown + 1)) // all the way down
+ i = q-m;
+ else
+ i = ((dir_on[menudepthleft] - numaddonsshown) * (q-m))/(sizedirmenu - (2*numaddonsshown + 1));
}
- if (i != 0)
- V_DrawString(19, y+4 - (skullAnimCounter/5), V_YELLOWMAP, "\x1A");
+ V_DrawFill(x + MAXSTRINGLENGTH*8+5 - 21, (y - 1) + i, 1, m, hilicol);
- for (; i < max; i++)
+ // get bottom...
+ m = dir_on[menudepthleft] + numaddonsshown + 1;
+ if (m > (ssize_t)sizedirmenu)
+ m = sizedirmenu;
+
+ // then compute top and adjust bottom if needed!
+ if (m < (2*numaddonsshown + 1))
+ {
+ m = min(sizedirmenu, 2*numaddonsshown + 1);
+ i = 0;
+ }
+ else
+ i = m - (2*numaddonsshown + 1);
+
+ if (i != 0)
+ V_DrawString(19, y+4 - (skullAnimCounter/5), highlightflags, "\x1A");
+
+ if (skullAnimCounter < 4)
+ flashcol = V_GetStringColormap(highlightflags);
+
+ for (; i < m; i++)
{
UINT32 flags = V_ALLOWLOWERCASE;
if (y > BASEVIDHEIGHT) break;
@@ -5588,17 +5637,18 @@ static void M_DrawAddons(void)
#define type (UINT8)(dirmenu[i][DIR_TYPE])
{
if (type & EXT_LOADED)
- flags |= V_TRANSLUCENT;
-
- V_DrawSmallScaledPatch(x-(16+4), y, (flags & V_TRANSLUCENT), addonsp[((UINT8)(dirmenu[i][DIR_TYPE]) & ~EXT_LOADED)]);
-
- if (type & EXT_LOADED)
- V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[NUM_EXT+3]);
+ {
+ flags |= V_TRANSLUCENT;
+ V_DrawSmallScaledPatch(x-(16+4), y, V_TRANSLUCENT, addonsp[(type & ~EXT_LOADED)]);
+ V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[NUM_EXT+2]);
+ }
+ else
+ V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[(type & ~EXT_LOADED)]);
if ((size_t)i == dir_on[menudepthleft])
{
- V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[NUM_EXT+1+((skullAnimCounter/4) ? 1 : 0)]);
- flags = V_ALLOWLOWERCASE|V_YELLOWMAP;
+ V_DrawFixedPatch((x-(16+4))<y + 1;
@@ -5627,26 +5677,22 @@ static void M_DrawAddons(void)
'_' | 0x80, false);
x -= (21 + 5 + 16);
- V_DrawSmallScaledPatch(x, y + 4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]);
+ V_DrawSmallScaledPatch(x, y + 4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+3]);
x = BASEVIDWIDTH - x - 16;
- V_DrawSmallScaledPatch(x, y + 4, ((!modifiedgame || savemoddata) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+5]);
+ V_DrawSmallScaledPatch(x, y + 4, ((!modifiedgame || savemoddata) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]);
if (modifiedgame)
- V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+3]);
+ V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+2]);
}
-#ifdef FIXUPO0
-#pragma GCC reset_options
-#endif
-
static void M_AddonExec(INT32 ch)
{
if (ch != 'y' && ch != KEY_ENTER)
return;
S_StartSound(NULL, sfx_zoom);
- COM_BufAddText(va("exec %s%s", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING));
+ COM_BufAddText(va("exec \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING));
}
#define len menusearch[0]
@@ -5696,12 +5742,15 @@ static void M_HandleAddons(INT32 choice)
char *tempname = NULL;
if (dirmenu && dirmenu[dir_on[menudepthleft]])
tempname = Z_StrDup(dirmenu[dir_on[menudepthleft]]+DIR_STRING); // don't need to I_Error if can't make - not important, just QoL
- searchfilemenu(tempname);
- /*if (!preparefilemenu(true))
+#if 0 // much slower
+ if (!preparefilemenu(true))
{
UNEXIST;
return;
- }*/
+ }
+#else // streamlined
+ searchfilemenu(tempname);
+#endif
}
switch (choice)
@@ -5751,7 +5800,7 @@ static void M_HandleAddons(INT32 choice)
if (!preparefilemenu(false))
{
S_StartSound(NULL, sfx_skid);
- M_StartMessage(va("\x82%s\x80\nThis folder is empty.\n\n(Press a key)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING);
+ M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
menupath[menupathindex[++menudepthleft]] = 0;
if (!preparefilemenu(true))
@@ -5770,7 +5819,7 @@ static void M_HandleAddons(INT32 choice)
else
{
S_StartSound(NULL, sfx_lose);
- M_StartMessage(va("\x82%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING);
+ M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING);
menupath[menupathindex[menudepthleft]] = 0;
}
break;
@@ -5784,7 +5833,7 @@ static void M_HandleAddons(INT32 choice)
}
break;
case EXT_TXT:
- M_StartMessage(va("\x82%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press 'Y' to confirm)\n", dirmenu[dir_on[menudepthleft]]+DIR_STRING),M_AddonExec,MM_YESNO);
+ M_StartMessage(va("%c%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press 'Y' to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),M_AddonExec,MM_YESNO);
break;
case EXT_CFG:
M_AddonExec(KEY_ENTER);
@@ -5792,12 +5841,15 @@ static void M_HandleAddons(INT32 choice)
case EXT_LUA:
#ifndef HAVE_BLUA
S_StartSound(NULL, sfx_lose);
- M_StartMessage(va("\x82%s\x80\nThis copy of SRB2 was compiled\nwithout support for .lua files.\n\n(Press a key)\n", dirmenu[dir_on[menudepthleft]]+DIR_STRING),NULL,MM_NOTHING);
+ M_StartMessage(va("%c%s\x80\nThis copy of SRB2 was compiled\nwithout support for .lua files.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),NULL,MM_NOTHING);
break;
#endif
- // else intentional fallthrough
+ /* FALLTHRU */
case EXT_SOC:
case EXT_WAD:
+#ifdef USE_KART
+ case EXT_KART:
+#endif
case EXT_PK3:
COM_BufAddText(va("addfile \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING));
break;
@@ -5947,7 +5999,7 @@ static void M_Options(INT32 choice)
(void)choice;
// if the player is not admin or server, disable server options
- OP_MainMenu[5].status = (Playing() && !(server || adminplayer == consoleplayer)) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL);
+ OP_MainMenu[5].status = (Playing() && !(server || IsPlayerAdmin(consoleplayer))) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL);
// if the player is playing _at all_, disable the erase data options
OP_DataOptionsMenu[2].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
@@ -8844,7 +8896,7 @@ void M_SortServerList(void)
#ifndef NONET
#ifdef UPDATE_ALERT
-static int M_CheckMODVersion(void)
+static boolean M_CheckMODVersion(void)
{
char updatestring[500];
const char *updatecheck = GetMODVersion();
@@ -9699,17 +9751,18 @@ static void M_DrawJoystick(void)
for (i = 0; i <= 4; i++) // See MAX_JOYSTICKS
{
- M_DrawSaveLoadBorder(OP_JoystickSetDef.x+4, OP_JoystickSetDef.y+1+LINEHEIGHT*i);
+ M_DrawTextBox(OP_JoystickSetDef.x-8, OP_JoystickSetDef.y+LINEHEIGHT*i-12, 28, 1);
+ //M_DrawSaveLoadBorder(OP_JoystickSetDef.x+4, OP_JoystickSetDef.y+1+LINEHEIGHT*i);
if ((setupcontrols_secondaryplayer && (i == cv_usejoystick2.value))
|| (!setupcontrols_secondaryplayer && (i == cv_usejoystick.value)))
- V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i,V_GREENMAP,joystickInfo[i]);
+ V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,V_GREENMAP,joystickInfo[i]);
else
- V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i,0,joystickInfo[i]);
+ V_DrawString(OP_JoystickSetDef.x, OP_JoystickSetDef.y+LINEHEIGHT*i-4,0,joystickInfo[i]);
if (i == itemOn)
{
- V_DrawScaledPatch(currentMenu->x - 24, OP_JoystickSetDef.y+LINEHEIGHT*i, 0,
+ V_DrawScaledPatch(currentMenu->x - 24, OP_JoystickSetDef.y+LINEHEIGHT*i-4, 0,
W_CachePatchName("M_CURSOR", PU_CACHE));
}
}
@@ -9718,20 +9771,16 @@ static void M_DrawJoystick(void)
static void M_SetupJoystickMenu(INT32 choice)
{
INT32 i = 0;
- const char *joyname = "None";
const char *joyNA = "Unavailable";
INT32 n = I_NumJoys();
(void)choice;
- strcpy(joystickInfo[i], joyname);
+ strcpy(joystickInfo[i], "None");
for (i = 1; i < 8; i++)
{
- if (i <= n && (joyname = I_GetJoyName(i)) != NULL)
- {
- strncpy(joystickInfo[i], joyname, 24);
- joystickInfo[i][24] = '\0';
- }
+ if (i <= n && (I_GetJoyName(i)) != NULL)
+ strncpy(joystickInfo[i], I_GetJoyName(i), 28);
else
strcpy(joystickInfo[i], joyNA);
}
@@ -9780,19 +9829,23 @@ static void M_Setup1PControlsMenu(INT32 choice)
setupcontrols = gamecontrol; // was called from main Options (for console player, then)
currentMenu->lastOn = itemOn;
- // Unhide the five non-P2 controls and their headers
- OP_ChangeControlsMenu[18+0].status = IT_HEADER;
- OP_ChangeControlsMenu[18+1].status = IT_SPACE;
+ // Unhide the nine non-P2 controls and their headers
+ //OP_ChangeControlsMenu[18+0].status = IT_HEADER;
+ //OP_ChangeControlsMenu[18+1].status = IT_SPACE;
// ...
OP_ChangeControlsMenu[18+2].status = IT_CALL|IT_STRING2;
OP_ChangeControlsMenu[18+3].status = IT_CALL|IT_STRING2;
OP_ChangeControlsMenu[18+4].status = IT_CALL|IT_STRING2;
+ OP_ChangeControlsMenu[18+5].status = IT_CALL|IT_STRING2;
+ OP_ChangeControlsMenu[18+6].status = IT_CALL|IT_STRING2;
+ //OP_ChangeControlsMenu[18+7].status = IT_CALL|IT_STRING2;
+ OP_ChangeControlsMenu[18+8].status = IT_CALL|IT_STRING2;
// ...
- OP_ChangeControlsMenu[23+0].status = IT_HEADER;
- OP_ChangeControlsMenu[23+1].status = IT_SPACE;
+ OP_ChangeControlsMenu[27+0].status = IT_HEADER;
+ OP_ChangeControlsMenu[27+1].status = IT_SPACE;
// ...
- OP_ChangeControlsMenu[23+2].status = IT_CALL|IT_STRING2;
- OP_ChangeControlsMenu[23+3].status = IT_CALL|IT_STRING2;
+ OP_ChangeControlsMenu[27+2].status = IT_CALL|IT_STRING2;
+ OP_ChangeControlsMenu[27+3].status = IT_CALL|IT_STRING2;
OP_ChangeControlsDef.prevMenu = &OP_P1ControlsDef;
OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove first level (<< 6)
@@ -9807,19 +9860,23 @@ static void M_Setup2PControlsMenu(INT32 choice)
setupcontrols = gamecontrolbis;
currentMenu->lastOn = itemOn;
- // Hide the five non-P2 controls and their headers
- OP_ChangeControlsMenu[18+0].status = IT_GRAYEDOUT2;
- OP_ChangeControlsMenu[18+1].status = IT_GRAYEDOUT2;
+ // Hide the nine non-P2 controls and their headers
+ //OP_ChangeControlsMenu[18+0].status = IT_GRAYEDOUT2;
+ //OP_ChangeControlsMenu[18+1].status = IT_GRAYEDOUT2;
// ...
OP_ChangeControlsMenu[18+2].status = IT_GRAYEDOUT2;
OP_ChangeControlsMenu[18+3].status = IT_GRAYEDOUT2;
OP_ChangeControlsMenu[18+4].status = IT_GRAYEDOUT2;
+ OP_ChangeControlsMenu[18+5].status = IT_GRAYEDOUT2;
+ OP_ChangeControlsMenu[18+6].status = IT_GRAYEDOUT2;
+ //OP_ChangeControlsMenu[18+7].status = IT_GRAYEDOUT2;
+ OP_ChangeControlsMenu[18+8].status = IT_GRAYEDOUT2;
// ...
- OP_ChangeControlsMenu[23+0].status = IT_GRAYEDOUT2;
- OP_ChangeControlsMenu[23+1].status = IT_GRAYEDOUT2;
+ OP_ChangeControlsMenu[27+0].status = IT_GRAYEDOUT2;
+ OP_ChangeControlsMenu[27+1].status = IT_GRAYEDOUT2;
// ...
- OP_ChangeControlsMenu[23+2].status = IT_GRAYEDOUT2;
- OP_ChangeControlsMenu[23+3].status = IT_GRAYEDOUT2;
+ OP_ChangeControlsMenu[27+2].status = IT_GRAYEDOUT2;
+ OP_ChangeControlsMenu[27+3].status = IT_GRAYEDOUT2;
OP_ChangeControlsDef.prevMenu = &OP_P2ControlsDef;
OP_ChangeControlsDef.menuid &= ~(((1 << MENUBITS) - 1) << MENUBITS); // remove first level (<< 6)
@@ -9949,7 +10006,7 @@ static void M_DrawControl(void)
#undef controlbuffer
static INT32 controltochange;
-static char controltochangetext[55];
+static char controltochangetext[33];
static void M_ChangecontrolResponse(event_t *ev)
{
@@ -10014,14 +10071,15 @@ static void M_ChangecontrolResponse(event_t *ev)
found = 0;
setupcontrols[control][1] = KEY_NULL; //replace key 1,clear key2
}
- G_CheckDoubleUsage(ch);
+ (void)G_CheckDoubleUsage(ch, true);
setupcontrols[control][found] = ch;
}
S_StartSound(NULL, sfx_strpst);
}
else if (ch == KEY_PAUSE)
{
- static char tmp[155];
+ // This buffer assumes a 125-character message plus a 32-character control name (per controltochangetext buffer size)
+ static char tmp[158];
menu_t *prev = currentMenu->prevMenu;
if (controltochange == gc_pause)
@@ -10045,7 +10103,9 @@ static void M_ChangecontrolResponse(event_t *ev)
static void M_ChangeControl(INT32 choice)
{
- static char tmp[55];
+ // This buffer assumes a 35-character message (per below) plus a max control name limit of 32 chars (per controltochangetext)
+ // If you change the below message, then change the size of this buffer!
+ static char tmp[68];
if (tutorialmode && tutorialgcs) // don't allow control changes if temp control override is active
return;
@@ -10053,228 +10113,11 @@ static void M_ChangeControl(INT32 choice)
controltochange = currentMenu->menuitems[choice].alphaKey;
sprintf(tmp, M_GetText("Hit the new key for\n%s\nESC for Cancel"),
currentMenu->menuitems[choice].text);
- strncpy(controltochangetext, currentMenu->menuitems[choice].text, 55);
+ strlcpy(controltochangetext, currentMenu->menuitems[choice].text, 33);
M_StartMessage(tmp, M_ChangecontrolResponse, MM_EVENTHANDLER);
}
-// =====
-// SOUND
-// =====
-
-static void M_SoundMenu(INT32 choice)
-{
- (void)choice;
-
- OP_SoundOptionsMenu[6].status = (sound_disabled ? IT_GRAYEDOUT : (IT_STRING | IT_CVAR));
- M_SetupNextMenu(&OP_SoundOptionsDef);
-}
-
-void M_DrawSoundMenu(void)
-{
- const char* onstring = "ON";
- const char* offstring = "OFF";
- INT32 lengthstring;
- M_DrawGenericMenu();
-
- V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x,
- currentMenu->y+currentMenu->menuitems[0].alphaKey,
- (sound_disabled ? V_REDMAP : V_YELLOWMAP),
- (sound_disabled ? offstring : onstring));
-
- V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x,
- currentMenu->y+currentMenu->menuitems[2].alphaKey,
- (digital_disabled ? V_REDMAP : V_YELLOWMAP),
- (digital_disabled ? offstring : onstring));
-
- V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x,
- currentMenu->y+currentMenu->menuitems[4].alphaKey,
- (midi_disabled ? V_REDMAP : V_YELLOWMAP),
- (midi_disabled ? offstring : onstring));
-
- if (itemOn == 0)
- lengthstring = (sound_disabled ? 3 : 2);
- else if (itemOn == 2)
- lengthstring = (digital_disabled ? 3 : 2);
- else if (itemOn == 4)
- lengthstring = (midi_disabled ? 3 : 2);
- else
- return;
-
- V_DrawCharacter(BASEVIDWIDTH - currentMenu->x - 10 - (lengthstring*8) - (skullAnimCounter/5), currentMenu->y+currentMenu->menuitems[itemOn].alphaKey,
- '\x1C' | V_YELLOWMAP, false);
- V_DrawCharacter(BASEVIDWIDTH - currentMenu->x + 2 + (skullAnimCounter/5), currentMenu->y+currentMenu->menuitems[itemOn].alphaKey,
- '\x1D' | V_YELLOWMAP, false);
-}
-
-// Toggles sound systems in-game.
-static void M_ToggleSFX(INT32 choice)
-{
- switch (choice)
- {
- case KEY_DOWNARROW:
- S_StartSound(NULL, sfx_menu1);
- itemOn++;
- return;
-
- case KEY_UPARROW:
- S_StartSound(NULL, sfx_menu1);
- itemOn = currentMenu->numitems-1;
- return;
-
- case KEY_ESCAPE:
- if (currentMenu->prevMenu)
- M_SetupNextMenu(currentMenu->prevMenu);
- else
- M_ClearMenus(true);
- return;
- default:
- break;
- }
-
- if (sound_disabled)
- {
- sound_disabled = false;
- S_InitSfxChannels(cv_soundvolume.value);
- S_StartSound(NULL, sfx_strpst);
- OP_SoundOptionsMenu[6].status = IT_STRING | IT_CVAR;
- //M_StartMessage(M_GetText("SFX Enabled\n"), NULL, MM_NOTHING);
- }
- else
- {
- sound_disabled = true;
- S_StopSounds();
- OP_SoundOptionsMenu[6].status = IT_GRAYEDOUT;
- //M_StartMessage(M_GetText("SFX Disabled\n"), NULL, MM_NOTHING);
- }
-}
-
-static void M_ToggleDigital(INT32 choice)
-{
- switch (choice)
- {
- case KEY_DOWNARROW:
- S_StartSound(NULL, sfx_menu1);
- itemOn++;
- return;
-
- case KEY_UPARROW:
- S_StartSound(NULL, sfx_menu1);
- itemOn--;
- return;
-
- case KEY_ESCAPE:
- if (currentMenu->prevMenu)
- M_SetupNextMenu(currentMenu->prevMenu);
- else
- M_ClearMenus(true);
- return;
- default:
- break;
- }
-
- if (digital_disabled)
- {
- digital_disabled = false;
- I_InitMusic();
- S_StopMusic();
- if (Playing())
- P_RestoreMusic(&players[consoleplayer]);
- else
- S_ChangeMusicInternal("_clear", false);
- //M_StartMessage(M_GetText("Digital Music Enabled\n"), NULL, MM_NOTHING);
- }
- else
- {
- digital_disabled = true;
- if (S_MusicType() != MU_MID)
- {
- if (midi_disabled)
- S_StopMusic();
- else
- {
- char mmusic[7];
- UINT16 mflags;
- boolean looping;
-
- if (S_MusicInfo(mmusic, &mflags, &looping) && S_MIDIExists(mmusic))
- {
- S_StopMusic();
- S_ChangeMusic(mmusic, mflags, looping);
- }
- else
- S_StopMusic();
- }
- }
- //M_StartMessage(M_GetText("Digital Music Disabled\n"), NULL, MM_NOTHING);
- }
-}
-
-static void M_ToggleMIDI(INT32 choice)
-{
- switch (choice)
- {
- case KEY_DOWNARROW:
- S_StartSound(NULL, sfx_menu1);
- itemOn++;
- return;
-
- case KEY_UPARROW:
- S_StartSound(NULL, sfx_menu1);
- itemOn--;
- return;
-
- case KEY_LEFTARROW:
- case KEY_RIGHTARROW:
- if (S_MusicType() != MU_MID && S_MusicType() != MU_NONE)
- S_StartSound(NULL, sfx_menu1);
- break;
-
- case KEY_ESCAPE:
- if (currentMenu->prevMenu)
- M_SetupNextMenu(currentMenu->prevMenu);
- else
- M_ClearMenus(true);
- return;
- default:
- break;
- }
- if (midi_disabled)
- {
- midi_disabled = false;
- I_InitMusic();
- if (Playing())
- P_RestoreMusic(&players[consoleplayer]);
- else
- S_ChangeMusicInternal("_clear", false);
- //M_StartMessage(M_GetText("MIDI Music Enabled\n"), NULL, MM_NOTHING);
- }
- else
- {
- midi_disabled = true;
- if (S_MusicType() == MU_MID)
- {
- if (digital_disabled)
- S_StopMusic();
- else
- {
- char mmusic[7];
- UINT16 mflags;
- boolean looping;
-
- if (S_MusicInfo(mmusic, &mflags, &looping) && S_DigExists(mmusic))
- {
- S_StopMusic();
- S_ChangeMusic(mmusic, mflags, looping);
- }
- else
- S_StopMusic();
- }
- }
- //M_StartMessage(M_GetText("MIDI Music Disabled\n"), NULL, MM_NOTHING);
- }
-}
-
// ===============
// VIDEO MODE MENU
// ===============
diff --git a/src/m_menu.h b/src/m_menu.h
index 709be616b..04146ebdc 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -3,7 +3,7 @@
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2011-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -172,6 +172,9 @@ void M_Drawer(void);
// Called by D_SRB2Main, loads the config file.
void M_Init(void);
+// Called by D_SRB2Main also, sets up the playermenu and description tables.
+void M_InitCharacterTables(void);
+
// Called by intro code to force menu up upon a keypress,
// does nothing if menu is already up.
void M_StartControlPanel(void);
@@ -361,7 +364,7 @@ typedef struct
INT32 gamemap;
} saveinfo_t;
-extern description_t description[32];
+extern description_t description[MAXSKINS];
extern consvar_t cv_newgametype, cv_nextmap, cv_chooseskin, cv_serversort;
extern CV_PossibleValue_t gametype_cons_t[];
diff --git a/src/m_misc.c b/src/m_misc.c
index fb3ffa5a7..c967548c6 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -37,6 +37,7 @@
#include "d_main.h"
#include "m_argv.h"
#include "i_system.h"
+#include "command.h" // cv_execversion
#include "m_anigif.h"
@@ -90,7 +91,8 @@ typedef off_t off64_t;
#ifdef PNG_WRITE_SUPPORTED
#define USE_PNG // Only actually use PNG if write is supported.
#if defined (PNG_WRITE_APNG_SUPPORTED) //|| !defined(PNG_STATIC)
- #define USE_APNG
+ #include "apng.h"
+ #define USE_APNG
#endif
// See hardware/hw_draw.c for a similar check to this one.
#endif
@@ -440,7 +442,23 @@ void Command_LoadConfig_f(void)
strcpy(configfile, COM_Argv(1));
FIL_ForceExtension(configfile, ".cfg");
+
+ // load default control
+ G_ClearAllControlKeys();
+ G_CopyControls(gamecontrol, gamecontroldefault[gcs_fps], NULL, 0);
+ G_CopyControls(gamecontrolbis, gamecontrolbisdefault[gcs_fps], NULL, 0);
+
+ // temporarily reset execversion to default
+ CV_ToggleExecVersion(true);
+ COM_BufInsertText(va("%s \"%s\"\n", cv_execversion.name, cv_execversion.defaultvalue));
+ CV_InitFilterVar();
+
+ // exec the config
COM_BufInsertText(va("exec \"%s\"\n", configfile));
+
+ // don't filter anymore vars and don't let this convsvar be changed
+ COM_BufInsertText(va("%s \"%d\"\n", cv_execversion.name, EXECVERSION));
+ CV_ToggleExecVersion(false);
}
/** Saves the current configuration and loads another.
@@ -477,11 +495,25 @@ void M_FirstLoadConfig(void)
// load default control
G_DefineDefaultControls();
G_CopyControls(gamecontrol, gamecontroldefault[gcs_fps], NULL, 0);
+ G_CopyControls(gamecontrolbis, gamecontrolbisdefault[gcs_fps], NULL, 0);
+
+ // register execversion here before we load any configs
+ CV_RegisterVar(&cv_execversion);
+
+ // temporarily reset execversion to default
+ // we shouldn't need to do this, but JUST in case...
+ CV_ToggleExecVersion(true);
+ COM_BufInsertText(va("%s \"%s\"\n", cv_execversion.name, cv_execversion.defaultvalue));
+ CV_InitFilterVar();
// load config, make sure those commands doesnt require the screen...
COM_BufInsertText(va("exec \"%s\"\n", configfile));
// no COM_BufExecute() needed; that does it right away
+ // don't filter anymore vars and don't let this convsvar be changed
+ COM_BufInsertText(va("%s \"%d\"\n", cv_execversion.name, EXECVERSION));
+ CV_ToggleExecVersion(false);
+
// make sure I_Quit() will write back the correct config
// (do not write back the config if it crash before)
gameconfig_loaded = true;
@@ -494,6 +526,7 @@ void M_FirstLoadConfig(void)
void M_SaveConfig(const char *filename)
{
FILE *f;
+ char *filepath;
// make sure not to write back the config until it's been correctly loaded
if (!gameconfig_loaded)
@@ -508,13 +541,20 @@ void M_SaveConfig(const char *filename)
return;
}
- f = fopen(filename, "w");
+ // append srb2home to beginning of filename
+ // but check if srb2home isn't already there, first
+ if (!strstr(filename, srb2home))
+ filepath = va(pandf,srb2home, filename);
+ else
+ filepath = Z_StrDup(filename);
+
+ f = fopen(filepath, "w");
// change it only if valid
if (f)
- strcpy(configfile, filename);
+ strcpy(configfile, filepath);
else
{
- CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), filename);
+ CONS_Alert(CONS_ERROR, M_GetText("Couldn't save game config file %s\n"), filepath);
return;
}
}
@@ -537,6 +577,10 @@ void M_SaveConfig(const char *filename)
// header message
fprintf(f, "// SRB2 configuration file.\n");
+ // print execversion FIRST, because subsequent consvars need to be filtered
+ // always print current EXECVERSION
+ fprintf(f, "%s \"%d\"\n", cv_execversion.name, EXECVERSION);
+
// FIXME: save key aliases if ever implemented..
if (tutorialmode && tutorialgcs)
@@ -777,13 +821,13 @@ static inline void M_PNGImage(png_structp png_ptr, png_infop png_info_ptr, PNG_C
#ifdef USE_APNG
static png_structp apng_ptr = NULL;
static png_infop apng_info_ptr = NULL;
+static apng_infop apng_ainfo_ptr = NULL;
static png_FILE_p apng_FILE = NULL;
static png_uint_32 apng_frames = 0;
-static png_byte acTL_cn[5] = { 97, 99, 84, 76, '\0'};
#ifdef PNG_STATIC // Win32 build have static libpng
-#define apng_set_acTL png_set_acTL
-#define apng_write_frame_head png_write_frame_head
-#define apng_write_frame_tail png_write_frame_tail
+#define aPNG_set_acTL png_set_acTL
+#define aPNG_write_frame_head png_write_frame_head
+#define aPNG_write_frame_tail png_write_frame_tail
#else // outside libpng may not have apng support
#ifndef PNG_WRITE_APNG_SUPPORTED // libpng header may not have apng patch
@@ -820,20 +864,20 @@ static png_byte acTL_cn[5] = { 97, 99, 84, 76, '\0'};
#endif
#endif
-typedef PNG_EXPORT(png_uint_32, (*P_png_set_acTL)) PNGARG((png_structp png_ptr,
- png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays));
-typedef PNG_EXPORT (void, (*P_png_write_frame_head)) PNGARG((png_structp png_ptr,
+typedef png_uint_32 (*P_png_set_acTL) (png_structp png_ptr,
+ png_infop info_ptr, png_uint_32 num_frames, png_uint_32 num_plays);
+typedef void (*P_png_write_frame_head) (png_structp png_ptr,
png_infop info_ptr, png_bytepp row_pointers,
png_uint_32 width, png_uint_32 height,
png_uint_32 x_offset, png_uint_32 y_offset,
png_uint_16 delay_num, png_uint_16 delay_den, png_byte dispose_op,
- png_byte blend_op));
+ png_byte blend_op);
-typedef PNG_EXPORT (void, (*P_png_write_frame_tail)) PNGARG((png_structp png_ptr,
- png_infop info_ptr));
-static P_png_set_acTL apng_set_acTL = NULL;
-static P_png_write_frame_head apng_write_frame_head = NULL;
-static P_png_write_frame_tail apng_write_frame_tail = NULL;
+typedef void (*P_png_write_frame_tail) (png_structp png_ptr,
+ png_infop info_ptr);
+static P_png_set_acTL aPNG_set_acTL = NULL;
+static P_png_write_frame_head aPNG_write_frame_head = NULL;
+static P_png_write_frame_tail aPNG_write_frame_tail = NULL;
#endif
static inline boolean M_PNGLib(void)
@@ -842,7 +886,7 @@ static inline boolean M_PNGLib(void)
return true;
#else
static void *pnglib = NULL;
- if (apng_set_acTL && apng_write_frame_head && apng_write_frame_tail)
+ if (aPNG_set_acTL && aPNG_write_frame_head && aPNG_write_frame_tail)
return true;
if (pnglib)
return false;
@@ -862,16 +906,16 @@ static inline boolean M_PNGLib(void)
if (!pnglib)
return false;
#ifdef HAVE_SDL
- apng_set_acTL = hwSym("png_set_acTL", pnglib);
- apng_write_frame_head = hwSym("png_write_frame_head", pnglib);
- apng_write_frame_tail = hwSym("png_write_frame_tail", pnglib);
+ aPNG_set_acTL = hwSym("png_set_acTL", pnglib);
+ aPNG_write_frame_head = hwSym("png_write_frame_head", pnglib);
+ aPNG_write_frame_tail = hwSym("png_write_frame_tail", pnglib);
#endif
#ifdef _WIN32
- apng_set_acTL = GetProcAddress("png_set_acTL", pnglib);
- apng_write_frame_head = GetProcAddress("png_write_frame_head", pnglib);
- apng_write_frame_tail = GetProcAddress("png_write_frame_tail", pnglib);
+ aPNG_set_acTL = GetProcAddress("png_set_acTL", pnglib);
+ aPNG_write_frame_head = GetProcAddress("png_write_frame_head", pnglib);
+ aPNG_write_frame_tail = GetProcAddress("png_write_frame_tail", pnglib);
#endif
- return (apng_set_acTL && apng_write_frame_head && apng_write_frame_tail);
+ return (aPNG_set_acTL && aPNG_write_frame_head && aPNG_write_frame_tail);
#endif
}
@@ -885,11 +929,6 @@ static void M_PNGFrame(png_structp png_ptr, png_infop png_info_ptr, png_bytep pn
apng_frames++;
-#ifndef PNG_STATIC
- if (apng_set_acTL)
-#endif
- apng_set_acTL(apng_ptr, apng_info_ptr, apng_frames, 0);
-
for (y = 0; y < height; y++)
{
row_pointers[y] = png_buf;
@@ -897,9 +936,9 @@ static void M_PNGFrame(png_structp png_ptr, png_infop png_info_ptr, png_bytep pn
}
#ifndef PNG_STATIC
- if (apng_write_frame_head)
+ if (aPNG_write_frame_head)
#endif
- apng_write_frame_head(apng_ptr, apng_info_ptr, row_pointers,
+ aPNG_write_frame_head(apng_ptr, apng_info_ptr, row_pointers,
vid.width, /* width */
height, /* height */
0, /* x offset */
@@ -912,57 +951,21 @@ static void M_PNGFrame(png_structp png_ptr, png_infop png_info_ptr, png_bytep pn
png_write_image(png_ptr, row_pointers);
#ifndef PNG_STATIC
- if (apng_write_frame_tail)
+ if (aPNG_write_frame_tail)
#endif
- apng_write_frame_tail(apng_ptr, apng_info_ptr);
+ aPNG_write_frame_tail(apng_ptr, apng_info_ptr);
png_free(png_ptr, (png_voidp)row_pointers);
}
-static inline boolean M_PNGfind_acTL(void)
+static void M_PNGfix_acTL(png_structp png_ptr, png_infop png_info_ptr,
+ apng_infop png_ainfo_ptr)
{
- png_byte cn[8]; // 4 bytes for len then 4 byes for name
- long endpos = ftell(apng_FILE); // not the real end of file, just what of libpng wrote
- for (fseek(apng_FILE, 0, SEEK_SET); // let go to the start of the file
- ftell(apng_FILE)+12 < endpos; // let not go over the file bound
- fseek(apng_FILE, 1, SEEK_CUR) // we went 8 steps back and now we go 1 step forward
- )
- {
- if (fread(cn, sizeof(cn), 1, apng_FILE) != 1) // read 8 bytes
- return false; // failed to read data
- if (fseek(apng_FILE, -8, SEEK_CUR) != 0) //rewind 8 bytes
- return false; // failed to rewird
- if (!png_memcmp(cn+4, acTL_cn, 4)) //cmp for chuck header
- return true; // found it
- }
- return false; // acTL chuck not found
-}
-
-static void M_PNGfix_acTL(png_structp png_ptr, png_infop png_info_ptr)
-{
- png_byte data[16];
- long oldpos;
-
-#ifndef PNG_STATIC
- if (apng_set_acTL)
-#endif
- apng_set_acTL(png_ptr, png_info_ptr, apng_frames, 0);
+ apng_set_acTL(png_ptr, png_info_ptr, png_ainfo_ptr, apng_frames, 0);
#ifndef NO_PNG_DEBUG
png_debug(1, "in png_write_acTL\n");
#endif
-
- png_ptr->num_frames_to_write = apng_frames;
-
- png_save_uint_32(data, apng_frames);
- png_save_uint_32(data + 4, 0);
-
- oldpos = ftell(apng_FILE);
-
- if (M_PNGfind_acTL())
- png_write_chunk(png_ptr, (png_bytep)acTL_cn, data, (png_size_t)8);
-
- fseek(apng_FILE, oldpos, SEEK_SET);
}
static boolean M_SetupaPNG(png_const_charp filename, boolean palette)
@@ -994,6 +997,16 @@ static boolean M_SetupaPNG(png_const_charp filename, boolean palette)
return false;
}
+ apng_ainfo_ptr = apng_create_info_struct(apng_ptr);
+ if (!apng_ainfo_ptr)
+ {
+ CONS_Debug(DBG_RENDER, "M_StartMovie: Error on allocate for apng\n");
+ png_destroy_write_struct(&apng_ptr, &apng_info_ptr);
+ fclose(apng_FILE);
+ remove(filename);
+ return false;
+ }
+
png_init_io(apng_ptr, apng_FILE);
#ifdef PNG_SET_USER_LIMITS_SUPPORTED
@@ -1011,12 +1024,11 @@ static boolean M_SetupaPNG(png_const_charp filename, boolean palette)
M_PNGText(apng_ptr, apng_info_ptr, true);
-#ifndef PNG_STATIC
- if (apng_set_acTL)
-#endif
- apng_set_acTL(apng_ptr, apng_info_ptr, PNG_UINT_31_MAX, 0);
+ apng_set_set_acTL_fn(apng_ptr, apng_ainfo_ptr, aPNG_set_acTL);
- png_write_info(apng_ptr, apng_info_ptr);
+ apng_set_acTL(apng_ptr, apng_info_ptr, apng_ainfo_ptr, PNG_UINT_31_MAX, 0);
+
+ apng_write_info(apng_ptr, apng_info_ptr, apng_ainfo_ptr);
apng_frames = 0;
@@ -1219,8 +1231,8 @@ void M_StopMovie(void)
if (apng_frames)
{
- M_PNGfix_acTL(apng_ptr, apng_info_ptr);
- png_write_end(apng_ptr, apng_info_ptr);
+ M_PNGfix_acTL(apng_ptr, apng_info_ptr, apng_ainfo_ptr);
+ apng_write_end(apng_ptr, apng_info_ptr, apng_ainfo_ptr);
}
png_destroy_write_struct(&apng_ptr, &apng_info_ptr);
@@ -1529,9 +1541,13 @@ boolean M_ScreenshotResponder(event_t *ev)
return false;
ch = ev->data1;
- if (ch == KEY_F8)
+
+ if (ch >= KEY_MOUSE1 && menuactive) // If it's not a keyboard key, then don't allow it in the menus!
+ return false;
+
+ if (ch == KEY_F8 || ch == gamecontrol[gc_screenshot][0] || ch == gamecontrol[gc_screenshot][1]) // remappable F8
M_ScreenShot();
- else if (ch == KEY_F9)
+ else if (ch == KEY_F9 || ch == gamecontrol[gc_recordgif][0] || ch == gamecontrol[gc_recordgif][1]) // remappable F9
((moviemode) ? M_StopMovie : M_StartMovie)();
else
return false;
diff --git a/src/m_misc.h b/src/m_misc.h
index 14f590516..28d9cd5a5 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_queue.c b/src/m_queue.c
index daa7d7a24..a3aa04871 100644
--- a/src/m_queue.c
+++ b/src/m_queue.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2003 by James Haley
-// Copyright (C) 2003-2016 by Sonic Team Junior.
+// Copyright (C) 2003-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_queue.h b/src/m_queue.h
index 7e0d58d39..eebb21468 100644
--- a/src/m_queue.h
+++ b/src/m_queue.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2003 by James Haley
-// Copyright (C) 2003-2016 by Sonic Team Junior.
+// Copyright (C) 2003-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_random.c b/src/m_random.c
index 839b06e40..3d46f76b7 100644
--- a/src/m_random.c
+++ b/src/m_random.c
@@ -3,7 +3,7 @@
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2012-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_random.h b/src/m_random.h
index 76dd9f1db..6187bcf1c 100644
--- a/src/m_random.h
+++ b/src/m_random.h
@@ -3,7 +3,7 @@
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 2012-2016 by Matthew "Inuyasha" Walsh.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/m_swap.h b/src/m_swap.h
index 2352a0b23..2d42f6138 100644
--- a/src/m_swap.h
+++ b/src/m_swap.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/mserv.c b/src/mserv.c
index 76383ac35..cf636dfdf 100644
--- a/src/mserv.c
+++ b/src/mserv.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -177,7 +177,7 @@ static void ServerName_OnChange(void);
#define DEF_PORT "28900"
consvar_t cv_masterserver = {"masterserver", "ms.srb2.org:"DEF_PORT, CV_SAVE, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_servername = {"servername", "SRB2 server", CV_SAVE, NULL, ServerName_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_servername = {"servername", "SRB2 server", CV_SAVE|CV_CALL|CV_NOINIT, NULL, ServerName_OnChange, 0, NULL, NULL, 0, 0, NULL};
INT16 ms_RoomId = -1;
@@ -528,9 +528,21 @@ const char *GetMODVersion(void)
msg.room = MODID; // Might as well use it for something.
sprintf(msg.buffer,"%d",MODVERSION);
if (MS_Write(&msg) < 0)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("Could not send to the Master Server\n"));
+ M_StartMessage(M_GetText("Could not send to the Master Server\n"), NULL, MM_NOTHING);
+ CloseConnection();
return NULL;
+ }
+
+ if (MS_Read(&msg) < 0)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("No reply from the Master Server\n"));
+ M_StartMessage(M_GetText("No reply from the Master Server\n"), NULL, MM_NOTHING);
+ CloseConnection();
+ return NULL;
+ }
- MS_Read(&msg);
CloseConnection();
if(strcmp(msg.buffer,"NULL") != 0)
@@ -558,9 +570,19 @@ void GetMODVersion_Console(void)
msg.room = MODID; // Might as well use it for something.
sprintf(msg.buffer,"%d",MODVERSION);
if (MS_Write(&msg) < 0)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("Could not send to the Master Server\n"));
+ CloseConnection();
return;
+ }
+
+ if (MS_Read(&msg) < 0)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("No reply from the Master Server\n"));
+ CloseConnection();
+ return;
+ }
- MS_Read(&msg);
CloseConnection();
if(strcmp(msg.buffer,"NULL") != 0)
@@ -927,8 +949,8 @@ void MasterClient_Ticker(void)
static void ServerName_OnChange(void)
{
- UnregisterServer();
- RegisterServer();
+ if (con_state == MSCS_REGISTERED)
+ AddToMasterServer(false);
}
static void MasterServer_OnChange(void)
diff --git a/src/mserv.h b/src/mserv.h
index c28ece264..09cd4f089 100644
--- a/src/mserv.h
+++ b/src/mserv.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_ceilng.c b/src/p_ceilng.c
index 27d739414..757edebae 100644
--- a/src/p_ceilng.c
+++ b/src/p_ceilng.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_enemy.c b/src/p_enemy.c
index f4fffc5d6..9d2425e53 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -1,11854 +1,11854 @@
-// SONIC ROBO BLAST 2
-//-----------------------------------------------------------------------------
-// Copyright (C) 1993-1996 by id Software, Inc.
-// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
-//
-// This program is free software distributed under the
-// terms of the GNU General Public License, version 2.
-// See the 'LICENSE' file for more details.
-//-----------------------------------------------------------------------------
-/// \file p_enemy.c
-/// \brief Enemy thinking, AI
-/// Action Pointer Functions that are associated with states/frames
-
-#include "doomdef.h"
-#include "g_game.h"
-#include "p_local.h"
-#include "r_main.h"
-#include "r_state.h"
-#include "s_sound.h"
-#include "m_random.h"
-#include "m_misc.h"
-#include "r_things.h"
-#include "i_video.h"
-#include "lua_hook.h"
-
-#ifdef HW3SOUND
-#include "hardware/hw3sound.h"
-#endif
-
-#ifdef HAVE_BLUA
-boolean LUA_CallAction(const char *action, mobj_t *actor);
-#endif
-
-player_t *stplyr;
-INT32 var1;
-INT32 var2;
-
-//
-// P_NewChaseDir related LUT.
-//
-static dirtype_t opposite[] =
-{
- DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST,
- DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR
-};
-
-static dirtype_t diags[] =
-{
- DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST
-};
-
-//Real Prototypes to A_*
-void A_Fall(mobj_t *actor);
-void A_Look(mobj_t *actor);
-void A_Chase(mobj_t *actor);
-void A_FaceStabChase(mobj_t *actor);
-void A_FaceStabRev(mobj_t *actor);
-void A_FaceStabHurl(mobj_t *actor);
-void A_FaceStabMiss(mobj_t *actor);
-void A_StatueBurst(mobj_t *actor);
-void A_JetJawRoam(mobj_t *actor);
-void A_JetJawChomp(mobj_t *actor);
-void A_PointyThink(mobj_t *actor);
-void A_CheckBuddy(mobj_t *actor);
-void A_HoodFire(mobj_t *actor);
-void A_HoodThink(mobj_t *actor);
-void A_HoodFall(mobj_t *actor);
-void A_ArrowBonks(mobj_t *actor);
-void A_SnailerThink(mobj_t *actor);
-void A_SharpChase(mobj_t *actor);
-void A_SharpSpin(mobj_t *actor);
-void A_SharpDecel(mobj_t *actor);
-void A_CrushstaceanWalk(mobj_t *actor);
-void A_CrushstaceanPunch(mobj_t *actor);
-void A_CrushclawAim(mobj_t *actor);
-void A_CrushclawLaunch(mobj_t *actor);
-void A_VultureVtol(mobj_t *actor);
-void A_VultureCheck(mobj_t *actor);
-void A_SkimChase(mobj_t *actor);
-void A_FaceTarget(mobj_t *actor);
-void A_FaceTracer(mobj_t *actor);
-void A_LobShot(mobj_t *actor);
-void A_FireShot(mobj_t *actor);
-void A_SuperFireShot(mobj_t *actor);
-void A_BossFireShot(mobj_t *actor);
-void A_Boss7FireMissiles(mobj_t *actor);
-void A_Boss1Laser(mobj_t *actor);
-void A_FocusTarget(mobj_t *actor);
-void A_Boss4Reverse(mobj_t *actor);
-void A_Boss4SpeedUp(mobj_t *actor);
-void A_Boss4Raise(mobj_t *actor);
-void A_SkullAttack(mobj_t *actor);
-void A_BossZoom(mobj_t *actor);
-void A_BossScream(mobj_t *actor);
-void A_Scream(mobj_t *actor);
-void A_Pain(mobj_t *actor);
-void A_1upThinker(mobj_t *actor);
-void A_MonitorPop(mobj_t *actor);
-void A_GoldMonitorPop(mobj_t *actor);
-void A_GoldMonitorRestore(mobj_t *actor);
-void A_GoldMonitorSparkle(mobj_t *actor);
-void A_Explode(mobj_t *actor);
-void A_BossDeath(mobj_t *actor);
-void A_CustomPower(mobj_t *actor);
-void A_GiveWeapon(mobj_t *actor);
-void A_RingBox(mobj_t *actor);
-void A_Invincibility(mobj_t *actor);
-void A_SuperSneakers(mobj_t *actor);
-void A_AwardScore(mobj_t *actor);
-void A_ExtraLife(mobj_t *actor);
-void A_GiveShield(mobj_t *actor);
-void A_GravityBox(mobj_t *actor);
-void A_ScoreRise(mobj_t *actor);
-void A_BunnyHop(mobj_t *actor);
-void A_BubbleSpawn(mobj_t *actor);
-void A_FanBubbleSpawn(mobj_t *actor);
-void A_BubbleRise(mobj_t *actor);
-void A_BubbleCheck(mobj_t *actor);
-void A_AttractChase(mobj_t *actor);
-void A_DropMine(mobj_t *actor);
-void A_FishJump(mobj_t *actor);
-void A_ThrownRing(mobj_t *actor);
-void A_SetSolidSteam(mobj_t *actor);
-void A_UnsetSolidSteam(mobj_t *actor);
-void A_SignPlayer(mobj_t *actor);
-void A_OverlayThink(mobj_t *actor);
-void A_JetChase(mobj_t *actor);
-void A_JetbThink(mobj_t *actor);
-void A_JetgShoot(mobj_t *actor);
-void A_JetgThink(mobj_t *actor);
-void A_ShootBullet(mobj_t *actor);
-void A_MinusDigging(mobj_t *actor);
-void A_MinusPopup(mobj_t *actor);
-void A_MinusCheck(mobj_t *actor);
-void A_ChickenCheck(mobj_t *actor);
-void A_MouseThink(mobj_t *actor);
-void A_DetonChase(mobj_t *actor);
-void A_CapeChase(mobj_t *actor);
-void A_RotateSpikeBall(mobj_t *actor);
-void A_SlingAppear(mobj_t *actor);
-void A_UnidusBall(mobj_t *actor);
-void A_RockSpawn(mobj_t *actor);
-void A_SetFuse(mobj_t *actor);
-void A_CrawlaCommanderThink(mobj_t *actor);
-void A_RingExplode(mobj_t *actor);
-void A_OldRingExplode(mobj_t *actor);
-void A_MixUp(mobj_t *actor);
-void A_RecyclePowers(mobj_t *actor);
-void A_Boss2TakeDamage(mobj_t *actor);
-void A_Boss7Chase(mobj_t *actor);
-void A_GoopSplat(mobj_t *actor);
-void A_Boss2PogoSFX(mobj_t *actor);
-void A_Boss2PogoTarget(mobj_t *actor);
-void A_EggmanBox(mobj_t *actor);
-void A_TurretFire(mobj_t *actor);
-void A_SuperTurretFire(mobj_t *actor);
-void A_TurretStop(mobj_t *actor);
-void A_SparkFollow(mobj_t *actor);
-void A_BuzzFly(mobj_t *actor);
-void A_GuardChase(mobj_t *actor);
-void A_EggShield(mobj_t *actor);
-void A_SetReactionTime(mobj_t *actor);
-void A_Boss1Spikeballs(mobj_t *actor);
-void A_Boss3TakeDamage(mobj_t *actor);
-void A_Boss3Path(mobj_t *actor);
-void A_LinedefExecute(mobj_t *actor);
-void A_PlaySeeSound(mobj_t *actor);
-void A_PlayAttackSound(mobj_t *actor);
-void A_PlayActiveSound(mobj_t *actor);
-void A_SmokeTrailer(mobj_t *actor);
-void A_SpawnObjectAbsolute(mobj_t *actor);
-void A_SpawnObjectRelative(mobj_t *actor);
-void A_ChangeAngleRelative(mobj_t *actor);
-void A_ChangeAngleAbsolute(mobj_t *actor);
-void A_PlaySound(mobj_t *actor);
-void A_FindTarget(mobj_t *actor);
-void A_FindTracer(mobj_t *actor);
-void A_SetTics(mobj_t *actor);
-void A_SetRandomTics(mobj_t *actor);
-void A_ChangeColorRelative(mobj_t *actor);
-void A_ChangeColorAbsolute(mobj_t *actor);
-void A_MoveRelative(mobj_t *actor);
-void A_MoveAbsolute(mobj_t *actor);
-void A_Thrust(mobj_t *actor);
-void A_ZThrust(mobj_t *actor);
-void A_SetTargetsTarget(mobj_t *actor);
-void A_SetObjectFlags(mobj_t *actor);
-void A_SetObjectFlags2(mobj_t *actor);
-void A_RandomState(mobj_t *actor);
-void A_RandomStateRange(mobj_t *actor);
-void A_DualAction(mobj_t *actor);
-void A_RemoteAction(mobj_t *actor);
-void A_ToggleFlameJet(mobj_t *actor);
-void A_OrbitNights(mobj_t *actor);
-void A_GhostMe(mobj_t *actor);
-void A_SetObjectState(mobj_t *actor);
-void A_SetObjectTypeState(mobj_t *actor);
-void A_KnockBack(mobj_t *actor);
-void A_PushAway(mobj_t *actor);
-void A_RingDrain(mobj_t *actor);
-void A_SplitShot(mobj_t *actor);
-void A_MissileSplit(mobj_t *actor);
-void A_MultiShot(mobj_t *actor);
-void A_InstaLoop(mobj_t *actor);
-void A_Custom3DRotate(mobj_t *actor);
-void A_SearchForPlayers(mobj_t *actor);
-void A_CheckRandom(mobj_t *actor);
-void A_CheckTargetRings(mobj_t *actor);
-void A_CheckRings(mobj_t *actor);
-void A_CheckTotalRings(mobj_t *actor);
-void A_CheckHealth(mobj_t *actor);
-void A_CheckRange(mobj_t *actor);
-void A_CheckHeight(mobj_t *actor);
-void A_CheckTrueRange(mobj_t *actor);
-void A_CheckThingCount(mobj_t *actor);
-void A_CheckAmbush(mobj_t *actor);
-void A_CheckCustomValue(mobj_t *actor);
-void A_CheckCusValMemo(mobj_t *actor);
-void A_SetCustomValue(mobj_t *actor);
-void A_UseCusValMemo(mobj_t *actor);
-void A_RelayCustomValue(mobj_t *actor);
-void A_CusValAction(mobj_t *actor);
-void A_ForceStop(mobj_t *actor);
-void A_ForceWin(mobj_t *actor);
-void A_SpikeRetract(mobj_t *actor);
-void A_InfoState(mobj_t *actor);
-void A_Repeat(mobj_t *actor);
-void A_SetScale(mobj_t *actor);
-void A_RemoteDamage(mobj_t *actor);
-void A_HomingChase(mobj_t *actor);
-void A_TrapShot(mobj_t *actor);
-void A_Boss1Chase(mobj_t *actor);
-void A_Boss2Chase(mobj_t *actor);
-void A_Boss2Pogo(mobj_t *actor);
-void A_BossJetFume(mobj_t *actor);
-void A_VileTarget(mobj_t *actor);
-void A_VileAttack(mobj_t *actor);
-void A_VileFire(mobj_t *actor);
-void A_BrakChase(mobj_t *actor);
-void A_BrakFireShot(mobj_t *actor);
-void A_BrakLobShot(mobj_t *actor);
-void A_NapalmScatter(mobj_t *actor);
-void A_SpawnFreshCopy(mobj_t *actor);
-void A_FlickySpawn(mobj_t *actor);
-void A_FlickyCenter(mobj_t *actor);
-void A_FlickyAim(mobj_t *actor);
-void A_FlickyFly(mobj_t *actor);
-void A_FlickySoar(mobj_t *actor);
-void A_FlickyCoast(mobj_t *actor);
-void A_FlickyHop(mobj_t *actor);
-void A_FlickyFlounder(mobj_t *actor);
-void A_FlickyCheck(mobj_t *actor);
-void A_FlickyHeightCheck(mobj_t *actor);
-void A_FlickyFlutter(mobj_t *actor);
-void A_FlameParticle(mobj_t *actor);
-void A_FadeOverlay(mobj_t *actor);
-void A_Boss5Jump(mobj_t *actor);
-void A_LightBeamReset(mobj_t *actor);
-void A_MineExplode(mobj_t *actor);
-void A_MineRange(mobj_t *actor);
-void A_ConnectToGround(mobj_t *actor);
-void A_SpawnParticleRelative(mobj_t *actor);
-void A_MultiShotDist(mobj_t *actor);
-void A_WhoCaresIfYourSonIsABee(mobj_t *actor);
-void A_ParentTriesToSleep(mobj_t *actor);
-void A_CryingToMomma(mobj_t *actor);
-void A_CheckFlags2(mobj_t *actor);
-//for p_enemy.c
-
-//
-// ENEMY THINKING
-// Enemies are always spawned with targetplayer = -1, threshold = 0
-// Most monsters are spawned unaware of all players, but some can be made preaware.
-//
-
-//
-// P_CheckMeleeRange
-//
-boolean P_CheckMeleeRange(mobj_t *actor)
-{
- mobj_t *pl;
- fixed_t dist;
-
- if (!actor->target)
- return false;
-
- pl = actor->target;
- dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
-
- if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
- return false;
-
- // check height now, so that damn crawlas cant attack
- // you if you stand on a higher ledge.
- if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
- return false;
-
- if (!P_CheckSight(actor, actor->target))
- return false;
-
- return true;
-}
-
-// P_CheckMeleeRange for Jettysyn Bomber.
-boolean P_JetbCheckMeleeRange(mobj_t *actor)
-{
- mobj_t *pl;
- fixed_t dist;
-
- if (!actor->target)
- return false;
-
- pl = actor->target;
- dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
-
- if (dist >= (actor->radius + pl->radius)*2)
- return false;
-
- if (actor->eflags & MFE_VERTICALFLIP)
- {
- if (pl->z < actor->z + actor->height + FixedMul(40<scale))
- return false;
- }
- else
- {
- if (pl->z + pl->height > actor->z - FixedMul(40<scale))
- return false;
- }
-
- return true;
-}
-
-// P_CheckMeleeRange for CastleBot FaceStabber.
-boolean P_FaceStabCheckMeleeRange(mobj_t *actor)
-{
- mobj_t *pl;
- fixed_t dist;
-
- if (!actor->target)
- return false;
-
- pl = actor->target;
- dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
-
- if (dist >= (actor->radius + pl->radius)*4)
- return false;
-
- if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
- return false;
-
- if (!P_CheckSight(actor, actor->target))
- return false;
-
- return true;
-}
-
-// P_CheckMeleeRange for Skim.
-boolean P_SkimCheckMeleeRange(mobj_t *actor)
-{
- mobj_t *pl;
- fixed_t dist;
-
- if (!actor->target)
- return false;
-
- pl = actor->target;
- dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
-
- if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
- return false;
-
- if (actor->eflags & MFE_VERTICALFLIP)
- {
- if (pl->z < actor->z + actor->height + FixedMul(24<scale))
- return false;
- }
- else
- {
- if (pl->z + pl->height > actor->z - FixedMul(24<scale))
- return false;
- }
-
- return true;
-}
-
-//
-// P_CheckMissileRange
-//
-boolean P_CheckMissileRange(mobj_t *actor)
-{
- fixed_t dist;
-
- if (!actor->target)
- return false;
-
- if (actor->reactiontime)
- return false; // do not attack yet
-
- if (!P_CheckSight(actor, actor->target))
- return false;
-
- // OPTIMIZE: get this from a global checksight
- dist = P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) - FixedMul(64*FRACUNIT, actor->scale);
-
- if (!actor->info->meleestate)
- dist -= FixedMul(128*FRACUNIT, actor->scale); // no melee attack, so fire more
-
- dist >>= FRACBITS;
-
- if (actor->type == MT_EGGMOBILE)
- dist >>= 1;
-
- if (dist > 200)
- dist = 200;
-
- if (actor->type == MT_EGGMOBILE && dist > 160)
- dist = 160;
-
- if (P_RandomByte() < dist)
- return false;
-
- return true;
-}
-
-/** Checks for water in a sector.
- * Used by Skim movements.
- *
- * \param x X coordinate on the map.
- * \param y Y coordinate on the map.
- * \return True if there's water at this location, false if not.
- * \sa ::MT_SKIM
- */
-static boolean P_WaterInSector(mobj_t *mobj, fixed_t x, fixed_t y)
-{
- sector_t *sector;
-
- sector = R_PointInSubsector(x, y)->sector;
-
- if (sector->ffloors)
- {
- ffloor_t *rover;
-
- for (rover = sector->ffloors; rover; rover = rover->next)
- {
- if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE))
- continue;
-
- if (*rover->topheight >= mobj->floorz && *rover->topheight <= mobj->z)
- return true; // we found water!!
- }
- }
-
- return false;
-}
-
-static const fixed_t xspeed[NUMDIRS] = {FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS)), 0, 46341>>(16-FRACBITS)};
-static const fixed_t yspeed[NUMDIRS] = {0, 46341>>(16-FRACBITS), FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS))};
-
-/** Moves an actor in its current direction.
- *
- * \param actor Actor object to move.
- * \return False if the move is blocked, otherwise true.
- */
-boolean P_Move(mobj_t *actor, fixed_t speed)
-{
- fixed_t tryx, tryy;
- dirtype_t movedir = actor->movedir;
-
- if (movedir == DI_NODIR || !actor->health)
- return false;
-
- I_Assert(movedir < NUMDIRS);
-
- tryx = actor->x + FixedMul(speed*xspeed[movedir], actor->scale);
- if (twodlevel || actor->flags2 & MF2_TWOD)
- tryy = actor->y;
- else
- tryy = actor->y + FixedMul(speed*yspeed[movedir], actor->scale);
-
- if (actor->type == MT_SKIM && !P_WaterInSector(actor, tryx, tryy)) // bail out if sector lacks water
- return false;
-
- if (!P_TryMove(actor, tryx, tryy, false))
- {
- if (actor->flags & MF_FLOAT && floatok)
- {
- // must adjust height
- if (actor->z < tmfloorz)
- actor->z += FixedMul(FLOATSPEED, actor->scale);
- else
- actor->z -= FixedMul(FLOATSPEED, actor->scale);
-
- if (actor->type == MT_JETJAW && actor->z + actor->height > actor->watertop)
- actor->z = actor->watertop - actor->height;
-
- actor->flags2 |= MF2_INFLOAT;
- return true;
- }
-
- return false;
- }
- else
- actor->flags2 &= ~MF2_INFLOAT;
-
- return true;
-}
-
-/** Attempts to move an actor on in its current direction.
- * If the move succeeds, the actor's move count is reset
- * randomly to a value from 0 to 15.
- *
- * \param actor Actor to move.
- * \return True if the move succeeds, false if the move is blocked.
- */
-static boolean P_TryWalk(mobj_t *actor)
-{
- if (!P_Move(actor, actor->info->speed))
- return false;
- actor->movecount = P_RandomByte() & 15;
- return true;
-}
-
-void P_NewChaseDir(mobj_t *actor)
-{
- fixed_t deltax, deltay;
- dirtype_t d[3];
- dirtype_t tdir = DI_NODIR, olddir, turnaround;
-
- I_Assert(actor->target != NULL);
- I_Assert(!P_MobjWasRemoved(actor->target));
-
- olddir = actor->movedir;
-
- if (olddir >= NUMDIRS)
- olddir = DI_NODIR;
-
- if (olddir != DI_NODIR)
- turnaround = opposite[olddir];
- else
- turnaround = olddir;
-
- deltax = actor->target->x - actor->x;
- deltay = actor->target->y - actor->y;
-
- if (deltax > FixedMul(10*FRACUNIT, actor->scale))
- d[1] = DI_EAST;
- else if (deltax < -FixedMul(10*FRACUNIT, actor->scale))
- d[1] = DI_WEST;
- else
- d[1] = DI_NODIR;
-
- if (twodlevel || actor->flags2 & MF2_TWOD)
- d[2] = DI_NODIR;
- if (deltay < -FixedMul(10*FRACUNIT, actor->scale))
- d[2] = DI_SOUTH;
- else if (deltay > FixedMul(10*FRACUNIT, actor->scale))
- d[2] = DI_NORTH;
- else
- d[2] = DI_NODIR;
-
- // try direct route
- if (d[1] != DI_NODIR && d[2] != DI_NODIR)
- {
- dirtype_t newdir = diags[((deltay < 0)<<1) + (deltax > 0)];
-
- actor->movedir = newdir;
- if ((newdir != turnaround) && P_TryWalk(actor))
- return;
- }
-
- // try other directions
- if (P_RandomChance(25*FRACUNIT/32) || abs(deltay) > abs(deltax))
- {
- tdir = d[1];
- d[1] = d[2];
- d[2] = tdir;
- }
-
- if (d[1] == turnaround)
- d[1] = DI_NODIR;
- if (d[2] == turnaround)
- d[2] = DI_NODIR;
-
- if (d[1] != DI_NODIR)
- {
- actor->movedir = d[1];
-
- if (P_TryWalk(actor))
- return; // either moved forward or attacked
- }
-
- if (d[2] != DI_NODIR)
- {
- actor->movedir = d[2];
-
- if (P_TryWalk(actor))
- return;
- }
-
- // there is no direct path to the player, so pick another direction.
- if (olddir != DI_NODIR)
- {
- actor->movedir =olddir;
-
- if (P_TryWalk(actor))
- return;
- }
-
- // randomly determine direction of search
- if (P_RandomChance(FRACUNIT/2))
- {
- for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
- {
- if (tdir != turnaround)
- {
- actor->movedir = tdir;
-
- if (P_TryWalk(actor))
- return;
- }
- }
- }
- else
- {
- for (tdir = DI_SOUTHEAST; tdir >= DI_EAST; tdir--)
- {
- if (tdir != turnaround)
- {
- actor->movedir = tdir;
-
- if (P_TryWalk(actor))
- return;
- }
- }
- }
-
- if (turnaround != DI_NODIR)
- {
- actor->movedir = turnaround;
-
- if (P_TryWalk(actor))
- return;
- }
-
- actor->movedir = (angle_t)DI_NODIR; // cannot move
-}
-
-/** Looks for players to chase after, aim at, or whatever.
- *
- * \param actor The object looking for flesh.
- * \param allaround Look all around? If false, only players in a 180-degree
- * range in front will be spotted.
- * \param dist If > 0, checks distance
- * \return True if a player is found, otherwise false.
- * \sa P_SupermanLook4Players
- */
-boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist)
-{
- INT32 c = 0, stop;
- player_t *player;
- angle_t an;
-
- // BP: first time init, this allow minimum lastlook changes
- if (actor->lastlook < 0)
- actor->lastlook = P_RandomByte();
-
- actor->lastlook %= MAXPLAYERS;
-
- stop = (actor->lastlook - 1) & PLAYERSMASK;
-
- for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
- {
- // done looking
- if (actor->lastlook == stop)
- return false;
-
- if (!playeringame[actor->lastlook])
- continue;
-
- if (c++ == 2)
- return false;
-
- player = &players[actor->lastlook];
-
- if ((netgame || multiplayer) && player->spectator)
- continue;
-
- if (player->pflags & PF_INVIS)
- continue; // ignore notarget
-
- if (!player->mo || P_MobjWasRemoved(player->mo))
- continue;
-
- if (player->mo->health <= 0)
- continue; // dead
-
- if (dist > 0
- && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist)
- continue; // Too far away
-
- if (!allaround)
- {
- an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
- if (an > ANGLE_90 && an < ANGLE_270)
- {
- dist = P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y);
- // if real close, react anyway
- if (dist > FixedMul(MELEERANGE, actor->scale))
- continue; // behind back
- }
- }
-
- if (!P_CheckSight(actor, player->mo))
- continue; // out of sight
-
- if (tracer)
- P_SetTarget(&actor->tracer, player->mo);
- else
- P_SetTarget(&actor->target, player->mo);
- return true;
- }
-
- //return false;
-}
-
-/** Looks for a player with a ring shield.
- * Used by rings.
- *
- * \param actor Ring looking for a shield to be attracted to.
- * \return True if a player with ring shield is found, otherwise false.
- * \sa A_AttractChase
- */
-static boolean P_LookForShield(mobj_t *actor)
-{
- INT32 c = 0, stop;
- player_t *player;
-
- // BP: first time init, this allow minimum lastlook changes
- if (actor->lastlook < 0)
- actor->lastlook = P_RandomByte();
-
- actor->lastlook %= MAXPLAYERS;
-
- stop = (actor->lastlook - 1) & PLAYERSMASK;
-
- for (; ; actor->lastlook = ((actor->lastlook + 1) & PLAYERSMASK))
- {
- // done looking
- if (actor->lastlook == stop)
- return false;
-
- if (!playeringame[actor->lastlook])
- continue;
-
- if (c++ == 2)
- return false;
-
- player = &players[actor->lastlook];
-
- if (!player->mo || player->mo->health <= 0)
- continue; // dead
-
- //When in CTF, don't pull rings that you cannot pick up.
- if ((actor->type == MT_REDTEAMRING && player->ctfteam != 1) ||
- (actor->type == MT_BLUETEAMRING && player->ctfteam != 2))
- continue;
-
- if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
- && (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
- {
- P_SetTarget(&actor->tracer, player->mo);
-
- if (actor->hnext)
- P_SetTarget(&actor->hnext->hprev, actor->hprev);
- if (actor->hprev)
- P_SetTarget(&actor->hprev->hnext, actor->hnext);
-
- return true;
- }
- }
-
- //return false;
-}
-
-#ifdef WEIGHTEDRECYCLER
-// Compares players to see who currently has the "best" items, etc.
-static int P_RecycleCompare(const void *p1, const void *p2)
-{
- player_t *player1 = &players[*(const UINT8 *)p1];
- player_t *player2 = &players[*(const UINT8 *)p2];
-
- // Non-shooting gametypes
- if (!G_PlatformGametype())
- {
- // Invincibility.
- if (player1->powers[pw_invulnerability] > player2->powers[pw_invulnerability]) return -1;
- else if (player2->powers[pw_invulnerability] > player1->powers[pw_invulnerability]) return 1;
-
- // One has a shield, the other doesn't.
- if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
- else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
-
- // Sneakers.
- if (player1->powers[pw_sneakers] > player2->powers[pw_sneakers]) return -1;
- else if (player2->powers[pw_sneakers] > player1->powers[pw_sneakers]) return 1;
- }
- else // Match, Team Match, CTF, Tag, Etc.
- {
- UINT8 player1_em = M_CountBits((UINT32)player1->powers[pw_emeralds], 7);
- UINT8 player2_em = M_CountBits((UINT32)player2->powers[pw_emeralds], 7);
-
- UINT8 player1_rw = M_CountBits((UINT32)player1->ringweapons, NUM_WEAPONS-1);
- UINT8 player2_rw = M_CountBits((UINT32)player2->ringweapons, NUM_WEAPONS-1);
-
- UINT16 player1_am = player1->powers[pw_infinityring] // max 800
- + player1->powers[pw_automaticring] // max 300
- + (player1->powers[pw_bouncering] * 3) // max 100
- + (player1->powers[pw_explosionring] * 6) // max 50
- + (player1->powers[pw_scatterring] * 3) // max 100
- + (player1->powers[pw_grenadering] * 6) // max 50
- + (player1->powers[pw_railring] * 6); // max 50
- UINT16 player2_am = player2->powers[pw_infinityring] // max 800
- + player2->powers[pw_automaticring] // max 300
- + (player2->powers[pw_bouncering] * 3) // max 100
- + (player2->powers[pw_explosionring] * 6) // max 50
- + (player2->powers[pw_scatterring] * 3) // max 100
- + (player2->powers[pw_grenadering] * 6) // max 50
- + (player2->powers[pw_railring] * 6); // max 50
-
- // Super trumps everything.
- if (player1->powers[pw_super] && !player2->powers[pw_super]) return -1;
- else if (player2->powers[pw_super] && !player1->powers[pw_super]) return 1;
-
- // Emerald count if neither player is Super.
- if (player1_em > player2_em) return -1;
- else if (player1_em < player2_em) return 1;
-
- // One has a shield, the other doesn't.
- // (the likelihood of a shielded player being worse off than one without one is low.)
- if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
- else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
-
- // Ring weapons count
- if (player1_rw > player2_rw) return -1;
- else if (player1_rw < player2_rw) return 1;
-
- // Ring ammo if they have the same number of weapons
- if (player1_am > player2_am) return -1;
- else if (player1_am < player2_am) return 1;
- }
-
- // Identical for our purposes
- return 0;
-}
-#endif
-
-// Handles random monitor weights via console.
-static mobjtype_t P_DoRandomBoxChances(void)
-{
- mobjtype_t spawnchance[256];
- INT32 numchoices = 0, i = 0;
-
- if (!(netgame || multiplayer))
- {
- switch (P_RandomKey(10))
- {
- case 0:
- return MT_RING_ICON;
- case 1:
- return MT_SNEAKERS_ICON;
- case 2:
- return MT_INVULN_ICON;
- case 3:
- return MT_WHIRLWIND_ICON;
- case 4:
- return MT_ELEMENTAL_ICON;
- case 5:
- return MT_ATTRACT_ICON;
- case 6:
- return MT_FORCE_ICON;
- case 7:
- return MT_ARMAGEDDON_ICON;
- case 8:
- return MT_1UP_ICON;
- case 9:
- return MT_EGGMAN_ICON;
- }
- return MT_NULL;
- }
-
-#define QUESTIONBOXCHANCES(type, cvar) \
-for (i = cvar.value; i; --i) spawnchance[numchoices++] = type
- QUESTIONBOXCHANCES(MT_RING_ICON, cv_superring);
- QUESTIONBOXCHANCES(MT_SNEAKERS_ICON, cv_supersneakers);
- QUESTIONBOXCHANCES(MT_INVULN_ICON, cv_invincibility);
- QUESTIONBOXCHANCES(MT_WHIRLWIND_ICON, cv_jumpshield);
- QUESTIONBOXCHANCES(MT_ELEMENTAL_ICON, cv_watershield);
- QUESTIONBOXCHANCES(MT_ATTRACT_ICON, cv_ringshield);
- QUESTIONBOXCHANCES(MT_FORCE_ICON, cv_forceshield);
- QUESTIONBOXCHANCES(MT_ARMAGEDDON_ICON, cv_bombshield);
- QUESTIONBOXCHANCES(MT_1UP_ICON, cv_1up);
- QUESTIONBOXCHANCES(MT_EGGMAN_ICON, cv_eggmanbox);
- QUESTIONBOXCHANCES(MT_MIXUP_ICON, cv_teleporters);
- QUESTIONBOXCHANCES(MT_RECYCLER_ICON, cv_recycler);
-#undef QUESTIONBOXCHANCES
-
- if (numchoices == 0) return MT_NULL;
- return spawnchance[P_RandomKey(numchoices)];
-}
-
-//
-// ACTION ROUTINES
-//
-
-// Function: A_Look
-//
-// Description: Look for a player and set your target to them.
-//
-// var1:
-// lower 16 bits = look all around
-// upper 16 bits = distance limit
-// var2 = If 1, only change to seestate. If 2, only play seesound. If 0, do both.
-//
-void A_Look(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Look", actor))
- return;
-#endif
-
- if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale)))
- return;
-
- // go into chase state
- if (!locvar2)
- {
- P_SetMobjState(actor, actor->info->seestate);
- A_PlaySeeSound(actor);
- }
- else if (locvar2 == 1) // Only go into seestate
- P_SetMobjState(actor, actor->info->seestate);
- else if (locvar2 == 2) // Only play seesound
- A_PlaySeeSound(actor);
-}
-
-// Function: A_Chase
-//
-// Description: Chase after your target.
-//
-// var1:
-// 1 = don't check meleestate
-// 2 = don't check missilestate
-// 3 = don't check meleestate and missilestate
-// var2 = unused
-//
-void A_Chase(mobj_t *actor)
-{
- INT32 delta;
- INT32 locvar1 = var1;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Chase", actor))
- return;
-#endif
-
- I_Assert(actor != NULL);
- I_Assert(!P_MobjWasRemoved(actor));
-
- if (actor->reactiontime)
- actor->reactiontime--;
-
- // modify target threshold
- if (actor->threshold)
- {
- if (!actor->target || actor->target->health <= 0)
- actor->threshold = 0;
- else
- actor->threshold--;
- }
-
- // turn towards movement direction if not there yet
- if (actor->movedir < NUMDIRS)
- {
- actor->angle &= (7<<29);
- delta = actor->angle - (actor->movedir << 29);
-
- if (delta > 0)
- actor->angle -= ANGLE_45;
- else if (delta < 0)
- actor->angle += ANGLE_45;
- }
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- P_SetMobjStateNF(actor, actor->info->spawnstate);
- return;
- }
-
- // do not attack twice in a row
- if (actor->flags2 & MF2_JUSTATTACKED)
- {
- actor->flags2 &= ~MF2_JUSTATTACKED;
- P_NewChaseDir(actor);
- return;
- }
-
- // check for melee attack
- if (!(locvar1 & 1) && actor->info->meleestate && P_CheckMeleeRange(actor))
- {
- if (actor->info->attacksound)
- S_StartAttackSound(actor, actor->info->attacksound);
-
- P_SetMobjState(actor, actor->info->meleestate);
- return;
- }
-
- // check for missile attack
- if (!(locvar1 & 2) && actor->info->missilestate)
- {
- if (actor->movecount || !P_CheckMissileRange(actor))
- goto nomissile;
-
- P_SetMobjState(actor, actor->info->missilestate);
- actor->flags2 |= MF2_JUSTATTACKED;
- return;
- }
-
-nomissile:
- // possibly choose another target
- if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
- && P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- // chase towards player
- if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
- P_NewChaseDir(actor);
-}
-
-// Function: A_FaceStabChase
-//
-// Description: Unused variant of A_Chase for Castlebot Facestabber.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_FaceStabChase(mobj_t *actor)
-{
- INT32 delta;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FaceStabChase", actor))
- return;
-#endif
-
- if (actor->reactiontime)
- actor->reactiontime--;
-
- // modify target threshold
- if (actor->threshold)
- {
- if (!actor->target || actor->target->health <= 0)
- actor->threshold = 0;
- else
- actor->threshold--;
- }
-
- // turn towards movement direction if not there yet
- if (actor->movedir < NUMDIRS)
- {
- actor->angle &= (7<<29);
- delta = actor->angle - (actor->movedir << 29);
-
- if (delta > 0)
- actor->angle -= ANGLE_45;
- else if (delta < 0)
- actor->angle += ANGLE_45;
- }
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- P_SetMobjStateNF(actor, actor->info->spawnstate);
- return;
- }
-
- // do not attack twice in a row
- if (actor->flags2 & MF2_JUSTATTACKED)
- {
- actor->flags2 &= ~MF2_JUSTATTACKED;
- P_NewChaseDir(actor);
- return;
- }
-
- // check for melee attack
- if (actor->info->meleestate && P_FaceStabCheckMeleeRange(actor))
- {
- if (actor->info->attacksound)
- S_StartAttackSound(actor, actor->info->attacksound);
-
- P_SetMobjState(actor, actor->info->meleestate);
- return;
- }
-
- // check for missile attack
- if (actor->info->missilestate)
- {
- if (actor->movecount || !P_CheckMissileRange(actor))
- goto nomissile;
-
- P_SetMobjState(actor, actor->info->missilestate);
- actor->flags2 |= MF2_JUSTATTACKED;
- return;
- }
-
-nomissile:
- // possibly choose another target
- if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
- && P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- // chase towards player
- if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
- P_NewChaseDir(actor);
-}
-
-static void P_SharpDust(mobj_t *actor, mobjtype_t type, angle_t ang)
-{
- mobj_t *dust;
-
- if (!type || !P_IsObjectOnGround(actor))
- return;
-
- dust = P_SpawnMobjFromMobj(actor,
- -P_ReturnThrustX(actor, ang, 16<angle, actor->radius),
- -P_ReturnThrustY(actor, actor->angle, actor->radius),
- actor->height/3,
- MT_PARTICLE);
- flume->destscale = actor->scale*3;
- P_SetScale(flume, flume->destscale);
- P_SetTarget(&flume->target, actor);
- flume->sprite = SPR_JETF;
- flume->frame = FF_FULLBRIGHT;
- flume->tics = 2;
-}
-
-// Function: A_FaceStabRev
-//
-// Description: Facestabber rev action
-//
-// var1 = effective duration
-// var2 = effective nextstate
-//
-void A_FaceStabRev(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FaceStabRev", actor))
- return;
-#endif
-
- if (!actor->target)
- {
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- actor->extravalue1 = 0;
-
- if (!actor->reactiontime)
- {
- actor->reactiontime = locvar1;
- S_StartSound(actor, actor->info->activesound);
- }
- else
- {
- if ((--actor->reactiontime) == 0)
- {
- S_StartSound(actor, actor->info->attacksound);
- P_SetMobjState(actor, locvar2);
- }
- else
- {
- P_TryMove(actor, actor->x - P_ReturnThrustX(actor, actor->angle, 2<y - P_ReturnThrustY(actor, actor->angle, 2<target)
- {
- angle_t visang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
- // Calculate new direction.
- angle_t dirang = actor->angle;
- angle_t diffang = visang - dirang;
-
- if (locvar1) // Allow homing?
- {
- if (diffang > ANGLE_180)
- {
- angle_t workang = locvar1*(InvAngle(diffang)>>5);
- diffang += InvAngle(workang);
- }
- else
- diffang += (locvar1*(diffang>>5));
- }
- diffang += ANGLE_45;
-
- // Check the sight cone.
- if (diffang < ANGLE_90)
- {
- actor->angle = dirang;
- if (++actor->extravalue2 < 4)
- actor->extravalue2 = 4;
- else if (actor->extravalue2 > 26)
- actor->extravalue2 = 26;
-
- if (P_TryMove(actor,
- actor->x + P_ReturnThrustX(actor, dirang, actor->extravalue2<y + P_ReturnThrustY(actor, dirang, actor->extravalue2<extravalue1);
- fixed_t basesize = FRACUNIT/MAXVAL;
- mobj_t *hwork = actor;
- INT32 dist = 113;
- fixed_t xo = P_ReturnThrustX(actor, actor->angle, dist*basesize);
- fixed_t yo = P_ReturnThrustY(actor, actor->angle, dist*basesize);
-
- while (step > 0)
- {
- if (!hwork->hnext)
- P_SetTarget(&hwork->hnext, P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_FACESTABBERSPEAR));
- hwork = hwork->hnext;
- hwork->angle = actor->angle + ANGLE_90;
- hwork->destscale = FixedSqrt(step*basesize);
- P_SetScale(hwork, hwork->destscale);
- hwork->fuse = 2;
- P_TeleportMove(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<extravalue1 >= MAXVAL)
- actor->extravalue1 -= NUMGRADS;
-
- if ((step % 5) == 0)
- P_SharpDust(actor, MT_SPINDUST, actor->angle);
-
- P_FaceStabFlume(actor);
- return;
-#undef MAXVAL
-#undef NUMGRADS
-#undef NUMSTEPS
- }
- }
- }
-
- P_SetMobjState(actor, locvar2);
- actor->reactiontime = actor->info->reactiontime;
-}
-
-// Function: A_FaceStabMiss
-//
-// Description: Facestabber miss action
-//
-// var1 = unused
-// var2 = effective nextstate
-//
-void A_FaceStabMiss(mobj_t *actor)
-{
- //INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FaceStabMiss", actor))
- return;
-#endif
-
- if (++actor->extravalue1 >= 3)
- {
- actor->extravalue2 -= 2;
- actor->extravalue1 = 0;
- S_StartSound(actor, sfx_s3k47);
- P_SharpDust(actor, MT_SPINDUST, actor->angle);
- }
-
- if (actor->extravalue2 <= 0 || !P_TryMove(actor,
- actor->x + P_ReturnThrustX(actor, actor->angle, actor->extravalue2<y + P_ReturnThrustY(actor, actor->angle, actor->extravalue2<extravalue2 = 0;
- P_SetMobjState(actor, locvar2);
- }
-}
-
-// Function: A_StatueBurst
-//
-// Description: For suspicious statues only...
-//
-// var1 = object to create
-// var2 = effective nextstate for created object
-//
-void A_StatueBurst(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobjtype_t chunktype = (mobjtype_t)actor->info->raisestate;
- mobj_t *new;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_StatueBurst", actor))
- return;
-#endif
-
- if (!locvar1 || !(new = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1)))
- return;
-
- new->angle = actor->angle;
- new->target = actor->target;
- if (locvar2)
- P_SetMobjState(new, (statenum_t)locvar2);
- S_StartSound(new, new->info->attacksound);
- S_StopSound(actor);
- S_StartSound(actor, sfx_s3k96);
-
- {
- fixed_t a, b;
- fixed_t c = (actor->height>>2) - FixedMul(actor->scale, mobjinfo[chunktype].height>>1);
- fixed_t v = 4<radius>>1);
- mobj_t *spawned;
- UINT8 i;
- for (i = 0; i < 8; i++)
- {
- a = ((i & 1) ? r : (-r));
- b = ((i & 2) ? r : (-r));
- if (i == 4)
- {
- c += (actor->height>>1);
- v = 8<fuse = 3*TICRATE;
- }
- }
-}
-
-// Function: A_JetJawRoam
-//
-// Description: Roaming routine for JetJaw
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetJawRoam(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_JetJawRoam", actor))
- return;
-#endif
- if (actor->reactiontime)
- {
- actor->reactiontime--;
- P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed*FRACUNIT/4, actor->scale));
- }
- else
- {
- actor->reactiontime = actor->info->reactiontime;
- actor->angle += ANGLE_180;
- }
-
- if (P_LookForPlayers(actor, false, false, actor->radius * 16))
- P_SetMobjState(actor, actor->info->seestate);
-}
-
-// Function: A_JetJawChomp
-//
-// Description: Chase and chomp at the target, as long as it is in view
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetJawChomp(mobj_t *actor)
-{
- INT32 delta;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_JetJawChomp", actor))
- return;
-#endif
-
- // turn towards movement direction if not there yet
- if (actor->movedir < NUMDIRS)
- {
- actor->angle &= (7<<29);
- delta = actor->angle - (actor->movedir << 29);
-
- if (delta > 0)
- actor->angle -= ANGLE_45;
- else if (delta < 0)
- actor->angle += ANGLE_45;
- }
-
- // Stop chomping if target's dead or you can't see it
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)
- || actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
- {
- P_SetMobjStateNF(actor, actor->info->spawnstate);
- return;
- }
-
- // chase towards player
- if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
- P_NewChaseDir(actor);
-}
-
-// Function: A_PointyThink
-//
-// Description: Thinker function for Pointy
-//
-// var1 = unused
-// var2 = unused
-//
-void A_PointyThink(mobj_t *actor)
-{
- INT32 i;
- player_t *player = NULL;
- mobj_t *ball;
- TVector v;
- TVector *res;
- angle_t fa;
- fixed_t radius = FixedMul(actor->info->radius*actor->info->reactiontime, actor->scale);
- boolean firsttime = true;
- INT32 sign;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_PointyThink", actor))
- return;
-#endif
- actor->momx = actor->momy = actor->momz = 0;
-
- // Find nearest player
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator)
- continue;
-
- if (!players[i].mo)
- continue;
-
- if (!players[i].mo->health)
- continue;
-
- if (!P_CheckSight(actor, players[i].mo))
- continue;
-
- if (firsttime)
- {
- firsttime = false;
- player = &players[i];
- }
- else
- {
- if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) <
- P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y))
- player = &players[i];
- }
- }
-
- if (!player)
- return;
-
- // Okay, we found the closest player. Let's move based on his movement.
- P_SetTarget(&actor->target, player->mo);
- A_FaceTarget(actor);
-
- if (P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) < P_AproxDistance(player->mo->x + player->mo->momx - actor->x, player->mo->y + player->mo->momy - actor->y))
- sign = -1; // Player is moving away
- else
- sign = 1; // Player is moving closer
-
- if (player->mo->momx || player->mo->momy)
- {
- P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y), FixedMul(actor->info->speed*sign, actor->scale));
-
- // Rotate our spike balls
- actor->lastlook += actor->info->damage;
- actor->lastlook %= FINEANGLES/4;
- }
-
- if (!actor->tracer) // For some reason we do not have spike balls...
- return;
-
- // Position spike balls relative to the value of 'lastlook'.
- ball = actor->tracer;
-
- i = 0;
- while (ball)
- {
- fa = actor->lastlook+i;
- v[0] = FixedMul(FINECOSINE(fa),radius);
- v[1] = 0;
- v[2] = FixedMul(FINESINE(fa),radius);
- v[3] = FRACUNIT;
-
- res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(actor->lastlook+i)));
- M_Memcpy(&v, res, sizeof (v));
- res = VectorMatrixMultiply(v, *RotateZMatrix(actor->angle+ANGLE_180));
- M_Memcpy(&v, res, sizeof (v));
-
- P_UnsetThingPosition(ball);
- ball->x = actor->x + v[0];
- ball->y = actor->y + v[1];
- ball->z = actor->z + (actor->height>>1) + v[2];
- P_SetThingPosition(ball);
-
- ball = ball->tracer;
- i += ANGLE_90 >> ANGLETOFINESHIFT;
- }
-}
-
-// Function: A_CheckBuddy
-//
-// Description: Checks if target/tracer exists/has health. If not, the object removes itself.
-//
-// var1:
-// 0 = target
-// 1 = tracer
-// var2 = unused
-//
-void A_CheckBuddy(mobj_t *actor)
-{
- INT32 locvar1 = var1;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckBuddy", actor))
- return;
-#endif
- if (locvar1 && (!actor->tracer || actor->tracer->health <= 0))
- P_RemoveMobj(actor);
- else if (!locvar1 && (!actor->target || actor->target->health <= 0))
- P_RemoveMobj(actor);
-}
-
-// Helper function for the Robo Hood.
-// Don't ask me how it works. Nev3r made it with dark majyks.
-static void P_ParabolicMove(mobj_t *actor, fixed_t x, fixed_t y, fixed_t z, fixed_t speed)
-{
- fixed_t dh;
-
- x -= actor->x;
- y -= actor->y;
- z -= actor->z;
-
- dh = P_AproxDistance(x, y);
-
- actor->momx = FixedMul(FixedDiv(x, dh), speed);
- actor->momy = FixedMul(FixedDiv(y, dh), speed);
-
- if (!gravity)
- return;
-
- dh = FixedDiv(FixedMul(dh, gravity), speed);
- actor->momz = (dh>>1) + FixedDiv(z, dh<<1);
-}
-
-// Function: A_HoodFire
-//
-// Description: Firing Robo-Hood
-//
-// var1 = object type to fire
-// var2 = unused
-//
-void A_HoodFire(mobj_t *actor)
-{
- mobj_t *arrow;
- INT32 locvar1 = var1;
- //INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_HoodFire", actor))
- return;
-#endif
-
- // Check target first.
- if (!actor->target)
- {
- actor->reactiontime = actor->info->reactiontime;
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- A_FaceTarget(actor);
-
- if (!(arrow = P_SpawnMissile(actor, actor->target, (mobjtype_t)locvar1)))
- return;
-
- // Set a parabolic trajectory for the arrow.
- P_ParabolicMove(arrow, actor->target->x, actor->target->y, actor->target->z, arrow->info->speed);
-}
-
-// Function: A_HoodThink
-//
-// Description: Thinker for Robo-Hood
-//
-// var1 = unused
-// var2 = unused
-//
-void A_HoodThink(mobj_t *actor)
-{
- fixed_t dx, dy, dz, dm;
- boolean checksight;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_HoodThink", actor))
- return;
-#endif
-
- // Check target first.
- if (!actor->target)
- {
- actor->reactiontime = actor->info->reactiontime;
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- dx = (actor->target->x - actor->x), dy = (actor->target->y - actor->y), dz = (actor->target->z - actor->z);
- dm = P_AproxDistance(dx, dy);
- // Target dangerously close to robohood, retreat then.
- if ((dm < 256<info->raisestate);
- return;
- }
-
- // If target on sight, look at it.
- if ((checksight = P_CheckSight(actor, actor->target)))
- {
- angle_t dang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
- if (actor->angle >= ANGLE_180)
- {
- actor->angle = InvAngle(actor->angle)>>1;
- actor->angle = InvAngle(actor->angle);
- }
- else
- actor->angle >>= 1;
-
- if (dang >= ANGLE_180)
- {
- dang = InvAngle(dang)>>1;
- dang = InvAngle(dang);
- }
- else
- dang >>= 1;
-
- actor->angle += dang;
- }
-
- // Check whether to do anything.
- if ((--actor->reactiontime) <= 0)
- {
- actor->reactiontime = actor->info->reactiontime;
-
- // If way too far, don't shoot.
- if ((dm < (3072<info->missilestate);
- return;
- }
- }
-}
-
-// Function: A_HoodFall
-//
-// Description: Falling Robo-Hood
-//
-// var1 = unused
-// var2 = unused
-//
-void A_HoodFall(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_HoodFall", actor))
- return;
-#endif
-
- if (!P_IsObjectOnGround(actor))
- return;
-
- actor->momx = actor->momy = 0;
- actor->reactiontime = actor->info->reactiontime;
- P_SetMobjState(actor, actor->info->seestate);
-}
-
-// Function: A_ArrowBonks
-//
-// Description: Arrow momentum setting on collision
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ArrowBonks(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ArrowBonks", actor))
- return;
-#endif
-
- if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)
- || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
- actor->angle += ANGLE_180;
-
- P_SetObjectMomZ(actor, 8*actor->scale, false);
- P_InstaThrust(actor, actor->angle, -6*actor->scale);
-
- actor->flags = (actor->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY;
- actor->z += P_MobjFlip(actor);
-}
-
-// Function: A_SnailerThink
-//
-// Description: Thinker function for Snailer
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SnailerThink(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SnailerThink", actor))
- return;
-#endif
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (!P_LookForPlayers(actor, true, false, 0))
- return;
- }
-
- // We now have a target. Oh bliss, rapture, and contentment!
-
- if (actor->target->z + actor->target->height > actor->z - FixedMul(32*FRACUNIT, actor->scale)
- && actor->target->z < actor->z + actor->height + FixedMul(32*FRACUNIT, actor->scale)
- && !(leveltime % (TICRATE*2)))
- {
- angle_t an;
- fixed_t z;
-
- // Actor shouldn't face target, so we'll do things a bit differently here
-
- an = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle;
-
- z = actor->z + actor->height/2;
-
- if (an > ANGLE_45 && an < ANGLE_315) // fire as close as you can to the target, even if too sharp an angle from your front
- {
- fixed_t dist;
- fixed_t dx, dy;
-
- dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
-
- if (an > ANGLE_45 && an <= ANGLE_90) // fire at 45 degrees to the left
- {
- dx = actor->x + P_ReturnThrustX(actor, actor->angle + ANGLE_45, dist);
- dy = actor->y + P_ReturnThrustY(actor, actor->angle + ANGLE_45, dist);
- }
- else if (an >= ANGLE_270 && an < ANGLE_315) // fire at 45 degrees to the right
- {
- dx = actor->x + P_ReturnThrustX(actor, actor->angle - ANGLE_45, dist);
- dy = actor->y + P_ReturnThrustY(actor, actor->angle - ANGLE_45, dist);
- }
- else // fire straight ahead
- {
- dx = actor->x + P_ReturnThrustX(actor, actor->angle, dist);
- dy = actor->y + P_ReturnThrustY(actor, actor->angle, dist);
- }
-
- P_SpawnPointMissile(actor, dx, dy, actor->target->z, MT_ROCKET, actor->x, actor->y, z);
- }
- else
- P_SpawnXYZMissile(actor, actor->target, MT_ROCKET, actor->x, actor->y, z);
- }
-
- if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z > actor->z)
- || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) > (actor->z + actor->height)))
- actor->momz += FixedMul(actor->info->speed, actor->scale);
- else if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z < actor->z)
- || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) < (actor->z + actor->height)))
- actor->momz -= FixedMul(actor->info->speed, actor->scale);
-
- actor->momz /= 2;
-}
-
-// Function: A_SharpChase
-//
-// Description: Thinker/Chase routine for Spincushions
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SharpChase(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SharpChase", actor))
- return;
-#endif
-
- if (actor->reactiontime)
- {
- INT32 delta;
-
- actor->reactiontime--;
-
- // turn towards movement direction if not there yet
- if (actor->movedir < NUMDIRS)
- {
- actor->angle &= (7<<29);
- delta = actor->angle - (actor->movedir << 29);
-
- if (delta > 0)
- actor->angle -= ANGLE_45;
- else if (delta < 0)
- actor->angle += ANGLE_45;
- }
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- // chase towards player
- if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
- P_NewChaseDir(actor);
- }
- else
- {
- actor->threshold = actor->info->painchance;
- P_SetMobjState(actor, actor->info->missilestate);
- S_StartSound(actor, actor->info->attacksound);
- }
-}
-
-// Function: A_SharpSpin
-//
-// Description: Spin chase routine for Spincushions
-//
-// var1 = object # to spawn as dust (if not provided not done)
-// var2 = if nonzero, do the old-style spinning using this as the angle difference
-//
-void A_SharpSpin(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- angle_t oldang = actor->angle;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SharpSpin", actor))
- return;
-#endif
-
- if (actor->threshold && actor->target)
- {
- angle_t ang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
- P_Thrust(actor, ang, actor->info->speed*actor->scale);
- if (locvar2)
- actor->angle += locvar2; // ANGLE_22h;
- else
- actor->angle = ang;
- actor->threshold--;
- if (leveltime & 1)
- S_StartSound(actor, actor->info->painsound);
- }
- else
- {
- actor->reactiontime = actor->info->reactiontime;
- P_SetMobjState(actor, actor->info->meleestate);
- }
-
- P_SharpDust(actor, locvar1, oldang);
-}
-
-// Function: A_SharpDecel
-//
-// Description: Slow down the Spincushion
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SharpDecel(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SharpDecel", actor))
- return;
-#endif
-
- if (actor->momx > 2 || actor->momy > 2)
- {
- actor->momx >>= 1;
- actor->momy >>= 1;
- }
- else
- P_SetMobjState(actor, actor->info->xdeathstate);
-}
-
-// Function: A_CrushstaceanWalk
-//
-// Description: Crushstacean movement
-//
-// var1 = speed (actor info's speed if 0)
-// var2 = state to switch to when blocked (spawnstate if 0)
-//
-void A_CrushstaceanWalk(mobj_t *actor)
-{
- INT32 locvar1 = (var1 ? var1 : (INT32)actor->info->speed);
- INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate);
- angle_t ang = actor->angle + ((actor->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CrushstaceanWalk", actor))
- return;
-#endif
-
- actor->reactiontime--;
-
- if (!P_TryMove(actor,
- actor->x + P_ReturnThrustX(actor, ang, locvar1*actor->scale),
- actor->y + P_ReturnThrustY(actor, ang, locvar1*actor->scale),
- false)
- || (actor->reactiontime-- <= 0))
- {
- actor->flags2 ^= MF2_AMBUSH;
- P_SetMobjState(actor, locvar2);
- actor->reactiontime = actor->info->reactiontime;
- }
-}
-
-// Function: A_CrushstaceanPunch
-//
-// Description: Crushstacean attack
-//
-// var1 = unused
-// var2 = state to go to if unsuccessful (spawnstate if 0)
-//
-void A_CrushstaceanPunch(mobj_t *actor)
-{
- //INT32 locvar1 = var1;
- INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CrushstaceanPunch", actor))
- return;
-#endif
-
- if (!actor->tracer)
- return;
-
- if (!actor->target)
- {
- P_SetMobjState(actor, locvar2);
- return;
- }
-
- actor->tracer->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
- P_SetMobjState(actor->tracer, actor->tracer->info->missilestate);
- actor->tracer->extravalue1 = actor->tracer->extravalue2 = 0;
- S_StartSound(actor, actor->info->attacksound);
-}
-
-// Function: A_CrushclawAim
-//
-// Description: Crushstacean claw aiming
-//
-// var1 = sideways offset
-// var2 = vertical offset
-//
-void A_CrushclawAim(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *crab = actor->tracer;
- angle_t ang;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CrushclawAim", actor))
- return;
-#endif
-
- if (!crab)
- {
- P_RemoveMobj(actor);
- return; // there is only one step and it is crab
- }
-
- if (crab->target || P_LookForPlayers(crab, true, false, 600*crab->scale))
- ang = R_PointToAngle2(crab->x, crab->y, crab->target->x, crab->target->y);
- else
- ang = crab->angle + ((crab->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);
- ang -= actor->angle;
-
-#define anglimit ANGLE_22h
-#define angfactor 5
- if (ang < ANGLE_180)
- {
- if (ang > anglimit)
- ang = anglimit;
- ang /= angfactor;
- }
- else
- {
- ang = InvAngle(ang);
- if (ang > anglimit)
- ang = anglimit;
- ang = InvAngle(ang/angfactor);
- }
- actor->angle += ang;
-#undef anglimit
-#undef angfactor
-
- P_TeleportMove(actor,
- crab->x + P_ReturnThrustX(actor, actor->angle, locvar1*crab->scale),
- crab->y + P_ReturnThrustY(actor, actor->angle, locvar1*crab->scale),
- crab->z + locvar2*crab->scale);
-
- if (!crab->target || !crab->info->missilestate || (statenum_t)(crab->state-states) == crab->info->missilestate)
- return;
-
- if (((ang + ANG1) < ANG2) || P_AproxDistance(crab->x - crab->target->x, crab->y - crab->target->y) < 333*crab->scale)
- P_SetMobjState(crab, crab->info->missilestate);
-}
-
-// Function: A_CrushclawLaunch
-//
-// Description: Crushstacean claw launching
-//
-// var1:
-// 0 - forwards
-// anything else - backwards
-// var2 = state to change to when done
-//
-void A_CrushclawLaunch(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *crab = actor->tracer;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CrushclawLaunch", actor))
- return;
-#endif
-
- if (!crab)
- {
- mobj_t *chainnext;
- while (actor)
- {
- chainnext = actor->target;
- P_RemoveMobj(actor);
- actor = chainnext;
- }
- return; // there is only one step and it is crab
- }
-
- if (!actor->extravalue1)
- {
- S_StartSound(actor, actor->info->activesound);
- actor->extravalue1 = ((locvar1) ? -1 : 32);
- }
- else if (actor->extravalue1 != 1)
- actor->extravalue1 -= 1;
-
-#define CSEGS 5
- if (!actor->target)
- {
- mobj_t *prevchain = actor;
- UINT8 i = 0;
- for (i = 0; (i < CSEGS); i++)
- {
- mobj_t *newchain = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->info->raisestate);
- prevchain->target = newchain;
- prevchain = newchain;
- }
- actor->target->angle = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y);
- }
-
- if ((!locvar1) && crab->target)
- {
-#define anglimit ANGLE_22h
-#define angfactor 7
- angle_t ang = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y) - actor->target->angle;
- if (ang < ANGLE_180)
- {
- if (ang > anglimit)
- ang = anglimit;
- ang /= angfactor;
- }
- else
- {
- ang = InvAngle(ang);
- if (ang > anglimit)
- ang = anglimit;
- ang /= angfactor;
- ang = InvAngle(ang);
- }
- actor->target->angle += ang;
- actor->angle = actor->target->angle;
- }
-
- actor->extravalue2 += actor->extravalue1;
-
- if (!P_TryMove(actor,
- actor->target->x + P_ReturnThrustX(actor, actor->target->angle, actor->extravalue2*actor->scale),
- actor->target->y + P_ReturnThrustY(actor, actor->target->angle, actor->extravalue2*actor->scale),
- true)
- && !locvar1)
- {
- actor->extravalue1 = 0;
- actor->extravalue2 = FixedHypot(actor->x - actor->target->x, actor->y - actor->target->y)>>FRACBITS;
- P_SetMobjState(actor, locvar2);
- S_StopSound(actor);
- S_StartSound(actor, sfx_s3k49);
- }
- else
- {
- actor->z = actor->target->z;
- if ((!locvar1 && (actor->extravalue2 > 256)) || (locvar1 && (actor->extravalue2 < 16)))
- {
- if (locvar1) // In case of retracting, resume crab and remove the chain.
- {
- mobj_t *chain = actor->target, *chainnext;
- while (chain)
- {
- chainnext = chain->target;
- P_RemoveMobj(chain);
- chain = chainnext;
- }
- actor->extravalue2 = 0;
- actor->angle = R_PointToAngle2(crab->x, crab->y, actor->x, actor->y);
- P_SetTarget(&actor->target, NULL);
- P_SetTarget(&crab->target, NULL);
- P_SetMobjState(crab, crab->state->nextstate);
- }
- actor->extravalue1 = 0;
- P_SetMobjState(actor, locvar2);
- S_StopSound(actor);
- if (!locvar1)
- S_StartSound(actor, sfx_s3k64);
- }
- }
-
- if (!actor->target)
- return;
-
- {
- mobj_t *chain = actor->target->target;
- fixed_t dx = (actor->x - actor->target->x)/CSEGS, dy = (actor->y - actor->target->y)/CSEGS, dz = (actor->z - actor->target->z)/CSEGS;
- fixed_t idx = dx, idy = dy, idz = dz;
- while (chain)
- {
- P_TeleportMove(chain, actor->target->x + idx, actor->target->y + idy, actor->target->z + idz);
- chain->watertop = chain->z;
- idx += dx;
- idy += dy;
- idz += dz;
- chain = chain->target;
- }
- }
-#undef CSEGS
-}
-
-// Function: A_VultureVtol
-//
-// Description: Vulture rising up to match target's height
-//
-// var1 = unused
-// var2 = unused
-//
-void A_VultureVtol(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_VultureVtol", actor))
- return;
-#endif
-
- if (!actor->target)
- return;
-
- actor->flags |= MF_NOGRAVITY;
- actor->flags |= MF_FLOAT;
-
- A_FaceTarget(actor);
-
- S_StopSound(actor);
-
- if (actor->z < actor->target->z+(actor->target->height/4) && actor->z + actor->height < actor->ceilingz)
- actor->momz = FixedMul(2*FRACUNIT, actor->scale);
- else if (actor->z > (actor->target->z+(actor->target->height/4)*3) && actor->z > actor->floorz)
- actor->momz = FixedMul(-2*FRACUNIT, actor->scale);
- else
- {
- // Attack!
- actor->momz = 0;
- P_SetMobjState(actor, actor->info->missilestate);
- S_StartSound(actor, actor->info->activesound);
- }
-}
-
-// Function: A_VultureCheck
-//
-// Description: If the vulture is stopped, look for a new target
-//
-// var1 = unused
-// var2 = unused
-//
-void A_VultureCheck(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_VultureCheck", actor))
- return;
-#endif
-
- if (actor->momx || actor->momy)
- return;
-
- actor->flags &= ~MF_NOGRAVITY; // Fall down
-
- if (actor->z <= actor->floorz)
- {
- actor->angle -= ANGLE_180; // turn around
- P_SetMobjState(actor, actor->info->spawnstate);
- }
-}
-
-// Function: A_SkimChase
-//
-// Description: Thinker/Chase routine for Skims
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SkimChase(mobj_t *actor)
-{
- INT32 delta;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SkimChase", actor))
- return;
-#endif
- if (actor->reactiontime)
- actor->reactiontime--;
-
- // modify target threshold
- if (actor->threshold)
- {
- if (!actor->target || actor->target->health <= 0)
- actor->threshold = 0;
- else
- actor->threshold--;
- }
-
- // turn towards movement direction if not there yet
- if (actor->movedir < NUMDIRS)
- {
- actor->angle &= (7<<29);
- delta = actor->angle - (actor->movedir << 29);
-
- if (delta > 0)
- actor->angle -= ANGLE_45;
- else if (delta < 0)
- actor->angle += ANGLE_45;
- }
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- P_LookForPlayers(actor, true, false, 0);
-
- // the spawnstate for skims already calls this function so just return either way
- // without changing state
- return;
- }
-
- // do not attack twice in a row
- if (actor->flags2 & MF2_JUSTATTACKED)
- {
- actor->flags2 &= ~MF2_JUSTATTACKED;
- P_NewChaseDir(actor);
- return;
- }
-
- // check for melee attack
- if (actor->info->meleestate && P_SkimCheckMeleeRange(actor))
- {
- if (actor->info->attacksound)
- S_StartAttackSound(actor, actor->info->attacksound);
-
- P_SetMobjState(actor, actor->info->meleestate);
- return;
- }
-
- // check for missile attack
- if (actor->info->missilestate)
- {
- if (actor->movecount || !P_CheckMissileRange(actor))
- goto nomissile;
-
- P_SetMobjState(actor, actor->info->missilestate);
- actor->flags2 |= MF2_JUSTATTACKED;
- return;
- }
-
-nomissile:
- // possibly choose another target
- if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
- && P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- // chase towards player
- if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
- P_NewChaseDir(actor);
-}
-
-// Function: A_FaceTarget
-//
-// Description: Immediately turn to face towards your target.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_FaceTarget(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FaceTarget", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-}
-
-// Function: A_FaceTracer
-//
-// Description: Immediately turn to face towards your tracer.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_FaceTracer(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FaceTracer", actor))
- return;
-#endif
- if (!actor->tracer)
- return;
-
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
-}
-
-// Function: A_LobShot
-//
-// Description: Lob an object at your target.
-//
-// var1 = object # to lob
-// var2:
-// var2 >> 16 = height offset
-// var2 & 65535 = airtime
-//
-void A_LobShot(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2 >> 16;
- mobj_t *shot, *hitspot;
- angle_t an;
- fixed_t z;
- fixed_t dist;
- fixed_t vertical, horizontal;
- fixed_t airtime = var2 & 65535;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_LobShot", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- A_FaceTarget(actor);
-
- if (actor->eflags & MFE_VERTICALFLIP)
- {
- z = actor->z + actor->height - FixedMul(locvar2*FRACUNIT, actor->scale);
- if (actor->type == MT_BLACKEGGMAN)
- z -= FixedMul(mobjinfo[locvar1].height, actor->scale/2);
- else
- z -= FixedMul(mobjinfo[locvar1].height, actor->scale);
- }
- else
- z = actor->z + FixedMul(locvar2*FRACUNIT, actor->scale);
-
- shot = P_SpawnMobj(actor->x, actor->y, z, locvar1);
-
- if (actor->type == MT_BLACKEGGMAN)
- {
- shot->destscale = actor->scale/2;
- P_SetScale(shot, actor->scale/2);
- }
- else
- {
- shot->destscale = actor->scale;
- P_SetScale(shot, actor->scale);
- }
-
- // Keep track of where it's going to land
- hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL);
- hitspot->tics = airtime;
- P_SetTarget(&shot->tracer, hitspot);
-
- P_SetTarget(&shot->target, actor); // where it came from
-
- shot->angle = an = actor->angle;
- an >>= ANGLETOFINESHIFT;
-
- dist = P_AproxDistance(actor->target->x - shot->x, actor->target->y - shot->y);
-
- horizontal = dist / airtime;
- vertical = FixedMul((gravity*airtime)/2, shot->scale);
-
- shot->momx = FixedMul(horizontal, FINECOSINE(an));
- shot->momy = FixedMul(horizontal, FINESINE(an));
- shot->momz = vertical;
-
-/* Try to adjust when destination is not the same height
- if (actor->z != actor->target->z)
- {
- fixed_t launchhyp;
- fixed_t diff;
- fixed_t orig;
-
- diff = actor->z - actor->target->z;
- {
- launchhyp = P_AproxDistance(horizontal, vertical);
-
- orig = FixedMul(FixedDiv(vertical, horizontal), diff);
-
- CONS_Debug(DBG_GAMELOGIC, "orig: %d\n", (orig)>>FRACBITS);
-
- horizontal = dist / airtime;
- vertical = (gravity*airtime)/2;
- }
- dist -= orig;
- shot->momx = FixedMul(horizontal, FINECOSINE(an));
- shot->momy = FixedMul(horizontal, FINESINE(an));
- shot->momz = vertical;
-*/
-
- if (shot->info->seesound)
- S_StartSound(shot, shot->info->seesound);
-
- if (!(actor->flags & MF_BOSS))
- {
- if (ultimatemode)
- actor->reactiontime = actor->info->reactiontime*TICRATE;
- else
- actor->reactiontime = actor->info->reactiontime*TICRATE*2;
- }
-}
-
-// Function: A_FireShot
-//
-// Description: Shoot an object at your target.
-//
-// var1 = object # to shoot
-// var2 = height offset
-//
-void A_FireShot(mobj_t *actor)
-{
- fixed_t z;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FireShot", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- A_FaceTarget(actor);
-
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
-
- P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
-
- if (!(actor->flags & MF_BOSS))
- {
- if (ultimatemode)
- actor->reactiontime = actor->info->reactiontime*TICRATE;
- else
- actor->reactiontime = actor->info->reactiontime*TICRATE*2;
- }
-}
-
-// Function: A_SuperFireShot
-//
-// Description: Shoot an object at your target that will even stall Super Sonic.
-//
-// var1 = object # to shoot
-// var2 = height offset
-//
-void A_SuperFireShot(mobj_t *actor)
-{
- fixed_t z;
- mobj_t *mo;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SuperFireShot", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- A_FaceTarget(actor);
-
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
-
- mo = P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
-
- if (mo)
- mo->flags2 |= MF2_SUPERFIRE;
-
- if (!(actor->flags & MF_BOSS))
- {
- if (ultimatemode)
- actor->reactiontime = actor->info->reactiontime*TICRATE;
- else
- actor->reactiontime = actor->info->reactiontime*TICRATE*2;
- }
-}
-
-// Function: A_BossFireShot
-//
-// Description: Shoot an object at your target ala Bosses:
-//
-// var1 = object # to shoot
-// var2:
-// 0 - Boss 1 Left side
-// 1 - Boss 1 Right side
-// 2 - Boss 3 Left side upper
-// 3 - Boss 3 Left side lower
-// 4 - Boss 3 Right side upper
-// 5 - Boss 3 Right side lower
-//
-void A_BossFireShot(mobj_t *actor)
-{
- fixed_t x, y, z;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BossFireShot", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- A_FaceTarget(actor);
-
- switch (locvar2)
- {
- case 0:
- x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
- y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
- break;
- case 1:
- x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
- y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
- break;
- case 2:
- x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
- y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
- break;
- case 3:
- x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
- y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
- break;
- case 4:
- x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
- y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
- break;
- case 5:
- x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
- y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
- break;
- default:
- x = actor->x;
- y = actor->y;
- z = actor->z + actor->height/2;
- break;
- }
-
- P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
-}
-
-// Function: A_Boss7FireMissiles
-//
-// Description: Shoot 4 missiles of a specific object type at your target ala Black Eggman
-//
-// var1 = object # to shoot
-// var2 = firing sound
-//
-void A_Boss7FireMissiles(mobj_t *actor)
-{
- mobj_t dummymo;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss7FireMissiles", actor))
- return;
-#endif
-
- if (!actor->target)
- {
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- A_FaceTarget(actor);
-
- S_StartSound(NULL, locvar2);
-
- // set dummymo's coordinates
- dummymo.x = actor->target->x;
- dummymo.y = actor->target->y;
- dummymo.z = actor->target->z + FixedMul(16*FRACUNIT, actor->scale); // raised height
-
- P_SpawnXYZMissile(actor, &dummymo, locvar1,
- actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
- actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
- actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
-
- P_SpawnXYZMissile(actor, &dummymo, locvar1,
- actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
- actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
- actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
-
- P_SpawnXYZMissile(actor, &dummymo, locvar1,
- actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
- actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
- actor->z + actor->height/2);
-
- P_SpawnXYZMissile(actor, &dummymo, locvar1,
- actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
- actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
- actor->z + actor->height/2);
-}
-
-// Function: A_Boss1Laser
-//
-// Description: Shoot an object at your target ala Bosses:
-//
-// var1 = object # to shoot
-// var2:
-// 0 - Boss 1 Left side
-// 1 - Boss 1 Right side
-//
-void A_Boss1Laser(mobj_t *actor)
-{
- fixed_t x, y, z, floorz, speed;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- INT32 i;
- angle_t angle;
- mobj_t *point;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss1Laser", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- switch (locvar2)
- {
- case 0:
- x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
- y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
- else
- z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
- break;
- case 1:
- x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
- y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
- else
- z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
- break;
- default:
- x = actor->x;
- y = actor->y;
- z = actor->z + actor->height/2;
- break;
- }
-
- if (!(actor->flags2 & MF2_FIRING))
- {
- actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);
- if (mobjinfo[locvar1].seesound)
- S_StartSound(actor, mobjinfo[locvar1].seesound);
- if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
- {
- point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET);
- point->angle = actor->angle;
- point->fuse = actor->tics+1;
- P_SetTarget(&point->target, actor->target);
- P_SetTarget(&actor->target, point);
- }
- }
- /* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path
- else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
- actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/
-
- if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)
- angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT);
- else
- angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y));
- point = P_SpawnMobj(x, y, z, locvar1);
- P_SetTarget(&point->target, actor);
- point->angle = actor->angle;
- speed = point->radius*2;
- point->momz = FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT), speed);
- point->momx = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(point->angle>>ANGLETOFINESHIFT), speed));
- point->momy = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINESINE(point->angle>>ANGLETOFINESHIFT), speed));
-
- for (i = 0; i < 256; i++)
- {
- mobj_t *mo = P_SpawnMobj(point->x, point->y, point->z, point->type);
- mo->angle = point->angle;
- P_UnsetThingPosition(mo);
- mo->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY;
- P_SetThingPosition(mo);
-
- x = point->x, y = point->y, z = point->z;
- if (P_RailThinker(point))
- break;
- }
-
- floorz = P_FloorzAtPos(x, y, z, mobjinfo[MT_EGGMOBILE_FIRE].height);
- if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1)
- {
- point = P_SpawnMobj(x, y, floorz+1, MT_EGGMOBILE_FIRE);
- point->target = actor;
- point->destscale = 3*FRACUNIT;
- point->scalespeed = FRACUNIT>>2;
- point->fuse = TICRATE;
- }
-
- if (actor->tics > 1)
- actor->flags2 |= MF2_FIRING;
- else
- actor->flags2 &= ~MF2_FIRING;
-}
-
-// Function: A_FocusTarget
-//
-// Description: Home in on your target.
-//
-// var1:
-// 0 - accelerative focus with friction
-// 1 - steady focus with fixed movement speed
-// anything else - don't move
-// var2:
-// 0 - don't trace target, just move forwards
-// & 1 - change horizontal angle
-// & 2 - change vertical angle
-//
-void A_FocusTarget(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FocusTarget", actor))
- return;
-#endif
-
- if (actor->target)
- {
- fixed_t speed = FixedMul(actor->info->speed, actor->scale);
- fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1);
- angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle);
- angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90);
- switch(locvar1)
- {
- case 0:
- {
- actor->momx -= actor->momx>>4, actor->momy -= actor->momy>>4, actor->momz -= actor->momz>>4;
- actor->momz += FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
- actor->momx += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
- actor->momy += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
- }
- break;
- case 1:
- if (dist > speed)
- {
- actor->momz = FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
- actor->momx = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
- actor->momy = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
- }
- else
- {
- actor->momx = 0, actor->momy = 0, actor->momz = 0;
- actor->z = actor->target->z + (actor->target->height>>1);
- P_TryMove(actor, actor->target->x, actor->target->y, true);
- }
- break;
- default:
- break;
- }
- }
-}
-
-// Function: A_Boss4Reverse
-//
-// Description: Reverse arms direction.
-//
-// var1 = sfx to play
-// var2 = unused
-//
-void A_Boss4Reverse(mobj_t *actor)
-{
- sfxenum_t locvar1 = (sfxenum_t)var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss4Reverse", actor))
- return;
-#endif
- S_StartSound(NULL, locvar1);
- actor->reactiontime = 0;
- if (actor->movedir == 1)
- actor->movedir = 2;
- else
- actor->movedir = 1;
-}
-
-// Function: A_Boss4SpeedUp
-//
-// Description: Speed up arms
-//
-// var1 = sfx to play
-// var2 = unused
-//
-void A_Boss4SpeedUp(mobj_t *actor)
-{
- sfxenum_t locvar1 = (sfxenum_t)var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss4SpeedUp", actor))
- return;
-#endif
- S_StartSound(NULL, locvar1);
- actor->reactiontime = 2;
-}
-
-// Function: A_Boss4Raise
-//
-// Description: Raise helmet
-//
-// var1 = sfx to play
-// var2 = unused
-//
-void A_Boss4Raise(mobj_t *actor)
-{
- sfxenum_t locvar1 = (sfxenum_t)var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss4Raise", actor))
- return;
-#endif
- S_StartSound(NULL, locvar1);
- actor->reactiontime = 1;
-}
-
-// Function: A_SkullAttack
-//
-// Description: Fly at the player like a missile.
-//
-// var1:
-// 0 - Fly at the player
-// 1 - Fly away from the player
-// 2 - Strafe in relation to the player
-// var2:
-// 0 - Fly horizontally and vertically
-// 1 - Fly horizontal-only (momz = 0)
-//
-#define SKULLSPEED (20*FRACUNIT)
-
-void A_SkullAttack(mobj_t *actor)
-{
- mobj_t *dest;
- angle_t an;
- INT32 dist;
- INT32 speed;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SkullAttack", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- speed = FixedMul(SKULLSPEED, actor->scale);
-
- dest = actor->target;
- actor->flags2 |= MF2_SKULLFLY;
- if (actor->info->activesound)
- S_StartSound(actor, actor->info->activesound);
- A_FaceTarget(actor);
-
- if (locvar1 == 1)
- actor->angle += ANGLE_180;
- else if (locvar1 == 2)
- actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90;
-
- an = actor->angle >> ANGLETOFINESHIFT;
-
- actor->momx = FixedMul(speed, FINECOSINE(an));
- actor->momy = FixedMul(speed, FINESINE(an));
- dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
- dist = dist / speed;
-
- if (dist < 1)
- dist = 1;
-
- actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
-
- if (locvar1 == 1)
- actor->momz = -actor->momz;
- if (locvar2 == 1)
- actor->momz = 0;
-}
-
-// Function: A_BossZoom
-//
-// Description: Like A_SkullAttack, but used by Boss 1.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_BossZoom(mobj_t *actor)
-{
- mobj_t *dest;
- angle_t an;
- INT32 dist;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BossZoom", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- dest = actor->target;
- actor->flags2 |= MF2_SKULLFLY;
- if (actor->info->attacksound)
- S_StartAttackSound(actor, actor->info->attacksound);
- A_FaceTarget(actor);
- an = actor->angle >> ANGLETOFINESHIFT;
- actor->momx = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINECOSINE(an));
- actor->momy = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINESINE(an));
- dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
- dist = dist / FixedMul(actor->info->speed*5*FRACUNIT, actor->scale);
-
- if (dist < 1)
- dist = 1;
- actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
-}
-
-// Function: A_BossScream
-//
-// Description: Spawns explosions and plays appropriate sounds around the defeated boss.
-//
-// var1:
-// 0 - Use movecount to spawn explosions evenly
-// 1 - Use P_Random to spawn explosions at complete random
-// var2 = Object to spawn. Default is MT_BOSSEXPLODE.
-//
-void A_BossScream(mobj_t *actor)
-{
- mobj_t *mo;
- fixed_t x, y, z;
- angle_t fa;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobjtype_t explodetype;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BossScream", actor))
- return;
-#endif
- switch (locvar1)
- {
- default:
- case 0:
- actor->movecount += 4*16;
- actor->movecount %= 360;
- fa = (FixedAngle(actor->movecount*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
- break;
- case 1:
- fa = (FixedAngle(P_RandomKey(360)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
- break;
- }
- x = actor->x + FixedMul(FINECOSINE(fa),actor->radius);
- y = actor->y + FixedMul(FINESINE(fa),actor->radius);
-
- // Determine what mobj to spawn. If undefined or invalid, use MT_BOSSEXPLODE as default.
- if (locvar2 <= 0 || locvar2 >= NUMMOBJTYPES)
- explodetype = MT_BOSSEXPLODE;
- else
- explodetype = (mobjtype_t)locvar2;
-
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - mobjinfo[explodetype].height - FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
-
- mo = P_SpawnMobj(x, y, z, explodetype);
- if (actor->eflags & MFE_VERTICALFLIP)
- mo->flags2 |= MF2_OBJECTFLIP;
- mo->destscale = actor->scale;
- P_SetScale(mo, mo->destscale);
- if (actor->info->deathsound)
- S_StartSound(mo, actor->info->deathsound);
-}
-
-// Function: A_Scream
-//
-// Description: Starts the death sound of the object.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Scream(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Scream", actor))
- return;
-#endif
- if (actor->tracer && (actor->tracer->type == MT_SHELL || actor->tracer->type == MT_FIREBALL))
- S_StartScreamSound(actor, sfx_mario2);
- else if (actor->info->deathsound)
- S_StartScreamSound(actor, actor->info->deathsound);
-}
-
-// Function: A_Pain
-//
-// Description: Starts the pain sound of the object.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Pain(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Pain", actor))
- return;
-#endif
- if (actor->info->painsound)
- S_StartSound(actor, actor->info->painsound);
-
- actor->flags2 &= ~MF2_FIRING;
- actor->flags2 &= ~MF2_SUPERFIRE;
-}
-
-// Function: A_Fall
-//
-// Description: Changes a dying object's flags to reflect its having fallen to the ground.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Fall(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Fall", actor))
- return;
-#endif
- // actor is on ground, it can be walked over
- actor->flags &= ~MF_SOLID;
-
- // fall through the floor
- actor->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY;
-
- // So change this if corpse objects
- // are meant to be obstacles.
-}
-
-#define LIVESBOXDISPLAYPLAYER // Use displayplayer instead of closest player
-
-// Function: A_1upThinker
-//
-// Description: Used by the 1up box to show the player's face.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_1upThinker(mobj_t *actor)
-{
- INT32 i;
- fixed_t dist = INT32_MAX;
- fixed_t temp;
- INT32 closestplayer = -1;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_1upThinker", actor))
- return;
-#endif
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].bot || players[i].spectator)
- continue;
-
- if (!players[i].mo)
- continue;
-
- if ((netgame || multiplayer) && players[i].playerstate != PST_LIVE)
- continue;
-
- temp = P_AproxDistance(players[i].mo->x-actor->x, players[i].mo->y-actor->y);
-
- if (temp < dist)
- {
- closestplayer = i;
- dist = temp;
- }
- }
-
- if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0)
- { // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite.
- if (actor->tracer) {
- P_RemoveMobj(actor->tracer);
- actor->tracer = NULL;
- }
- return;
- }
-
- // We're using the overlay, so use the overlay 1up box (no text)
- actor->sprite = SPR_TV1P;
-
- if (!actor->tracer)
- {
- P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY));
- P_SetTarget(&actor->tracer->target, actor);
- actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame
- P_SetMobjState(actor->tracer, actor->info->seestate);
-
- // The overlay is going to be one tic early turning off and on
- // because it's going to get its thinker run the frame we spawned it.
- // So make it take one tic longer if it just spawned.
- ++actor->tracer->tics;
- }
-
- actor->tracer->color = players[closestplayer].mo->color;
- actor->tracer->skin = &skins[players[closestplayer].skin];
-}
-
-// Function: A_MonitorPop
-//
-// Description: Used by monitors when they explode.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MonitorPop(mobj_t *actor)
-{
- mobjtype_t item = 0;
- mobj_t *newmobj;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MonitorPop", actor))
- return;
-#endif
-
- // Spawn the "pop" explosion.
- if (actor->info->deathsound)
- S_StartSound(actor, actor->info->deathsound);
- P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_EXPLODE);
-
- // We're dead now. De-solidify.
- actor->health = 0;
- P_UnsetThingPosition(actor);
- actor->flags &= ~MF_SOLID;
- actor->flags |= MF_NOCLIP;
- P_SetThingPosition(actor);
-
- if (actor->info->damage == MT_UNKNOWN)
- {
- // MT_UNKNOWN is random. Because it's unknown to us... get it?
- item = P_DoRandomBoxChances();
-
- if (item == MT_NULL)
- {
- CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
- return;
- }
- }
- else
- item = actor->info->damage;
-
- if (item == 0)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_MonitorPop\n");
- return;
- }
-
- newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item);
- P_SetTarget(&newmobj->target, actor->target); // Transfer target
-
- if (item == MT_1UP_ICON)
- {
- if (actor->tracer) // Remove the old lives icon.
- P_RemoveMobj(actor->tracer);
-
- if (!newmobj->target
- || !newmobj->target->player
- || !newmobj->target->skin
- || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
- {} // No lives icon for this player, use the default.
- else
- { // Spawn the lives icon.
- mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
- P_SetTarget(&livesico->target, newmobj);
- P_SetTarget(&newmobj->tracer, livesico);
-
- livesico->color = newmobj->target->player->mo->color;
- livesico->skin = &skins[newmobj->target->player->skin];
- P_SetMobjState(livesico, newmobj->info->seestate);
-
- // We're using the overlay, so use the overlay 1up sprite (no text)
- newmobj->sprite = SPR_TV1P;
- }
- }
-
- // Run a linedef executor immediately upon popping
- // You may want to delay your effects by 18 tics to sync with the reward giving
- if (actor->spawnpoint && actor->lastlook)
- P_LinedefExecute(actor->lastlook, actor->target, NULL);
-}
-
-// Function: A_GoldMonitorPop
-//
-// Description: Used by repeating monitors when they turn off. They don't really pop, but, you know...
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GoldMonitorPop(mobj_t *actor)
-{
- mobjtype_t item = 0;
- mobj_t *newmobj;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_GoldMonitorPop", actor))
- return;
-#endif
-
- // Don't spawn the "pop" explosion, because the monitor isn't broken.
- if (actor->info->deathsound)
- S_StartSound(actor, actor->info->deathsound);
- //P_SpawnMobjFromMobj(actor, 0, 0, actor.height/4, MT_EXPLODE);
-
- // Remove our flags for a bit.
- // Players can now stand on top of us.
- P_UnsetThingPosition(actor);
- actor->flags &= ~(MF_MONITOR|MF_SHOOTABLE);
- P_SetThingPosition(actor);
-
- // Don't count this box in statistics. Sorry.
- if (actor->target && actor->target->player)
- --actor->target->player->numboxes;
- actor->fuse = 0; // Don't let the monitor code screw us up.
-
- if (actor->info->damage == MT_UNKNOWN)
- {
- // MT_UNKNOWN is random. Because it's unknown to us... get it?
- item = P_DoRandomBoxChances();
-
- if (item == MT_NULL)
- {
- CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
- return;
- }
- }
- else
- item = actor->info->damage;
-
- if (item == 0)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_GoldMonitorPop\n");
- return;
- }
-
- // Note: the icon spawns 1 fracunit higher
- newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 14*FRACUNIT, item);
- P_SetTarget(&newmobj->target, actor->target); // Transfer target
-
- if (item == MT_1UP_ICON)
- {
- if (actor->tracer) // Remove the old lives icon.
- P_RemoveMobj(actor->tracer);
-
- if (!newmobj->target
- || !newmobj->target->player
- || !newmobj->target->skin
- || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
- {} // No lives icon for this player, use the default.
- else
- { // Spawn the lives icon.
- mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
- P_SetTarget(&livesico->target, newmobj);
- P_SetTarget(&newmobj->tracer, livesico);
-
- livesico->color = newmobj->target->player->mo->color;
- livesico->skin = &skins[newmobj->target->player->skin];
- P_SetMobjState(livesico, newmobj->info->seestate);
-
- // We're using the overlay, so use the overlay 1up sprite (no text)
- newmobj->sprite = SPR_TV1P;
- }
- }
-
- // Run a linedef executor immediately upon popping
- // You may want to delay your effects by 18 tics to sync with the reward giving
- if (actor->spawnpoint && actor->lastlook)
- P_LinedefExecute(actor->lastlook, actor->target, NULL);
-}
-
-// Function: A_GoldMonitorRestore
-//
-// Description: A repeating monitor is coming back to life. Reset monitor flags, etc.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GoldMonitorRestore(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_GoldMonitorRestore", actor))
- return;
-#endif
-
- actor->flags |= MF_MONITOR|MF_SHOOTABLE;
- actor->health = 1; // Just in case.
-}
-
-// Function: A_GoldMonitorSparkle
-//
-// Description: Spawns the little sparkly effect around big monitors. Looks pretty, doesn't it?
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GoldMonitorSparkle(mobj_t *actor)
-{
- fixed_t i, ngangle, xofs, yofs;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_GoldMonitorSparkle", actor))
- return;
-#endif
-
- ngangle = FixedAngle(((leveltime * 21) % 360) << FRACBITS);
- xofs = FINESINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS);
- yofs = FINECOSINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS);
-
- for (i = FRACUNIT*2; i <= FRACUNIT*3; i += FRACUNIT/2)
- P_SetObjectMomZ(P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE), i, false);
-}
-
-// Function: A_Explode
-//
-// Description: Explodes an object, doing damage to any objects nearby. The target is used as the cause of the explosion. Damage value is used as explosion range.
-//
-// var1 = damagetype
-// var2 = unused
-//
-void A_Explode(mobj_t *actor)
-{
- INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Explode", actor))
- return;
-#endif
- P_RadiusAttack(actor, actor->target, actor->info->damage, locvar1);
-}
-
-// Function: A_BossDeath
-//
-// Description: Possibly trigger special effects when boss dies.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_BossDeath(mobj_t *mo)
-{
- thinker_t *th;
- mobj_t *mo2;
- line_t junk;
- INT32 i;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BossDeath", mo))
- return;
-#endif
-
- P_LinedefExecute(LE_BOSSDEAD, mo, NULL);
- mo->health = 0;
-
- // Boss is dead (but not necessarily fleeing...)
- // Lua may use this to ignore bosses after they start fleeing
- mo->flags2 |= MF2_BOSSDEAD;
-
- // make sure there is a player alive for victory
- for (i = 0; i < MAXPLAYERS; i++)
- if (playeringame[i] && ((players[i].mo && players[i].mo->health)
- || ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
- break;
-
- if (i == MAXPLAYERS)
- return; // no one left alive, so do not end game
-
- // scan the remaining thinkers to see
- // if all bosses are dead
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo2 = (mobj_t *)th;
- if (mo2 != mo && (mo2->flags & MF_BOSS) && mo2->health > 0)
- goto bossjustdie; // other boss not dead - just go straight to dying!
- }
-
- // victory!
- P_LinedefExecute(LE_ALLBOSSESDEAD, mo, NULL);
- if (mo->flags2 & MF2_BOSSNOTRAP)
- {
- for (i = 0; i < MAXPLAYERS; i++)
- P_DoPlayerExit(&players[i]);
- }
- else
- {
- // Bring the egg trap up to the surface
- junk.tag = 680;
- EV_DoElevator(&junk, elevateHighest, false);
- junk.tag = 681;
- EV_DoElevator(&junk, elevateUp, false);
- junk.tag = 682;
- EV_DoElevator(&junk, elevateHighest, false);
- }
-
-bossjustdie:
-#ifdef HAVE_BLUA
- if (LUAh_BossDeath(mo))
- return;
- else if (P_MobjWasRemoved(mo))
- return;
-#endif
- if (mo->type == MT_BLACKEGGMAN || mo->type == MT_CYBRAKDEMON)
- {
- mo->flags |= MF_NOCLIP;
- mo->flags &= ~MF_SPECIAL;
-
- S_StartSound(NULL, sfx_befall);
- }
- else if (mo->type == MT_KOOPA)
- {
- junk.tag = 650;
- EV_DoCeiling(&junk, raiseToHighest);
- return;
- }
- else // eggmobiles
- {
- // Stop exploding and prepare to run.
- P_SetMobjState(mo, mo->info->xdeathstate);
- if (P_MobjWasRemoved(mo))
- return;
-
- P_SetTarget(&mo->target, NULL);
-
- // Flee! Flee! Find a point to escape to! If none, just shoot upward!
- // scan the thinkers to find the runaway point
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo2 = (mobj_t *)th;
-
- if (mo2->type == MT_BOSSFLYPOINT)
- {
- // If this one's closer then the last one, go for it.
- if (!mo->target ||
- P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) <
- P_AproxDistance(P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y), mo->z - mo->target->z))
- P_SetTarget(&mo->target, mo2);
- // Otherwise... Don't!
- }
- }
-
- mo->flags |= MF_NOGRAVITY|MF_NOCLIP;
- mo->flags |= MF_NOCLIPHEIGHT;
-
- if (mo->target)
- {
- mo->angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y);
- mo->flags2 |= MF2_BOSSFLEE;
- mo->momz = FixedMul(FixedDiv(mo->target->z - mo->z, P_AproxDistance(mo->x-mo->target->x,mo->y-mo->target->y)), FixedMul(2*FRACUNIT, mo->scale));
- }
- else
- mo->momz = FixedMul(2*FRACUNIT, mo->scale);
- }
-
- if (mo->type == MT_EGGMOBILE2)
- {
- mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
- mo->y + P_ReturnThrustY(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
- mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK1].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK1); // Right tank
- mo2->angle = mo->angle;
- mo2->destscale = mo->scale;
- P_SetScale(mo2, mo2->destscale);
- if (mo->eflags & MFE_VERTICALFLIP)
- {
- mo2->eflags |= MFE_VERTICALFLIP;
- mo2->flags2 |= MF2_OBJECTFLIP;
- }
- P_InstaThrust(mo2, mo2->angle - ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale));
- P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
-
- mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
- mo->y + P_ReturnThrustY(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
- mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK2].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK2); // Left tank
- mo2->angle = mo->angle;
- mo2->destscale = mo->scale;
- P_SetScale(mo2, mo2->destscale);
- if (mo->eflags & MFE_VERTICALFLIP)
- {
- mo2->eflags |= MFE_VERTICALFLIP;
- mo2->flags2 |= MF2_OBJECTFLIP;
- }
- P_InstaThrust(mo2, mo2->angle + ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale));
- P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
-
- mo2 = P_SpawnMobj(mo->x, mo->y,
- mo->z + ((mo->eflags & MFE_VERTICALFLIP)? mobjinfo[MT_BOSSSPIGOT].height-FixedMul(32*FRACUNIT,mo->scale): mo->height + FixedMul(32*FRACUNIT, mo->scale)), MT_BOSSSPIGOT);
- mo2->angle = mo->angle;
- mo2->destscale = mo->scale;
- P_SetScale(mo2, mo2->destscale);
- if (mo->eflags & MFE_VERTICALFLIP)
- {
- mo2->eflags |= MFE_VERTICALFLIP;
- mo2->flags2 |= MF2_OBJECTFLIP;
- }
- P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
- return;
- }
-}
-
-// Function: A_CustomPower
-//
-// Description: Provides a custom powerup. Target (must be a player) is awarded the powerup. Reactiontime of the object is used as an index to the powers array.
-//
-// var1 = Power index #
-// var2 = Power duration in tics
-//
-void A_CustomPower(mobj_t *actor)
-{
- player_t *player;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- boolean spawnshield = false;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CustomPower", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- if (locvar1 >= NUMPOWERS)
- {
- CONS_Debug(DBG_GAMELOGIC, "Power #%d out of range!\n", locvar1);
- return;
- }
-
- player = actor->target->player;
-
- if (locvar1 == pw_shield && player->powers[pw_shield] != locvar2)
- spawnshield = true;
-
- player->powers[locvar1] = (UINT16)locvar2;
- if (actor->info->seesound)
- S_StartSound(player->mo, actor->info->seesound);
-
- if (spawnshield) //workaround for a bug
- P_SpawnShieldOrb(player);
-}
-
-// Function: A_GiveWeapon
-//
-// Description: Gives the player the specified weapon panels.
-//
-// var1 = Weapon index #
-// var2 = unused
-//
-void A_GiveWeapon(mobj_t *actor)
-{
- player_t *player;
- INT32 locvar1 = var1;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_GiveWeapon", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- if (locvar1 >= 1<<(NUM_WEAPONS-1))
- {
- CONS_Debug(DBG_GAMELOGIC, "Weapon #%d out of range!\n", locvar1);
- return;
- }
-
- player = actor->target->player;
-
- player->ringweapons |= locvar1;
- if (actor->info->seesound)
- S_StartSound(player->mo, actor->info->seesound);
-}
-
-// Function: A_RingBox
-//
-// Description: Awards the player 10 rings.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_RingBox(mobj_t *actor)
-{
- player_t *player;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RingBox", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- player = actor->target->player;
-
- P_GivePlayerRings(player, actor->info->reactiontime);
- if (actor->info->seesound)
- S_StartSound(player->mo, actor->info->seesound);
-}
-
-// Function: A_Invincibility
-//
-// Description: Awards the player invincibility.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Invincibility(mobj_t *actor)
-{
- player_t *player;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Invincibility", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- player = actor->target->player;
- player->powers[pw_invulnerability] = invulntics + 1;
-
- if (P_IsLocalPlayer(player) && !player->powers[pw_super])
- {
- S_StopMusic();
- if (mariomode)
- G_GhostAddColor(GHC_INVINCIBLE);
- strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
- S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
- S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
- }
-}
-
-// Function: A_SuperSneakers
-//
-// Description: Awards the player super sneakers.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SuperSneakers(mobj_t *actor)
-{
- player_t *player;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SuperSneakers", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- player = actor->target->player;
-
- actor->target->player->powers[pw_sneakers] = sneakertics + 1;
-
- if (P_IsLocalPlayer(player) && !player->powers[pw_super])
- {
- if (S_SpeedMusic(0.0f) && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC))
- S_SpeedMusic(1.4f);
- else
- {
- S_StopMusic();
- S_ChangeMusicInternal("_shoes", false);
- }
- strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
- S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]);
- }
-}
-
-// Function: A_AwardScore
-//
-// Description: Adds a set amount of points to the player's score.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_AwardScore(mobj_t *actor)
-{
- player_t *player;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_AwardScore", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- player = actor->target->player;
-
- P_AddPlayerScore(player, actor->info->reactiontime);
- if (actor->info->seesound)
- S_StartSound(player->mo, actor->info->seesound);
-}
-
-// Function: A_ExtraLife
-//
-// Description: Awards the player an extra life.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ExtraLife(mobj_t *actor)
-{
- player_t *player;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ExtraLife", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- player = actor->target->player;
-
- if (actor->type == MT_1UP_ICON && actor->tracer)
- {
- // We're using the overlay, so use the overlay 1up sprite (no text)
- actor->sprite = SPR_TV1P;
- }
-
- if (ultimatemode) //I don't THINK so!
- {
- S_StartSound(player->mo, sfx_lose);
- return;
- }
-
- P_GiveCoopLives(player, 1, true);
-}
-
-// Function: A_GiveShield
-//
-// Description: Awards the player a specified shield.
-//
-// var1 = Shield type (make with SH_ constants)
-// var2 = unused
-//
-void A_GiveShield(mobj_t *actor)
-{
- player_t *player;
- UINT16 locvar1 = var1;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_GiveShield", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- player = actor->target->player;
-
- P_SwitchShield(player, locvar1);
- S_StartSound(player->mo, actor->info->seesound);
-}
-
-// Function: A_GravityBox
-//
-// Description: Awards the player gravity boots.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GravityBox(mobj_t *actor)
-{
- player_t *player;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_GravityBox", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- player = actor->target->player;
-
- S_StartSound(player, actor->info->activesound);
-
- player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
-}
-
-// Function: A_ScoreRise
-//
-// Description: Makes the little score logos rise. Speed value sets speed.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ScoreRise(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ScoreRise", actor))
- return;
-#endif
- // make logo rise!
- P_SetObjectMomZ(actor, actor->info->speed, false);
-}
-
-// Function: A_BunnyHop
-//
-// Description: Makes object hop like a bunny.
-//
-// var1 = jump strength
-// var2 = horizontal movement
-//
-void A_BunnyHop(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BunnyHop", actor))
- return;
-#endif
- if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)
- || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
- {
- P_SetObjectMomZ(actor, locvar1*FRACUNIT, false);
- P_InstaThrust(actor, actor->angle, FixedMul(locvar2*FRACUNIT, actor->scale)); // Launch the hopping action! PHOOM!!
- }
-}
-
-// Function: A_BubbleSpawn
-//
-// Description: Spawns a randomly sized bubble from the object's location. Only works underwater.
-//
-// var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
-// var2 = unused
-//
-void A_BubbleSpawn(mobj_t *actor)
-{
- INT32 i, locvar1 = var1;
- UINT8 prandom;
- mobj_t *bubble = NULL;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BubbleSpawn", actor))
- return;
-#endif
- if (!(actor->eflags & MFE_UNDERWATER))
- {
- // Don't draw or spawn bubbles above water
- actor->flags2 |= MF2_DONTDRAW;
- return;
- }
- actor->flags2 &= ~MF2_DONTDRAW;
-
- if (!(actor->flags2 & MF2_AMBUSH))
- {
- // Quick! Look through players!
- // Don't spawn bubbles unless a player is relatively close by (var1).
- for (i = 0; i < MAXPLAYERS; ++i)
- if (playeringame[i] && players[i].mo
- && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<x, actor->y, actor->z + (actor->height / 2), MT_EXTRALARGEBUBBLE);
- else if (prandom > 128)
- bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_SMALLBUBBLE);
- else if (prandom < 128 && prandom > 96)
- bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_MEDIUMBUBBLE);
-
- if (bubble)
- {
- bubble->destscale = actor->scale;
- P_SetScale(bubble, actor->scale);
- }
-}
-
-// Function: A_FanBubbleSpawn
-//
-// Description: Spawns bubbles from fans, if they're underwater.
-//
-// var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
-// var2 = unused
-//
-void A_FanBubbleSpawn(mobj_t *actor)
-{
- INT32 i, locvar1 = var1;
- UINT8 prandom;
- mobj_t *bubble = NULL;
- fixed_t hz = actor->z + (4*actor->height)/5;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FanBubbleSpawn", actor))
- return;
-#endif
- if (!(actor->eflags & MFE_UNDERWATER))
- return;
-
- if (!(actor->flags2 & MF2_AMBUSH))
- {
- // Quick! Look through players!
- // Don't spawn bubbles unless a player is relatively close by (var2).
- for (i = 0; i < MAXPLAYERS; ++i)
- if (playeringame[i] && players[i].mo
- && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<x, actor->y, hz, MT_SMALLBUBBLE);
- else if ((prandom & 0xF0) == 0xF0)
- bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_MEDIUMBUBBLE);
-
- if (bubble)
- {
- bubble->destscale = actor->scale;
- P_SetScale(bubble, actor->scale);
- }
-}
-
-// Function: A_BubbleRise
-//
-// Description: Raises a bubble
-//
-// var1:
-// 0 = Bend around the water abit, looking more realistic
-// 1 = Rise straight up
-// var2 = rising speed
-//
-void A_BubbleRise(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BubbleRise", actor))
- return;
-#endif
- if (actor->type == MT_EXTRALARGEBUBBLE)
- P_SetObjectMomZ(actor, FixedDiv(6*FRACUNIT,5*FRACUNIT), false); // make bubbles rise!
- else
- {
- P_SetObjectMomZ(actor, locvar2, true); // make bubbles rise!
-
- // Move around slightly to make it look like it's bending around the water
- if (!locvar1)
- {
- UINT8 prandom = P_RandomByte();
- if (!(prandom & 0x7)) // *****000
- {
- P_InstaThrust(actor, prandom & 0x70 ? actor->angle + ANGLE_90 : actor->angle,
- FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
- }
- else if (!(prandom & 0x38)) // **000***
- {
- P_InstaThrust(actor, prandom & 0x70 ? actor->angle - ANGLE_90 : actor->angle - ANGLE_180,
- FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
- }
- }
- }
-}
-
-// Function: A_BubbleCheck
-//
-// Description: Checks if a bubble should be drawn or not. Bubbles are not drawn above water.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_BubbleCheck(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BubbleCheck", actor))
- return;
-#endif
- if (actor->eflags & MFE_UNDERWATER)
- actor->flags2 &= ~MF2_DONTDRAW; // underwater so draw
- else
- actor->flags2 |= MF2_DONTDRAW; // above water so don't draw
-}
-
-// Function: A_AttractChase
-//
-// Description: Makes a ring chase after a player with a ring shield and also causes spilled rings to flicker.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_AttractChase(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_AttractChase", actor))
- return;
-#endif
- if (actor->flags2 & MF2_NIGHTSPULL || !actor->health)
- return;
-
- // spilled rings flicker before disappearing
- if (leveltime & 1 && actor->type == (mobjtype_t)actor->info->reactiontime && actor->fuse && actor->fuse < 2*TICRATE)
- actor->flags2 |= MF2_DONTDRAW;
- else
- actor->flags2 &= ~MF2_DONTDRAW;
-
- // Turn flingrings back into regular rings if attracted.
- if (actor->tracer && actor->tracer->player
- && !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
- {
- mobj_t *newring;
- newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime);
- newring->momx = actor->momx;
- newring->momy = actor->momy;
- newring->momz = actor->momz;
- P_RemoveMobj(actor);
- return;
- }
-
- P_LookForShield(actor); // Go find 'em, boy!
-
- if (!actor->tracer
- || !actor->tracer->player
- || !actor->tracer->health
- || !P_CheckSight(actor, actor->tracer)) // You have to be able to SEE it...sorta
- {
- // Lost attracted rings don't through walls anymore.
- actor->flags &= ~MF_NOCLIP;
- P_SetTarget(&actor->tracer, NULL);
- return;
- }
-
- // If a FlingRing gets attracted by a shield, change it into a normal ring.
- if (actor->type == (mobjtype_t)actor->info->reactiontime)
- {
- P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance);
- P_RemoveMobj(actor);
- return;
- }
-
- // Keep stuff from going down inside floors and junk
- actor->flags &= ~MF_NOCLIPHEIGHT;
-
- // Let attracted rings move through walls and such.
- actor->flags |= MF_NOCLIP;
-
- P_Attract(actor, actor->tracer, false);
-}
-
-// Function: A_DropMine
-//
-// Description: Drops a mine. Raisestate specifies the object # to use for the mine.
-//
-// var1 = height offset
-// var2:
-// lower 16 bits = proximity check distance (0 disables)
-// upper 16 bits = 0 to check proximity with target, 1 for tracer
-//
-void A_DropMine(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- fixed_t z;
- mobj_t *mine;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_DropMine", actor))
- return;
-#endif
-
- if (locvar2 & 65535)
- {
- fixed_t dist;
- mobj_t *target;
-
- if (locvar2 >> 16)
- target = actor->tracer;
- else
- target = actor->target;
-
- if (!target)
- return;
-
- dist = P_AproxDistance(actor->x-target->x, actor->y-target->y)>>FRACBITS;
-
- if (dist > FixedMul((locvar2 & 65535), actor->scale))
- return;
- }
-
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - mobjinfo[actor->info->raisestate].height - FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
-
- // Use raisestate instead of MT_MINE
- mine = P_SpawnMobj(actor->x, actor->y, z, (mobjtype_t)actor->info->raisestate);
- if (actor->eflags & MFE_VERTICALFLIP)
- mine->eflags |= MFE_VERTICALFLIP;
- mine->momz = actor->momz + actor->pmomz;
-
- S_StartSound(actor, actor->info->attacksound);
-}
-
-// Function: A_FishJump
-//
-// Description: Makes the stupid harmless fish in Greenflower Zone jump.
-//
-// var1 = Jump strength (in FRACBITS), if specified. Otherwise, uses the angle value.
-// var2 = unused
-//
-void A_FishJump(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FishJump", actor))
- return;
-#endif
-
- if (locvar2)
- {
- fixed_t rad = actor->radius>>FRACBITS;
- P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale)))
- {
- fixed_t jumpval;
-
- if (locvar1)
- jumpval = var1;
- else
- jumpval = FixedMul(AngleFixed(actor->angle)/4, actor->scale);
-
- if (!jumpval) jumpval = FixedMul(44*(FRACUNIT/4), actor->scale);
- actor->momz = jumpval;
- P_SetMobjStateNF(actor, actor->info->seestate);
- }
-
- if (actor->momz < 0
- && (actor->state < &states[actor->info->meleestate] || actor->state > &states[actor->info->xdeathstate]))
- P_SetMobjStateNF(actor, actor->info->meleestate);
-}
-
-// Function:A_ThrownRing
-//
-// Description: Thinker for thrown rings/sparkle trail
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ThrownRing(mobj_t *actor)
-{
- INT32 c = 0;
- INT32 stop;
- player_t *player;
- fixed_t dist;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ThrownRing", actor))
- return;
-#endif
-
- if (leveltime % (TICRATE/7) == 0)
- {
- mobj_t *ring = NULL;
-
- if (actor->flags2 & MF2_EXPLOSION)
- {
- if (actor->momx != 0 || actor->momy != 0)
- ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE);
- // Else spawn nothing because it's totally stationary and constantly smoking would be weird -SH
- }
- else if (actor->flags2 & MF2_AUTOMATIC)
- ring = P_SpawnGhostMobj(actor);
- else if (!(actor->flags2 & MF2_RAILRING))
- ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPARK);
-
- if (ring)
- {
- /*
- P_SetTarget(&ring->target, actor);
- ring->color = actor->color; //copy color
- */
- ring->destscale = actor->scale;
- P_SetScale(ring, actor->scale);
- }
- }
-
- // A_GrenadeRing beeping lives once moooooore -SH
- if (actor->type == MT_THROWNGRENADE && actor->fuse % TICRATE == 0)
- S_StartSound(actor, actor->info->attacksound);
-
- // decrement bounce ring time
- if (actor->flags2 & MF2_BOUNCERING)
- {
- if (actor->fuse)
- actor->fuse--;
- else {
- P_RemoveMobj(actor);
- return;
- }
- }
-
- // spilled rings (and thrown bounce) flicker before disappearing
- if (leveltime & 1 && actor->fuse > 0 && actor->fuse < 2*TICRATE
- && actor->type != MT_THROWNGRENADE)
- actor->flags2 |= MF2_DONTDRAW;
- else
- actor->flags2 &= ~MF2_DONTDRAW;
-
- if (actor->tracer && actor->tracer->health <= 0)
- P_SetTarget(&actor->tracer, NULL);
-
- // Updated homing ring special capability
- // If you have a ring shield, all rings thrown
- // at you become homing (except rail)!
- if (actor->tracer)
- {
- // A non-homing ring getting attracted by a
- // magnetic player. If he gets too far away, make
- // sure to stop the attraction!
- if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC)
- && P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x,
- actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale)))
- {
- P_SetTarget(&actor->tracer, NULL);
- }
-
- if (actor->tracer && (actor->tracer->health)
- && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow.
- {
- const INT32 temp = actor->threshold;
- actor->threshold = 32000;
- P_HomingAttack(actor, actor->tracer);
- actor->threshold = temp;
- return;
- }
- }
-
- // first time init, this allow minimum lastlook changes
- if (actor->lastlook < 0)
- actor->lastlook = P_RandomByte();
-
- actor->lastlook %= MAXPLAYERS;
-
- stop = (actor->lastlook - 1) & PLAYERSMASK;
-
- for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
- {
- // done looking
- if (actor->lastlook == stop)
- return;
-
- if (!playeringame[actor->lastlook])
- continue;
-
- if (c++ == 2)
- return;
-
- player = &players[actor->lastlook];
-
- if (!player->mo)
- continue;
-
- if (player->mo->health <= 0)
- continue; // dead
-
- if ((netgame || multiplayer) && player->spectator)
- continue; // spectator
-
- if (actor->target && actor->target->player)
- {
- if (player->mo == actor->target)
- continue;
-
- // Don't home in on teammates.
- if (gametype == GT_CTF
- && actor->target->player->ctfteam == player->ctfteam)
- continue;
- }
-
- dist = P_AproxDistance(P_AproxDistance(player->mo->x-actor->x,
- player->mo->y-actor->y), player->mo->z-actor->z);
-
- // check distance
- if (actor->flags2 & MF2_RAILRING)
- {
- if (dist > FixedMul(RING_DIST/2, player->mo->scale))
- continue;
- }
- else if (dist > FixedMul(RING_DIST, player->mo->scale))
- continue;
-
- // do this after distance check because it's more computationally expensive
- if (!P_CheckSight(actor, player->mo))
- continue; // out of sight
-
- if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
- && dist < FixedMul(RING_DIST/4, player->mo->scale))
- P_SetTarget(&actor->tracer, player->mo);
- return;
- }
-
- return;
-}
-
-// Function: A_SetSolidSteam
-//
-// Description: Makes steam solid so it collides with the player to boost them.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SetSolidSteam(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetSolidSteam", actor))
- return;
-#endif
- actor->flags &= ~MF_NOCLIP;
- actor->flags |= MF_SOLID;
- if (!(actor->flags2 & MF2_AMBUSH))
- {
- if (P_RandomChance(FRACUNIT/8))
- {
- if (actor->info->deathsound)
- S_StartSound(actor, actor->info->deathsound); // Hiss!
- }
- else
- {
- if (actor->info->painsound)
- S_StartSound(actor, actor->info->painsound);
- }
- }
-
- P_SetObjectMomZ (actor, 1, true);
-}
-
-// Function: A_UnsetSolidSteam
-//
-// Description: Makes an object non-solid and also noclip. Used by the steam.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_UnsetSolidSteam(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_UnsetSolidSteam", actor))
- return;
-#endif
- actor->flags &= ~MF_SOLID;
- actor->flags |= MF_NOCLIP;
-}
-
-// Function: A_SignPlayer
-//
-// Description: Changes the state of a level end sign to reflect the player that hit it.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SignPlayer(mobj_t *actor)
-{
- mobj_t *ov;
- skin_t *skin;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SignPlayer", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- if (!actor->target->player)
- return;
-
- skin = &skins[actor->target->player->skin];
-
- if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor?
- {
- actor->color = skin->prefoppositecolor;
- /*
- If you're here from the comment above Color_Opposite,
- the following line is the one which is dependent on the
- array being symmetrical. It gets the opposite of the
- opposite of your desired colour just so it can get the
- brightness frame for the End Sign. It's not a great
- design choice, but it's constant time array access and
- the idea that the colours should be OPPOSITES is kind
- of in the name. If you have a better idea, feel free
- to let me know. ~toast 2016/07/20
- */
- actor->frame += (15 - Color_Opposite[(Color_Opposite[(skin->prefoppositecolor - 1)*2] - 1)*2 + 1]);
- }
- else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor.
- {
- actor->color = Color_Opposite[(actor->target->player->skincolor - 1)*2];
- actor->frame += (15 - Color_Opposite[(actor->target->player->skincolor - 1)*2 + 1]);
- }
-
- if (skin->sprites[SPR2_SIGN].numframes)
- {
- // spawn an overlay of the player's face.
- ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
- P_SetTarget(&ov->target, actor);
- ov->color = actor->target->player->skincolor;
- ov->skin = skin;
- P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN
- }
-}
-
-// Function: A_OverlayThink
-//
-// Description: Moves the overlay to the position of its target.
-//
-// var1 = unused
-// var2 = invert, z offset
-//
-void A_OverlayThink(mobj_t *actor)
-{
- fixed_t destx, desty;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_OverlayThink", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- if (!splitscreen && rendermode != render_soft)
- {
- angle_t viewingangle;
-
- if (players[displayplayer].awayviewtics)
- viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y);
- else if (!camera.chase && players[displayplayer].mo)
- viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y);
- else
- viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, camera.x, camera.y);
-
- destx = actor->target->x + P_ReturnThrustX(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
- desty = actor->target->y + P_ReturnThrustY(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
- }
- else
- {
- destx = actor->target->x;
- desty = actor->target->y;
- }
- P_UnsetThingPosition(actor);
- actor->x = destx;
- actor->y = desty;
- P_SetThingPosition(actor);
- if (actor->eflags & MFE_VERTICALFLIP)
- actor->z = actor->target->z + actor->target->height - mobjinfo[actor->type].height - ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
- else
- actor->z = actor->target->z + ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
- actor->angle = actor->target->angle;
- actor->eflags = actor->target->eflags;
-
- actor->momx = actor->target->momx;
- actor->momy = actor->target->momy;
- actor->momz = actor->target->momz; // assume target has correct momz! Do not use P_SetObjectMomZ!
-}
-
-// Function: A_JetChase
-//
-// Description: A_Chase for Jettysyns
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetChase(mobj_t *actor)
-{
- fixed_t thefloor;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_JetChase", actor))
- return;
-#endif
-
- if (actor->flags2 & MF2_AMBUSH)
- return;
-
- if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
- && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
- thefloor = actor->watertop;
- else
- thefloor = actor->floorz;
-
- if (actor->reactiontime)
- actor->reactiontime--;
-
- if (P_RandomChance(FRACUNIT/32))
- {
- actor->momx = actor->momx / 2;
- actor->momy = actor->momy / 2;
- actor->momz = actor->momz / 2;
- }
-
- // Bounce if too close to floor or ceiling -
- // ideal for Jetty-Syns above you on 3d floors
- if (actor->momz && ((actor->z - FixedMul((32<scale)) < thefloor) && !((thefloor + FixedMul(32*FRACUNIT, actor->scale) + actor->height) > actor->ceilingz))
- actor->momz = -actor->momz/2;
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- actor->momx = actor->momy = actor->momz = 0;
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- // modify target threshold
- if (actor->threshold)
- {
- if (!actor->target || actor->target->health <= 0)
- actor->threshold = 0;
- else
- actor->threshold--;
- }
-
- // turn towards movement direction if not there yet
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-
- if ((multiplayer || netgame) && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)))
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- // If the player is over 3072 fracunits away, then look for another player
- if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
- actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
- {
- return; // got a new target
- }
-
- // chase towards player
- if (ultimatemode)
- P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/2, actor->scale));
- else
- P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/4, actor->scale));
-
- // must adjust height
- if (ultimatemode)
- {
- if (actor->z < (actor->target->z + actor->target->height + FixedMul((64<scale)))
- actor->momz += FixedMul(FRACUNIT/2, actor->scale);
- else
- actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
- }
- else
- {
- if (actor->z < (actor->target->z + actor->target->height + FixedMul((32<scale)))
- actor->momz += FixedMul(FRACUNIT/2, actor->scale);
- else
- actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
- }
-}
-
-// Function: A_JetbThink
-//
-// Description: Thinker for Jetty-Syn bombers
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetbThink(mobj_t *actor)
-{
- sector_t *nextsector;
- fixed_t thefloor;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_JetbThink", actor))
- return;
-#endif
-
- if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
- && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
- thefloor = actor->watertop;
- else
- thefloor = actor->floorz;
-
- if (actor->target)
- {
- A_JetChase(actor);
- // check for melee attack
- if (actor->info->raisestate
- && (actor->z > (actor->floorz + FixedMul((32<scale)))
- && P_JetbCheckMeleeRange(actor) && !actor->reactiontime
- && (actor->target->z >= actor->floorz))
- {
- mobj_t *bomb;
- if (actor->info->attacksound)
- S_StartAttackSound(actor, actor->info->attacksound);
-
- // use raisestate instead of MT_MINE
- bomb = P_SpawnMobj(actor->x, actor->y, actor->z - FixedMul((32<scale), (mobjtype_t)actor->info->raisestate);
-
- P_SetTarget(&bomb->target, actor);
- bomb->destscale = actor->scale;
- P_SetScale(bomb, actor->scale);
- actor->reactiontime = TICRATE; // one second
- S_StartSound(actor, actor->info->attacksound);
- }
- }
- else if (((actor->z - FixedMul((32<scale)) < thefloor) && !((thefloor + FixedMul((32<scale) + actor->height) > actor->ceilingz))
- actor->z = thefloor+FixedMul((32<scale);
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
-
- // Move downwards or upwards to go through a passageway.
- if (nextsector->ceilingheight < actor->z + actor->height)
- actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
- else if (nextsector->floorheight > actor->z)
- actor->momz += FixedMul(5*FRACUNIT, actor->scale);
-}
-
-// Function: A_JetgShoot
-//
-// Description: Firing function for Jetty-Syn gunners.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetgShoot(mobj_t *actor)
-{
- fixed_t dist;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_JetgShoot", actor))
- return;
-#endif
-
- if (!actor->target)
- return;
-
- if (actor->reactiontime)
- return;
-
- dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
-
- if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
- return;
-
- if (dist < FixedMul(64*FRACUNIT, actor->scale))
- return;
-
- A_FaceTarget(actor);
- P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
-
- if (ultimatemode)
- actor->reactiontime = actor->info->reactiontime*TICRATE;
- else
- actor->reactiontime = actor->info->reactiontime*TICRATE*2;
-
- if (actor->info->attacksound)
- S_StartSound(actor, actor->info->attacksound);
-}
-
-// Function: A_ShootBullet
-//
-// Description: Shoots a bullet. Raisestate defines object # to use as projectile.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ShootBullet(mobj_t *actor)
-{
- fixed_t dist;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ShootBullet", actor))
- return;
-#endif
-
- if (!actor->target)
- return;
-
- dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z);
-
- if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
- return;
-
- A_FaceTarget(actor);
- P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
-
- if (actor->info->attacksound)
- S_StartSound(actor, actor->info->attacksound);
-}
-
-// Function: A_MinusDigging
-//
-// Description: Minus digging in the ground.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MinusDigging(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MinusDigging", actor))
- return;
-#endif
- actor->flags &= ~MF_SPECIAL;
- actor->flags &= ~MF_SHOOTABLE;
-
- if (!actor->target)
- {
- A_Look(actor);
- return;
- }
-
- if (actor->reactiontime)
- {
- actor->reactiontime--;
- return;
- }
-
- // Dirt trail
- P_SpawnGhostMobj(actor);
-
- actor->flags |= MF_NOCLIPTHING;
- var1 = 3;
- A_Chase(actor);
- actor->flags &= ~MF_NOCLIPTHING;
-
- // Play digging sound
- if (!(leveltime & 15))
- S_StartSound(actor, actor->info->activesound);
-
- // If we're close enough to our target, pop out of the ground
- if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) < actor->radius
- && abs(actor->target->z - actor->z) < 2*actor->height)
- P_SetMobjState(actor, actor->info->missilestate);
-
- // Snap to ground
- if (actor->eflags & MFE_VERTICALFLIP)
- actor->z = actor->ceilingz - actor->height;
- else
- actor->z = actor->floorz;
-}
-
-// Function: A_MinusPopup
-//
-// Description: Minus popping out of the ground.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MinusPopup(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MinusPopup", actor))
- return;
-#endif
- P_SetObjectMomZ(actor, 10*FRACUNIT, false);
-
- actor->flags |= MF_SPECIAL;
- actor->flags |= MF_SHOOTABLE;
-
- // Sound for busting out of the ground.
- S_StartSound(actor, actor->info->attacksound);
-}
-
-// Function: A_MinusCheck
-//
-// Description: If the minus hits the floor, dig back into the ground.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MinusCheck(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MinusCheck", actor))
- return;
-#endif
- if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
- || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))
- {
- actor->flags &= ~MF_SPECIAL;
- actor->flags &= ~MF_SHOOTABLE;
- actor->reactiontime = TICRATE;
- P_SetMobjState(actor, actor->info->seestate);
- return;
- }
-
- // 'Falling' animation
- if (P_MobjFlip(actor)*actor->momz < 0 && actor->state < &states[actor->info->meleestate])
- P_SetMobjState(actor, actor->info->meleestate);
-}
-
-// Function: A_ChickenCheck
-//
-// Description: Resets the chicken once it hits the floor again.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ChickenCheck(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ChickenCheck", actor))
- return;
-#endif
- if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
- || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height >= actor->ceilingz))
- {
- if (!(actor->momx || actor->momy || actor->momz)
- && actor->state > &states[actor->info->seestate])
- {
- A_Chase(actor);
- P_SetMobjState(actor, actor->info->seestate);
- }
-
- actor->momx >>= 2;
- actor->momy >>= 2;
- }
-}
-
-// Function: A_JetgThink
-//
-// Description: Thinker for Jetty-Syn Gunners
-//
-// var1 = unused
-// var2 = unused
-//
-void A_JetgThink(mobj_t *actor)
-{
- sector_t *nextsector;
-
- fixed_t thefloor;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_JetgThink", actor))
- return;
-#endif
-
- if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
- && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
- thefloor = actor->watertop;
- else
- thefloor = actor->floorz;
-
- if (actor->target)
- {
- if (P_RandomChance(FRACUNIT/8) && !actor->reactiontime)
- P_SetMobjState(actor, actor->info->missilestate);
- else
- A_JetChase (actor);
- }
- else if (actor->z - FixedMul((32<scale) < thefloor && !(thefloor + FixedMul((32<scale)
- + actor->height > actor->ceilingz))
- {
- actor->z = thefloor + FixedMul((32<scale);
- }
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
-
- // Move downwards or upwards to go through a passageway.
- if (nextsector->ceilingheight < actor->z + actor->height)
- actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
- else if (nextsector->floorheight > actor->z)
- actor->momz += FixedMul(5*FRACUNIT, actor->scale);
-}
-
-// Function: A_MouseThink
-//
-// Description: Thinker for scurrying mice.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MouseThink(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MouseThink", actor))
- return;
-#endif
-
- if (actor->reactiontime)
- actor->reactiontime--;
-
- if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z == actor->floorz)
- || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height == actor->ceilingz))
- && !actor->reactiontime)
- {
- if (twodlevel || actor->flags2 & MF2_TWOD)
- {
- if (P_RandomChance(FRACUNIT/2))
- actor->angle += ANGLE_180;
- }
- else if (P_RandomChance(FRACUNIT/2))
- actor->angle += ANGLE_90;
- else
- actor->angle -= ANGLE_90;
-
- P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
- actor->reactiontime = TICRATE/5;
- }
-}
-
-// Function: A_DetonChase
-//
-// Description: Chases a Deton after a player.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_DetonChase(mobj_t *actor)
-{
- angle_t exact;
- fixed_t xydist, dist;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_DetonChase", actor))
- return;
-#endif
-
- // modify tracer threshold
- if (!actor->tracer || actor->tracer->health <= 0)
- actor->threshold = 0;
- else
- actor->threshold = 1;
-
- if (!actor->tracer || !(actor->tracer->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, true, 0))
- return; // got a new target
-
- actor->momx = actor->momy = actor->momz = 0;
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- if (multiplayer && !actor->threshold && P_LookForPlayers(actor, true, true, 0))
- return; // got a new target
-
- // Face movement direction if not doing so
- exact = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
- actor->angle = exact;
- /*if (exact != actor->angle)
- {
- if (exact - actor->angle > ANGLE_180)
- {
- actor->angle -= actor->info->raisestate;
- if (exact - actor->angle < ANGLE_180)
- actor->angle = exact;
- }
- else
- {
- actor->angle += actor->info->raisestate;
- if (exact - actor->angle > ANGLE_180)
- actor->angle = exact;
- }
- }*/
- // movedir is up/down angle: how much it has to go up as it goes over to the player
- xydist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
- exact = R_PointToAngle2(0, 0, xydist, actor->tracer->z - actor->z);
- actor->movedir = exact;
- /*if (exact != actor->movedir)
- {
- if (exact - actor->movedir > ANGLE_180)
- {
- actor->movedir -= actor->info->raisestate;
- if (exact - actor->movedir < ANGLE_180)
- actor->movedir = exact;
- }
- else
- {
- actor->movedir += actor->info->raisestate;
- if (exact - actor->movedir > ANGLE_180)
- actor->movedir = exact;
- }
- }*/
-
- // check for melee attack
- if (actor->tracer)
- {
- if (P_AproxDistance(actor->tracer->x-actor->x, actor->tracer->y-actor->y) < actor->radius+actor->tracer->radius)
- {
- if (!((actor->tracer->z > actor->z + actor->height) || (actor->z > actor->tracer->z + actor->tracer->height)))
- {
- P_ExplodeMissile(actor);
- return;
- }
- }
- }
-
- // chase towards player
- if ((dist = P_AproxDistance(xydist, actor->tracer->z-actor->z))
- > FixedMul((actor->info->painchance << FRACBITS), actor->scale))
- {
- P_SetTarget(&actor->tracer, NULL); // Too far away
- return;
- }
-
- if (actor->reactiontime == 0)
- {
- actor->reactiontime = actor->info->reactiontime;
- return;
- }
-
- if (actor->reactiontime > 1)
- {
- actor->reactiontime--;
- return;
- }
-
- if (actor->reactiontime > 0)
- {
- actor->reactiontime = -42;
-
- if (actor->info->seesound)
- S_StartScreamSound(actor, actor->info->seesound);
- }
-
- if (actor->reactiontime == -42)
- {
- fixed_t xyspeed;
-
- actor->reactiontime = -42;
-
- exact = actor->movedir>>ANGLETOFINESHIFT;
- xyspeed = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINECOSINE(exact));
- actor->momz = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINESINE(exact));
-
- exact = actor->angle>>ANGLETOFINESHIFT;
- actor->momx = FixedMul(xyspeed, FINECOSINE(exact));
- actor->momy = FixedMul(xyspeed, FINESINE(exact));
-
- // Variable re-use
- xyspeed = (P_AproxDistance(actor->tracer->x - actor->x, P_AproxDistance(actor->tracer->y - actor->y, actor->tracer->z - actor->z))>>(FRACBITS+6));
-
- if (xyspeed < 1)
- xyspeed = 1;
-
- if (leveltime % xyspeed == 0)
- S_StartSound(actor, sfx_deton);
- }
-}
-
-// Function: A_CapeChase
-//
-// Description: Set an object's location to its target or tracer.
-//
-// var1:
-// 0 = Use target
-// 1 = Use tracer
-// upper 16 bits = Z offset
-// var2:
-// upper 16 bits = forward/backward offset
-// lower 16 bits = sideways offset
-//
-void A_CapeChase(mobj_t *actor)
-{
- mobj_t *chaser;
- fixed_t foffsetx, foffsety, boffsetx, boffsety;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- angle_t angle;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CapeChase", actor))
- return;
-#endif
-
- CONS_Debug(DBG_GAMELOGIC, "A_CapeChase called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
- if (locvar1 & 65535)
- chaser = actor->tracer;
- else
- chaser = actor->target;
-
- if (!chaser || (chaser->health <= 0))
- {
- if (chaser)
- CONS_Debug(DBG_GAMELOGIC, "Hmm, the guy I'm chasing (object type %d) has no health.. so I'll die too!\n", chaser->type);
-
- P_RemoveMobj(actor);
- return;
- }
-
- angle = (chaser->player ? chaser->player->drawangle : chaser->angle);
-
- foffsetx = P_ReturnThrustX(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
- foffsety = P_ReturnThrustY(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
-
- boffsetx = P_ReturnThrustX(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
- boffsety = P_ReturnThrustY(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
-
- P_UnsetThingPosition(actor);
- actor->x = chaser->x + foffsetx + boffsetx;
- actor->y = chaser->y + foffsety + boffsety;
- if (chaser->eflags & MFE_VERTICALFLIP)
- {
- actor->eflags |= MFE_VERTICALFLIP;
- actor->flags2 |= MF2_OBJECTFLIP;
- actor->z = chaser->z + chaser->height - actor->height - FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
- }
- else
- {
- actor->eflags &= ~MFE_VERTICALFLIP;
- actor->flags2 &= ~MF2_OBJECTFLIP;
- actor->z = chaser->z + FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
- }
- actor->angle = angle;
- P_SetThingPosition(actor);
-}
-
-// Function: A_RotateSpikeBall
-//
-// Description: Rotates a spike ball around its target/tracer.
-//
-// var1:
-// 0 = Use target
-// 1 = Use tracer
-// var2 = unused
-//
-void A_RotateSpikeBall(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- const fixed_t radius = FixedMul(12*actor->info->speed, actor->scale);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RotateSpikeBall", actor))
- return;
-#endif
-
- if (!((!locvar1 && (actor->target)) || (locvar1 && (actor->tracer))))// This should NEVER happen.
- {
- CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Spikeball has no target\n");
- P_RemoveMobj(actor);
- return;
- }
-
- if (!actor->info->speed)
- {
- CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Object has no speed.\n");
- return;
- }
-
- actor->angle += FixedAngle(actor->info->speed);
- P_UnsetThingPosition(actor);
- {
- const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
- if (!locvar1)
- {
- actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
- actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
- actor->z = actor->target->z + actor->target->height/2;
- }
- else
- {
- actor->x = actor->tracer->x + FixedMul(FINECOSINE(fa),radius);
- actor->y = actor->tracer->y + FixedMul(FINESINE(fa),radius);
- actor->z = actor->tracer->z + actor->tracer->height/2;
- }
- P_SetThingPosition(actor);
- }
-}
-
-// Function: A_UnidusBall
-//
-// Description: Rotates a spike ball around its target.
-//
-// var1:
-// 0 = Don't throw
-// 1 = Throw
-// 2 = Throw when target leaves MF2_SKULLFLY.
-// var2 = unused
-//
-void A_UnidusBall(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- boolean canthrow = false;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_UnidusBall", actor))
- return;
-#endif
-
- actor->angle += ANGLE_11hh;
-
- if (actor->movecount)
- {
- if (P_AproxDistance(actor->momx, actor->momy) < FixedMul(actor->info->damage/2, actor->scale))
- P_ExplodeMissile(actor);
- return;
- }
-
- if (!actor->target || !actor->target->health)
- {
- CONS_Debug(DBG_GAMELOGIC, "A_UnidusBall: Removing unthrown spikeball from nonexistant Unidus\n");
- P_RemoveMobj(actor);
- return;
- }
-
- P_UnsetThingPosition(actor);
- {
- const angle_t angle = actor->movedir + FixedAngle(actor->info->speed*(leveltime%360));
- const UINT16 fa = angle>>ANGLETOFINESHIFT;
-
- actor->x = actor->target->x + FixedMul(FINECOSINE(fa),actor->threshold);
- actor->y = actor->target->y + FixedMul( FINESINE(fa),actor->threshold);
- actor->z = actor->target->z + actor->target->height/2 - actor->height/2;
-
- if (locvar1 == 1 && actor->target->target)
- {
- const angle_t tang = R_PointToAngle2(actor->target->x, actor->target->y, actor->target->target->x, actor->target->target->y);
- const angle_t mina = tang-ANGLE_11hh;
- canthrow = (angle-mina < FixedAngle(actor->info->speed*3));
- }
- }
- P_SetThingPosition(actor);
-
- if (locvar1 == 1 && canthrow)
- {
- if (P_AproxDistance(actor->target->target->x - actor->target->x, actor->target->target->y - actor->target->y) > FixedMul(MISSILERANGE>>1, actor->scale)
- || !P_CheckSight(actor, actor->target->target))
- return;
-
- actor->movecount = actor->info->damage>>FRACBITS;
- actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
- P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->target->x, actor->target->target->y), FixedMul(actor->info->damage, actor->scale));
- }
- else if (locvar1 == 2)
- {
- boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY;
- if (actor->target->state == &states[actor->target->info->painstate])
- {
- P_KillMobj(actor, NULL, NULL, 0);
- return;
- }
- switch(actor->extravalue2)
- {
- case 0: // at least one frame where not dashing
- if (!skull) ++actor->extravalue2;
- else break;
- /* FALLTHRU */
- case 1: // at least one frame where ARE dashing
- if (skull) ++actor->extravalue2;
- else break;
- /* FALLTHRU */
- case 2: // not dashing again?
- if (skull) break;
- // launch.
- {
- mobj_t *target = actor->target;
- if (actor->target->target)
- target = actor->target->target;
- actor->movecount = actor->info->damage>>FRACBITS;
- actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
- P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, target->x, target->y), FixedMul(actor->info->damage, actor->scale));
- }
- default: // from our compiler appeasement program (CAP).
- break;
- }
- }
-}
-
-// Function: A_RockSpawn
-//
-// Spawns rocks at a specified interval
-//
-// var1 = unused
-// var2 = unused
-void A_RockSpawn(mobj_t *actor)
-{
- mobj_t *mo;
- mobjtype_t type;
- INT32 i = P_FindSpecialLineFromTag(12, (INT16)actor->threshold, -1);
- line_t *line;
- fixed_t dist;
- fixed_t randomoomph;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RockSpawn", actor))
- return;
-#endif
-
- if (i == -1)
- {
- CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: Unable to find parameter line 12 (tag %d)!\n", actor->threshold);
- return;
- }
-
- line = &lines[i];
-
- if (!(sides[line->sidenum[0]].textureoffset >> FRACBITS))
- {
- CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: No X-offset detected! (tag %d)!\n", actor->threshold);
- return;
- }
-
- dist = P_AproxDistance(line->dx, line->dy)/16;
-
- if (dist < 1)
- dist = 1;
-
- type = MT_ROCKCRUMBLE1 + (sides[line->sidenum[0]].rowoffset >> FRACBITS);
-
- if (line->flags & ML_NOCLIMB)
- randomoomph = P_RandomByte() * (FRACUNIT/32);
- else
- randomoomph = 0;
-
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FALLINGROCK);
- P_SetMobjState(mo, mobjinfo[type].spawnstate);
- mo->angle = R_PointToAngle2(line->v2->x, line->v2->y, line->v1->x, line->v1->y);
-
- P_InstaThrust(mo, mo->angle, dist + randomoomph);
- mo->momz = dist + randomoomph;
-
- var1 = sides[line->sidenum[0]].textureoffset >> FRACBITS;
- A_SetTics(actor);
-}
-
-//
-// Function: A_SlingAppear
-//
-// Appears a sling.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SlingAppear(mobj_t *actor)
-{
- UINT8 mlength = 4;
- mobj_t *spawnee, *hprev;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SlingAppear", actor))
- return;
-#endif
-
- P_UnsetThingPosition(actor);
- actor->flags &= ~(MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
- P_SetThingPosition(actor);
- actor->lastlook = 128;
- actor->movecount = actor->lastlook;
- actor->threshold = 0;
- actor->movefactor = actor->threshold;
- actor->friction = 128;
-
- hprev = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLGRABCHAIN);
- P_SetTarget(&hprev->tracer, actor);
- P_SetTarget(&hprev->hprev, actor);
- P_SetTarget(&actor->hnext, hprev);
- hprev->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
- hprev->movecount = mlength;
-
- mlength--;
-
- while (mlength > 0)
- {
- spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN);
- P_SetTarget(&spawnee->tracer, actor);
- P_SetTarget(&spawnee->hprev, hprev);
- P_SetTarget(&hprev->hnext, spawnee);
- hprev = spawnee;
-
- spawnee->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
- spawnee->movecount = mlength;
-
- mlength--;
- }
-}
-
-// Function: A_SetFuse
-//
-// Description: Sets the actor's fuse timer if not set already. May also change state when fuse reaches the last tic, otherwise by default the actor will die or disappear. (Replaces A_SnowBall)
-//
-// var1 = fuse timer duration (in tics).
-// var2:
-// lower 16 bits = if > 0, state to change to when fuse = 1
-// upper 16 bits: 0 = (default) don't set fuse unless 0, 1 = force change, 2 = force no change
-//
-void A_SetFuse(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetFuse", actor))
- return;
-#endif
-
- if ((!actor->fuse || (locvar2 >> 16)) && (locvar2 >> 16) != 2) // set the actor's fuse value
- actor->fuse = locvar1;
-
- if (actor->fuse == 1 && (locvar2 & 65535)) // change state on the very last tic (fuse is handled before actions in P_MobjThinker)
- {
- actor->fuse = 0; // don't die/disappear the next tic!
- P_SetMobjState(actor, locvar2 & 65535);
- }
-}
-
-// Function: A_CrawlaCommanderThink
-//
-// Description: Thinker for Crawla Commander.
-//
-// var1 = shoot bullets?
-// var2 = "pogo mode" speed
-//
-void A_CrawlaCommanderThink(mobj_t *actor)
-{
- fixed_t dist;
- sector_t *nextsector;
- fixed_t thefloor;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- boolean hovermode = (actor->health > 1 || actor->fuse);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CrawlaCommanderThink", actor))
- return;
-#endif
-
- if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
- && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
- thefloor = actor->watertop;
- else
- thefloor = actor->floorz;
-
- if (!actor->fuse && actor->flags2 & MF2_FRET)
- {
- if (actor->info->painsound)
- S_StartSound(actor, actor->info->painsound);
-
- actor->fuse = TICRATE/2;
- actor->momz = 0;
-
- P_InstaThrust(actor, actor->angle-ANGLE_180, FixedMul(5*FRACUNIT, actor->scale));
- }
-
- if (actor->reactiontime > 0)
- actor->reactiontime--;
-
- if (actor->fuse < 2)
- {
- actor->fuse = 0;
- actor->flags2 &= ~MF2_FRET;
- }
-
- // Hover mode
- if (hovermode)
- {
- if (actor->z < thefloor + FixedMul(16*FRACUNIT, actor->scale))
- actor->momz += FixedMul(FRACUNIT, actor->scale);
- else if (actor->z < thefloor + FixedMul(32*FRACUNIT, actor->scale))
- actor->momz += FixedMul(FRACUNIT/2, actor->scale);
- else
- actor->momz += FixedMul(16, actor->scale);
- }
-
- if (!actor->target)
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- if (actor->state != &states[actor->info->spawnstate])
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
-
- if (actor->target->player && (!hovermode || actor->reactiontime <= 2*TICRATE))
- {
- if (dist < FixedMul(64<<(FRACBITS+(hovermode ? 1 : 0)), actor->scale)
- && ((actor->target->player->pflags & PF_JUMPED) || (actor->target->player->pflags & PF_SPINNING)))
- {
- // Auugh! She's trying to kill you! Strafe! STRAAAAFFEEE!!
- P_InstaThrust(actor, actor->angle - ANGLE_180, FixedMul(20*FRACUNIT, actor->scale));
- return;
- }
- }
-
- if (locvar1)
- {
- if (actor->health < 2 && P_RandomChance(FRACUNIT/128))
- P_SpawnMissile(actor, actor->target, locvar1);
- }
-
- // Face the player
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-
- if (actor->threshold && dist > FixedMul(256*FRACUNIT, actor->scale))
- actor->momx = actor->momy = 0;
-
- if (actor->reactiontime && actor->reactiontime <= 2*TICRATE && dist > actor->target->radius - FixedMul(FRACUNIT, actor->scale))
- {
- actor->threshold = 0;
-
- // Roam around, somewhat in the player's direction.
- actor->angle += (P_RandomByte()<<10);
- actor->angle -= (P_RandomByte()<<10);
-
- if (hovermode)
- {
- fixed_t mom;
- P_Thrust(actor, actor->angle, 2*actor->scale);
- mom = P_AproxDistance(actor->momx, actor->momy);
- if (mom > 20*actor->scale)
- {
- mom += 20*actor->scale;
- mom >>= 1;
- P_InstaThrust(actor, R_PointToAngle2(0, 0, actor->momx, actor->momy), mom);
- }
- }
- }
- else if (!actor->reactiontime)
- {
- if (hovermode && !(actor->flags2 & MF2_FRET)) // Hover Mode
- {
- if (dist < FixedMul(512*FRACUNIT, actor->scale))
- {
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
- P_InstaThrust(actor, actor->angle, FixedMul(40*FRACUNIT, actor->scale));
- actor->threshold = 1;
- if (actor->info->attacksound)
- S_StartSound(actor, actor->info->attacksound);
- }
- }
- actor->reactiontime = 3*TICRATE + (P_RandomByte()>>2);
- }
-
- if (actor->health == 1)
- P_Thrust(actor, actor->angle, 1);
-
- // Pogo Mode
- if (!hovermode && actor->z <= actor->floorz)
- {
- if (actor->info->activesound)
- S_StartSound(actor, actor->info->activesound);
-
- if (dist < FixedMul(256*FRACUNIT, actor->scale))
- {
- actor->momz = FixedMul(locvar2, actor->scale);
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
- P_InstaThrust(actor, actor->angle, FixedMul(locvar2/8, actor->scale));
- // pogo on player
- }
- else
- {
- UINT8 prandom = P_RandomByte();
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
- P_InstaThrust(actor, actor->angle, FixedDiv(FixedMul(locvar2, actor->scale), 3*FRACUNIT/2));
- actor->momz = FixedMul(locvar2, actor->scale); // Bounce up in air
- }
- }
-
- nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
-
- // Move downwards or upwards to go through a passageway.
- if (nextsector->floorheight > actor->z && nextsector->floorheight - actor->z < FixedMul(128*FRACUNIT, actor->scale))
- actor->momz += (nextsector->floorheight - actor->z) / 4;
-}
-
-// Function: A_RingExplode
-//
-// Description: An explosion ring exploding
-//
-// var1 = unused
-// var2 = unused
-//
-void A_RingExplode(mobj_t *actor)
-{
- mobj_t *mo2;
- thinker_t *th;
- angle_t d;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RingExplode", actor))
- return;
-#endif
-
- for (d = 0; d < 16; d++)
- P_SpawnParaloop(actor->x, actor->y, actor->z + actor->height, FixedMul(actor->info->painchance, actor->scale), 16, MT_NIGHTSPARKLE, S_NULL, d*(ANGLE_22h), true);
-
- S_StartSound(actor, sfx_prloop);
-
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo2 = (mobj_t *)th;
-
- if (mo2 == actor) // Don't explode yourself! Endless loop!
- continue;
-
- if (P_AproxDistance(P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y), mo2->z - actor->z) > FixedMul(actor->info->painchance, actor->scale))
- continue;
-
- if (mo2->flags & MF_SHOOTABLE)
- {
- actor->flags2 |= MF2_DEBRIS;
- P_DamageMobj(mo2, actor, actor->target, 1, 0);
- continue;
- }
- }
- return;
-}
-
-// Function: A_OldRingExplode
-//
-// Description: An explosion ring exploding, 1.09.4 style
-//
-// var1 = object # to explode as debris
-// var2 = unused
-//
-void A_OldRingExplode(mobj_t *actor) {
- UINT8 i;
- mobj_t *mo;
- const fixed_t ns = FixedMul(20 * FRACUNIT, actor->scale);
- INT32 locvar1 = var1;
- //INT32 locvar2 = var2;
- boolean changecolor = (actor->target && actor->target->player);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_OldRingExplode", actor))
- return;
-#endif
-
- for (i = 0; i < 32; i++)
- {
- const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
-
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
- P_SetTarget(&mo->target, actor->target); // Transfer target so player gets the points
-
- mo->momx = FixedMul(FINECOSINE(fa),ns);
- mo->momy = FixedMul(FINESINE(fa),ns);
-
- if (i > 15)
- {
- if (i & 1)
- mo->momz = ns;
- else
- mo->momz = -ns;
- }
-
- mo->flags2 |= MF2_DEBRIS;
- mo->fuse = TICRATE/5;
-
- if (changecolor)
- {
- if (gametype != GT_CTF)
- mo->color = actor->target->color; //copy color
- else if (actor->target->player->ctfteam == 2)
- mo->color = skincolor_bluering;
- }
- }
-
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
-
- P_SetTarget(&mo->target, actor->target);
- mo->momz = ns;
- mo->flags2 |= MF2_DEBRIS;
- mo->fuse = TICRATE/5;
-
- if (changecolor)
- {
- if (gametype != GT_CTF)
- mo->color = actor->target->color; //copy color
- else if (actor->target->player->ctfteam == 2)
- mo->color = skincolor_bluering;
- }
-
- mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
-
- P_SetTarget(&mo->target, actor->target);
- mo->momz = -ns;
- mo->flags2 |= MF2_DEBRIS;
- mo->fuse = TICRATE/5;
-
- if (changecolor)
- {
- if (gametype != GT_CTF)
- mo->color = actor->target->color; //copy color
- else if (actor->target->player->ctfteam == 2)
- mo->color = skincolor_bluering;
- }
-}
-
-// Function: A_MixUp
-//
-// Description: Mix up all of the player positions.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MixUp(mobj_t *actor)
-{
- boolean teleported[MAXPLAYERS];
- INT32 i, numplayers = 0, prandom = 0;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MixUp", actor))
- return;
-#else
- (void)actor;
-#endif
-
- if (!multiplayer)
- return;
-
- // No mix-up monitors in hide and seek or time only race.
- // The random factor is okay for other game modes, but in these, it is cripplingly unfair.
- if (gametype == GT_HIDEANDSEEK || gametype == GT_RACE)
- {
- S_StartSound(actor, sfx_lose);
- return;
- }
-
- numplayers = 0;
- memset(teleported, 0, sizeof (teleported));
-
- // Count the number of players in the game
- // and grab their xyz coords
- for (i = 0; i < MAXPLAYERS; i++)
- if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
- && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
- {
- if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
- continue;
-
- numplayers++;
- }
-
- if (numplayers <= 1) // Not enough players to mix up.
- {
- S_StartSound(actor, sfx_lose);
- return;
- }
- else if (numplayers == 2) // Special case -- simple swap
- {
- fixed_t x, y, z;
- angle_t angle;
- INT32 one = -1, two = 0; // default value 0 to make the compiler shut up
-
- // Zoom tube stuff
- mobj_t *tempthing = NULL; //tracer
- UINT16 carry1,carry2; //carry
- INT32 transspeed; //player speed
-
- // Starpost stuff
- INT16 starpostx, starposty, starpostz;
- INT32 starpostnum;
- tic_t starposttime;
- angle_t starpostangle;
-
- INT32 mflags2;
-
- for (i = 0; i < MAXPLAYERS; i++)
- if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
- && !players[i].exiting && !players[i].powers[pw_super])
- {
- if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
- continue;
-
- if (one == -1)
- one = i;
- else
- {
- two = i;
- break;
- }
- }
-
- //get this done first!
- tempthing = players[one].mo->tracer;
- P_SetTarget(&players[one].mo->tracer, players[two].mo->tracer);
- P_SetTarget(&players[two].mo->tracer, tempthing);
-
- //zoom tubes use player->speed to determine direction and speed
- transspeed = players[one].speed;
- players[one].speed = players[two].speed;
- players[two].speed = transspeed;
-
- //set flags variables now but DON'T set them.
- carry1 = (players[one].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[one].powers[pw_carry]);
- carry2 = (players[two].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[two].powers[pw_carry]);
-
- x = players[one].mo->x;
- y = players[one].mo->y;
- z = players[one].mo->z;
- angle = players[one].mo->angle;
-
- starpostx = players[one].starpostx;
- starposty = players[one].starposty;
- starpostz = players[one].starpostz;
- starpostangle = players[one].starpostangle;
- starpostnum = players[one].starpostnum;
- starposttime = players[one].starposttime;
-
- mflags2 = players[one].mo->flags2;
-
- P_MixUp(players[one].mo, players[two].mo->x, players[two].mo->y, players[two].mo->z, players[two].mo->angle,
- players[two].starpostx, players[two].starposty, players[two].starpostz,
- players[two].starpostnum, players[two].starposttime, players[two].starpostangle,
- players[two].mo->flags2);
-
- P_MixUp(players[two].mo, x, y, z, angle, starpostx, starposty, starpostz,
- starpostnum, starposttime, starpostangle,
- mflags2);
-
- //carry set after mixup. Stupid P_ResetPlayer() takes away some of the stuff we look for...
- //but not all of it! So we need to make sure they aren't set wrong or anything.
- players[one].powers[pw_carry] = carry2;
- players[two].powers[pw_carry] = carry1;
-
- teleported[one] = true;
- teleported[two] = true;
- }
- else
- {
- fixed_t position[MAXPLAYERS][3];
- angle_t anglepos[MAXPLAYERS];
- INT32 pindex[MAXPLAYERS], counter = 0, teleportfrom = 0;
-
- // Zoom tube stuff
- mobj_t *transtracer[MAXPLAYERS]; //tracer
- //pflags_t transflag[MAXPLAYERS]; //cyan pink white pink cyan
- UINT16 transcarry[MAXPLAYERS]; //player carry
- INT32 transspeed[MAXPLAYERS]; //player speed
-
- // Star post stuff
- INT16 spposition[MAXPLAYERS][3];
- INT32 starpostnum[MAXPLAYERS];
- tic_t starposttime[MAXPLAYERS];
- angle_t starpostangle[MAXPLAYERS];
-
- INT32 flags2[MAXPLAYERS];
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- position[i][0] = position[i][1] = position[i][2] = anglepos[i] = pindex[i] = -1;
- teleported[i] = false;
- }
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (playeringame[i] && players[i].playerstate == PST_LIVE
- && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
- {
- if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
- continue;
-
- position[counter][0] = players[i].mo->x;
- position[counter][1] = players[i].mo->y;
- position[counter][2] = players[i].mo->z;
- pindex[counter] = i;
- anglepos[counter] = players[i].mo->angle;
- players[i].mo->momx = players[i].mo->momy = players[i].mo->momz =
- players[i].rmomx = players[i].rmomy = 1;
- players[i].cmomx = players[i].cmomy = 0;
-
- transcarry[counter] = (players[i].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[i].powers[pw_carry]);
- transspeed[counter] = players[i].speed;
- transtracer[counter] = players[i].mo->tracer;
-
- spposition[counter][0] = players[i].starpostx;
- spposition[counter][1] = players[i].starposty;
- spposition[counter][2] = players[i].starpostz;
- starpostnum[counter] = players[i].starpostnum;
- starposttime[counter] = players[i].starposttime;
- starpostangle[counter] = players[i].starpostangle;
-
- flags2[counter] = players[i].mo->flags2;
-
- counter++;
- }
- }
-
- counter = 0;
-
- // Mix them up!
- for (;;)
- {
- if (counter > 255) // fail-safe to avoid endless loop
- break;
- prandom = P_RandomByte();
- prandom %= numplayers; // I love modular arithmetic, don't you?
- if (prandom) // Make sure it's not a useless mix
- break;
- counter++;
- }
-
- counter = 0;
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (playeringame[i] && players[i].playerstate == PST_LIVE
- && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
- {
- if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
- continue;
-
- teleportfrom = (counter + prandom) % numplayers;
-
- //speed and tracer come before...
- players[i].speed = transspeed[teleportfrom];
- P_SetTarget(&players[i].mo->tracer, transtracer[teleportfrom]);
-
- P_MixUp(players[i].mo, position[teleportfrom][0], position[teleportfrom][1], position[teleportfrom][2], anglepos[teleportfrom],
- spposition[teleportfrom][0], spposition[teleportfrom][1], spposition[teleportfrom][2],
- starpostnum[teleportfrom], starposttime[teleportfrom], starpostangle[teleportfrom],
- flags2[teleportfrom]);
-
- //...carry after. same reasoning.
- players[i].powers[pw_carry] = transcarry[teleportfrom];
-
- teleported[i] = true;
- counter++;
- }
- }
- }
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (teleported[i])
- {
- if (playeringame[i] && players[i].playerstate == PST_LIVE
- && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
- {
- if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
- continue;
-
- P_SetThingPosition(players[i].mo);
-
-#ifdef ESLOPE
- players[i].mo->floorz = P_GetFloorZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
- players[i].mo->ceilingz = P_GetCeilingZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
-#else
- players[i].mo->floorz = players[i].mo->subsector->sector->floorheight;
- players[i].mo->ceilingz = players[i].mo->subsector->sector->ceilingheight;
-#endif
-
- P_CheckPosition(players[i].mo, players[i].mo->x, players[i].mo->y);
- }
- }
- }
-
- // Play the 'bowrwoosh!' sound
- S_StartSound(NULL, sfx_mixup);
-}
-
-// Function: A_RecyclePowers
-//
-// Description: Take all player's powers, and swap 'em.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_RecyclePowers(mobj_t *actor)
-{
- INT32 i, j, k, numplayers = 0;
-
-#ifdef WEIGHTEDRECYCLER
- UINT8 beneficiary = 255;
-#endif
- UINT8 playerslist[MAXPLAYERS];
- UINT8 postscramble[MAXPLAYERS];
-
- UINT16 powers[MAXPLAYERS][NUMPOWERS];
- INT32 weapons[MAXPLAYERS];
- INT32 weaponheld[MAXPLAYERS];
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RecyclePowers", actor))
- return;
-#endif
-
-#if !defined(WEIGHTEDRECYCLER) && !defined(HAVE_BLUA)
- // actor is used in all scenarios but this one, funny enough
- (void)actor;
-#endif
-
- if (!multiplayer)
- {
- S_StartSound(actor, sfx_lose);
- return;
- }
-
- numplayers = 0;
-
- // Count the number of players in the game
- for (i = 0, j = 0; i < MAXPLAYERS; i++)
- {
- if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
- && !players[i].exiting && !((netgame || multiplayer) && players[i].spectator))
- {
-#ifndef WEIGHTEDRECYCLER
- if (players[i].powers[pw_super])
- continue; // Ignore super players
-#endif
-
- numplayers++;
- postscramble[j] = playerslist[j] = (UINT8)i;
-
-#ifdef WEIGHTEDRECYCLER
- // The guy who started the recycle gets the best result
- if (actor && actor->target && actor->target->player && &players[i] == actor->target->player)
- beneficiary = (UINT8)i;
-#endif
-
- // Save powers
- for (k = 0; k < NUMPOWERS; k++)
- powers[i][k] = players[i].powers[k];
- //1.1: ring weapons too
- weapons[i] = players[i].ringweapons;
- weaponheld[i] = players[i].currentweapon;
-
- j++;
- }
- }
-
- if (numplayers <= 1)
- {
- S_StartSound(actor, sfx_lose);
- return; //nobody to touch!
- }
-
- //shuffle the post scramble list, whee!
- // hardcoded 0-1 to 1-0 for two players
- if (numplayers == 2)
- {
- postscramble[0] = playerslist[1];
- postscramble[1] = playerslist[0];
- }
- else
- for (j = 0; j < numplayers; j++)
- {
- UINT8 tempint;
-
- i = j + ((P_RandomByte() + leveltime) % (numplayers - j));
- tempint = postscramble[j];
- postscramble[j] = postscramble[i];
- postscramble[i] = tempint;
- }
-
-#ifdef WEIGHTEDRECYCLER
- //the joys of qsort...
- if (beneficiary != 255) {
- qsort(playerslist, numplayers, sizeof(UINT8), P_RecycleCompare);
-
- // now, make sure the benificiary is in the best slot
- // swap out whatever poor sap was going to get the best items
- for (i = 0; i < numplayers; i++)
- {
- if (postscramble[i] == beneficiary)
- {
- postscramble[i] = postscramble[0];
- postscramble[0] = beneficiary;
- break;
- }
- }
- }
-#endif
-
- // now assign!
- for (i = 0; i < numplayers; i++)
- {
- UINT8 send_pl = playerslist[i];
- UINT8 recv_pl = postscramble[i];
-
- // debugF
- CONS_Debug(DBG_GAMELOGIC, "sending player %hu's items to %hu\n", (UINT16)send_pl, (UINT16)recv_pl);
-
- for (j = 0; j < NUMPOWERS; j++)
- {
- if (j == pw_flashing || j == pw_underwater || j == pw_spacetime || j == pw_carry
- || j == pw_tailsfly || j == pw_extralife || j == pw_nocontrol || j == pw_super)
- continue;
- players[recv_pl].powers[j] = powers[send_pl][j];
- }
-
- //1.1: weapon rings too
- players[recv_pl].ringweapons = weapons[send_pl];
- players[recv_pl].currentweapon = weaponheld[send_pl];
-
- P_SpawnShieldOrb(&players[recv_pl]);
- if (P_IsLocalPlayer(&players[recv_pl]))
- P_RestoreMusic(&players[recv_pl]);
- P_FlashPal(&players[recv_pl], PAL_RECYCLE, 10);
- }
-
- S_StartSound(NULL, sfx_gravch); //heh, the sound effect I used is already in
-}
-
-// Function: A_Boss1Chase
-//
-// Description: Like A_Chase, but for Boss 1.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss1Chase(mobj_t *actor)
-{
- INT32 delta;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss1Chase", actor))
- return;
-#endif
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- P_SetMobjStateNF(actor, actor->info->spawnstate);
- return;
- }
-
- if (actor->reactiontime)
- actor->reactiontime--;
-
- // turn towards movement direction if not there yet
- if (actor->movedir < NUMDIRS)
- {
- actor->angle &= (7<<29);
- delta = actor->angle - (actor->movedir << 29);
-
- if (delta > 0)
- actor->angle -= ANGLE_45;
- else if (delta < 0)
- actor->angle += ANGLE_45;
- }
-
- // do not attack twice in a row
- if (actor->flags2 & MF2_JUSTATTACKED)
- {
- actor->flags2 &= ~MF2_JUSTATTACKED;
- P_NewChaseDir(actor);
- return;
- }
-
- if (actor->movecount)
- goto nomissile;
-
- if (!P_CheckMissileRange(actor))
- goto nomissile;
-
- if (actor->reactiontime <= 0)
- {
- if (actor->health > actor->info->damage)
- {
- if (P_RandomChance(FRACUNIT/2))
- P_SetMobjState(actor, actor->info->missilestate);
- else
- P_SetMobjState(actor, actor->info->meleestate);
- }
- else
- {
- P_LinedefExecute(LE_PINCHPHASE, actor, NULL);
- P_SetMobjState(actor, actor->info->raisestate);
- }
-
- actor->flags2 |= MF2_JUSTATTACKED;
- actor->reactiontime = actor->info->reactiontime;
- return;
- }
-
- // ?
-nomissile:
- // possibly choose another target
- if (multiplayer && P_RandomChance(FRACUNIT/128))
- {
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
- }
-
- if (actor->flags & MF_FLOAT && !(actor->flags2 & MF2_SKULLFLY))
- { // Float up/down to your target's position. Stay above them, but not out of jump range.
- fixed_t target_min = actor->target->floorz+FixedMul(64*FRACUNIT, actor->scale);
- if (target_min < actor->target->z - actor->height)
- target_min = actor->target->z - actor->height;
- if (target_min < actor->floorz+FixedMul(33*FRACUNIT, actor->scale))
- target_min = actor->floorz+FixedMul(33*FRACUNIT, actor->scale);
- if (actor->z > target_min+FixedMul(16*FRACUNIT, actor->scale))
- actor->momz = FixedMul((-actor->info->speed<<(FRACBITS-1)), actor->scale);
- else if (actor->z < target_min)
- actor->momz = FixedMul(actor->info->speed<<(FRACBITS-1), actor->scale);
- else
- actor->momz = FixedMul(actor->momz,7*FRACUNIT/8);
- }
-
- // chase towards player
- if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) > actor->radius+actor->target->radius)
- {
- if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
- P_NewChaseDir(actor);
- }
- // too close, don't want to chase.
- else if (--actor->movecount < 0)
- {
- // A mini-A_FaceTarget based on P_NewChaseDir.
- // Yes, it really is this simple when you get down to it.
- fixed_t deltax, deltay;
-
- deltax = actor->target->x - actor->x;
- deltay = actor->target->y - actor->y;
-
- actor->movedir = diags[((deltay < 0)<<1) + (deltax > 0)];
- actor->movecount = P_RandomByte() & 15;
- }
-}
-
-// Function: A_Boss2Chase
-//
-// Description: Really doesn't 'chase', but rather goes in a circle.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss2Chase(mobj_t *actor)
-{
- fixed_t radius;
- boolean reverse = false;
- INT32 speedvar;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss2Chase", actor))
- return;
-#endif
-
- if (actor->health <= 0)
- return;
-
- // Startup randomness
- if (actor->reactiontime <= -666)
- actor->reactiontime = 2*TICRATE + P_RandomByte();
-
- // When reactiontime hits zero, he will go the other way
- if (--actor->reactiontime <= 0)
- {
- reverse = true;
- actor->reactiontime = 2*TICRATE + P_RandomByte();
- }
-
- P_SetTarget(&actor->target, P_GetClosestAxis(actor));
-
- if (!actor->target) // This should NEVER happen.
- {
- CONS_Debug(DBG_GAMELOGIC, "Boss2 has no target!\n");
- A_BossDeath(actor);
- return;
- }
-
- radius = actor->target->radius;
-
- if (reverse)
- {
- actor->watertop = -actor->watertop;
- actor->extravalue1 = 18;
- if (actor->flags2 & MF2_AMBUSH)
- actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2;
- actor->extravalue2 = actor->extravalue1;
- }
-
- // Turnaround
- if (actor->extravalue1 > 0)
- {
- --actor->extravalue1;
-
- // Set base angle
- {
- const angle_t fa = (actor->target->angle + FixedAngle(actor->watertop))>>ANGLETOFINESHIFT;
- const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
- const fixed_t fs = FixedMul(FINESINE(fa),radius);
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
- }
-
- // Now turn you around!
- // Note that the start position is the final position, we move it back around
- // to intermediary positions...
- actor->angle -= FixedAngle(FixedMul(FixedDiv(180<extravalue2<extravalue1<flags2 & MF2_AMBUSH)
- speedvar = actor->health;
- else
- speedvar = actor->info->spawnhealth;
-
- actor->target->angle += // Don't use FixedAngleC!
- FixedAngle(FixedDiv(FixedMul(actor->watertop, (actor->info->spawnhealth*(FRACUNIT/4)*3)), speedvar*FRACUNIT));
-
- P_UnsetThingPosition(actor);
- {
- const angle_t fa = actor->target->angle>>ANGLETOFINESHIFT;
- const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
- const fixed_t fs = FixedMul(FINESINE(fa),radius);
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
- actor->x = actor->target->x + fc;
- actor->y = actor->target->y + fs;
- }
- P_SetThingPosition(actor);
-
- // Spray goo once every second
- if (leveltime % (speedvar*15/10)-1 == 0)
- {
- const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
- mobj_t *goop;
- fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
- angle_t fa;
- // actor->movedir is used to determine the last
- // direction goo was sprayed in. There are 8 possible
- // directions to spray. (45-degree increments)
-
- actor->movedir++;
- actor->movedir %= NUMDIRS;
- fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
-
- goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
- goop->momx = FixedMul(FINECOSINE(fa),ns);
- goop->momy = FixedMul(FINESINE(fa),ns);
- goop->momz = FixedMul(4*FRACUNIT, actor->scale);
- goop->fuse = 10*TICRATE;
-
- if (actor->info->attacksound)
- S_StartAttackSound(actor, actor->info->attacksound);
-
- if (P_RandomChance(FRACUNIT/2))
- {
- goop->momx *= 2;
- goop->momy *= 2;
- }
- else if (P_RandomChance(129*FRACUNIT/256))
- {
- goop->momx *= 3;
- goop->momy *= 3;
- }
-
- actor->flags2 |= MF2_JUSTATTACKED;
- }
- }
-}
-
-// Function: A_Boss2Pogo
-//
-// Description: Pogo part of Boss 2 AI.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss2Pogo(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss2Pogo", actor))
- return;
-#endif
- if (actor->z <= actor->floorz + FixedMul(8*FRACUNIT, actor->scale) && actor->momz <= 0)
- {
- if (actor->state != &states[actor->info->raisestate])
- P_SetMobjState(actor, actor->info->raisestate);
- // Pogo Mode
- }
- else if (actor->momz < 0 && actor->reactiontime)
- {
- const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
- mobj_t *goop;
- fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
- angle_t fa;
- INT32 i;
- // spray in all 8 directions!
- for (i = 0; i < 8; i++)
- {
- actor->movedir++;
- actor->movedir %= NUMDIRS;
- fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
-
- goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
- goop->momx = FixedMul(FINECOSINE(fa),ns);
- goop->momy = FixedMul(FINESINE(fa),ns);
- goop->momz = FixedMul(4*FRACUNIT, actor->scale);
-
- goop->fuse = 10*TICRATE;
- }
- actor->reactiontime = 0; // we already shot goop, so don't do it again!
- if (actor->info->attacksound)
- S_StartAttackSound(actor, actor->info->attacksound);
- actor->flags2 |= MF2_JUSTATTACKED;
- }
-}
-
-// Function: A_Boss2TakeDamage
-//
-// Description: Special function for Boss 2 so you can't just sit and destroy him.
-//
-// var1 = Invincibility duration
-// var2 = unused
-//
-void A_Boss2TakeDamage(mobj_t *actor)
-{
- INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss2TakeDamage", actor))
- return;
-#endif
- A_Pain(actor);
- actor->reactiontime = 1; // turn around
- if (locvar1 == 0) // old A_Invincibilerize behavior
- actor->movecount = TICRATE;
- else
- actor->movecount = locvar1; // become flashing invulnerable for this long.
-}
-
-// Function: A_Boss7Chase
-//
-// Description: Like A_Chase, but for Black Eggman
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss7Chase(mobj_t *actor)
-{
- INT32 delta;
- INT32 i;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss7Chase", actor))
- return;
-#endif
-
- if (actor->z != actor->floorz)
- return;
-
- // Self-adjust if stuck on the edge
- if (actor->tracer)
- {
- if (P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y) > 128*FRACUNIT - actor->radius)
- P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y), FRACUNIT);
- }
-
- if (actor->flags2 & MF2_FRET)
- {
- P_SetMobjState(actor, S_BLACKEGG_DESTROYPLAT1);
- S_StartSound(0, sfx_s3k53);
- actor->flags2 &= ~MF2_FRET;
- return;
- }
-
- // turn towards movement direction if not there yet
- if (actor->movedir < NUMDIRS)
- {
- actor->angle &= (7<<29);
- delta = actor->angle - (actor->movedir << 29);
-
- if (delta > 0)
- actor->angle -= ANGLE_45;
- else if (delta < 0)
- actor->angle += ANGLE_45;
- }
-
- // Is a player on top of us?
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator)
- continue;
-
- if (!players[i].mo)
- continue;
-
- if (players[i].mo->health <= 0)
- continue;
-
- if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) > actor->radius)
- continue;
-
- if (players[i].mo->z > actor->z + actor->height - 2*FRACUNIT
- && players[i].mo->z < actor->z + actor->height + 32*FRACUNIT)
- {
- // Punch him!
- P_SetMobjState(actor, actor->info->meleestate);
- S_StartSound(0, sfx_begrnd); // warning sound
- return;
- }
- }
-
- if (actor->health <= actor->info->damage
- && actor->target
- && actor->target->player
- && (actor->target->player->powers[pw_carry] == CR_GENERIC))
- {
- A_FaceTarget(actor);
- P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
- actor->movecount = TICRATE + P_RandomByte()/2;
- return;
- }
-
- if (actor->reactiontime)
- actor->reactiontime--;
-
- if (actor->reactiontime <= 0 && actor->z == actor->floorz)
- {
- // Here, we'll call P_RandomByte() and decide what kind of attack to do
- switch(actor->threshold)
- {
- case 0: // Lob cannon balls
- if (actor->z < 1056*FRACUNIT)
- {
- A_FaceTarget(actor);
- P_SetMobjState(actor, actor->info->xdeathstate);
- actor->movecount = 7*TICRATE + P_RandomByte();
- break;
- }
- actor->threshold++;
- /* FALLTHRU */
- case 1: // Chaingun Goop
- A_FaceTarget(actor);
- P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
-
- if (actor->health > actor->info->damage)
- actor->movecount = TICRATE + P_RandomByte()/3;
- else
- actor->movecount = TICRATE + P_RandomByte()/2;
- break;
- case 2: // Homing Missile
- A_FaceTarget(actor);
- P_SetMobjState(actor, actor->info->missilestate);
- S_StartSound(0, sfx_beflap);
- break;
- }
-
- actor->threshold++;
- actor->threshold %= 3;
- return;
- }
-
- // possibly choose another target
- if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
- && P_BossTargetPlayer(actor, false))
- return; // got a new target
-
- if (leveltime & 1)
- {
- // chase towards player
- if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
- P_NewChaseDir(actor);
- }
-}
-
-// Function: A_GoopSplat
-//
-// Description: Black Eggman goop hits a target and sticks around for awhile.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GoopSplat(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_GoopSplat", actor))
- return;
-#endif
- P_UnsetThingPosition(actor);
- if (sector_list)
- {
- P_DelSeclist(sector_list);
- sector_list = NULL;
- }
- actor->flags = MF_SPECIAL; // Not a typo
- P_SetThingPosition(actor);
-}
-
-// Function: A_Boss2PogoSFX
-//
-// Description: Pogoing for Boss 2
-//
-// var1 = pogo jump strength
-// var2 = idle pogo speed
-//
-void A_Boss2PogoSFX(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss2PogoSFX", actor))
- return;
-#endif
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- return;
- }
-
- // Boing!
- if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(256*FRACUNIT, actor->scale))
- {
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
- P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
- // pogo on player
- }
- else
- {
- UINT8 prandom = P_RandomByte();
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
- P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale));
- }
- if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
- actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
- actor->reactiontime = 1;
-}
-
-// Function: A_Boss2PogoTarget
-//
-// Description: Pogoing for Boss 2, tries to actually land on the player directly.
-//
-// var1 = pogo jump strength
-// var2 = idle pogo speed
-//
-void A_Boss2PogoTarget(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss2PogoTarget", actor))
- return;
-#endif
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || (actor->target->player && actor->target->player->powers[pw_flashing])
- || P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) >= FixedMul(512*FRACUNIT, actor->scale))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 512*FRACUNIT))
- ; // got a new target
- else if (P_LookForPlayers(actor, true, false, 0))
- ; // got a new target
- else
- return;
- }
-
- // Target hit, retreat!
- if (actor->target->player->powers[pw_flashing] > TICRATE || actor->flags2 & MF2_FRET)
- {
- UINT8 prandom = P_RandomByte();
- actor->z++; // unstick from the floor
- actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
- P_InstaThrust(actor, actor->angle+ANGLE_180, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
- }
- // Try to land on top of the player.
- else if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(512*FRACUNIT, actor->scale))
- {
- fixed_t airtime, gravityadd, zoffs;
-
- // check gravity in the sector (for later math)
- P_CheckGravity(actor, true);
- gravityadd = actor->momz;
-
- actor->z++; // unstick from the floor
- actor->momz = FixedMul(locvar1 + (locvar1>>2), actor->scale); // Bounce up in air
-
- /*badmath = 0;
- airtime = 0;
- do {
- badmath += momz;
- momz += gravityadd;
- airtime++;
- } while(badmath > 0);
- airtime = 2*airtime<momz<<1, gravityadd)<<1; // going from 0 to 0 is much simpler
- zoffs = (P_GetPlayerHeight(actor->target->player)>>1) + (actor->target->floorz - actor->floorz); // offset by the difference in floor height plus half the player height,
- airtime = FixedDiv((-actor->momz - FixedSqrt(FixedMul(actor->momz,actor->momz)+zoffs)), gravityadd)<<1; // to try and land on their head rather than on their feet
-
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
- P_InstaThrust(actor, actor->angle, FixedDiv(P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y), airtime));
- }
- // Wander semi-randomly towards the player to get closer.
- else
- {
- UINT8 prandom = P_RandomByte();
- actor->z++; // unstick from the floor
- actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
- P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
- }
- // Boing!
- if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
-
- if (actor->info->missilestate) // spawn the pogo stick collision box
- {
- mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate);
- pogo->target = actor;
- }
-
- actor->reactiontime = 1;
-}
-
-// Function: A_EggmanBox
-//
-// Description: Harms the player
-//
-// var1 = unused
-// var2 = unused
-//
-void A_EggmanBox(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_EggmanBox", actor))
- return;
-#endif
- if (!actor->target || !actor->target->player)
- {
- CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
- return;
- }
-
- P_DamageMobj(actor->target, actor, actor, 1, 0); // Ow!
-}
-
-// Function: A_TurretFire
-//
-// Description: Initiates turret fire.
-//
-// var1 = object # to repeatedly fire
-// var2 = distance threshold
-//
-void A_TurretFire(mobj_t *actor)
-{
- INT32 count = 0;
- fixed_t dist;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_TurretFire", actor))
- return;
-#endif
-
- if (locvar2)
- dist = FixedMul(locvar2*FRACUNIT, actor->scale);
- else
- dist = FixedMul(2048*FRACUNIT, actor->scale);
-
- if (!locvar1)
- locvar1 = MT_TURRETLASER;
-
- while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
- {
- if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
- {
- actor->flags2 |= MF2_FIRING;
- actor->extravalue1 = locvar1;
- break;
- }
-
- count++;
- }
-}
-
-// Function: A_SuperTurretFire
-//
-// Description: Initiates turret fire that even stops Super Sonic.
-//
-// var1 = object # to repeatedly fire
-// var2 = distance threshold
-//
-void A_SuperTurretFire(mobj_t *actor)
-{
- INT32 count = 0;
- fixed_t dist;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SuperTurretFire", actor))
- return;
-#endif
-
- if (locvar2)
- dist = FixedMul(locvar2*FRACUNIT, actor->scale);
- else
- dist = FixedMul(2048*FRACUNIT, actor->scale);
-
- if (!locvar1)
- locvar1 = MT_TURRETLASER;
-
- while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
- {
- if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
- {
- actor->flags2 |= MF2_FIRING;
- actor->flags2 |= MF2_SUPERFIRE;
- actor->extravalue1 = locvar1;
- break;
- }
-
- count++;
- }
-}
-
-// Function: A_TurretStop
-//
-// Description: Stops the turret fire.
-//
-// var1 = Don't play activesound?
-// var2 = unused
-//
-void A_TurretStop(mobj_t *actor)
-{
- INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_TurretStop", actor))
- return;
-#endif
-
- actor->flags2 &= ~MF2_FIRING;
- actor->flags2 &= ~MF2_SUPERFIRE;
-
- if (actor->target && actor->info->activesound && !locvar1)
- S_StartSound(actor, actor->info->activesound);
-}
-
-// Function: A_SparkFollow
-//
-// Description: Used by the hyper sparks to rotate around their target.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SparkFollow(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SparkFollow", actor))
- return;
-#endif
-
- if ((!actor->target || (actor->target->health <= 0))
- || (actor->target->player && !actor->target->player->powers[pw_super]))
- {
- P_RemoveMobj(actor);
- return;
- }
-
- actor->angle += FixedAngle(actor->info->damage*FRACUNIT);
- P_UnsetThingPosition(actor);
- {
- const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
- actor->x = actor->target->x + FixedMul(FINECOSINE(fa),FixedMul(actor->info->speed, actor->scale));
- actor->y = actor->target->y + FixedMul(FINESINE(fa),FixedMul(actor->info->speed, actor->scale));
- if (actor->target->eflags & MFE_VERTICALFLIP)
- actor->z = actor->target->z + actor->target->height - FixedDiv(actor->target->height,3*FRACUNIT);
- else
- actor->z = actor->target->z + FixedDiv(actor->target->height,3*FRACUNIT) - actor->height;
- }
- P_SetThingPosition(actor);
-}
-
-// Function: A_BuzzFly
-//
-// Description: Makes an object slowly fly after a player, in the manner of a Buzz.
-//
-// var1 = sfx to play
-// var2 = length of sfx, set to threshold if played
-//
-void A_BuzzFly(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BuzzFly", actor))
- return;
-#endif
- if (actor->flags2 & MF2_AMBUSH)
- return;
-
- if (actor->reactiontime)
- actor->reactiontime--;
-
- // modify target threshold
- if (actor->threshold)
- {
- if (!actor->target || actor->target->health <= 0)
- actor->threshold = 0;
- else
- actor->threshold--;
- }
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- actor->momz = actor->momy = actor->momx = 0;
- P_SetMobjState(actor, actor->info->spawnstate);
- return;
- }
-
- // turn towards movement direction if not there yet
- actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
-
- if (actor->target->health <= 0 || (!actor->threshold && !P_CheckSight(actor, actor->target)))
- {
- if ((multiplayer || netgame) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
- return; // got a new target
-
- actor->momx = actor->momy = actor->momz = 0;
- P_SetMobjState(actor, actor->info->spawnstate); // Go back to looking around
- return;
- }
-
- // If the player is over 3072 fracunits away, then look for another player
- if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
- actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale))
- {
- if (multiplayer || netgame)
- P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)); // maybe get a new target
-
- return;
- }
-
- // chase towards player
- {
- INT32 dist, realspeed;
- const fixed_t mf = 5*(FRACUNIT/4);
-
- if (ultimatemode)
- realspeed = FixedMul(FixedMul(actor->info->speed,mf), actor->scale);
- else
- realspeed = FixedMul(actor->info->speed, actor->scale);
-
- dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x,
- actor->target->y - actor->y), actor->target->z - actor->z);
-
- if (dist < 1)
- dist = 1;
-
- actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), realspeed);
- actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), realspeed);
- actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), realspeed);
-
- if (actor->z+actor->momz >= actor->waterbottom && actor->watertop > actor->floorz
- && actor->z+actor->momz > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)
- && actor->z+actor->momz <= actor->watertop)
- {
- actor->momz = 0;
- actor->z = actor->watertop;
- }
- }
-
- if (locvar1 != sfx_None && !actor->threshold)
- {
- S_StartSound(actor, locvar1);
- actor->threshold = locvar2;
- }
-}
-
-// Function: A_GuardChase
-//
-// Description: Modified A_Chase for Egg Guard
-//
-// var1 = unused
-// var2 = unused
-//
-void A_GuardChase(mobj_t *actor)
-{
- INT32 delta;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_GuardChase", actor))
- return;
-#endif
-
- if (actor->reactiontime)
- actor->reactiontime--;
-
- if (actor->threshold != 42) // In formation...
- {
- fixed_t speed;
-
- if (!actor->tracer || !actor->tracer->health)
- {
- P_SetTarget(&actor->tracer, NULL);
- actor->threshold = 42;
- P_SetMobjState(actor, actor->info->painstate);
- actor->flags |= MF_SPECIAL|MF_SHOOTABLE;
- return;
- }
-
- speed = actor->extravalue1*actor->scale;
-
- if (actor->flags2 & MF2_AMBUSH)
- speed <<= 1;
-
- if (speed
- && !P_TryMove(actor,
- actor->x + P_ReturnThrustX(actor, actor->angle, speed),
- actor->y + P_ReturnThrustY(actor, actor->angle, speed),
- false)
- && speed > 0) // can't be the same check as previous so that P_TryMove gets to happen.
- {
- if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_OBJECTSPECIAL))
- actor->angle += ANGLE_90;
- else if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_EXTRA))
- actor->angle -= ANGLE_90;
- else
- actor->angle += ANGLE_180;
- }
-
- if (actor->extravalue1 < actor->info->speed)
- actor->extravalue1++;
- }
- else // Break ranks!
- {
- // turn towards movement direction if not there yet
- if (actor->movedir < NUMDIRS)
- {
- actor->angle &= (7<<29);
- delta = actor->angle - (actor->movedir << 29);
-
- if (delta > 0)
- actor->angle -= ANGLE_45;
- else if (delta < 0)
- actor->angle += ANGLE_45;
- }
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- P_SetMobjStateNF(actor, actor->info->spawnstate);
- return;
- }
-
- // possibly choose another target
- if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
- && P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- // chase towards player
- if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
- {
- P_NewChaseDir(actor);
- actor->movecount += 5; // Increase tics before change in direction allowed.
- }
- }
-
- // Now that we've moved, its time for our shield to move!
- // Otherwise it'll never act as a proper overlay.
- if (actor->tracer && actor->tracer->state
- && actor->tracer->state->action.acp1)
- {
- var1 = actor->tracer->state->var1, var2 = actor->tracer->state->var2;
- actor->tracer->state->action.acp1(actor->tracer);
- }
-}
-
-// Function: A_EggShield
-//
-// Description: Modified A_Chase for Egg Guard's shield
-//
-// var1 = unused
-// var2 = unused
-//
-void A_EggShield(mobj_t *actor)
-{
- INT32 i;
- player_t *player;
- fixed_t blockdist;
- fixed_t newx, newy;
- fixed_t movex, movey;
- angle_t angle;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_EggShield", actor))
- return;
-#endif
-
- if (!actor->target || !actor->target->health)
- {
- P_RemoveMobj(actor);
- return;
- }
-
- newx = actor->target->x + P_ReturnThrustX(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
- newy = actor->target->y + P_ReturnThrustY(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
-
- movex = newx - actor->x;
- movey = newy - actor->y;
-
- actor->angle = actor->target->angle;
- if (actor->target->eflags & MFE_VERTICALFLIP)
- {
- actor->eflags |= MFE_VERTICALFLIP;
- actor->z = actor->target->z + actor->target->height - actor->height;
- }
- else
- actor->z = actor->target->z;
-
- actor->destscale = actor->target->destscale;
- P_SetScale(actor, actor->target->scale);
-
- actor->floorz = actor->target->floorz;
- actor->ceilingz = actor->target->ceilingz;
-
- if (!movex && !movey)
- return;
-
- P_UnsetThingPosition(actor);
- actor->x = newx;
- actor->y = newy;
- P_SetThingPosition(actor);
-
- // Search for players to push
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator)
- continue;
-
- player = &players[i];
-
- if (!player->mo)
- continue;
-
- if (player->mo->z > actor->z + actor->height)
- continue;
-
- if (player->mo->z + player->mo->height < actor->z)
- continue;
-
- blockdist = actor->radius + player->mo->radius;
-
- if (abs(actor->x - player->mo->x) >= blockdist || abs(actor->y - player->mo->y) >= blockdist)
- continue; // didn't hit it
-
- angle = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
-
- if (angle > ANGLE_90 && angle < ANGLE_270)
- continue;
-
- // Blocked by the shield
- player->mo->momx += movex;
- player->mo->momy += movey;
- return;
- }
-}
-
-
-// Function: A_SetReactionTime
-//
-// Description: Sets the object's reaction time.
-//
-// var1 = 1 (use value in var2); 0 (use info table value)
-// var2 = if var1 = 1, then value to set
-//
-void A_SetReactionTime(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetReactionTime", actor))
- return;
-#endif
- if (var1)
- actor->reactiontime = var2;
- else
- actor->reactiontime = actor->info->reactiontime;
-}
-
-// Function: A_Boss1Spikeballs
-//
-// Description: Boss 1 spikeball spawning loop.
-//
-// var1 = ball number
-// var2 = total balls
-//
-void A_Boss1Spikeballs(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *ball;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss1Spikeballs", actor))
- return;
-#endif
-
- ball = P_SpawnMobj(actor->x, actor->y, actor->z, MT_EGGMOBILE_BALL);
- P_SetTarget(&ball->target, actor);
- ball->movedir = FixedAngle(FixedMul(FixedDiv(locvar1<threshold = ball->radius + actor->radius + ball->info->painchance;
-
- S_StartSound(ball, ball->info->seesound);
- var1 = ball->state->var1, var2 = ball->state->var2;
- ball->state->action.acp1(ball);
-}
-
-// Function: A_Boss3TakeDamage
-//
-// Description: Called when Boss 3 takes damage.
-//
-// var1 = movecount value
-// var2 = unused
-//
-void A_Boss3TakeDamage(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss3TakeDamage", actor))
- return;
-#endif
- actor->movecount = var1;
-
- if (actor->target && actor->target->spawnpoint)
- actor->threshold = actor->target->spawnpoint->extrainfo;
-}
-
-// Function: A_Boss3Path
-//
-// Description: Does pathfinding along Boss 3's nodes.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss3Path(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss3Path", actor))
- return;
-#endif
-
- if (actor->tracer && actor->tracer->health && actor->tracer->movecount)
- actor->movecount |= 1;
- else if (actor->movecount & 1)
- actor->movecount = 0;
-
- if (actor->movecount & 2) // We've reached a firing point?
- {
- // Wait here and pretend to be angry or something.
- actor->momx = 0;
- actor->momy = 0;
- actor->momz = 0;
- P_SetTarget(&actor->target, actor->tracer->target);
- var1 = 0, var2 = 0;
- A_FaceTarget(actor);
- if (actor->tracer->state == &states[actor->tracer->info->missilestate])
- P_SetMobjState(actor, actor->info->missilestate);
- return;
- }
- else if (actor->threshold >= 0) // Traveling mode
- {
- thinker_t *th;
- mobj_t *mo2;
- fixed_t dist, dist2;
- fixed_t speed;
-
- P_SetTarget(&actor->target, NULL);
-
- // scan the thinkers
- // to find a point that matches
- // the number
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo2 = (mobj_t *)th;
- if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint && mo2->spawnpoint->angle == actor->threshold)
- {
- P_SetTarget(&actor->target, mo2);
- break;
- }
- }
-
- if (!actor->target) // Should NEVER happen
- {
- CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy was unable to find specified waypoint: %d\n", actor->threshold);
- return;
- }
-
- dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z);
-
- if (dist < 1)
- dist = 1;
-
- if (actor->tracer && ((actor->tracer->movedir)
- || (actor->tracer->health <= actor->tracer->info->damage)))
- speed = actor->info->speed * 2;
- else
- speed = actor->info->speed;
-
- actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), speed);
- actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), speed);
- actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), speed);
-
- if (actor->momx != 0 || actor->momy != 0)
- actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy);
-
- dist2 = P_AproxDistance(P_AproxDistance(actor->target->x - (actor->x + actor->momx), actor->target->y - (actor->y + actor->momy)), actor->target->z - (actor->z + actor->momz));
-
- if (dist2 < 1)
- dist2 = 1;
-
- if ((dist >> FRACBITS) <= (dist2 >> FRACBITS))
- {
- // If further away, set XYZ of mobj to waypoint location
- P_UnsetThingPosition(actor);
- actor->x = actor->target->x;
- actor->y = actor->target->y;
- actor->z = actor->target->z;
- actor->momx = actor->momy = actor->momz = 0;
- P_SetThingPosition(actor);
-
- if (actor->threshold == 0)
- {
- P_RemoveMobj(actor); // Cycle completed. Dummy removed.
- return;
- }
-
- // Set to next waypoint in sequence
- if (actor->target->spawnpoint)
- {
- // From the center point, choose one of the five paths
- if (actor->target->spawnpoint->angle == 0)
- {
- P_RemoveMobj(actor); // Cycle completed. Dummy removed.
- return;
- }
- else
- actor->threshold = actor->target->spawnpoint->extrainfo;
-
- // If the deaf flag is set, go into firing mode
- if (actor->target->spawnpoint->options & MTF_AMBUSH)
- actor->movecount |= 2;
- }
- else // This should never happen, as well
- CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy waypoint has no spawnpoint associated with it.\n");
- }
- }
-}
-
-// Function: A_LinedefExecute
-//
-// Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well.
-//
-// var1 = tag
-// var2 = add angle to tag (optional)
-//
-void A_LinedefExecute(mobj_t *actor)
-{
- INT32 tagnum;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_LinedefExecute", actor))
- return;
-#endif
-
- tagnum = locvar1;
- // state numbers option is no more, custom states cannot be guaranteed to stay the same number anymore, now that they can be defined by names instead
-
- if (locvar2)
- tagnum += locvar2*(AngleFixed(actor->angle)>>FRACBITS);
-
- CONS_Debug(DBG_GAMELOGIC, "A_LinedefExecute: Running mobjtype %d's sector with tag %d\n", actor->type, tagnum);
-
- // tag 32768 displayed in map editors is actually tag -32768, tag 32769 is -32767, 65535 is -1 etc.
- P_LinedefExecute((INT16)tagnum, actor, actor->subsector->sector);
-}
-
-// Function: A_PlaySeeSound
-//
-// Description: Plays the object's seesound.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_PlaySeeSound(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_PlaySeeSound", actor))
- return;
-#endif
- if (actor->info->seesound)
- S_StartScreamSound(actor, actor->info->seesound);
-}
-
-// Function: A_PlayAttackSound
-//
-// Description: Plays the object's attacksound.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_PlayAttackSound(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_PlayAttackSound", actor))
- return;
-#endif
- if (actor->info->attacksound)
- S_StartAttackSound(actor, actor->info->attacksound);
-}
-
-// Function: A_PlayActiveSound
-//
-// Description: Plays the object's activesound.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_PlayActiveSound(mobj_t *actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_PlayActiveSound", actor))
- return;
-#endif
- if (actor->info->activesound)
- S_StartSound(actor, actor->info->activesound);
-}
-
-// Function: A_SmokeTrailer
-//
-// Description: Adds smoke trails to an object.
-//
-// var1 = object # to spawn as smoke
-// var2 = unused
-//
-void A_SmokeTrailer(mobj_t *actor)
-{
- mobj_t *th;
- INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SmokeTrailer", actor))
- return;
-#endif
-
- if (leveltime % 4)
- return;
-
- // add the smoke behind the rocket
- if (actor->eflags & MFE_VERTICALFLIP)
- {
- th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z + actor->height - FixedMul(mobjinfo[locvar1].height, actor->scale), locvar1);
- th->flags2 |= MF2_OBJECTFLIP;
- }
- else
- th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z, locvar1);
- P_SetObjectMomZ(th, FRACUNIT, false);
- th->destscale = actor->scale;
- P_SetScale(th, actor->scale);
- th->tics -= P_RandomByte() & 3;
- if (th->tics < 1)
- th->tics = 1;
-}
-
-// Function: A_SpawnObjectAbsolute
-//
-// Description: Spawns an object at an absolute position
-//
-// var1:
-// var1 >> 16 = x
-// var1 & 65535 = y
-// var2:
-// var2 >> 16 = z
-// var2 & 65535 = type
-//
-void A_SpawnObjectAbsolute(mobj_t *actor)
-{
- INT16 x, y, z; // Want to be sure we can use negative values
- mobjtype_t type;
- mobj_t *mo;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SpawnObjectAbsolute", actor))
- return;
-#endif
-
- x = (INT16)(locvar1>>16);
- y = (INT16)(locvar1&65535);
- z = (INT16)(locvar2>>16);
- type = (mobjtype_t)(locvar2&65535);
-
- mo = P_SpawnMobj(x<angle = actor->angle;
-
- if (actor->eflags & MFE_VERTICALFLIP)
- mo->flags2 |= MF2_OBJECTFLIP;
-}
-
-// Function: A_SpawnObjectRelative
-//
-// Description: Spawns an object relative to the location of the actor
-//
-// var1:
-// var1 >> 16 = x
-// var1 & 65535 = y
-// var2:
-// var2 >> 16 = z
-// var2 & 65535 = type
-//
-void A_SpawnObjectRelative(mobj_t *actor)
-{
- INT16 x, y, z; // Want to be sure we can use negative values
- mobjtype_t type;
- mobj_t *mo;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SpawnObjectRelative", actor))
- return;
-#endif
-
- CONS_Debug(DBG_GAMELOGIC, "A_SpawnObjectRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
- x = (INT16)(locvar1>>16);
- y = (INT16)(locvar1&65535);
- z = (INT16)(locvar2>>16);
- type = (mobjtype_t)(locvar2&65535);
-
- // Spawn objects correctly in reverse gravity.
- // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame
- mo = P_SpawnMobj(actor->x + FixedMul(x<scale),
- actor->y + FixedMul(y<scale),
- (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[type].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), type);
-
- // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
- mo->angle = actor->angle;
-
- if (actor->eflags & MFE_VERTICALFLIP)
- mo->flags2 |= MF2_OBJECTFLIP;
-
-}
-
-// Function: A_ChangeAngleRelative
-//
-// Description: Changes the angle to a random relative value between the min and max. Set min and max to the same value to eliminate randomness
-//
-// var1 = min
-// var2 = max
-//
-void A_ChangeAngleRelative(mobj_t *actor)
-{
- // Oh god, the old code /sucked/. Changed this and the absolute version to get a random range using amin and amax instead of
- // getting a random angle from the _entire_ spectrum and then clipping. While we're at it, do the angle conversion to the result
- // rather than the ranges, so <0 and >360 work as possible values. -Red
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- //angle_t angle = (P_RandomByte()+1)<<24;
- const fixed_t amin = locvar1*FRACUNIT;
- const fixed_t amax = locvar2*FRACUNIT;
- //const angle_t amin = FixedAngle(locvar1*FRACUNIT);
- //const angle_t amax = FixedAngle(locvar2*FRACUNIT);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ChangeAngleRelative", actor))
- return;
-#endif
-
-#ifdef PARANOIA
- if (amin > amax)
- I_Error("A_ChangeAngleRelative: var1 is greater then var2");
-#endif
-/*
- if (angle < amin)
- angle = amin;
- if (angle > amax)
- angle = amax;*/
-
- actor->angle += FixedAngle(P_RandomRange(amin, amax));
-}
-
-// Function: A_ChangeAngleAbsolute
-//
-// Description: Changes the angle to a random absolute value between the min and max. Set min and max to the same value to eliminate randomness
-//
-// var1 = min
-// var2 = max
-//
-void A_ChangeAngleAbsolute(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- //angle_t angle = (P_RandomByte()+1)<<24;
- const fixed_t amin = locvar1*FRACUNIT;
- const fixed_t amax = locvar2*FRACUNIT;
- //const angle_t amin = FixedAngle(locvar1*FRACUNIT);
- //const angle_t amax = FixedAngle(locvar2*FRACUNIT);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ChangeAngleAbsolute", actor))
- return;
-#endif
-
-#ifdef PARANOIA
- if (amin > amax)
- I_Error("A_ChangeAngleAbsolute: var1 is greater then var2");
-#endif
-/*
- if (angle < amin)
- angle = amin;
- if (angle > amax)
- angle = amax;*/
-
- actor->angle = FixedAngle(P_RandomRange(amin, amax));
-}
-
-// Function: A_PlaySound
-//
-// Description: Plays a sound
-//
-// var1 = sound # to play
-// var2:
-// 0 = Play sound without an origin
-// 1 = Play sound using calling object as origin
-//
-void A_PlaySound(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_PlaySound", actor))
- return;
-#endif
-
- S_StartSound(locvar2 ? actor : NULL, locvar1);
-}
-
-// Function: A_FindTarget
-//
-// Description: Finds the nearest/furthest mobj of the specified type and sets actor->target to it.
-//
-// var1 = mobj type
-// var2 = if (0) nearest; else furthest;
-//
-void A_FindTarget(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *targetedmobj = NULL;
- thinker_t *th;
- mobj_t *mo2;
- fixed_t dist1 = 0, dist2 = 0;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FindTarget", actor))
- return;
-#endif
-
- CONS_Debug(DBG_GAMELOGIC, "A_FindTarget called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
- // scan the thinkers
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo2 = (mobj_t *)th;
-
- if (mo2->type == (mobjtype_t)locvar1)
- {
- if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
- continue; // Ignore spectators
- if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
- continue; // Ignore dead things
- if (targetedmobj == NULL)
- {
- targetedmobj = mo2;
- dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
- }
- else
- {
- dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
-
- if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
- {
- targetedmobj = mo2;
- dist2 = dist1;
- }
- }
- }
- }
-
- if (!targetedmobj)
- {
- CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Unable to find the specified object to target.\n");
- return; // Oops, nothing found..
- }
-
- CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Found a target.\n");
-
- P_SetTarget(&actor->target, targetedmobj);
-}
-
-// Function: A_FindTracer
-//
-// Description: Finds the nearest/furthest mobj of the specified type and sets actor->tracer to it.
-//
-// var1 = mobj type
-// var2 = if (0) nearest; else furthest;
-//
-void A_FindTracer(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *targetedmobj = NULL;
- thinker_t *th;
- mobj_t *mo2;
- fixed_t dist1 = 0, dist2 = 0;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FindTracer", actor))
- return;
-#endif
-
- CONS_Debug(DBG_GAMELOGIC, "A_FindTracer called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
- // scan the thinkers
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo2 = (mobj_t *)th;
-
- if (mo2->type == (mobjtype_t)locvar1)
- {
- if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
- continue; // Ignore spectators
- if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
- continue; // Ignore dead things
- if (targetedmobj == NULL)
- {
- targetedmobj = mo2;
- dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
- }
- else
- {
- dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
-
- if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
- {
- targetedmobj = mo2;
- dist2 = dist1;
- }
- }
- }
- }
-
- if (!targetedmobj)
- {
- CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Unable to find the specified object to target.\n");
- return; // Oops, nothing found..
- }
-
- CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Found a target.\n");
-
- P_SetTarget(&actor->tracer, targetedmobj);
-}
-
-// Function: A_SetTics
-//
-// Description: Sets the animation tics of an object
-//
-// var1 = tics to set to
-// var2 = if this is set, and no var1 is supplied, the mobj's threshold value will be used.
-//
-void A_SetTics(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetTics", actor))
- return;
-#endif
-
- if (locvar1)
- actor->tics = locvar1;
- else if (locvar2)
- actor->tics = actor->threshold;
-}
-
-// Function: A_SetRandomTics
-//
-// Description: Sets the animation tics of an object to a random value
-//
-// var1 = lower bound
-// var2 = upper bound
-//
-void A_SetRandomTics(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetRandomTics", actor))
- return;
-#endif
-
- actor->tics = P_RandomRange(locvar1, locvar2);
-}
-
-// Function: A_ChangeColorRelative
-//
-// Description: Changes the color of an object
-//
-// var1 = if (var1 > 0), find target and add its color value to yours
-// var2 = if (var1 = 0), color value to add
-//
-void A_ChangeColorRelative(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ChangeColorRelative", actor))
- return;
-#endif
-
- if (locvar1)
- {
- // Have you ever seen anything so hideous?
- if (actor->target)
- actor->color = (UINT8)(actor->color + actor->target->color);
- }
- else
- actor->color = (UINT8)(actor->color + locvar2);
-}
-
-// Function: A_ChangeColorAbsolute
-//
-// Description: Changes the color of an object by an absolute value. Note: 0 is default colormap.
-//
-// var1 = if (var1 > 0), set your color to your target's color
-// var2 = if (var1 = 0), color value to set to
-//
-void A_ChangeColorAbsolute(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ChangeColorAbsolute", actor))
- return;
-#endif
-
- if (locvar1)
- {
- if (actor->target)
- actor->color = actor->target->color;
- }
- else
- actor->color = (UINT8)locvar2;
-}
-
-// Function: A_MoveRelative
-//
-// Description: Moves an object (wrapper for P_Thrust)
-//
-// var1 = angle
-// var2 = force
-//
-void A_MoveRelative(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MoveRelative", actor))
- return;
-#endif
-
- P_Thrust(actor, actor->angle+FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
-}
-
-// Function: A_MoveAbsolute
-//
-// Description: Moves an object (wrapper for P_InstaThrust)
-//
-// var1 = angle
-// var2 = force
-//
-void A_MoveAbsolute(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MoveAbsolute", actor))
- return;
-#endif
-
- P_InstaThrust(actor, FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
-}
-
-// Function: A_Thrust
-//
-// Description: Pushes the object horizontally at its current angle.
-//
-// var1 = amount of force
-// var2 = If 1, xy momentum is lost. If 0, xy momentum is kept
-//
-void A_Thrust(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Thrust", actor))
- return;
-#endif
-
- if (!locvar1)
- CONS_Debug(DBG_GAMELOGIC, "A_Thrust: Var1 not specified!\n");
-
- if (locvar2)
- P_InstaThrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
- else
- P_Thrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
-}
-
-// Function: A_ZThrust
-//
-// Description: Pushes the object up or down.
-//
-// var1 = amount of force
-// var2:
-// lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
-// upper 16 bits = If 1, z momentum is lost. If 0, z momentum is kept
-//
-void A_ZThrust(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ZThrust", actor))
- return;
-#endif
-
- if (!locvar1)
- CONS_Debug(DBG_GAMELOGIC, "A_ZThrust: Var1 not specified!\n");
-
- if (locvar2 & 65535)
- actor->momx = actor->momy = 0;
-
- if (actor->eflags & MFE_VERTICALFLIP)
- actor->z--;
- else
- actor->z++;
-
- P_SetObjectMomZ(actor, locvar1*FRACUNIT, !(locvar2 >> 16));
-}
-
-// Function: A_SetTargetsTarget
-//
-// Description: Sets your target to the object who your target is targeting. Yikes! If it happens to be NULL, you're just out of luck.
-//
-// var1: (Your target)
-// 0 = target
-// 1 = tracer
-// var2: (Your target's target)
-// 0 = target/tracer's target
-// 1 = target/tracer's tracer
-//
-void A_SetTargetsTarget(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *oldtarg = NULL, *newtarg = NULL;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetTargetsTarget", actor))
- return;
-#endif
-
- // actor's target
- if (locvar1) // or tracer
- oldtarg = actor->tracer;
- else
- oldtarg = actor->target;
-
- if (P_MobjWasRemoved(oldtarg))
- return;
-
- // actor's target's target!
- if (locvar2) // or tracer
- newtarg = oldtarg->tracer;
- else
- newtarg = oldtarg->target;
-
- if (P_MobjWasRemoved(newtarg))
- return;
-
- // set actor's new target
- if (locvar1) // or tracer
- P_SetTarget(&actor->tracer, newtarg);
- else
- P_SetTarget(&actor->target, newtarg);
-}
-
-// Function: A_SetObjectFlags
-//
-// Description: Sets the flags of an object
-//
-// var1 = flag value to set
-// var2:
-// if var2 == 2, add the flag to the current flags
-// else if var2 == 1, remove the flag from the current flags
-// else if var2 == 0, set the flags to the exact value
-//
-void A_SetObjectFlags(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- boolean unlinkthings = false;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetObjectFlags", actor))
- return;
-#endif
-
- if (locvar2 == 2)
- locvar1 = actor->flags | locvar1;
- else if (locvar2 == 1)
- locvar1 = actor->flags & ~locvar1;
-
- if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
- unlinkthings = true;
-
- if (unlinkthings) {
- P_UnsetThingPosition(actor);
- if (sector_list)
- {
- P_DelSeclist(sector_list);
- sector_list = NULL;
- }
- }
-
- actor->flags = locvar1;
-
- if (unlinkthings)
- P_SetThingPosition(actor);
-}
-
-// Function: A_SetObjectFlags2
-//
-// Description: Sets the flags2 of an object
-//
-// var1 = flag value to set
-// var2:
-// if var2 == 2, add the flag to the current flags
-// else if var2 == 1, remove the flag from the current flags
-// else if var2 == 0, set the flags to the exact value
-//
-void A_SetObjectFlags2(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetObjectFlags2", actor))
- return;
-#endif
-
- if (locvar2 == 2)
- actor->flags2 |= locvar1;
- else if (locvar2 == 1)
- actor->flags2 &= ~locvar1;
- else
- actor->flags2 = locvar1;
-}
-
-// Function: A_BossJetFume
-//
-// Description: Spawns jet fumes/other attachment miscellany for the boss. To only be used when he is spawned.
-//
-// var1:
-// 0 - Triple jet fume pattern
-// 1 - Boss 3's propeller
-// 2 - Metal Sonic jet fume
-// 3 - Boss 4 jet flame
-// var2 = unused
-//
-void A_BossJetFume(mobj_t *actor)
-{
- mobj_t *filler;
- INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BossJetFume", actor))
- return;
-#endif
-
- if (locvar1 == 0) // Boss1 jet fumes
- {
- fixed_t jetx, jety, jetz;
-
- jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
- jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- jetz = actor->z + actor->height - FixedMul(38*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
- else
- jetz = actor->z + FixedMul(38*FRACUNIT, actor->scale);
-
- filler = P_SpawnMobj(jetx, jety, jetz, MT_JETFUME1);
- P_SetTarget(&filler->target, actor);
- filler->destscale = actor->scale;
- P_SetScale(filler, filler->destscale);
- if (actor->eflags & MFE_VERTICALFLIP)
- filler->flags2 |= MF2_OBJECTFLIP;
- filler->fuse = 56;
-
- if (actor->eflags & MFE_VERTICALFLIP)
- jetz = actor->z + actor->height - FixedMul(12*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
- else
- jetz = actor->z + FixedMul(12*FRACUNIT, actor->scale);
-
- filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
- jety + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
- jetz, MT_JETFUME1);
- P_SetTarget(&filler->target, actor);
- filler->destscale = actor->scale;
- P_SetScale(filler, filler->destscale);
- if (actor->eflags & MFE_VERTICALFLIP)
- filler->flags2 |= MF2_OBJECTFLIP;
- filler->fuse = 57;
-
- filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
- jety + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
- jetz, MT_JETFUME1);
- P_SetTarget(&filler->target, actor);
- filler->destscale = actor->scale;
- P_SetScale(filler, filler->destscale);
- if (actor->eflags & MFE_VERTICALFLIP)
- filler->flags2 |= MF2_OBJECTFLIP;
- filler->fuse = 58;
-
- P_SetTarget(&actor->tracer, filler);
- }
- else if (locvar1 == 1) // Boss 3 propeller
- {
- fixed_t jetx, jety, jetz;
-
- jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale));
- jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- jetz = actor->z + actor->height - FixedMul(17*FRACUNIT + mobjinfo[MT_PROPELLER].height, actor->scale);
- else
- jetz = actor->z + FixedMul(17*FRACUNIT, actor->scale);
-
- filler = P_SpawnMobj(jetx, jety, jetz, MT_PROPELLER);
- P_SetTarget(&filler->target, actor);
- filler->destscale = actor->scale;
- P_SetScale(filler, filler->destscale);
- if (actor->eflags & MFE_VERTICALFLIP)
- filler->flags2 |= MF2_OBJECTFLIP;
- filler->angle = actor->angle - ANGLE_180;
-
- P_SetTarget(&actor->tracer, filler);
- }
- else if (locvar1 == 2) // Metal Sonic jet fumes
- {
- filler = P_SpawnMobj(actor->x, actor->y, actor->z, MT_JETFUME1);
- P_SetTarget(&filler->target, actor);
- filler->fuse = 59;
- P_SetTarget(&actor->tracer, filler);
- filler->destscale = actor->scale/2;
- P_SetScale(filler, filler->destscale);
- if (actor->eflags & MFE_VERTICALFLIP)
- filler->flags2 |= MF2_OBJECTFLIP;
- }
- else if (locvar1 == 3) // Boss 4 jet flame
- {
- fixed_t jetz;
- if (actor->eflags & MFE_VERTICALFLIP)
- jetz = actor->z + actor->height + FixedMul(50*FRACUNIT - mobjinfo[MT_JETFLAME].height, actor->scale);
- else
- jetz = actor->z - FixedMul(50*FRACUNIT, actor->scale);
- filler = P_SpawnMobj(actor->x, actor->y, jetz, MT_JETFLAME);
- P_SetTarget(&filler->target, actor);
- // Boss 4 already uses its tracer for other things
- filler->destscale = actor->scale;
- P_SetScale(filler, filler->destscale);
- if (actor->eflags & MFE_VERTICALFLIP)
- filler->flags2 |= MF2_OBJECTFLIP;
- }
-}
-
-// Function: A_RandomState
-//
-// Description: Chooses one of the two state numbers supplied randomly.
-//
-// var1 = state number 1
-// var2 = state number 2
-//
-void A_RandomState(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RandomState", actor))
- return;
-#endif
-
- P_SetMobjState(actor, P_RandomChance(FRACUNIT/2) ? locvar1 : locvar2);
-}
-
-// Function: A_RandomStateRange
-//
-// Description: Chooses a random state within the range supplied.
-//
-// var1 = Minimum state number to choose.
-// var2 = Maximum state number to use.
-//
-void A_RandomStateRange(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RandomStateRange", actor))
- return;
-#endif
-
- P_SetMobjState(actor, P_RandomRange(locvar1, locvar2));
-}
-
-// Function: A_DualAction
-//
-// Description: Calls two actions. Be careful, if you reference the same state this action is called from, you can create an infinite loop.
-//
-// var1 = state # to use 1st action from
-// var2 = state # to use 2nd action from
-//
-void A_DualAction(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_DualAction", actor))
- return;
-#endif
-
- CONS_Debug(DBG_GAMELOGIC, "A_DualAction called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
- var1 = states[locvar1].var1;
- var2 = states[locvar1].var2;
-#ifdef HAVE_BLUA
- astate = &states[locvar1];
-#endif
-
- CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling First Action (state %d)...\n", locvar1);
- states[locvar1].action.acp1(actor);
-
- var1 = states[locvar2].var1;
- var2 = states[locvar2].var2;
-#ifdef HAVE_BLUA
- astate = &states[locvar2];
-#endif
-
- CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling Second Action (state %d)...\n", locvar2);
- states[locvar2].action.acp1(actor);
-}
-
-// Function: A_RemoteAction
-//
-// Description: var1 is the remote object. var2 is the state reference for calling the action called on var1. var1 becomes the actor's target, the action (var2) is called on var1. actor's target is restored
-//
-// var1 = remote object (-2 uses tracer, -1 uses target)
-// var2 = state reference for calling an action
-//
-void A_RemoteAction(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *originaltarget = actor->target; // Hold on to the target for later.
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RemoteAction", actor))
- return;
-#endif
-
- // If >=0, find the closest target.
- if (locvar1 >= 0)
- {
- ///* DO A_FINDTARGET STUFF *///
- mobj_t *targetedmobj = NULL;
- thinker_t *th;
- mobj_t *mo2;
- fixed_t dist1 = 0, dist2 = 0;
-
- // scan the thinkers
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo2 = (mobj_t *)th;
-
- if (mo2->type == (mobjtype_t)locvar1)
- {
- if (targetedmobj == NULL)
- {
- targetedmobj = mo2;
- dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
- }
- else
- {
- dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
-
- if ((locvar2 && dist1 < dist2) || (!locvar2 && dist1 > dist2))
- {
- targetedmobj = mo2;
- dist2 = dist1;
- }
- }
- }
- }
-
- if (!targetedmobj)
- {
- CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Unable to find the specified object to target.\n");
- return; // Oops, nothing found..
- }
-
- CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Found a target.\n");
-
- P_SetTarget(&actor->target, targetedmobj);
-
- ///* END A_FINDTARGET STUFF *///
- }
-
- // If -2, use the tracer as the target
- else if (locvar1 == -2)
- P_SetTarget(&actor->target, actor->tracer);
- // if -1 or anything else, just use the target.
-
- if (actor->target)
- {
- // Steal the var1 and var2 from "locvar2"
- var1 = states[locvar2].var1;
- var2 = states[locvar2].var2;
-#ifdef HAVE_BLUA
- astate = &states[locvar2];
-#endif
-
- CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Calling action on %p\n"
- "var1 is %d\nvar2 is %d\n", actor->target, var1, var2);
- states[locvar2].action.acp1(actor->target);
- }
-
- P_SetTarget(&actor->target, originaltarget); // Restore the original target.
-}
-
-// Function: A_ToggleFlameJet
-//
-// Description: Turns a flame jet on and off.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ToggleFlameJet(mobj_t* actor)
-{
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ToggleFlameJet", actor))
- return;
-#endif
- // threshold - off delay
- // movecount - on timer
-
- if (actor->flags2 & MF2_FIRING)
- {
- actor->flags2 &= ~MF2_FIRING;
-
- if (actor->threshold)
- actor->tics = actor->threshold;
- }
- else
- {
- actor->flags2 |= MF2_FIRING;
-
- if (actor->movecount)
- actor->tics = actor->movecount;
- }
-}
-
-// Function: A_OrbitNights
-//
-// Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.)
-//
-// var1 = Angle adjustment (aka orbit speed)
-// var2:
-// Bits 1-10: height offset, max 1023
-// Bits 11-16: X radius factor (max 63, default 20)
-// Bit 17: set if object is Nightopian Helper
-// Bit 18: set to define X/Y/Z rotation factor
-// Bits 19-20: Unused
-// Bits 21-26: Y radius factor (max 63, default 32)
-// Bits 27-32: Z radius factor (max 63, default 32)
-//
-// If MF_GRENADEBOUNCE is flagged on mobj, use actor->threshold to define X/Y/Z radius factor, max 1023 each:
-// Bits 1-10: X factor
-// Bits 11-20: Y factor
-// Bits 21-30: Z factor
-void A_OrbitNights(mobj_t* actor)
-{
- INT32 ofs = (var2 & 0x3FF);
- boolean ishelper = (var2 & 0x10000);
- boolean donotrescale = (var2 & 0x40000);
- INT32 xfactor = 32, yfactor = 32, zfactor = 20;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_OrbitNights", actor))
- return;
-#endif
-
- if (actor->flags & MF_GRENADEBOUNCE)
- {
- xfactor = (actor->threshold & 0x3FF);
- yfactor = (actor->threshold & 0xFFC00) >> 10;
- zfactor = (actor->threshold & 0x3FF00000) >> 20;
- }
- else if (var2 & 0x20000)
- {
- xfactor = (var2 & 0xFC00) >> 10;
- yfactor = (var2 & 0x3F00000) >> 20;
- zfactor = (var2 & 0xFC000000) >> 26;
- }
-
- if (!actor->target
- || (actor->target->player &&
- // if NiGHTS special stage and not NiGHTSmode.
- (((maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && !(actor->target->player->powers[pw_carry] == CR_NIGHTSMODE))
- // Also remove this object if they no longer have a NiGHTS helper
- || (ishelper && !actor->target->player->powers[pw_nights_helper]))))
- {
- P_RemoveMobj(actor);
- return;
- }
- else
- {
- actor->extravalue1 += var1;
- P_UnsetThingPosition(actor);
- {
- const angle_t fa = (angle_t)actor->extravalue1 >> ANGLETOFINESHIFT;
- const angle_t ofa = ((angle_t)actor->extravalue1 + (ofs*ANG1)) >> ANGLETOFINESHIFT;
-
- const fixed_t fc = FixedMul(FINECOSINE(fa),FixedMul(xfactor*FRACUNIT, actor->scale));
- const fixed_t fh = FixedMul(FINECOSINE(ofa),FixedMul(zfactor*FRACUNIT, actor->scale));
- const fixed_t fs = FixedMul(FINESINE(fa),FixedMul(yfactor*FRACUNIT, actor->scale));
-
- actor->x = actor->target->x + fc;
- actor->y = actor->target->y + fs;
- actor->z = actor->target->z + fh + FixedMul(16*FRACUNIT, actor->scale);
-
- // Semi-lazy hack
- actor->angle = (angle_t)actor->extravalue1 + ANGLE_90;
- }
- P_SetThingPosition(actor);
-
- if (ishelper && actor->target->player) // Flash a helper that's about to be removed.
- {
- if ((actor->target->player->powers[pw_nights_helper] < TICRATE)
- && (actor->target->player->powers[pw_nights_helper] & 1))
- actor->flags2 |= MF2_DONTDRAW;
- else
- actor->flags2 &= ~MF2_DONTDRAW;
- }
-
- if (!donotrescale && actor->destscale != actor->target->destscale)
- actor->destscale = actor->target->destscale;
- }
-}
-
-// Function: A_GhostMe
-//
-// Description: Spawns a "ghost" mobj of this actor, ala spindash trails and the minus's digging "trails"
-//
-// var1 = duration in tics
-// var2 = unused
-//
-void A_GhostMe(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- mobj_t *ghost;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_GhostMe", actor))
- return;
-#endif
- ghost = P_SpawnGhostMobj(actor);
- if (ghost && locvar1 > 0)
- ghost->fuse = locvar1;
-}
-
-// Function: A_SetObjectState
-//
-// Description: Changes the state of an object's target/tracer.
-//
-// var1 = state number
-// var2:
-// 0 = target
-// 1 = tracer
-//
-void A_SetObjectState(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *target;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetObjectState", actor))
- return;
-#endif
-
- if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer))
- {
- if (cv_debug)
- CONS_Printf("A_SetObjectState: No target to change state!\n");
- return;
- }
-
- if (!locvar2) // target
- target = actor->target;
- else // tracer
- target = actor->tracer;
-
- if (target->health > 0)
- {
- if (!target->player)
- P_SetMobjState(target, locvar1);
- else
- P_SetPlayerMobjState(target, locvar1);
- }
-}
-
-// Function: A_SetObjectTypeState
-//
-// Description: Changes the state of all active objects of a certain type in a certain range of the actor.
-//
-// var1 = state number
-// var2:
-// lower 16 bits = type
-// upper 16 bits = range (if == 0, across whole map)
-//
-void A_SetObjectTypeState(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
- const UINT16 loc2up = (UINT16)(locvar2 >> 16);
-
- thinker_t *th;
- mobj_t *mo2;
- fixed_t dist = 0;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetObjectTypeState", actor))
- return;
-#endif
-
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo2 = (mobj_t *)th;
-
- if (mo2->type == (mobjtype_t)loc2lw)
- {
- dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
-
- if (mo2->health > 0)
- {
- if (loc2up == 0)
- P_SetMobjState(mo2, locvar1);
- else
- {
- if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
- P_SetMobjState(mo2, locvar1);
- }
- }
- }
- }
-}
-
-// Function: A_KnockBack
-//
-// Description: Knocks back the object's target at its current speed.
-//
-// var1:
-// 0 = target
-// 1 = tracer
-// var2 = unused
-//
-void A_KnockBack(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- mobj_t *target;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_KnockBack", actor))
- return;
-#endif
-
- if (!locvar1)
- target = actor->target;
- else
- target = actor->tracer;
-
- if (!target)
- {
- if(cv_debug)
- CONS_Printf("A_KnockBack: No target!\n");
- return;
- }
-
- target->momx *= -1;
- target->momy *= -1;
-}
-
-// Function: A_PushAway
-//
-// Description: Pushes an object's target away from the calling object.
-//
-// var1 = amount of force
-// var2:
-// lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
-// upper 16 bits = 0 - target, 1 - tracer
-//
-void A_PushAway(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *target; // target
- angle_t an; // actor to target angle
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_PushAway", actor))
- return;
-#endif
-
- if ((!(locvar2 >> 16) && !actor->target) || ((locvar2 >> 16) && !actor->tracer))
- return;
-
- if (!locvar1)
- CONS_Printf("A_Thrust: Var1 not specified!\n");
-
- if (!(locvar2 >> 16)) // target
- target = actor->target;
- else // tracer
- target = actor->tracer;
-
- an = R_PointToAngle2(actor->x, actor->y, target->x, target->y);
-
- if (locvar2 & 65535)
- P_InstaThrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
- else
- P_Thrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
-}
-
-// Function: A_RingDrain
-//
-// Description: Drain targeted player's rings.
-//
-// var1 = ammount of drained rings
-// var2 = unused
-//
-void A_RingDrain(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- player_t *player;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RingDrain", actor))
- return;
-#endif
-
- if (!actor->target || !actor->target->player)
- {
- if(cv_debug)
- CONS_Printf("A_RingDrain: No player targeted!\n");
- return;
- }
-
- player = actor->target->player;
- P_GivePlayerRings(player, -min(locvar1, player->rings));
-}
-
-// Function: A_SplitShot
-//
-// Description: Shoots 2 missiles that hit next to the player.
-//
-// var1 = target x-y-offset
-// var2:
-// lower 16 bits = missile type
-// upper 16 bits = height offset
-//
-void A_SplitShot(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
- const UINT16 loc2up = (UINT16)(locvar2 >> 16);
- const fixed_t offs = (fixed_t)(locvar1*FRACUNIT);
- const fixed_t hoffs = (fixed_t)(loc2up*FRACUNIT);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SplitShot", actor))
- return;
-#endif
-
- A_FaceTarget(actor);
- {
- const angle_t an = (actor->angle + ANGLE_90) >> ANGLETOFINESHIFT;
- const fixed_t fasin = FINESINE(an);
- const fixed_t facos = FINECOSINE(an);
- fixed_t xs = FixedMul(facos,FixedMul(offs, actor->scale));
- fixed_t ys = FixedMul(fasin,FixedMul(offs, actor->scale));
- fixed_t z;
-
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(hoffs, actor->scale);
- else
- z = actor->z + FixedMul(hoffs, actor->scale);
-
- P_SpawnPointMissile(actor, actor->target->x+xs, actor->target->y+ys, actor->target->z, loc2lw, actor->x, actor->y, z);
- P_SpawnPointMissile(actor, actor->target->x-xs, actor->target->y-ys, actor->target->z, loc2lw, actor->x, actor->y, z);
- }
-}
-
-// Function: A_MissileSplit
-//
-// Description: If the object is a missile it will create a new missile with an alternate flight path owned by the one who shot the former missile.
-//
-// var1 = splitting missile type
-// var2 = splitting angle
-//
-void A_MissileSplit(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MissileSplit", actor))
- return;
-#endif
- if (actor->eflags & MFE_VERTICALFLIP)
- P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z+actor->height, locvar2);
- else
- P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z, locvar2);
-}
-
-// Function: A_MultiShot
-//
-// Description: Shoots objects horizontally that spread evenly in all directions.
-//
-// var1:
-// lower 16 bits = number of missiles
-// upper 16 bits = missile type #
-// var2 = height offset
-//
-void A_MultiShot(mobj_t *actor)
-{
- fixed_t z, xr, yr;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
- const UINT16 loc1up = (UINT16)(locvar1 >> 16);
- INT32 count = 0;
- fixed_t ad;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MultiShot", actor))
- return;
-#endif
-
- if (actor->target)
- A_FaceTarget(actor);
-
- if(loc1lw > 90)
- ad = FixedMul(90*FRACUNIT, actor->scale);
- else
- ad = FixedMul(loc1lw*FRACUNIT, actor->scale);
-
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
- xr = FixedMul((P_SignedRandom()/3)<scale); // please note p_mobj.c's P_Rand() abuse
- yr = FixedMul((P_SignedRandom()/3)<scale); // of rand(), RAND_MAX and signness mess
-
- while(count <= loc1lw && loc1lw >= 1)
- {
- const angle_t fa = FixedAngleC(count*FRACUNIT*360, ad)>>ANGLETOFINESHIFT;
- const fixed_t rc = FINECOSINE(fa);
- const fixed_t rs = FINESINE(fa);
- const fixed_t xrc = FixedMul(xr, rc);
- const fixed_t yrs = FixedMul(yr, rs);
- const fixed_t xrs = FixedMul(xr, rs);
- const fixed_t yrc = FixedMul(yr, rc);
-
- P_SpawnPointMissile(actor, xrc-yrs+actor->x, xrs+yrc+actor->y, z, loc1up, actor->x, actor->y, z);
- count++;
- }
-
- if (!(actor->flags & MF_BOSS))
- {
- if (ultimatemode)
- actor->reactiontime = actor->info->reactiontime*TICRATE;
- else
- actor->reactiontime = actor->info->reactiontime*TICRATE*2;
- }
-}
-
-// Function: A_InstaLoop
-//
-// Description: Makes the object move along a 2d (view angle, z) polygon.
-//
-// var1:
-// lower 16 bits = current step
-// upper 16 bits = maximum step #
-// var2 = force
-//
-void A_InstaLoop(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- fixed_t force = max(locvar2, 1)*FRACUNIT; // defaults to 1 if var2 < 1
- const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
- const UINT16 loc1up = (UINT16)(locvar1 >> 16);
- const angle_t fa = FixedAngleC(loc1lw*FRACUNIT*360, loc1up*FRACUNIT)>>ANGLETOFINESHIFT;
- const fixed_t ac = FINECOSINE(fa);
- const fixed_t as = FINESINE(fa);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_InstaLoop", actor))
- return;
-#endif
-
- P_InstaThrust(actor, actor->angle, FixedMul(ac, FixedMul(force, actor->scale)));
- P_SetObjectMomZ(actor, FixedMul(as, force), false);
-}
-
-// Function: A_Custom3DRotate
-//
-// Description: Rotates the actor around its target in 3 dimensions.
-//
-// var1:
-// lower 16 bits = radius in fracunits
-// upper 16 bits = vertical offset
-// var2:
-// lower 16 bits = vertical rotation speed in 1/10 fracunits per tic
-// upper 16 bits = horizontal rotation speed in 1/10 fracunits per tic
-//
-void A_Custom3DRotate(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
- const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
- const UINT16 loc1up = (UINT16)(locvar1 >> 16);
- const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
- const UINT16 loc2up = (UINT16)(locvar2 >> 16);
-
- const fixed_t radius = FixedMul(loc1lw*FRACUNIT, actor->scale);
- const fixed_t hOff = FixedMul(loc1up*FRACUNIT, actor->scale);
- const fixed_t hspeed = FixedMul(loc2up*FRACUNIT/10, actor->scale);
- const fixed_t vspeed = FixedMul(loc2lw*FRACUNIT/10, actor->scale);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Custom3DRotate", actor))
- return;
-#endif
-
- if (actor->target->health == 0)
- {
- P_RemoveMobj(actor);
- return;
- }
-
- if (!actor->target) // This should NEVER happen.
- {
- if (cv_debug)
- CONS_Printf("Error: Object has no target\n");
- P_RemoveMobj(actor);
- return;
- }
- if (hspeed==0 && vspeed==0)
- {
- CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n");
- return;
- }
-
- actor->angle += FixedAngle(hspeed);
- actor->movedir += FixedAngle(vspeed);
- P_UnsetThingPosition(actor);
- {
- const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
-
- if (vspeed == 0 && hspeed != 0)
- {
- actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
- actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
- actor->z = actor->target->z + actor->target->height/2 - actor->height/2 + hOff;
- }
- else
- {
- const angle_t md = actor->movedir>>ANGLETOFINESHIFT;
- actor->x = actor->target->x + FixedMul(FixedMul(FINESINE(md),FINECOSINE(fa)),radius);
- actor->y = actor->target->y + FixedMul(FixedMul(FINESINE(md),FINESINE(fa)),radius);
- actor->z = actor->target->z + FixedMul(FINECOSINE(md),radius) + actor->target->height/2 - actor->height/2 + hOff;
- }
- }
- P_SetThingPosition(actor);
-}
-
-// Function: A_SearchForPlayers
-//
-// Description: Checks if the actor has targeted a vulnerable player. If not a new player will be searched all around. If no players are available the object can call a specific state. (Useful for not moving enemies)
-//
-// var1:
-// if var1 == 0, if necessary call state with same state number as var2
-// else, do not call a specific state if no players are available
-// var2 = state number
-//
-void A_SearchForPlayers(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SearchForPlayers", actor))
- return;
-#endif
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- if(locvar1==0)
- {
- P_SetMobjStateNF(actor, locvar2);
- return;
- }
- }
-}
-
-// Function: A_CheckRandom
-//
-// Description: Calls a state by chance.
-//
-// var1:
-// lower 16 bits = denominator
-// upper 16 bits = numerator (defaults to 1 if zero)
-// var2 = state number
-//
-void A_CheckRandom(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- fixed_t chance = FRACUNIT;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckRandom", actor))
- return;
-#endif
- if ((locvar1 & 0xFFFF) == 0)
- return;
-
- // The PRNG doesn't suck anymore, OK?
- if (locvar1 >> 16)
- chance *= (locvar1 >> 16);
- chance /= (locvar1 & 0xFFFF);
-
- if (P_RandomChance(chance))
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckTargetRings
-//
-// Description: Calls a state depending on the ammount of rings currently owned by targeted players.
-//
-// var1 = if player rings >= var1 call state
-// var2 = state number
-//
-void A_CheckTargetRings(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckTargetRings", actor))
- return;
-#endif
-
- if (!(actor->target) || !(actor->target->player))
- return;
-
- if (actor->target->player->rings >= locvar1)
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckRings
-//
-// Description: Calls a state depending on the ammount of rings currently owned by all players.
-//
-// var1 = if player rings >= var1 call state
-// var2 = state number
-//
-void A_CheckRings(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- INT32 i, cntr = 0;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckRings", actor))
- return;
-#endif
-
- for (i = 0; i < MAXPLAYERS; i++)
- cntr += players[i].rings;
-
- if (cntr >= locvar1)
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckTotalRings
-//
-// Description: Calls a state depending on the maximum ammount of rings owned by all players during this try.
-//
-// var1 = if total player rings >= var1 call state
-// var2 = state number
-//
-void A_CheckTotalRings(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
- INT32 i, cntr = 0;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckTotalRings", actor))
- return;
-#endif
-
- for (i = 0; i < MAXPLAYERS; i++)
- cntr += players[i].totalring;
-
- if (cntr >= locvar1)
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckHealth
-//
-// Description: Calls a state depending on the object's current health.
-//
-// var1 = if health <= var1 call state
-// var2 = state number
-//
-void A_CheckHealth(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckHealth", actor))
- return;
-#endif
-
- if (actor->health <= locvar1)
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckRange
-//
-// Description: Calls a state if the object's target is in range.
-//
-// var1:
-// lower 16 bits = range
-// upper 16 bits = 0 - target, 1 - tracer
-// var2 = state number
-//
-void A_CheckRange(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- fixed_t dist;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckRange", actor))
- return;
-#endif
-
- if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
- return;
-
- if (!(locvar1 >> 16)) //target
- dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
- else //tracer
- dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
-
- if (dist <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckHeight
-//
-// Description: Calls a state if the object and it's target have a height offset <= var1 compared to each other.
-//
-// var1:
-// lower 16 bits = height offset
-// upper 16 bits = 0 - target, 1 - tracer
-// var2 = state number
-//
-void A_CheckHeight(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- fixed_t height;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckHeight", actor))
- return;
-#endif
-
- if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
- return;
-
- if (!(locvar1 >> 16)) // target
- height = abs(actor->target->z - actor->z);
- else // tracer
- height = abs(actor->tracer->z - actor->z);
-
- if (height <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckTrueRange
-//
-// Description: Calls a state if the object's target is in true range. (Checks height, too.)
-//
-// var1:
-// lower 16 bits = range
-// upper 16 bits = 0 - target, 1 - tracer
-// var2 = state number
-//
-void A_CheckTrueRange(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- fixed_t height; // vertical range
- fixed_t dist; // horizontal range
- fixed_t l; // true range
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckTrueRange", actor))
- return;
-#endif
-
- if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
- return;
-
- if (!(locvar1 >> 16)) // target
- {
- height = actor->target->z - actor->z;
- dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
-
- }
- else // tracer
- {
- height = actor->tracer->z - actor->z;
- dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
- }
-
- l = P_AproxDistance(dist, height);
-
- if (l <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
- P_SetMobjState(actor, locvar2);
-
-}
-
-// Function: A_CheckThingCount
-//
-// Description: Calls a state depending on the number of active things in range.
-//
-// var1:
-// lower 16 bits = number of things
-// upper 16 bits = thing type
-// var2:
-// lower 16 bits = state to call
-// upper 16 bits = range (if == 0, check whole map)
-//
-void A_CheckThingCount(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
- const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
- const UINT16 loc1up = (UINT16)(locvar1 >> 16);
- const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
- const UINT16 loc2up = (UINT16)(locvar2 >> 16);
-
- INT32 count = 0;
- thinker_t *th;
- mobj_t *mo2;
- fixed_t dist = 0;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckThingCount", actor))
- return;
-#endif
-
- for (th = thinkercap.next; th != &thinkercap; th = th->next)
- {
- if (th->function.acp1 != (actionf_p1)P_MobjThinker)
- continue;
-
- mo2 = (mobj_t *)th;
-
- if (mo2->type == (mobjtype_t)loc1up)
- {
- dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
-
- if (loc2up == 0)
- count++;
- else
- {
- if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
- count++;
- }
- }
- }
-
- if(loc1lw <= count)
- P_SetMobjState(actor, loc2lw);
-}
-
-// Function: A_CheckAmbush
-//
-// Description: Calls a state if the actor is behind its targeted player.
-//
-// var1:
-// 0 = target
-// 1 = tracer
-// var2 = state number
-//
-void A_CheckAmbush(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- angle_t at; // angle target is currently facing
- angle_t atp; // actor to target angle
- angle_t an; // angle between at and atp
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckAmbush", actor))
- return;
-#endif
-
- if ((!locvar1 && !actor->target) || (locvar1 && !actor->tracer))
- return;
-
- if (!locvar1) // target
- {
- at = actor->target->angle;
- atp = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
- }
- else // tracer
- {
- at = actor->tracer->angle;
- atp = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
- }
-
- an = atp - at;
-
- if (an > ANGLE_180) // flip angle if bigger than 180
- an = InvAngle(an);
-
- if (an < ANGLE_90+ANGLE_22h) // within an angle of 112.5 from each other?
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckCustomValue
-//
-// Description: Calls a state depending on the object's custom value.
-//
-// var1 = if custom value >= var1, call state
-// var2 = state number
-//
-void A_CheckCustomValue(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckCustomValue", actor))
- return;
-#endif
-
- if (actor->cusval >= locvar1)
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_CheckCusValMemo
-//
-// Description: Calls a state depending on the object's custom memory value.
-//
-// var1 = if memory value >= var1, call state
-// var2 = state number
-//
-void A_CheckCusValMemo(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckCusValMemo", actor))
- return;
-#endif
-
- if (actor->cvmem >= locvar1)
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_SetCustomValue
-//
-// Description: Changes the custom value of an object.
-//
-// var1 = manipulating value
-// var2:
-// if var2 == 5, multiply the custom value by var1
-// else if var2 == 4, divide the custom value by var1
-// else if var2 == 3, apply modulo var1 to the custom value
-// else if var2 == 2, add var1 to the custom value
-// else if var2 == 1, substract var1 from the custom value
-// else if var2 == 0, replace the custom value with var1
-//
-void A_SetCustomValue(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetCustomValue", actor))
- return;
-#endif
-
- if (cv_debug)
- CONS_Printf("Init custom value is %d\n", actor->cusval);
-
- if (locvar1 == 0 && locvar2 == 4)
- return; // DON'T DIVIDE BY ZERO
-
- // no need for a "temp" value here, just modify the cusval directly
- if (locvar2 == 5) // multiply
- actor->cusval *= locvar1;
- else if (locvar2 == 4) // divide
- actor->cusval /= locvar1;
- else if (locvar2 == 3) // modulo
- actor->cusval %= locvar1;
- else if (locvar2 == 2) // add
- actor->cusval += locvar1;
- else if (locvar2 == 1) // subtract
- actor->cusval -= locvar1;
- else // replace
- actor->cusval = locvar1;
-
- if(cv_debug)
- CONS_Printf("New custom value is %d\n", actor->cusval);
-}
-
-// Function: A_UseCusValMemo
-//
-// Description: Memorizes or recalls a current custom value.
-//
-// var1:
-// if var1 == 1, manipulate memory value
-// else, recall memory value replacing the custom value
-// var2:
-// if var2 == 5, mem = mem*cv || cv = cv*mem
-// else if var2 == 4, mem = mem/cv || cv = cv/mem
-// else if var2 == 3, mem = mem%cv || cv = cv%mem
-// else if var2 == 2, mem += cv || cv += mem
-// else if var2 == 1, mem -= cv || cv -= mem
-// else mem = cv || cv = mem
-//
-void A_UseCusValMemo(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
- INT32 temp = actor->cusval; // value being manipulated
- INT32 tempM = actor->cvmem; // value used to manipulate temp with
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_UseCusValMemo", actor))
- return;
-#endif
-
- if (locvar1 == 1) // cvmem being changed using cusval
- {
- temp = actor->cvmem;
- tempM = actor->cusval;
- }
- else // cusval being changed with cvmem
- {
- temp = actor->cusval;
- tempM = actor->cvmem;
- }
-
- if (tempM == 0 && locvar2 == 4)
- return; // DON'T DIVIDE BY ZERO
-
- // now get new value for cusval/cvmem using the other
- if (locvar2 == 5) // multiply
- temp *= tempM;
- else if (locvar2 == 4) // divide
- temp /= tempM;
- else if (locvar2 == 3) // modulo
- temp %= tempM;
- else if (locvar2 == 2) // add
- temp += tempM;
- else if (locvar2 == 1) // subtract
- temp -= tempM;
- else // replace
- temp = tempM;
-
- // finally, give cusval/cvmem the new value!
- if (locvar1 == 1)
- actor->cvmem = temp;
- else
- actor->cusval = temp;
-}
-
-// Function: A_RelayCustomValue
-//
-// Description: Manipulates the custom value of the object's target/tracer.
-//
-// var1:
-// lower 16 bits:
-// if var1 == 0, use own custom value
-// else, use var1 value
-// upper 16 bits = 0 - target, 1 - tracer
-// var2:
-// if var2 == 5, multiply the target's custom value by var1
-// else if var2 == 4, divide the target's custom value by var1
-// else if var2 == 3, apply modulo var1 to the target's custom value
-// else if var2 == 2, add var1 to the target's custom value
-// else if var2 == 1, substract var1 from the target's custom value
-// else if var2 == 0, replace the target's custom value with var1
-//
-void A_RelayCustomValue(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
- INT32 temp; // reference value - var1 lower 16 bits changes this
- INT32 tempT; // target's value - changed to tracer if var1 upper 16 bits set, then modified to become final value
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RelayCustomValue", actor))
- return;
-#endif
-
- if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
- return;
-
- // reference custom value
- if ((locvar1 & 65535) == 0)
- temp = actor->cusval; // your own custom value
- else
- temp = (locvar1 & 65535); // var1 value
-
- if (!(locvar1 >> 16)) // target's custom value
- tempT = actor->target->cusval;
- else // tracer's custom value
- tempT = actor->tracer->cusval;
-
- if (temp == 0 && locvar2 == 4)
- return; // DON'T DIVIDE BY ZERO
-
- // now get new cusval using target's and the reference
- if (locvar2 == 5) // multiply
- tempT *= temp;
- else if (locvar2 == 4) // divide
- tempT /= temp;
- else if (locvar2 == 3) // modulo
- tempT %= temp;
- else if (locvar2 == 2) // add
- tempT += temp;
- else if (locvar2 == 1) // subtract
- tempT -= temp;
- else // replace
- tempT = temp;
-
- // finally, give target/tracer the new cusval!
- if (!(locvar1 >> 16)) // target
- actor->target->cusval = tempT;
- else // tracer
- actor->tracer->cusval = tempT;
-}
-
-// Function: A_CusValAction
-//
-// Description: Calls an action from a reference state applying custom value parameters.
-//
-// var1 = state # to use action from
-// var2:
-// if var2 == 5, only replace new action's var2 with memory value
-// else if var2 == 4, only replace new action's var1 with memory value
-// else if var2 == 3, replace new action's var2 with custom value and var1 with memory value
-// else if var2 == 2, replace new action's var1 with custom value and var2 with memory value
-// else if var2 == 1, only replace new action's var2 with custom value
-// else if var2 == 0, only replace new action's var1 with custom value
-//
-void A_CusValAction(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CusValAction", actor))
- return;
-#endif
-
- if (locvar2 == 5)
- {
- var1 = states[locvar1].var1;
- var2 = (INT32)actor->cvmem;
- }
- else if (locvar2 == 4)
- {
- var1 = (INT32)actor->cvmem;
- var2 = states[locvar1].var2;
- }
- else if (locvar2 == 3)
- {
- var1 = (INT32)actor->cvmem;
- var2 = (INT32)actor->cusval;
- }
- else if (locvar2 == 2)
- {
- var1 = (INT32)actor->cusval;
- var2 = (INT32)actor->cvmem;
- }
- else if (locvar2 == 1)
- {
- var1 = states[locvar1].var1;
- var2 = (INT32)actor->cusval;
- }
- else
- {
- var1 = (INT32)actor->cusval;
- var2 = states[locvar1].var2;
- }
-
-#ifdef HAVE_BLUA
- astate = &states[locvar1];
-#endif
- states[locvar1].action.acp1(actor);
-}
-
-// Function: A_ForceStop
-//
-// Description: Actor immediately stops its current movement.
-//
-// var1:
-// if var1 == 0, stop x-y-z-movement
-// else, stop x-y-movement only
-// var2 = unused
-//
-void A_ForceStop(mobj_t *actor)
-{
- INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ForceStop", actor))
- return;
-#endif
-
- actor->momx = actor->momy = 0;
- if (locvar1 == 0)
- actor->momz = 0;
-}
-
-// Function: A_ForceWin
-//
-// Description: Makes all players win the level.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_ForceWin(mobj_t *actor)
-{
- INT32 i;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ForceWin", actor))
- return;
-#else
- (void)actor;
-#endif
-
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (playeringame[i] && ((players[i].mo && players[i].mo->health)
- || ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
- break;
- }
-
- if (i == MAXPLAYERS)
- return;
-
- for (i = 0; i < MAXPLAYERS; i++)
- P_DoPlayerExit(&players[i]);
-}
-
-// Function: A_SpikeRetract
-//
-// Description: Toggles actor solid flag.
-//
-// var1:
-// if var1 == 0, actor no collide
-// else, actor solid
-// var2 = unused
-//
-void A_SpikeRetract(mobj_t *actor)
-{
- INT32 locvar1 = var1;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SpikeRetract", actor))
- return;
-#endif
-
- if (actor->flags & MF_NOBLOCKMAP)
- return;
-
- if (locvar1 == 0)
- {
- actor->flags &= ~MF_SOLID;
- actor->flags |= MF_NOCLIPTHING;
- }
- else
- {
- actor->flags |= MF_SOLID;
- actor->flags &= ~MF_NOCLIPTHING;
- }
- if (actor->flags & MF_SOLID)
- P_CheckPosition(actor, actor->x, actor->y);
-}
-
-// Function: A_InfoState
-//
-// Description: Set mobj state to one predefined in mobjinfo.
-//
-// var1:
-// if var1 == 0, set actor to spawnstate
-// else if var1 == 1, set actor to seestate
-// else if var1 == 2, set actor to meleestate
-// else if var1 == 3, set actor to missilestate
-// else if var1 == 4, set actor to deathstate
-// else if var1 == 5, set actor to xdeathstate
-// else if var1 == 6, set actor to raisestate
-// var2 = unused
-//
-void A_InfoState(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- switch (locvar1)
- {
- case 0:
- if (actor->state != &states[actor->info->spawnstate])
- P_SetMobjState(actor, actor->info->spawnstate);
- break;
- case 1:
- if (actor->state != &states[actor->info->seestate])
- P_SetMobjState(actor, actor->info->seestate);
- break;
- case 2:
- if (actor->state != &states[actor->info->meleestate])
- P_SetMobjState(actor, actor->info->meleestate);
- break;
- case 3:
- if (actor->state != &states[actor->info->missilestate])
- P_SetMobjState(actor, actor->info->missilestate);
- break;
- case 4:
- if (actor->state != &states[actor->info->deathstate])
- P_SetMobjState(actor, actor->info->deathstate);
- break;
- case 5:
- if (actor->state != &states[actor->info->xdeathstate])
- P_SetMobjState(actor, actor->info->xdeathstate);
- break;
- case 6:
- if (actor->state != &states[actor->info->raisestate])
- P_SetMobjState(actor, actor->info->raisestate);
- break;
- default:
- break;
- }
-}
-
-// Function: A_Repeat
-//
-// Description: Returns to state var2 until animation has been used var1 times, then continues to nextstate.
-//
-// var1 = repeat count
-// var2 = state to return to if extravalue2 > 0
-//
-void A_Repeat(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Repeat", actor))
- return;
-#endif
-
- if (locvar1 && (!actor->extravalue2 || actor->extravalue2 > locvar1))
- actor->extravalue2 = locvar1;
-
- if (--actor->extravalue2 > 0)
- P_SetMobjState(actor, locvar2);
-}
-
-// Function: A_SetScale
-//
-// Description: Changes the scale of the actor or its target/tracer
-//
-// var1 = new scale (1*FRACUNIT = 100%)
-// var2:
-// upper 16 bits: 0 = actor, 1 = target, 2 = tracer
-// lower 16 bits: 0 = instant change, 1 = smooth change
-//
-void A_SetScale(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *target;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SetScale", actor))
- return;
-#endif
-
- if (locvar1 <= 0)
- {
- if(cv_debug)
- CONS_Printf("A_SetScale: Valid scale not specified!\n");
- return;
- }
-
- if ((locvar2>>16) == 1)
- target = actor->target;
- else if ((locvar2>>16) == 2)
- target = actor->tracer;
- else // default to yourself!
- target = actor;
-
- if (!target)
- {
- if(cv_debug)
- CONS_Printf("A_SetScale: No target!\n");
- return;
- }
-
- target->destscale = locvar1; // destination scale
- if (!(locvar2 & 65535))
- P_SetScale(target, locvar1); // this instantly changes current scale to var1 if used, if not destscale will alter scale to var1 anyway
-}
-
-// Function: A_RemoteDamage
-//
-// Description: Damages, kills or even removes either the actor or its target/tracer. Actor acts as the inflictor/source unless harming itself
-//
-// var1 = Mobj affected: 0 - actor, 1 - target, 2 - tracer
-// var2 = Action: 0 - Damage, 1 - Kill, 2 - Remove
-//
-void A_RemoteDamage(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *target; // we MUST have a target
- mobj_t *source = NULL; // on the other hand we don't necessarily need a source
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_RemoteDamage", actor))
- return;
-#endif
- if (locvar1 == 1)
- target = actor->target;
- else if (locvar1 == 2)
- target = actor->tracer;
- else // default to yourself!
- target = actor;
-
- if (locvar1 == 1 || locvar1 == 2)
- source = actor;
-
- if (!target)
- {
- if(cv_debug)
- CONS_Printf("A_RemoteDamage: No target!\n");
- return;
- }
-
- if (locvar2 == 1) // Kill mobj!
- {
- if (target->player) // players die using P_DamageMobj instead for some reason
- P_DamageMobj(target, source, source, 1, DMG_INSTAKILL);
- else
- P_KillMobj(target, source, source, 0);
- }
- else if (locvar2 == 2) // Remove mobj!
- {
- if (target->player) //don't remove players!
- return;
-
- P_RemoveMobj(target);
- }
- else // default: Damage mobj!
- P_DamageMobj(target, source, source, 1, 0);
-}
-
-// Function: A_HomingChase
-//
-// Description: Actor chases directly towards its destination object
-//
-// var1 = speed multiple
-// var2 = destination: 0 = target, 1 = tracer
-//
-void A_HomingChase(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *dest;
- fixed_t dist;
- fixed_t speedmul;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_HomingChase", actor))
- return;
-#endif
-
- if (locvar2 == 1)
- dest = actor->tracer;
- else //default
- dest = actor->target;
-
- if (!dest || !dest->health)
- return;
-
- actor->angle = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y);
-
- dist = P_AproxDistance(P_AproxDistance(dest->x - actor->x, dest->y - actor->y), dest->z - actor->z);
-
- if (dist < 1)
- dist = 1;
-
- speedmul = FixedMul(locvar1, actor->scale);
-
- actor->momx = FixedMul(FixedDiv(dest->x - actor->x, dist), speedmul);
- actor->momy = FixedMul(FixedDiv(dest->y - actor->y, dist), speedmul);
- actor->momz = FixedMul(FixedDiv(dest->z - actor->z, dist), speedmul);
-}
-
-// Function: A_TrapShot
-//
-// Description: Fires a missile in a particular direction and angle rather than AT something, Trapgoyle-style!
-//
-// var1:
-// lower 16 bits = object # to fire
-// upper 16 bits = front offset
-// var2:
-// lower 15 bits = vertical angle variable
-// 16th bit:
-// - 0: use vertical angle variable as vertical angle in degrees
-// - 1: mimic P_SpawnXYZMissile
-// use z of actor minus z of missile as vertical distance to cover during momz calculation
-// use vertical angle variable as horizontal distance to cover during momz calculation
-// upper 16 bits = height offset
-//
-void A_TrapShot(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- boolean oldstyle = (locvar2 & 32768) ? true : false;
- mobjtype_t type = (mobjtype_t)(locvar1 & 65535);
- mobj_t *missile;
- INT16 frontoff = (INT16)(locvar1 >> 16);
- INT16 vertoff = (INT16)(locvar2 >> 16);
- fixed_t x, y, z;
- fixed_t speed;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_TrapShot", actor))
- return;
-#endif
-
- x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
- y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
-
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale);
- else
- z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale);
-
- CONS_Debug(DBG_GAMELOGIC, "A_TrapShot: missile no. = %d, front offset = %d, vertical angle = %d, z offset = %d\n",
- type, frontoff, (INT16)(locvar2 & 65535), vertoff);
-
- missile = P_SpawnMobj(x, y, z, type);
-
- if (actor->eflags & MFE_VERTICALFLIP)
- missile->flags2 |= MF2_OBJECTFLIP;
-
- missile->destscale = actor->scale;
- P_SetScale(missile, actor->scale);
-
- if (missile->info->seesound)
- S_StartSound(missile, missile->info->seesound);
-
- P_SetTarget(&missile->target, actor);
- missile->angle = actor->angle;
-
- speed = FixedMul(missile->info->speed, missile->scale);
-
- if (oldstyle)
- {
- missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed);
- missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed);
- // The below line basically mimics P_SpawnXYZMissile's momz calculation.
- missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed);
- P_CheckMissileSpawn(missile);
- }
- else
- {
- angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT);
- if (actor->eflags & MFE_VERTICALFLIP)
- vertang = InvAngle(vertang); // flip firing angle
- missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed));
- missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed));
- missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed);
- }
-}
-
-// Function: A_VileTarget
-//
-// Description: Spawns an object directly on the target, and sets this object as the actor's tracer.
-// Originally used by Archviles to summon a pillar of hellfire, hence the name.
-//
-// var1 = mobj to spawn
-// var2 = If 0, target only the actor's target. Else, target every player, period.
-//
-void A_VileTarget(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *fog;
- mobjtype_t fogtype;
- INT32 i;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_VileTarget", actor))
- return;
-#endif
-
- if (!actor->target)
- return;
-
- A_FaceTarget(actor);
-
- // Determine object to spawn
- if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
- fogtype = MT_CYBRAKDEMON_TARGET_RETICULE;
- else
- fogtype = (mobjtype_t)locvar1;
-
- if (!locvar2)
- {
- fog = P_SpawnMobj(actor->target->x,
- actor->target->y,
- actor->target->z + ((actor->target->eflags & MFE_VERTICALFLIP) ? actor->target->height - mobjinfo[fogtype].height : 0),
- fogtype);
- if (actor->target->eflags & MFE_VERTICALFLIP)
- {
- fog->eflags |= MFE_VERTICALFLIP;
- fog->flags2 |= MF2_OBJECTFLIP;
- }
- fog->destscale = actor->target->scale;
- P_SetScale(fog, fog->destscale);
-
- P_SetTarget(&actor->tracer, fog);
- P_SetTarget(&fog->target, actor);
- P_SetTarget(&fog->tracer, actor->target);
- A_VileFire(fog);
- }
- else
- {
- // Our "Archvile" here is actually Oprah. "YOU GET A TARGET! YOU GET A TARGET! YOU ALL GET A TARGET!"
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator)
- continue;
-
- if (!players[i].mo)
- continue;
-
- if (!players[i].mo->health)
- continue;
-
- fog = P_SpawnMobj(players[i].mo->x,
- players[i].mo->y,
- players[i].mo->z + ((players[i].mo->eflags & MFE_VERTICALFLIP) ? players[i].mo->height - mobjinfo[fogtype].height : 0),
- fogtype);
- if (players[i].mo->eflags & MFE_VERTICALFLIP)
- {
- fog->eflags |= MFE_VERTICALFLIP;
- fog->flags2 |= MF2_OBJECTFLIP;
- }
- fog->destscale = players[i].mo->scale;
- P_SetScale(fog, fog->destscale);
-
- if (players[i].mo == actor->target) // We only care to track the fog targeting who we REALLY hate right now
- P_SetTarget(&actor->tracer, fog);
- P_SetTarget(&fog->target, actor);
- P_SetTarget(&fog->tracer, players[i].mo);
- A_VileFire(fog);
- }
- }
-}
-
-// Function: A_VileAttack
-//
-// Description: Instantly hurts the actor's target, if it's in the actor's line of sight.
-// Originally used by Archviles to cause explosions where their hellfire pillars were, hence the name.
-//
-// var1 = sound to play
-// var2:
-// Lower 16 bits = optional explosion object
-// Upper 16 bits = If 0, attack only the actor's target. Else, attack all the players. All of them.
-//
-void A_VileAttack(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- sfxenum_t soundtoplay;
- mobjtype_t explosionType = MT_NULL;
- mobj_t *fire;
- INT32 i;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_VileAttack", actor))
- return;
-#endif
-
- if (!actor->target)
- return;
-
- A_FaceTarget(actor);
-
- if (locvar1 <= 0 || locvar1 >= NUMSFX)
- soundtoplay = sfx_brakrx;
- else
- soundtoplay = (sfxenum_t)locvar1;
-
- if ((locvar2 & 0xFFFF) > 0 && (locvar2 & 0xFFFF) <= NUMMOBJTYPES)
- {
- explosionType = (mobjtype_t)(locvar2 & 0xFFFF);
- }
-
- if (!(locvar2 & 0xFFFF0000)) {
- if (!P_CheckSight(actor, actor->target))
- return;
-
- S_StartSound(actor, soundtoplay);
- P_DamageMobj(actor->target, actor, actor, 1, 0);
- //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
- actor->target->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(actor->target); // How we're doing it
- if (explosionType != MT_NULL)
- {
- P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, explosionType);
- }
-
- // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
- fire = actor->tracer;
-
- if (!fire)
- return;
-
- // move the fire between the vile and the player
- //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
- //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
- P_TeleportMove(fire,
- actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
- actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
- fire->z);
- P_RadiusAttack(fire, actor, 70*FRACUNIT, 0);
- }
- else
- {
- // Oprahvile strikes again, but this time, she brings HOT PAIN
- for (i = 0; i < MAXPLAYERS; i++)
- {
- if (!playeringame[i] || players[i].spectator)
- continue;
-
- if (!players[i].mo)
- continue;
-
- if (!players[i].mo->health)
- continue;
-
- if (!P_CheckSight(actor, players[i].mo))
- continue;
-
- S_StartSound(actor, soundtoplay);
- P_DamageMobj(players[i].mo, actor, actor, 1, 0);
- //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
- players[i].mo->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(players[i].mo); // How we're doing it
- if (explosionType != MT_NULL)
- {
- P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z, explosionType);
- }
-
- // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
- // However, it ONLY applies to the actor's target. Nobody else matters!
- if (actor->target != players[i].mo)
- continue;
-
- fire = actor->tracer;
-
- if (!fire)
- continue;
-
- // move the fire between the vile and the player
- //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
- //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
- P_TeleportMove(fire,
- actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
- actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
- fire->z);
- P_RadiusAttack(fire, actor, 70*FRACUNIT, 0);
- }
- }
-
-}
-
-// Function: A_VileFire
-//
-// Description: Kind of like A_CapeChase; keeps this object in front of its tracer, unless its target can't see it.
-// Originally used by Archviles to keep their hellfire pillars on top of the player, hence the name (although it was just "A_Fire" there; added "Vile" to make it more specific).
-// Added some functionality to optionally draw a line directly to the enemy doing the targetting. Y'know, to hammer things in a bit.
-//
-// var1 = sound to play
-// var2:
-// Lower 16 bits = mobj to spawn (0 doesn't spawn a line at all)
-// Upper 16 bits = # to spawn (default is 8)
-//
-void A_VileFire(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- mobj_t *dest;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_VileFire", actor))
- return;
-#endif
-
- dest = actor->tracer;
- if (!dest)
- return;
-
- // don't move it if the vile lost sight
- if (!P_CheckSight(actor->target, dest))
- return;
-
- // keep to same scale and gravity as tracer ALWAYS
- actor->destscale = dest->scale;
- P_SetScale(actor, actor->destscale);
- if (dest->eflags & MFE_VERTICALFLIP)
- {
- actor->eflags |= MFE_VERTICALFLIP;
- actor->flags2 |= MF2_OBJECTFLIP;
- }
- else
- {
- actor->eflags &= ~MFE_VERTICALFLIP;
- actor->flags2 &= ~MF2_OBJECTFLIP;
- }
-
- P_UnsetThingPosition(actor);
- actor->x = dest->x + P_ReturnThrustX(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
- actor->y = dest->y + P_ReturnThrustY(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
- actor->z = dest->z + ((actor->eflags & MFE_VERTICALFLIP) ? dest->height-actor->height : 0);
- P_SetThingPosition(actor);
-
- // Play sound, if one's specified
- if (locvar1 > 0 && locvar1 < NUMSFX)
- S_StartSound(actor, (sfxenum_t)locvar1);
-
- // Now draw the line to the actor's target
- if (locvar2 & 0xFFFF)
- {
- mobjtype_t lineMobj;
- UINT16 numLineMobjs;
- fixed_t distX;
- fixed_t distY;
- fixed_t distZ;
- UINT16 i;
-
- lineMobj = (mobjtype_t)(locvar2 & 0xFFFF);
- numLineMobjs = (UINT16)(locvar2 >> 16);
- if (numLineMobjs == 0) {
- numLineMobjs = 8;
- }
-
- // Get distance for each step
- distX = (actor->target->x - actor->x) / numLineMobjs;
- distY = (actor->target->y - actor->y) / numLineMobjs;
- distZ = ((actor->target->z + FixedMul(actor->target->height/2, actor->target->scale)) - (actor->z + FixedMul(actor->height/2, actor->scale))) / numLineMobjs;
-
- for (i = 1; i <= numLineMobjs; i++)
- {
- P_SpawnMobj(actor->x + (distX * i), actor->y + (distY * i), actor->z + (distZ * i) + FixedMul(actor->height/2, actor->scale), lineMobj);
- }
- }
-}
-
-// Function: A_BrakChase
-//
-// Description: Chase after your target, but speed and attack are tied to health.
-//
-// Every time this is called, generate a random number from a 1/4 to 3/4 of mobj's spawn health.
-// If health is above that value, use missilestate to attack.
-// If health is at or below that value, use meleestate to attack (default to missile state if not available).
-//
-// Likewise, state will linearly speed up as health goes down.
-// Upper bound will be the frame's normal length.
-// Lower bound defaults to 1 tic (technically 0, but we round up), unless a lower bound is specified in var1.
-//
-// var1 = lower-bound of frame length, in tics
-// var2 = optional sound to play
-//
-void A_BrakChase(mobj_t *actor)
-{
- INT32 delta;
- INT32 lowerbound;
- INT32 newtics;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BrakChase", actor))
- return;
-#endif
-
- // Set new tics NOW, in case the state changes while we're doing this and we try applying this to the painstate or something silly
- if (actor->tics > 1 && locvar1 < actor->tics) // Not much point, otherwise
- {
- if (locvar1 < 0)
- lowerbound = 0;
- else
- lowerbound = locvar1;
-
- newtics = (((actor->tics - lowerbound) * actor->health) / actor->info->spawnhealth) + lowerbound;
- if (newtics < 1)
- newtics = 1;
-
- actor->tics = newtics;
- }
-
- if (actor->reactiontime)
- {
- actor->reactiontime--;
- if (actor->reactiontime == 0 && actor->type == MT_CYBRAKDEMON)
- S_StartSound(0, sfx_bewar1 + P_RandomKey(4));
- }
-
- // modify target threshold
- if (actor->threshold)
- {
- if (!actor->target || actor->target->health <= 0)
- actor->threshold = 0;
- else
- actor->threshold--;
- }
-
- // turn towards movement direction if not there yet
- if (actor->movedir < NUMDIRS)
- {
- actor->angle &= (7<<29);
- delta = actor->angle - (actor->movedir << 29);
-
- if (delta > 0)
- actor->angle -= ANGLE_45;
- else if (delta < 0)
- actor->angle += ANGLE_45;
- }
-
- if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
- {
- // look for a new target
- if (P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- P_SetMobjStateNF(actor, actor->info->spawnstate);
- return;
- }
-
- // do not attack twice in a row
- if (actor->flags2 & MF2_JUSTATTACKED)
- {
- actor->flags2 &= ~MF2_JUSTATTACKED;
- P_NewChaseDir(actor);
- return;
- }
-
- // Check if we can attack
- if (P_CheckMissileRange(actor) && !actor->movecount)
- {
- // Check if we should use "melee" attack first. (Yes, this still runs outside of melee range. Quiet, you.)
- if (actor->info->meleestate
- && actor->health <= P_RandomRange(actor->info->spawnhealth/4, (actor->info->spawnhealth * 3)/4)) // Guaranteed true if <= 1/4 health, guaranteed false if > 3/4 health
- {
- if (actor->info->attacksound)
- S_StartAttackSound(actor, actor->info->attacksound);
-
- P_SetMobjState(actor, actor->info->meleestate);
- actor->flags2 |= MF2_JUSTATTACKED;
- return;
- }
- // Else, check for missile attack.
- else if (actor->info->missilestate)
- {
- P_SetMobjState(actor, actor->info->missilestate);
- actor->flags2 |= MF2_JUSTATTACKED;
- return;
- }
- }
-
- // possibly choose another target
- if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
- && P_LookForPlayers(actor, true, false, 0))
- return; // got a new target
-
- // chase towards player
- if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
- P_NewChaseDir(actor);
-
- // Optionally play a sound effect
- if (locvar2 > 0 && locvar2 < NUMSFX)
- S_StartSound(actor, (sfxenum_t)locvar2);
-
- // make active sound
- if (actor->type != MT_CYBRAKDEMON && actor->info->activesound && P_RandomChance(3*FRACUNIT/256))
- {
- S_StartSound(actor, actor->info->activesound);
- }
-}
-
-// Function: A_BrakFireShot
-//
-// Description: Shoot an object at your target, offset to match where Brak's gun is.
-// Also, sets Brak's reaction time; behaves normally otherwise.
-//
-// var1 = object # to shoot
-// var2 = unused
-//
-void A_BrakFireShot(mobj_t *actor)
-{
- fixed_t x, y, z;
- INT32 locvar1 = var1;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BrakFireShot", actor))
- return;
-#endif
- if (!actor->target)
- return;
-
- A_FaceTarget(actor);
-
- x = actor->x
- + P_ReturnThrustX(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
- + P_ReturnThrustX(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
- y = actor->y
- + P_ReturnThrustY(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
- + P_ReturnThrustY(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
- if (actor->eflags & MFE_VERTICALFLIP)
- z = actor->z + actor->height - FixedMul(144*FRACUNIT, actor->scale);
- else
- z = actor->z + FixedMul(144*FRACUNIT, actor->scale);
-
- P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
-
- if (!(actor->flags & MF_BOSS))
- {
- if (ultimatemode)
- actor->reactiontime = actor->info->reactiontime*TICRATE;
- else
- actor->reactiontime = actor->info->reactiontime*TICRATE*2;
- }
-}
-
-// Function: A_BrakLobShot
-//
-// Description: Lobs an object at the floor about a third of the way toward your target.
-// Implication is it'll bounce the rest of the way.
-// (You can also just aim straight at the target, but whatever)
-// Formula grabbed from http://en.wikipedia.org/wiki/Trajectory_of_a_projectile#Angle_required_to_hit_coordinate_.28x.2Cy.29
-//
-// var1 = object # to lob
-// var2:
-// Lower 16 bits: height offset to shoot from, from the actor's bottom (none that "airtime" malarky)
-// Upper 16 bits: if 0, aim 1/3 of the way. Else, aim directly at target.
-//
-
-void A_BrakLobShot(mobj_t *actor)
-{
- fixed_t v; // Velocity to shoot object
- fixed_t a1, a2, aToUse; // Velocity squared
- fixed_t g; // Gravity
- fixed_t x; // Horizontal difference
- INT32 x_int; // x! But in integer form!
- fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
- INT32 y_int; // y! But in integer form!
- INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
- fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
- angle_t theta; // Angle of attack
- mobjtype_t typeOfShot;
- mobj_t *shot; // Object to shoot
- fixed_t newTargetX; // If not aiming directly
- fixed_t newTargetY; // If not aiming directly
- INT32 locvar1 = var1;
- INT32 locvar2 = var2 & 0x0000FFFF;
- INT32 aimDirect = var2 & 0xFFFF0000;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_BrakLobShot", actor))
- return;
-#endif
-
- if (!actor->target)
- return; // Don't even bother if we've got nothing to aim at.
-
- // Look up actor's current gravity situation
- if (actor->subsector->sector->gravity)
- g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
- else
- g = gravity;
-
- // Look up distance between actor and its target
- x = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
- if (!aimDirect)
- {
- // Distance should actually be a third of the way over
- x = FixedDiv(x, 3<x + P_ReturnThrustX(actor, actor->angle, x);
- newTargetY = actor->y + P_ReturnThrustY(actor, actor->angle, x);
- x = P_AproxDistance(newTargetX - actor->x, newTargetY - actor->y);
- // Look up height difference between actor and the ground 1/3 of the way to its target
- y = P_FloorzAtPos(newTargetX, newTargetY, actor->target->z, actor->target->height) - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
- }
- else
- {
- // Look up height difference between actor and its target
- y = actor->target->z - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
- }
-
- // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
- x_int = x>>FRACBITS;
- y_int = y>>FRACBITS;
- intHypotenuse = (x_int*x_int) + (y_int*y_int);
- fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
-
- // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
- a1 = FixedMul(g,y+fixedHypotenuse);
- a2 = FixedMul(g,y-fixedHypotenuse);
-
- // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
- if (a1 < 0 || a2 < 0)
- {
- if (a1 < 0 && a2 < 0)
- {
- //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
- return;
- }
- // Just find which one's NOT negative, and use that
- aToUse = max(a1,a2);
- }
- else
- {
- // Both are positive; use whichever's smaller so it can decay faster
- aToUse = min(a1,a2);
- }
- v = FixedSqrt(aToUse);
- // Okay, so we know the velocity. Let's actually find theta.
- // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
- //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
- theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
-
- // Okay, complicated math done. Let's fire our object already, sheesh.
- A_FaceTarget(actor);
- if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
- typeOfShot = MT_CANNONBALL;
- else typeOfShot = (mobjtype_t)locvar1;
- shot = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(locvar2*FRACUNIT, actor->scale), typeOfShot);
- if (shot->info->seesound)
- S_StartSound(shot, shot->info->seesound);
- P_SetTarget(&shot->target, actor); // where it came from
-
- shot->angle = actor->angle;
-
- // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
- shot->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(shot->angle >> ANGLETOFINESHIFT));
- shot->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINESINE(shot->angle >> ANGLETOFINESHIFT));
- // Then the vertical axis. No angle-correction needed here.
- shot->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
- // I hope that's all that's needed, ugh
-}
-
-// Function: A_NapalmScatter
-//
-// Description: Scatters a specific number of projectiles around in a circle.
-// Intended for use with objects that are affected by gravity; would be kind of silly otherwise.
-//
-// var1:
-// Lower 16 bits: object # to lob (TODO: come up with a default)
-// Upper 16 bits: Number to lob (default 8)
-// var2:
-// Lower 16 bits: distance to toss them (No default - 0 does just that - but negatives will revert to 128)
-// Upper 16 bits: airtime in tics (default 16)
-//
-void A_NapalmScatter(mobj_t *actor)
-{
- mobjtype_t typeOfShot = var1 & 0x0000FFFF; // Type
- INT32 numToShoot = (var1 & 0xFFFF0000) >> 16; // How many
- fixed_t distance = (var2 & 0x0000FFFF) << FRACBITS; // How far
- fixed_t airtime = var2 & 0xFFFF0000; // How long until impact (assuming no obstacles)
- fixed_t vx; // Horizontal momentum
- fixed_t vy; // Vertical momentum
- fixed_t g; // Gravity
- INT32 i; // for-loop cursor
- mobj_t *mo; // each and every spawned napalm burst
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_NapalmScatter", actor))
- return;
-#endif
-
- // Some quick sanity-checking
- if (typeOfShot >= NUMMOBJTYPES) // I'd add a <0 check, too, but 0x0000FFFF isn't negative in this case
- typeOfShot = MT_NULL;
- if (numToShoot <= 0) // Presumably you forgot to set var1 up; else, why are you calling this to shoot nothing?
- numToShoot = 8;
- else if (numToShoot > 8192) // If you seriously need this many objects spawned, stop and ask yourself "Why am I doing this?"
- numToShoot = 8192;
- if (distance < 0) // Presumably you thought this was an unsigned integer, you naive fool
- distance = 32767<subsector->sector->gravity)
- g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
- else
- g = gravity;
-
- // vy = (g*(airtime-1))/2
- vy = FixedMul(g,(airtime-(1<>1;
- // vx = distance/airtime
- vx = FixedDiv(distance, airtime);
-
- for (i = 0; ix, actor->y, actor->z, typeOfShot);
- P_SetTarget(&mo->target, actor->target); // Transfer target so Brak doesn't hit himself like an idiot
-
- mo->angle = fa << ANGLETOFINESHIFT;
- mo->momx = FixedMul(FINECOSINE(fa),vx);
- mo->momy = FixedMul(FINESINE(fa),vx);
- mo->momz = vy;
- }
-}
-
-// Function: A_SpawnFreshCopy
-//
-// Description: Spawns a copy of the mobj. x, y, z, angle, scale, target and tracer carry over; everything else starts anew.
-// Mostly writing this because I want to do multiple actions to pass these along in a single frame instead of several.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_SpawnFreshCopy(mobj_t *actor)
-{
- mobj_t *newObject;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SpawnFreshCopy", actor))
- return;
-#endif
-
- newObject = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->type);
- newObject->flags2 = actor->flags2 & MF2_AMBUSH;
- newObject->angle = actor->angle;
- newObject->color = actor->color;
- P_SetTarget(&newObject->target, actor->target);
- P_SetTarget(&newObject->tracer, actor->tracer);
-
- if (newObject->info->seesound)
- S_StartSound(newObject, newObject->info->seesound);
-}
-
-// Internal Flicky spawning function.
-mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers)
-{
- mobj_t *flicky;
-
- if (!flickytype)
- {
- if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
- return NULL;
- else
- {
- INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies);
- flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
- }
- }
-
- flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype);
- flicky->angle = actor->angle;
-
- if (flickytype == MT_SEED)
- flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2;
-
- if (actor->eflags & MFE_UNDERWATER)
- momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
-
- P_SetObjectMomZ(flicky, momz, false);
- flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1);
- flicky->fuse = P_RandomRange(595, 700); // originally 300, 350
- flicky->threshold = 0;
-
- if (lookforplayers)
- P_LookForPlayers(flicky, true, false, 0);
-
- return flicky;
-}
-
-// Function: A_FlickySpawn
-//
-// Description: Flicky spawning function.
-//
-// var1:
-// lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
-// upper 16 bits: if 0, no sound is played. Else, A_Scream is called.
-// var2 = upwards thrust for spawned flicky. If zero, default value is provided.
-//
-void A_FlickySpawn(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickySpawn", actor))
- return;
-#endif
-
- if (locvar1 >> 16) {
- A_Scream(actor); // A shortcut for the truly lazy.
- locvar1 &= 65535;
- }
-
- P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true);
-}
-
-// Internal Flicky color setting
-void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo)
-{
- UINT8 flickycolors[] = {
- SKINCOLOR_RED,
- SKINCOLOR_CYAN,
- SKINCOLOR_BLUE,
- SKINCOLOR_VAPOR,
- SKINCOLOR_PURPLE,
- SKINCOLOR_BUBBLEGUM,
- SKINCOLOR_NEON,
- SKINCOLOR_BLACK,
- SKINCOLOR_BEIGE,
- SKINCOLOR_LAVENDER,
- SKINCOLOR_RUBY,
- SKINCOLOR_SALMON,
- SKINCOLOR_SUNSET,
- SKINCOLOR_ORANGE,
- SKINCOLOR_YELLOW,
- };
-
- if (extrainfo == 0)
- // until we can customize flicky colors by level header, just stick to SRB2's defaults
- actor->color = flickycolors[P_RandomKey(2)]; //flickycolors[P_RandomKey(sizeof(flickycolors))];
- else
- actor->color = flickycolors[min(extrainfo-1, 14)]; // sizeof(flickycolors)-1
-}
-
-// Function: A_FlickyCenter
-//
-// Description: Place flickies in-level.
-//
-// var1:
-// Lower 16 bits = if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
-// Bits 17-20 = Flicky color, up to 15. Applies to fish.
-// Bit 21 = Flag MF_SLIDEME (see below)
-// Bit 22 = Flag MF_GRENADEBOUNCE (see below)
-// Bit 23 = Flag MF_NOCLIPTHING (see below)
-//
-// If actor is placed from a spawnpoint (map Thing), the Thing's properties take precedence.
-//
-// var2 = maximum default distance away from spawn the flickies are allowed to travel. If angle != 0, then that's the radius.
-//
-// If MTF_EXTRA (MF_SLIDEME): is flagged, Flickies move aimlessly. Else, orbit around the target.
-// If MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE): Flickies stand in-place without gravity (unless they hop, then gravity is applied.)
-// If MTF_AMBUSH (MF_NOCLIPTHING): is flagged, Flickies hop.
-//
-void A_FlickyCenter(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- UINT16 flickytype = (locvar1 & 0xFFFF);
- UINT8 flickycolor = ((locvar1 >> 16) & 0xFF);
- UINT8 flickyflags = ((locvar1 >> 20) & 0xF);
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickyCenter", actor))
- return;
-#endif
-
- if (!actor->tracer)
- {
- mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false);
- P_SetTarget(&flicky->target, actor);
- P_SetTarget(&actor->tracer, flicky);
-
- if (actor->spawnpoint)
- {
- actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
- actor->flags |= (
- ((actor->spawnpoint->options & MTF_EXTRA) ? MF_SLIDEME : 0)
- | ((actor->spawnpoint->options & MTF_OBJECTSPECIAL) ? MF_GRENADEBOUNCE : 0)
- | ((actor->spawnpoint->options & MTF_AMBUSH) ? MF_NOCLIPTHING : 0)
- );
- actor->extravalue1 = actor->spawnpoint->angle ? abs(actor->spawnpoint->angle) * FRACUNIT
- : locvar2 ? abs(locvar2) : 384 * FRACUNIT;
- actor->extravalue2 = actor->spawnpoint->extrainfo;
- actor->friction = actor->spawnpoint->x*FRACUNIT;
- actor->movefactor = actor->spawnpoint->y*FRACUNIT;
- actor->watertop = actor->spawnpoint->z*FRACUNIT;
- }
- else
- {
- actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
- actor->flags |= (
- ((flickyflags & 1) ? MF_SLIDEME : 0)
- | ((flickyflags & 2) ? MF_GRENADEBOUNCE : 0)
- | ((flickyflags & 4) ? MF_NOCLIPTHING : 0)
- );
- actor->extravalue1 = abs(locvar2);
- actor->extravalue2 = flickycolor;
- actor->friction = actor->x;
- actor->movefactor = actor->y;
- actor->watertop = actor->z;
- locvar1 = flickytype;
- }
-
- if (actor->flags & MF_GRENADEBOUNCE) // in-place
- actor->tracer->fuse = 0;
- else if (actor->flags & MF_SLIDEME) // aimless
- {
- actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly.
- actor->tracer->angle = P_RandomKey(180)*ANG2;
- }
- else //orbit
- actor->tracer->fuse = FRACUNIT;
-
- if (locvar1 == MT_FLICKY_08)
- P_InternalFlickySetColor(actor->tracer, actor->extravalue2);
-
- actor->extravalue2 = 0;
- }
-
- if (!(actor->flags & MF_SLIDEME) && !(actor->flags & MF_GRENADEBOUNCE))
- {
- fixed_t originx = actor->friction;
- fixed_t originy = actor->movefactor;
- fixed_t originz = actor->watertop;
-
- actor->tracer->fuse = FRACUNIT;
-
- // Impose default home radius if flicky orbits around player
- if (!actor->extravalue1)
- actor->extravalue1 = locvar2 ? abs(locvar2) : 384 * FRACUNIT;
-
- P_LookForPlayers(actor, true, false, actor->extravalue1);
-
- if (actor->target && P_AproxDistance(actor->target->x - originx, actor->target->y - originy) < actor->extravalue1)
- {
- actor->extravalue2 = 1;
- P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z);
- }
- else if(actor->extravalue2)
- {
- actor->extravalue2 = 0;
- P_TeleportMove(actor, originx, originy, originz);
- }
- }
-}
-
-// Internal Flicky bubbling function.
-void P_InternalFlickyBubble(mobj_t *actor)
-{
- if (actor->eflags & MFE_UNDERWATER)
- {
- mobj_t *overlay;
-
- if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer)
- return;
-
- overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
- P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate);
- P_SetTarget(&actor->tracer, overlay);
- P_SetTarget(&overlay->target, actor);
- return;
- }
-
- if (!actor->tracer || P_MobjWasRemoved(actor->tracer))
- return;
-
- P_RemoveMobj(actor->tracer);
- P_SetTarget(&actor->tracer, NULL);
-}
-
-// Function: A_FlickyAim
-//
-// Description: Flicky aiming function.
-//
-// var1 = how far around the target (in angle constants) the flicky should look
-// var2 = distance from target to aim for
-//
-void A_FlickyAim(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- boolean flickyhitwall = false;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickyAim", actor))
- return;
-#endif
-
- if ((actor->momx == actor->momy && actor->momy == 0)
- || (actor->target && P_IsFlickyCenter(actor->target->type)
- && actor->target->extravalue1 && (actor->target->flags & MF_SLIDEME)
- && P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) >= actor->target->extravalue1))
- flickyhitwall = true;
-
- P_InternalFlickyBubble(actor);
- P_InstaThrust(actor, 0, 0);
-
- if (!actor->target)
- {
- P_LookForPlayers(actor, true, false, 0);
- actor->angle = P_RandomKey(36)*ANG10;
- return;
- }
-
- if (actor->fuse > 2*TICRATE)
- {
- angle_t posvar;
- fixed_t chasevar, chasex, chasey;
-
- if (flickyhitwall)
- actor->movedir *= -1;
-
- posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK;
- chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2;
-
- chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar);
- chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar);
-
- if (P_AproxDistance(chasex - actor->x, chasey - actor->y))
- actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey);
- }
- else if (flickyhitwall)
- {
- if (actor->target && P_IsFlickyCenter(actor->target->type))
- actor->angle = R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + P_RandomRange(112, 248) * ANG1;
- else
- actor->angle += P_RandomRange(112, 248)*ANG1;
- actor->threshold = 0;
- }
-}
-
-//Internal Flicky flying function. Also usuable as an underwater swim thrust.
-void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez)
-{
- angle_t vertangle;
-
- flyspeed = FixedMul(flyspeed, actor->scale);
- actor->flags |= MF_NOGRAVITY;
-
- var1 = ANG30;
- var2 = 32*FRACUNIT;
- A_FlickyAim(actor);
-
- chasez *= 8;
- if (!actor->target || !(actor->fuse > 2*TICRATE))
- chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT);
- else
- {
- fixed_t add = actor->target->z + (actor->target->height - actor->height)/2;
- if (add > (actor->ceilingz - 24*actor->scale - actor->height))
- add = actor->ceilingz - 24*actor->scale - actor->height;
- else if (add < (actor->floorz + 24*actor->scale))
- add = actor->floorz + 24*actor->scale;
- chasez += add;
- }
-
- if (!targetdist)
- targetdist = 16*FRACUNIT; //Default!
-
- if (actor->target && abs(chasez - actor->z) > targetdist)
- targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
-
- if (actor->target
- && P_IsFlickyCenter(actor->target->type)
- && (actor->target->flags & MF_SLIDEME))
- vertangle = 0;
- else
- vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK;
-
- P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed));
- actor->momz = FixedMul(FINESINE(vertangle), flyspeed);
-}
-
-// Function: A_FlickyFly
-//
-// Description: Flicky flying function.
-//
-// var1 = how fast to fly
-// var2 = how far ahead the target should be considered
-//
-void A_FlickyFly(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickyFly", actor))
- return;
-#endif
- P_InternalFlickyFly(actor, locvar1, locvar2,
- FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK)
- );
-}
-
-// Function: A_FlickySoar
-//
-// Description: Flicky soaring function - specific to puffin.
-//
-// var1 = how fast to fly
-// var2 = how far ahead the target should be considered
-//
-void A_FlickySoar(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickySoar", actor))
- return;
-#endif
- P_InternalFlickyFly(actor, locvar1, locvar2,
- 2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK)))
- );
-
- if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10)
- actor->frame = 3;
-}
-
-//Function: A_FlickyCoast
-//
-// Description: Flicky swim-coasting function.
-//
-// var1 = speed to change state upon reaching
-// var2 = state to change to upon slowing down
-// the spawnstate of the mobj = state to change to when above water
-//
-void A_FlickyCoast(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickyCoast", actor))
- return;
-#endif
- if (actor->eflags & MFE_UNDERWATER)
- {
- actor->momx = (11*actor->momx)/12;
- actor->momy = (11*actor->momy)/12;
- actor->momz = (11*actor->momz)/12;
-
- if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1)
- P_SetMobjState(actor, locvar2);
-
- return;
- }
-
- actor->flags &= ~MF_NOGRAVITY;
- P_SetMobjState(actor, mobjinfo[actor->type].spawnstate);
-}
-
-// Internal Flicky hopping function.
-void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle)
-{
- if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
- || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
- {
- if (momz)
- {
- if (actor->eflags & MFE_UNDERWATER)
- momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
- P_SetObjectMomZ(actor, momz, false);
- }
- P_InstaThrust(actor, angle, FixedMul(momh, actor->scale));
- }
-}
-
-// Function: A_FlickyHop
-//
-// Description: Flicky hopping function.
-//
-// var1 = vertical thrust
-// var2 = horizontal thrust
-//
-void A_FlickyHop(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickyHop", actor))
- return;
-#endif
- P_InternalFlickyHop(actor, locvar1, locvar2, actor->angle);
-}
-
-// Function: A_FlickyFlounder
-//
-// Description: Flicky floundering function.
-//
-// var1 = intended vertical thrust
-// var2 = intended horizontal thrust
-//
-void A_FlickyFlounder(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- angle_t hopangle;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickyFlounder", actor))
- return;
-#endif
- locvar1 *= (P_RandomKey(2) + 1);
- locvar2 *= (P_RandomKey(2) + 1);
- hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2);
- P_InternalFlickyHop(actor, locvar1, locvar2, hopangle);
-}
-
-// Function: A_FlickyCheck
-//
-// Description: Flicky airtime check function.
-//
-// var1 = state to change to upon touching the floor
-// var2 = state to change to upon falling
-// the meleestate of the mobj = state to change to when underwater
-//
-void A_FlickyCheck(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickyCheck", actor))
- return;
-#endif
- if (actor->target
- && P_IsFlickyCenter(actor->target->type)
- && (actor->target->flags & MF_GRENADEBOUNCE))
- {
- if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping
- {
- actor->momz = 0;
- actor->flags |= MF_NOGRAVITY;
- }
- actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY;
- P_SetMobjState(actor, mobjinfo[actor->type].seestate);
- }
- else if (locvar2 && P_MobjFlip(actor)*actor->momz < 1)
- P_SetMobjState(actor, locvar2);
- else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
- || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
- P_SetMobjState(actor, locvar1);
- else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER))
- P_SetMobjState(actor, mobjinfo[actor->type].meleestate);
- P_InternalFlickyBubble(actor);
-}
-
-// Function: A_FlickyHeightCheck
-//
-// Description: Flicky height check function.
-//
-// var1 = state to change to when falling below height relative to target
-// var2 = height relative to target to change state at
-//
-void A_FlickyHeightCheck(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickyHeightCheck", actor))
- return;
-#endif
- if (actor->target
- && P_IsFlickyCenter(actor->target->type)
- && (actor->target->flags & MF_GRENADEBOUNCE))
- {
- if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping
- {
- actor->momz = 0;
- actor->flags |= MF_NOGRAVITY;
- }
- actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY;
- P_SetMobjState(actor, mobjinfo[actor->type].seestate);
- }
- else if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1
- && ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2)
- || (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz)))
- P_SetMobjState(actor, locvar1);
- P_InternalFlickyBubble(actor);
-}
-
-// Function: A_FlickyFlutter
-//
-// Description: Flicky fluttering function - specific to chicken.
-//
-// var1 = state to change to upon touching the floor
-// var2 = state to change to upon falling
-// the meleestate of the mobj = state to change to when underwater
-//
-void A_FlickyFlutter(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlickyFlutter", actor))
- return;
-#endif
- var1 = locvar1;
- var2 = locvar2;
- A_FlickyCheck(actor);
-
- var1 = ANG30;
- var2 = 32*FRACUNIT;
- A_FlickyAim(actor);
-
- P_InstaThrust(actor, actor->angle, 2*actor->scale);
- if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2)
- actor->momz = -P_MobjFlip(actor)*actor->scale/2;
-}
-
-#undef FLICKYHITWALL
-
-// Function: A_FlameParticle
-//
-// Description: Creates the mobj's painchance at a random position around the object's radius.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_FlameParticle(mobj_t *actor)
-{
- mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance);
- fixed_t rad, hei;
- mobj_t *particle;
- //INT32 locvar1 = var1;
- //INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_FlameParticle", actor))
- return;
-#endif
-
- if (!type)
- return;
-
- rad = actor->radius>>FRACBITS;
- hei = actor->height>>FRACBITS;
- particle = P_SpawnMobjFromMobj(actor,
- P_RandomRange(rad, -rad)<frame = actor->frame;
-
- if (!(locvar1 & 1))
- {
- fade->fuse = 15;
- fade->flags2 |= MF2_BOSSNOTRAP;
- }
- else
- fade->fuse = 20;
-
- if (!(locvar1 & 2))
- P_SetTarget(&actor->tracer, fade);
-}
-
-// Function: A_Boss5Jump
-//
-// Description: Makes an object jump in an arc to land on their tracer precicely.
-// Adapted from A_BrakLobShot, see there for explanation.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_Boss5Jump(mobj_t *actor)
-{
- fixed_t v; // Velocity to jump at
- fixed_t a1, a2, aToUse; // Velocity squared
- fixed_t g; // Gravity
- fixed_t x; // Horizontal difference
- INT32 x_int; // x! But in integer form!
- fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
- INT32 y_int; // y! But in integer form!
- INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
- fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
- angle_t theta; // Angle of attack
- // INT32 locvar1 = var1;
- // INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_Boss5Jump", actor))
- return;
-#endif
-
- if (!actor->tracer)
- return; // Don't even bother if we've got nothing to aim at.
-
- // Look up actor's current gravity situation
- if (actor->subsector->sector->gravity)
- g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
- else
- g = gravity;
-
- // Look up distance between actor and its tracer
- x = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
- // Look up height difference between actor and its tracer
- y = actor->tracer->z - actor->z;
-
- // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
- x_int = x>>FRACBITS;
- y_int = y>>FRACBITS;
- intHypotenuse = (x_int*x_int) + (y_int*y_int);
- fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
-
- // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
- a1 = FixedMul(g,y+fixedHypotenuse);
- a2 = FixedMul(g,y-fixedHypotenuse);
-
- // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
- if (a1 < 0 || a2 < 0)
- {
- if (a1 < 0 && a2 < 0)
- {
- //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
- return;
- }
- // Just find which one's NOT negative, and use that
- aToUse = max(a1,a2);
- }
- else
- {
- // Both are positive; use whichever's smaller so it can decay faster
- aToUse = min(a1,a2);
- }
- v = FixedSqrt(aToUse);
- // Okay, so we know the velocity. Let's actually find theta.
- // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
- //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
- theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
-
- // Okay, complicated math done. Let's make this object jump already.
- A_FaceTracer(actor);
-
- if (actor->eflags & MFE_VERTICALFLIP)
- actor->z--;
- else
- actor->z++;
-
- // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
- fixedHypotenuse = FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)); // variable reuse
- actor->momx = FixedMul(fixedHypotenuse, FINECOSINE(actor->angle >> ANGLETOFINESHIFT));
- actor->momy = FixedMul(fixedHypotenuse, FINESINE(actor->angle >> ANGLETOFINESHIFT));
- // Then the vertical axis. No angle-correction needed here.
- actor->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
- // I hope that's all that's needed, ugh
-}
-
-// Function: A_LightBeamReset
-// Description: Resets momentum and position for DSZ's projecting light beams
-//
-// var1 = unused
-// var2 = unused
-//
-void A_LightBeamReset(mobj_t *actor)
-{
- // INT32 locvar1 = var1;
- // INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_LightBeamReset", actor))
- return;
-#endif
-
- actor->destscale = FRACUNIT + P_SignedRandom()*FRACUNIT/256;
- P_SetScale(actor, actor->destscale);
-
- if (!actor->spawnpoint)
- return; // this can't work properly welp
-
- actor->momx = -(P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128;
- actor->momy = (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128;
- actor->momz = (P_SignedRandom()*FRACUNIT)/128;
-
- P_TeleportMove(actor,
- actor->spawnpoint->x*FRACUNIT - (P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2,
- actor->spawnpoint->y*FRACUNIT + (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2,
- actor->spawnpoint->z*FRACUNIT + (P_SignedRandom()*FRACUNIT)/2);
-}
-
-// Function: A_MineExplode
-// Description: Handles the explosion of a DSZ mine.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_MineExplode(mobj_t *actor)
-{
- // INT32 locvar1 = var1;
- // INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MineExplode", actor))
- return;
-#endif
-
- A_Scream(actor);
- actor->flags = MF_NOGRAVITY|MF_NOCLIP;
-
- quake.epicenter = NULL;
- quake.radius = 512*FRACUNIT;
- quake.intensity = 8*FRACUNIT;
- quake.time = TICRATE/3;
-
- P_RadiusAttack(actor, actor->tracer, 192*FRACUNIT, DMG_CANHURTSELF);
- P_MobjCheckWater(actor);
-
- {
-#define dist 64
- UINT8 i;
- mobjtype_t type = ((actor->eflags & MFE_UNDERWATER) ? MT_UWEXPLODE : MT_BOSSEXPLODE);
- S_StartSound(actor, ((actor->eflags & MFE_UNDERWATER) ? sfx_s3k57 : sfx_s3k4e));
- P_SpawnMobj(actor->x, actor->y, actor->z, type);
- for (i = 0; i < 16; i++)
- {
- mobj_t *b = P_SpawnMobj(actor->x+P_RandomRange(-dist, dist)*FRACUNIT,
- actor->y+P_RandomRange(-dist, dist)*FRACUNIT,
- actor->z+P_RandomRange(((actor->eflags & MFE_UNDERWATER) ? -dist : 0), dist)*FRACUNIT,
- type);
- fixed_t dx = b->x - actor->x, dy = b->y - actor->y, dz = b->z - actor->z;
- fixed_t dm = P_AproxDistance(dz, P_AproxDistance(dy, dx));
- b->momx = FixedDiv(dx, dm)*3;
- b->momy = FixedDiv(dy, dm)*3;
- b->momz = FixedDiv(dz, dm)*3;
- if ((actor->watertop == INT32_MAX) || (b->z + b->height > actor->watertop))
- b->flags &= ~MF_NOGRAVITY;
- }
-#undef dist
-
- if (actor->watertop != INT32_MAX)
- P_SpawnMobj(actor->x, actor->y, actor->watertop, MT_SPLISH);
- }
-}
-
-// Function: A_MineRange
-// Description: If the target gets too close, change the state to meleestate.
-//
-// var1 = Distance to alert at
-// var2 = unused
-//
-void A_MineRange(mobj_t *actor)
-{
- fixed_t dm;
- INT32 locvar1 = var1;
- // INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MineRange", actor))
- return;
-#endif
-
- if (!actor->target)
- return;
-
- dm = P_AproxDistance(actor->z - actor->target->z, P_AproxDistance(actor->y - actor->target->y, actor->x - actor->target->x));
- if ((dm>>FRACBITS) < locvar1)
- P_SetMobjState(actor, actor->info->meleestate);
-}
-
-// Function: A_ConnectToGround
-// Description: Create a palm tree trunk/mine chain.
-//
-// var1 = Object type to connect to ground
-// var2 = Object type to place on ground
-//
-void A_ConnectToGround(mobj_t *actor)
-{
- mobj_t *work;
- fixed_t workz;
- fixed_t workh;
- SINT8 dir;
- angle_t ang;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ConnectToGround", actor))
- return;
-#endif
-
- if (actor->subsector->sector->ffloors)
- P_AdjustMobjFloorZ_FFloors(actor, actor->subsector->sector, 2);
-
- if (actor->flags2 & MF2_OBJECTFLIP)
- {
- workz = actor->ceilingz - (actor->z + actor->height);
- dir = -1;
- }
- else
- {
- workz = actor->floorz - actor->z;
- dir = 1;
- }
-
- if (locvar2)
- {
- workh = FixedMul(mobjinfo[locvar2].height, actor->scale);
- if (actor->flags2 & MF2_OBJECTFLIP)
- workz -= workh;
- work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar2);
- workz += dir*workh;
- }
-
- if (!locvar1)
- return;
-
- if (!(workh = FixedMul(mobjinfo[locvar1].height, actor->scale)))
- return;
-
- if (actor->flags2 & MF2_OBJECTFLIP)
- workz -= workh;
-
- ang = actor->angle + ANGLE_45;
- while (dir*workz < 0)
- {
- work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar1);
- if (work)
- work->angle = ang;
- ang += ANGLE_90;
- workz += dir*workh;
- }
-
- if (workz != 0)
- actor->z += workz;
-}
-
-// Function: A_SpawnParticleRelative
-//
-// Description: Spawns a particle effect relative to the location of the actor
-//
-// var1:
-// var1 >> 16 = x
-// var1 & 65535 = y
-// var2:
-// var2 >> 16 = z
-// var2 & 65535 = state
-//
-void A_SpawnParticleRelative(mobj_t *actor)
-{
- INT16 x, y, z; // Want to be sure we can use negative values
- statenum_t state;
- mobj_t *mo;
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_SpawnParticleRelative", actor))
- return;
-#endif
-
- CONS_Debug(DBG_GAMELOGIC, "A_SpawnParticleRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
-
- x = (INT16)(locvar1>>16);
- y = (INT16)(locvar1&65535);
- z = (INT16)(locvar2>>16);
- state = (statenum_t)(locvar2&65535);
-
- // Spawn objects correctly in reverse gravity.
- // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame
- mo = P_SpawnMobj(actor->x + FixedMul(x<scale),
- actor->y + FixedMul(y<scale),
- (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[MT_PARTICLE].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), MT_PARTICLE);
-
- // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
- mo->angle = actor->angle;
-
- if (actor->eflags & MFE_VERTICALFLIP)
- mo->flags2 |= MF2_OBJECTFLIP;
-
- P_SetMobjState(mo, state);
-}
-
-// Function: A_MultiShotDist
-//
-// Description: Spawns multiple shots based on player proximity
-//
-// var1 = same as A_MultiShot
-// var2 = same as A_MultiShot
-//
-void A_MultiShotDist(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_MultiShotDist", actor))
- return;
-#endif
-
- {
- UINT8 i;
- // Quick! Look through players!
- // Don't spawn dust unless a player is relatively close by (var1).
- for (i = 0; i < MAXPLAYERS; ++i)
- if (playeringame[i] && players[i].mo
- && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (1600<> 16 = mobjtype of child
-// var2 & 65535 = vertical momentum
-// var2:
-// var2 >> 16 = forward offset
-// var2 & 65535 = vertical offset
-//
-void A_WhoCaresIfYourSonIsABee(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
- fixed_t foffsetx;
- fixed_t foffsety;
- mobj_t *son;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_WhoCaresIfYourSonIsABee", actor))
- return;
-#endif
-
- A_FaceTarget(actor);
-
- if (actor->extravalue1)
- actor->extravalue1--;
-
- if (actor->info->attacksound)
- S_StartSound(actor, actor->info->attacksound);
-
- foffsetx = P_ReturnThrustX(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
- foffsety = P_ReturnThrustY(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
-
- if (!(son = P_SpawnMobjFromMobj(actor, foffsetx, foffsety, (locvar2&65535)*FRACUNIT, (mobjtype_t)(locvar1 >> 16))))
- return;
-
- P_SetObjectMomZ(son, (locvar1 & 65535)<tracer, actor);
- P_SetTarget(&son->target, actor->target);
-}
-
-// Function: A_ParentTriesToSleep
-//
-// Description: If extravalue1 is less than or equal to var1, go to var2.
-//
-// var1 = state to go to when extravalue1
-// var2 = unused
-//
-void A_ParentTriesToSleep(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- //INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_ParentTriesToSleep", actor))
- return;
-#endif
-
- if (actor->extravalue1)
- {
- if (actor->info->seesound)
- S_StartSound(actor, actor->info->seesound);
- actor->reactiontime = 0;
- P_SetMobjState(actor, locvar1);
- }
- else if (!actor->reactiontime)
- {
- actor->reactiontime = 1;
- if (actor->info->activesound) // more like INactivesound doy hoy hoy
- S_StartSound(actor, actor->info->activesound);
- }
-}
-
-
-// Function: A_CryingToMomma
-//
-// Description: If you're a child, let your parent know something's happened to you through extravalue1. Also, prepare to die.
-//
-// var1 = unused
-// var2 = unused
-//
-void A_CryingToMomma(mobj_t *actor)
-{
- //INT32 locvar1 = var1;
- //INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CryingToMomma", actor))
- return;
-#endif
-
- if (actor->tracer)
- actor->tracer->extravalue1++;
-
- actor->momx = actor->momy = actor->momz = 0;
-
- P_UnsetThingPosition(actor);
- if (sector_list)
- {
- P_DelSeclist(sector_list);
- sector_list = NULL;
- }
- actor->flags = MF_NOBLOCKMAP|MF_NOCLIPTHING;
- P_SetThingPosition(actor);
-}
-
-// Function: A_CheckFlags2
-//
-// Description: If actor->flags2 & var1, goto var2.
-//
-// var1 = mask
-// var2 = state to go
-//
-void A_CheckFlags2(mobj_t *actor)
-{
- INT32 locvar1 = var1;
- INT32 locvar2 = var2;
-#ifdef HAVE_BLUA
- if (LUA_CallAction("A_CheckFlags2", actor))
- return;
-#endif
-
- if (actor->flags2 & locvar1)
- P_SetMobjState(actor, (statenum_t)locvar2);
-}
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 1993-1996 by id Software, Inc.
+// Copyright (C) 1998-2000 by DooM Legacy Team.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file p_enemy.c
+/// \brief Enemy thinking, AI
+/// Action Pointer Functions that are associated with states/frames
+
+#include "doomdef.h"
+#include "g_game.h"
+#include "p_local.h"
+#include "r_main.h"
+#include "r_state.h"
+#include "s_sound.h"
+#include "m_random.h"
+#include "m_misc.h"
+#include "r_things.h"
+#include "i_video.h"
+#include "lua_hook.h"
+
+#ifdef HW3SOUND
+#include "hardware/hw3sound.h"
+#endif
+
+#ifdef HAVE_BLUA
+boolean LUA_CallAction(const char *action, mobj_t *actor);
+#endif
+
+player_t *stplyr;
+INT32 var1;
+INT32 var2;
+
+//
+// P_NewChaseDir related LUT.
+//
+static dirtype_t opposite[] =
+{
+ DI_WEST, DI_SOUTHWEST, DI_SOUTH, DI_SOUTHEAST,
+ DI_EAST, DI_NORTHEAST, DI_NORTH, DI_NORTHWEST, DI_NODIR
+};
+
+static dirtype_t diags[] =
+{
+ DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST
+};
+
+//Real Prototypes to A_*
+void A_Fall(mobj_t *actor);
+void A_Look(mobj_t *actor);
+void A_Chase(mobj_t *actor);
+void A_FaceStabChase(mobj_t *actor);
+void A_FaceStabRev(mobj_t *actor);
+void A_FaceStabHurl(mobj_t *actor);
+void A_FaceStabMiss(mobj_t *actor);
+void A_StatueBurst(mobj_t *actor);
+void A_JetJawRoam(mobj_t *actor);
+void A_JetJawChomp(mobj_t *actor);
+void A_PointyThink(mobj_t *actor);
+void A_CheckBuddy(mobj_t *actor);
+void A_HoodFire(mobj_t *actor);
+void A_HoodThink(mobj_t *actor);
+void A_HoodFall(mobj_t *actor);
+void A_ArrowBonks(mobj_t *actor);
+void A_SnailerThink(mobj_t *actor);
+void A_SharpChase(mobj_t *actor);
+void A_SharpSpin(mobj_t *actor);
+void A_SharpDecel(mobj_t *actor);
+void A_CrushstaceanWalk(mobj_t *actor);
+void A_CrushstaceanPunch(mobj_t *actor);
+void A_CrushclawAim(mobj_t *actor);
+void A_CrushclawLaunch(mobj_t *actor);
+void A_VultureVtol(mobj_t *actor);
+void A_VultureCheck(mobj_t *actor);
+void A_SkimChase(mobj_t *actor);
+void A_FaceTarget(mobj_t *actor);
+void A_FaceTracer(mobj_t *actor);
+void A_LobShot(mobj_t *actor);
+void A_FireShot(mobj_t *actor);
+void A_SuperFireShot(mobj_t *actor);
+void A_BossFireShot(mobj_t *actor);
+void A_Boss7FireMissiles(mobj_t *actor);
+void A_Boss1Laser(mobj_t *actor);
+void A_FocusTarget(mobj_t *actor);
+void A_Boss4Reverse(mobj_t *actor);
+void A_Boss4SpeedUp(mobj_t *actor);
+void A_Boss4Raise(mobj_t *actor);
+void A_SkullAttack(mobj_t *actor);
+void A_BossZoom(mobj_t *actor);
+void A_BossScream(mobj_t *actor);
+void A_Scream(mobj_t *actor);
+void A_Pain(mobj_t *actor);
+void A_1upThinker(mobj_t *actor);
+void A_MonitorPop(mobj_t *actor);
+void A_GoldMonitorPop(mobj_t *actor);
+void A_GoldMonitorRestore(mobj_t *actor);
+void A_GoldMonitorSparkle(mobj_t *actor);
+void A_Explode(mobj_t *actor);
+void A_BossDeath(mobj_t *actor);
+void A_CustomPower(mobj_t *actor);
+void A_GiveWeapon(mobj_t *actor);
+void A_RingBox(mobj_t *actor);
+void A_Invincibility(mobj_t *actor);
+void A_SuperSneakers(mobj_t *actor);
+void A_AwardScore(mobj_t *actor);
+void A_ExtraLife(mobj_t *actor);
+void A_GiveShield(mobj_t *actor);
+void A_GravityBox(mobj_t *actor);
+void A_ScoreRise(mobj_t *actor);
+void A_BunnyHop(mobj_t *actor);
+void A_BubbleSpawn(mobj_t *actor);
+void A_FanBubbleSpawn(mobj_t *actor);
+void A_BubbleRise(mobj_t *actor);
+void A_BubbleCheck(mobj_t *actor);
+void A_AttractChase(mobj_t *actor);
+void A_DropMine(mobj_t *actor);
+void A_FishJump(mobj_t *actor);
+void A_ThrownRing(mobj_t *actor);
+void A_SetSolidSteam(mobj_t *actor);
+void A_UnsetSolidSteam(mobj_t *actor);
+void A_SignPlayer(mobj_t *actor);
+void A_OverlayThink(mobj_t *actor);
+void A_JetChase(mobj_t *actor);
+void A_JetbThink(mobj_t *actor);
+void A_JetgShoot(mobj_t *actor);
+void A_JetgThink(mobj_t *actor);
+void A_ShootBullet(mobj_t *actor);
+void A_MinusDigging(mobj_t *actor);
+void A_MinusPopup(mobj_t *actor);
+void A_MinusCheck(mobj_t *actor);
+void A_ChickenCheck(mobj_t *actor);
+void A_MouseThink(mobj_t *actor);
+void A_DetonChase(mobj_t *actor);
+void A_CapeChase(mobj_t *actor);
+void A_RotateSpikeBall(mobj_t *actor);
+void A_SlingAppear(mobj_t *actor);
+void A_UnidusBall(mobj_t *actor);
+void A_RockSpawn(mobj_t *actor);
+void A_SetFuse(mobj_t *actor);
+void A_CrawlaCommanderThink(mobj_t *actor);
+void A_RingExplode(mobj_t *actor);
+void A_OldRingExplode(mobj_t *actor);
+void A_MixUp(mobj_t *actor);
+void A_RecyclePowers(mobj_t *actor);
+void A_Boss2TakeDamage(mobj_t *actor);
+void A_Boss7Chase(mobj_t *actor);
+void A_GoopSplat(mobj_t *actor);
+void A_Boss2PogoSFX(mobj_t *actor);
+void A_Boss2PogoTarget(mobj_t *actor);
+void A_EggmanBox(mobj_t *actor);
+void A_TurretFire(mobj_t *actor);
+void A_SuperTurretFire(mobj_t *actor);
+void A_TurretStop(mobj_t *actor);
+void A_SparkFollow(mobj_t *actor);
+void A_BuzzFly(mobj_t *actor);
+void A_GuardChase(mobj_t *actor);
+void A_EggShield(mobj_t *actor);
+void A_SetReactionTime(mobj_t *actor);
+void A_Boss1Spikeballs(mobj_t *actor);
+void A_Boss3TakeDamage(mobj_t *actor);
+void A_Boss3Path(mobj_t *actor);
+void A_LinedefExecute(mobj_t *actor);
+void A_PlaySeeSound(mobj_t *actor);
+void A_PlayAttackSound(mobj_t *actor);
+void A_PlayActiveSound(mobj_t *actor);
+void A_SmokeTrailer(mobj_t *actor);
+void A_SpawnObjectAbsolute(mobj_t *actor);
+void A_SpawnObjectRelative(mobj_t *actor);
+void A_ChangeAngleRelative(mobj_t *actor);
+void A_ChangeAngleAbsolute(mobj_t *actor);
+void A_PlaySound(mobj_t *actor);
+void A_FindTarget(mobj_t *actor);
+void A_FindTracer(mobj_t *actor);
+void A_SetTics(mobj_t *actor);
+void A_SetRandomTics(mobj_t *actor);
+void A_ChangeColorRelative(mobj_t *actor);
+void A_ChangeColorAbsolute(mobj_t *actor);
+void A_MoveRelative(mobj_t *actor);
+void A_MoveAbsolute(mobj_t *actor);
+void A_Thrust(mobj_t *actor);
+void A_ZThrust(mobj_t *actor);
+void A_SetTargetsTarget(mobj_t *actor);
+void A_SetObjectFlags(mobj_t *actor);
+void A_SetObjectFlags2(mobj_t *actor);
+void A_RandomState(mobj_t *actor);
+void A_RandomStateRange(mobj_t *actor);
+void A_DualAction(mobj_t *actor);
+void A_RemoteAction(mobj_t *actor);
+void A_ToggleFlameJet(mobj_t *actor);
+void A_OrbitNights(mobj_t *actor);
+void A_GhostMe(mobj_t *actor);
+void A_SetObjectState(mobj_t *actor);
+void A_SetObjectTypeState(mobj_t *actor);
+void A_KnockBack(mobj_t *actor);
+void A_PushAway(mobj_t *actor);
+void A_RingDrain(mobj_t *actor);
+void A_SplitShot(mobj_t *actor);
+void A_MissileSplit(mobj_t *actor);
+void A_MultiShot(mobj_t *actor);
+void A_InstaLoop(mobj_t *actor);
+void A_Custom3DRotate(mobj_t *actor);
+void A_SearchForPlayers(mobj_t *actor);
+void A_CheckRandom(mobj_t *actor);
+void A_CheckTargetRings(mobj_t *actor);
+void A_CheckRings(mobj_t *actor);
+void A_CheckTotalRings(mobj_t *actor);
+void A_CheckHealth(mobj_t *actor);
+void A_CheckRange(mobj_t *actor);
+void A_CheckHeight(mobj_t *actor);
+void A_CheckTrueRange(mobj_t *actor);
+void A_CheckThingCount(mobj_t *actor);
+void A_CheckAmbush(mobj_t *actor);
+void A_CheckCustomValue(mobj_t *actor);
+void A_CheckCusValMemo(mobj_t *actor);
+void A_SetCustomValue(mobj_t *actor);
+void A_UseCusValMemo(mobj_t *actor);
+void A_RelayCustomValue(mobj_t *actor);
+void A_CusValAction(mobj_t *actor);
+void A_ForceStop(mobj_t *actor);
+void A_ForceWin(mobj_t *actor);
+void A_SpikeRetract(mobj_t *actor);
+void A_InfoState(mobj_t *actor);
+void A_Repeat(mobj_t *actor);
+void A_SetScale(mobj_t *actor);
+void A_RemoteDamage(mobj_t *actor);
+void A_HomingChase(mobj_t *actor);
+void A_TrapShot(mobj_t *actor);
+void A_Boss1Chase(mobj_t *actor);
+void A_Boss2Chase(mobj_t *actor);
+void A_Boss2Pogo(mobj_t *actor);
+void A_BossJetFume(mobj_t *actor);
+void A_VileTarget(mobj_t *actor);
+void A_VileAttack(mobj_t *actor);
+void A_VileFire(mobj_t *actor);
+void A_BrakChase(mobj_t *actor);
+void A_BrakFireShot(mobj_t *actor);
+void A_BrakLobShot(mobj_t *actor);
+void A_NapalmScatter(mobj_t *actor);
+void A_SpawnFreshCopy(mobj_t *actor);
+void A_FlickySpawn(mobj_t *actor);
+void A_FlickyCenter(mobj_t *actor);
+void A_FlickyAim(mobj_t *actor);
+void A_FlickyFly(mobj_t *actor);
+void A_FlickySoar(mobj_t *actor);
+void A_FlickyCoast(mobj_t *actor);
+void A_FlickyHop(mobj_t *actor);
+void A_FlickyFlounder(mobj_t *actor);
+void A_FlickyCheck(mobj_t *actor);
+void A_FlickyHeightCheck(mobj_t *actor);
+void A_FlickyFlutter(mobj_t *actor);
+void A_FlameParticle(mobj_t *actor);
+void A_FadeOverlay(mobj_t *actor);
+void A_Boss5Jump(mobj_t *actor);
+void A_LightBeamReset(mobj_t *actor);
+void A_MineExplode(mobj_t *actor);
+void A_MineRange(mobj_t *actor);
+void A_ConnectToGround(mobj_t *actor);
+void A_SpawnParticleRelative(mobj_t *actor);
+void A_MultiShotDist(mobj_t *actor);
+void A_WhoCaresIfYourSonIsABee(mobj_t *actor);
+void A_ParentTriesToSleep(mobj_t *actor);
+void A_CryingToMomma(mobj_t *actor);
+void A_CheckFlags2(mobj_t *actor);
+//for p_enemy.c
+
+//
+// ENEMY THINKING
+// Enemies are always spawned with targetplayer = -1, threshold = 0
+// Most monsters are spawned unaware of all players, but some can be made preaware.
+//
+
+//
+// P_CheckMeleeRange
+//
+boolean P_CheckMeleeRange(mobj_t *actor)
+{
+ mobj_t *pl;
+ fixed_t dist;
+
+ if (!actor->target)
+ return false;
+
+ pl = actor->target;
+ dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
+
+ if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
+ return false;
+
+ // check height now, so that damn crawlas cant attack
+ // you if you stand on a higher ledge.
+ if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
+ return false;
+
+ if (!P_CheckSight(actor, actor->target))
+ return false;
+
+ return true;
+}
+
+// P_CheckMeleeRange for Jettysyn Bomber.
+boolean P_JetbCheckMeleeRange(mobj_t *actor)
+{
+ mobj_t *pl;
+ fixed_t dist;
+
+ if (!actor->target)
+ return false;
+
+ pl = actor->target;
+ dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
+
+ if (dist >= (actor->radius + pl->radius)*2)
+ return false;
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ {
+ if (pl->z < actor->z + actor->height + FixedMul(40<scale))
+ return false;
+ }
+ else
+ {
+ if (pl->z + pl->height > actor->z - FixedMul(40<scale))
+ return false;
+ }
+
+ return true;
+}
+
+// P_CheckMeleeRange for CastleBot FaceStabber.
+boolean P_FaceStabCheckMeleeRange(mobj_t *actor)
+{
+ mobj_t *pl;
+ fixed_t dist;
+
+ if (!actor->target)
+ return false;
+
+ pl = actor->target;
+ dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
+
+ if (dist >= (actor->radius + pl->radius)*4)
+ return false;
+
+ if ((pl->z > actor->z + actor->height) || (actor->z > pl->z + pl->height))
+ return false;
+
+ if (!P_CheckSight(actor, actor->target))
+ return false;
+
+ return true;
+}
+
+// P_CheckMeleeRange for Skim.
+boolean P_SkimCheckMeleeRange(mobj_t *actor)
+{
+ mobj_t *pl;
+ fixed_t dist;
+
+ if (!actor->target)
+ return false;
+
+ pl = actor->target;
+ dist = P_AproxDistance(pl->x-actor->x, pl->y-actor->y);
+
+ if (dist >= FixedMul(MELEERANGE - 20*FRACUNIT, actor->scale) + pl->radius)
+ return false;
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ {
+ if (pl->z < actor->z + actor->height + FixedMul(24<scale))
+ return false;
+ }
+ else
+ {
+ if (pl->z + pl->height > actor->z - FixedMul(24<scale))
+ return false;
+ }
+
+ return true;
+}
+
+//
+// P_CheckMissileRange
+//
+boolean P_CheckMissileRange(mobj_t *actor)
+{
+ fixed_t dist;
+
+ if (!actor->target)
+ return false;
+
+ if (actor->reactiontime)
+ return false; // do not attack yet
+
+ if (!P_CheckSight(actor, actor->target))
+ return false;
+
+ // OPTIMIZE: get this from a global checksight
+ dist = P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) - FixedMul(64*FRACUNIT, actor->scale);
+
+ if (!actor->info->meleestate)
+ dist -= FixedMul(128*FRACUNIT, actor->scale); // no melee attack, so fire more
+
+ dist >>= FRACBITS;
+
+ if (actor->type == MT_EGGMOBILE)
+ dist >>= 1;
+
+ if (dist > 200)
+ dist = 200;
+
+ if (actor->type == MT_EGGMOBILE && dist > 160)
+ dist = 160;
+
+ if (P_RandomByte() < dist)
+ return false;
+
+ return true;
+}
+
+/** Checks for water in a sector.
+ * Used by Skim movements.
+ *
+ * \param x X coordinate on the map.
+ * \param y Y coordinate on the map.
+ * \return True if there's water at this location, false if not.
+ * \sa ::MT_SKIM
+ */
+static boolean P_WaterInSector(mobj_t *mobj, fixed_t x, fixed_t y)
+{
+ sector_t *sector;
+
+ sector = R_PointInSubsector(x, y)->sector;
+
+ if (sector->ffloors)
+ {
+ ffloor_t *rover;
+
+ for (rover = sector->ffloors; rover; rover = rover->next)
+ {
+ if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_SWIMMABLE))
+ continue;
+
+ if (*rover->topheight >= mobj->floorz && *rover->topheight <= mobj->z)
+ return true; // we found water!!
+ }
+ }
+
+ return false;
+}
+
+static const fixed_t xspeed[NUMDIRS] = {FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS)), 0, 46341>>(16-FRACBITS)};
+static const fixed_t yspeed[NUMDIRS] = {0, 46341>>(16-FRACBITS), FRACUNIT, 46341>>(16-FRACBITS), 0, -(46341>>(16-FRACBITS)), -FRACUNIT, -(46341>>(16-FRACBITS))};
+
+/** Moves an actor in its current direction.
+ *
+ * \param actor Actor object to move.
+ * \return False if the move is blocked, otherwise true.
+ */
+boolean P_Move(mobj_t *actor, fixed_t speed)
+{
+ fixed_t tryx, tryy;
+ dirtype_t movedir = actor->movedir;
+
+ if (movedir == DI_NODIR || !actor->health)
+ return false;
+
+ I_Assert(movedir < NUMDIRS);
+
+ tryx = actor->x + FixedMul(speed*xspeed[movedir], actor->scale);
+ if (twodlevel || actor->flags2 & MF2_TWOD)
+ tryy = actor->y;
+ else
+ tryy = actor->y + FixedMul(speed*yspeed[movedir], actor->scale);
+
+ if (actor->type == MT_SKIM && !P_WaterInSector(actor, tryx, tryy)) // bail out if sector lacks water
+ return false;
+
+ if (!P_TryMove(actor, tryx, tryy, false))
+ {
+ if (actor->flags & MF_FLOAT && floatok)
+ {
+ // must adjust height
+ if (actor->z < tmfloorz)
+ actor->z += FixedMul(FLOATSPEED, actor->scale);
+ else
+ actor->z -= FixedMul(FLOATSPEED, actor->scale);
+
+ if (actor->type == MT_JETJAW && actor->z + actor->height > actor->watertop)
+ actor->z = actor->watertop - actor->height;
+
+ actor->flags2 |= MF2_INFLOAT;
+ return true;
+ }
+
+ return false;
+ }
+ else
+ actor->flags2 &= ~MF2_INFLOAT;
+
+ return true;
+}
+
+/** Attempts to move an actor on in its current direction.
+ * If the move succeeds, the actor's move count is reset
+ * randomly to a value from 0 to 15.
+ *
+ * \param actor Actor to move.
+ * \return True if the move succeeds, false if the move is blocked.
+ */
+static boolean P_TryWalk(mobj_t *actor)
+{
+ if (!P_Move(actor, actor->info->speed))
+ return false;
+ actor->movecount = P_RandomByte() & 15;
+ return true;
+}
+
+void P_NewChaseDir(mobj_t *actor)
+{
+ fixed_t deltax, deltay;
+ dirtype_t d[3];
+ dirtype_t tdir = DI_NODIR, olddir, turnaround;
+
+ I_Assert(actor->target != NULL);
+ I_Assert(!P_MobjWasRemoved(actor->target));
+
+ olddir = actor->movedir;
+
+ if (olddir >= NUMDIRS)
+ olddir = DI_NODIR;
+
+ if (olddir != DI_NODIR)
+ turnaround = opposite[olddir];
+ else
+ turnaround = olddir;
+
+ deltax = actor->target->x - actor->x;
+ deltay = actor->target->y - actor->y;
+
+ if (deltax > FixedMul(10*FRACUNIT, actor->scale))
+ d[1] = DI_EAST;
+ else if (deltax < -FixedMul(10*FRACUNIT, actor->scale))
+ d[1] = DI_WEST;
+ else
+ d[1] = DI_NODIR;
+
+ if (twodlevel || actor->flags2 & MF2_TWOD)
+ d[2] = DI_NODIR;
+ if (deltay < -FixedMul(10*FRACUNIT, actor->scale))
+ d[2] = DI_SOUTH;
+ else if (deltay > FixedMul(10*FRACUNIT, actor->scale))
+ d[2] = DI_NORTH;
+ else
+ d[2] = DI_NODIR;
+
+ // try direct route
+ if (d[1] != DI_NODIR && d[2] != DI_NODIR)
+ {
+ dirtype_t newdir = diags[((deltay < 0)<<1) + (deltax > 0)];
+
+ actor->movedir = newdir;
+ if ((newdir != turnaround) && P_TryWalk(actor))
+ return;
+ }
+
+ // try other directions
+ if (P_RandomChance(25*FRACUNIT/32) || abs(deltay) > abs(deltax))
+ {
+ tdir = d[1];
+ d[1] = d[2];
+ d[2] = tdir;
+ }
+
+ if (d[1] == turnaround)
+ d[1] = DI_NODIR;
+ if (d[2] == turnaround)
+ d[2] = DI_NODIR;
+
+ if (d[1] != DI_NODIR)
+ {
+ actor->movedir = d[1];
+
+ if (P_TryWalk(actor))
+ return; // either moved forward or attacked
+ }
+
+ if (d[2] != DI_NODIR)
+ {
+ actor->movedir = d[2];
+
+ if (P_TryWalk(actor))
+ return;
+ }
+
+ // there is no direct path to the player, so pick another direction.
+ if (olddir != DI_NODIR)
+ {
+ actor->movedir =olddir;
+
+ if (P_TryWalk(actor))
+ return;
+ }
+
+ // randomly determine direction of search
+ if (P_RandomChance(FRACUNIT/2))
+ {
+ for (tdir = DI_EAST; tdir <= DI_SOUTHEAST; tdir++)
+ {
+ if (tdir != turnaround)
+ {
+ actor->movedir = tdir;
+
+ if (P_TryWalk(actor))
+ return;
+ }
+ }
+ }
+ else
+ {
+ for (tdir = DI_SOUTHEAST; tdir >= DI_EAST; tdir--)
+ {
+ if (tdir != turnaround)
+ {
+ actor->movedir = tdir;
+
+ if (P_TryWalk(actor))
+ return;
+ }
+ }
+ }
+
+ if (turnaround != DI_NODIR)
+ {
+ actor->movedir = turnaround;
+
+ if (P_TryWalk(actor))
+ return;
+ }
+
+ actor->movedir = (angle_t)DI_NODIR; // cannot move
+}
+
+/** Looks for players to chase after, aim at, or whatever.
+ *
+ * \param actor The object looking for flesh.
+ * \param allaround Look all around? If false, only players in a 180-degree
+ * range in front will be spotted.
+ * \param dist If > 0, checks distance
+ * \return True if a player is found, otherwise false.
+ * \sa P_SupermanLook4Players
+ */
+boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist)
+{
+ INT32 c = 0, stop;
+ player_t *player;
+ angle_t an;
+
+ // BP: first time init, this allow minimum lastlook changes
+ if (actor->lastlook < 0)
+ actor->lastlook = P_RandomByte();
+
+ actor->lastlook %= MAXPLAYERS;
+
+ stop = (actor->lastlook - 1) & PLAYERSMASK;
+
+ for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
+ {
+ // done looking
+ if (actor->lastlook == stop)
+ return false;
+
+ if (!playeringame[actor->lastlook])
+ continue;
+
+ if (c++ == 2)
+ return false;
+
+ player = &players[actor->lastlook];
+
+ if ((netgame || multiplayer) && player->spectator)
+ continue;
+
+ if (player->pflags & PF_INVIS)
+ continue; // ignore notarget
+
+ if (!player->mo || P_MobjWasRemoved(player->mo))
+ continue;
+
+ if (player->mo->health <= 0)
+ continue; // dead
+
+ if (dist > 0
+ && P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist)
+ continue; // Too far away
+
+ if (!allaround)
+ {
+ an = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
+ if (an > ANGLE_90 && an < ANGLE_270)
+ {
+ dist = P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y);
+ // if real close, react anyway
+ if (dist > FixedMul(MELEERANGE, actor->scale))
+ continue; // behind back
+ }
+ }
+
+ if (!P_CheckSight(actor, player->mo))
+ continue; // out of sight
+
+ if (tracer)
+ P_SetTarget(&actor->tracer, player->mo);
+ else
+ P_SetTarget(&actor->target, player->mo);
+ return true;
+ }
+
+ //return false;
+}
+
+/** Looks for a player with a ring shield.
+ * Used by rings.
+ *
+ * \param actor Ring looking for a shield to be attracted to.
+ * \return True if a player with ring shield is found, otherwise false.
+ * \sa A_AttractChase
+ */
+static boolean P_LookForShield(mobj_t *actor)
+{
+ INT32 c = 0, stop;
+ player_t *player;
+
+ // BP: first time init, this allow minimum lastlook changes
+ if (actor->lastlook < 0)
+ actor->lastlook = P_RandomByte();
+
+ actor->lastlook %= MAXPLAYERS;
+
+ stop = (actor->lastlook - 1) & PLAYERSMASK;
+
+ for (; ; actor->lastlook = ((actor->lastlook + 1) & PLAYERSMASK))
+ {
+ // done looking
+ if (actor->lastlook == stop)
+ return false;
+
+ if (!playeringame[actor->lastlook])
+ continue;
+
+ if (c++ == 2)
+ return false;
+
+ player = &players[actor->lastlook];
+
+ if (!player->mo || player->mo->health <= 0)
+ continue; // dead
+
+ //When in CTF, don't pull rings that you cannot pick up.
+ if ((actor->type == MT_REDTEAMRING && player->ctfteam != 1) ||
+ (actor->type == MT_BLUETEAMRING && player->ctfteam != 2))
+ continue;
+
+ if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
+ && (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
+ {
+ P_SetTarget(&actor->tracer, player->mo);
+
+ if (actor->hnext)
+ P_SetTarget(&actor->hnext->hprev, actor->hprev);
+ if (actor->hprev)
+ P_SetTarget(&actor->hprev->hnext, actor->hnext);
+
+ return true;
+ }
+ }
+
+ //return false;
+}
+
+#ifdef WEIGHTEDRECYCLER
+// Compares players to see who currently has the "best" items, etc.
+static int P_RecycleCompare(const void *p1, const void *p2)
+{
+ player_t *player1 = &players[*(const UINT8 *)p1];
+ player_t *player2 = &players[*(const UINT8 *)p2];
+
+ // Non-shooting gametypes
+ if (!G_PlatformGametype())
+ {
+ // Invincibility.
+ if (player1->powers[pw_invulnerability] > player2->powers[pw_invulnerability]) return -1;
+ else if (player2->powers[pw_invulnerability] > player1->powers[pw_invulnerability]) return 1;
+
+ // One has a shield, the other doesn't.
+ if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
+ else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
+
+ // Sneakers.
+ if (player1->powers[pw_sneakers] > player2->powers[pw_sneakers]) return -1;
+ else if (player2->powers[pw_sneakers] > player1->powers[pw_sneakers]) return 1;
+ }
+ else // Match, Team Match, CTF, Tag, Etc.
+ {
+ UINT8 player1_em = M_CountBits((UINT32)player1->powers[pw_emeralds], 7);
+ UINT8 player2_em = M_CountBits((UINT32)player2->powers[pw_emeralds], 7);
+
+ UINT8 player1_rw = M_CountBits((UINT32)player1->ringweapons, NUM_WEAPONS-1);
+ UINT8 player2_rw = M_CountBits((UINT32)player2->ringweapons, NUM_WEAPONS-1);
+
+ UINT16 player1_am = player1->powers[pw_infinityring] // max 800
+ + player1->powers[pw_automaticring] // max 300
+ + (player1->powers[pw_bouncering] * 3) // max 100
+ + (player1->powers[pw_explosionring] * 6) // max 50
+ + (player1->powers[pw_scatterring] * 3) // max 100
+ + (player1->powers[pw_grenadering] * 6) // max 50
+ + (player1->powers[pw_railring] * 6); // max 50
+ UINT16 player2_am = player2->powers[pw_infinityring] // max 800
+ + player2->powers[pw_automaticring] // max 300
+ + (player2->powers[pw_bouncering] * 3) // max 100
+ + (player2->powers[pw_explosionring] * 6) // max 50
+ + (player2->powers[pw_scatterring] * 3) // max 100
+ + (player2->powers[pw_grenadering] * 6) // max 50
+ + (player2->powers[pw_railring] * 6); // max 50
+
+ // Super trumps everything.
+ if (player1->powers[pw_super] && !player2->powers[pw_super]) return -1;
+ else if (player2->powers[pw_super] && !player1->powers[pw_super]) return 1;
+
+ // Emerald count if neither player is Super.
+ if (player1_em > player2_em) return -1;
+ else if (player1_em < player2_em) return 1;
+
+ // One has a shield, the other doesn't.
+ // (the likelihood of a shielded player being worse off than one without one is low.)
+ if (player1->powers[pw_shield] && !player2->powers[pw_shield]) return -1;
+ else if (player2->powers[pw_shield] && !player1->powers[pw_shield]) return 1;
+
+ // Ring weapons count
+ if (player1_rw > player2_rw) return -1;
+ else if (player1_rw < player2_rw) return 1;
+
+ // Ring ammo if they have the same number of weapons
+ if (player1_am > player2_am) return -1;
+ else if (player1_am < player2_am) return 1;
+ }
+
+ // Identical for our purposes
+ return 0;
+}
+#endif
+
+// Handles random monitor weights via console.
+static mobjtype_t P_DoRandomBoxChances(void)
+{
+ mobjtype_t spawnchance[256];
+ INT32 numchoices = 0, i = 0;
+
+ if (!(netgame || multiplayer))
+ {
+ switch (P_RandomKey(10))
+ {
+ case 0:
+ return MT_RING_ICON;
+ case 1:
+ return MT_SNEAKERS_ICON;
+ case 2:
+ return MT_INVULN_ICON;
+ case 3:
+ return MT_WHIRLWIND_ICON;
+ case 4:
+ return MT_ELEMENTAL_ICON;
+ case 5:
+ return MT_ATTRACT_ICON;
+ case 6:
+ return MT_FORCE_ICON;
+ case 7:
+ return MT_ARMAGEDDON_ICON;
+ case 8:
+ return MT_1UP_ICON;
+ case 9:
+ return MT_EGGMAN_ICON;
+ }
+ return MT_NULL;
+ }
+
+#define QUESTIONBOXCHANCES(type, cvar) \
+for (i = cvar.value; i; --i) spawnchance[numchoices++] = type
+ QUESTIONBOXCHANCES(MT_RING_ICON, cv_superring);
+ QUESTIONBOXCHANCES(MT_SNEAKERS_ICON, cv_supersneakers);
+ QUESTIONBOXCHANCES(MT_INVULN_ICON, cv_invincibility);
+ QUESTIONBOXCHANCES(MT_WHIRLWIND_ICON, cv_jumpshield);
+ QUESTIONBOXCHANCES(MT_ELEMENTAL_ICON, cv_watershield);
+ QUESTIONBOXCHANCES(MT_ATTRACT_ICON, cv_ringshield);
+ QUESTIONBOXCHANCES(MT_FORCE_ICON, cv_forceshield);
+ QUESTIONBOXCHANCES(MT_ARMAGEDDON_ICON, cv_bombshield);
+ QUESTIONBOXCHANCES(MT_1UP_ICON, cv_1up);
+ QUESTIONBOXCHANCES(MT_EGGMAN_ICON, cv_eggmanbox);
+ QUESTIONBOXCHANCES(MT_MIXUP_ICON, cv_teleporters);
+ QUESTIONBOXCHANCES(MT_RECYCLER_ICON, cv_recycler);
+#undef QUESTIONBOXCHANCES
+
+ if (numchoices == 0) return MT_NULL;
+ return spawnchance[P_RandomKey(numchoices)];
+}
+
+//
+// ACTION ROUTINES
+//
+
+// Function: A_Look
+//
+// Description: Look for a player and set your target to them.
+//
+// var1:
+// lower 16 bits = look all around
+// upper 16 bits = distance limit
+// var2 = If 1, only change to seestate. If 2, only play seesound. If 0, do both.
+//
+void A_Look(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Look", actor))
+ return;
+#endif
+
+ if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale)))
+ return;
+
+ // go into chase state
+ if (!locvar2)
+ {
+ P_SetMobjState(actor, actor->info->seestate);
+ A_PlaySeeSound(actor);
+ }
+ else if (locvar2 == 1) // Only go into seestate
+ P_SetMobjState(actor, actor->info->seestate);
+ else if (locvar2 == 2) // Only play seesound
+ A_PlaySeeSound(actor);
+}
+
+// Function: A_Chase
+//
+// Description: Chase after your target.
+//
+// var1:
+// 1 = don't check meleestate
+// 2 = don't check missilestate
+// 3 = don't check meleestate and missilestate
+// var2 = unused
+//
+void A_Chase(mobj_t *actor)
+{
+ INT32 delta;
+ INT32 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Chase", actor))
+ return;
+#endif
+
+ I_Assert(actor != NULL);
+ I_Assert(!P_MobjWasRemoved(actor));
+
+ if (actor->reactiontime)
+ actor->reactiontime--;
+
+ // modify target threshold
+ if (actor->threshold)
+ {
+ if (!actor->target || actor->target->health <= 0)
+ actor->threshold = 0;
+ else
+ actor->threshold--;
+ }
+
+ // turn towards movement direction if not there yet
+ if (actor->movedir < NUMDIRS)
+ {
+ actor->angle &= (7<<29);
+ delta = actor->angle - (actor->movedir << 29);
+
+ if (delta > 0)
+ actor->angle -= ANGLE_45;
+ else if (delta < 0)
+ actor->angle += ANGLE_45;
+ }
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ P_SetMobjStateNF(actor, actor->info->spawnstate);
+ return;
+ }
+
+ // do not attack twice in a row
+ if (actor->flags2 & MF2_JUSTATTACKED)
+ {
+ actor->flags2 &= ~MF2_JUSTATTACKED;
+ P_NewChaseDir(actor);
+ return;
+ }
+
+ // check for melee attack
+ if (!(locvar1 & 1) && actor->info->meleestate && P_CheckMeleeRange(actor))
+ {
+ if (actor->info->attacksound)
+ S_StartAttackSound(actor, actor->info->attacksound);
+
+ P_SetMobjState(actor, actor->info->meleestate);
+ return;
+ }
+
+ // check for missile attack
+ if (!(locvar1 & 2) && actor->info->missilestate)
+ {
+ if (actor->movecount || !P_CheckMissileRange(actor))
+ goto nomissile;
+
+ P_SetMobjState(actor, actor->info->missilestate);
+ actor->flags2 |= MF2_JUSTATTACKED;
+ return;
+ }
+
+nomissile:
+ // possibly choose another target
+ if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+ && P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ // chase towards player
+ if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+ P_NewChaseDir(actor);
+}
+
+// Function: A_FaceStabChase
+//
+// Description: Unused variant of A_Chase for Castlebot Facestabber.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_FaceStabChase(mobj_t *actor)
+{
+ INT32 delta;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FaceStabChase", actor))
+ return;
+#endif
+
+ if (actor->reactiontime)
+ actor->reactiontime--;
+
+ // modify target threshold
+ if (actor->threshold)
+ {
+ if (!actor->target || actor->target->health <= 0)
+ actor->threshold = 0;
+ else
+ actor->threshold--;
+ }
+
+ // turn towards movement direction if not there yet
+ if (actor->movedir < NUMDIRS)
+ {
+ actor->angle &= (7<<29);
+ delta = actor->angle - (actor->movedir << 29);
+
+ if (delta > 0)
+ actor->angle -= ANGLE_45;
+ else if (delta < 0)
+ actor->angle += ANGLE_45;
+ }
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ P_SetMobjStateNF(actor, actor->info->spawnstate);
+ return;
+ }
+
+ // do not attack twice in a row
+ if (actor->flags2 & MF2_JUSTATTACKED)
+ {
+ actor->flags2 &= ~MF2_JUSTATTACKED;
+ P_NewChaseDir(actor);
+ return;
+ }
+
+ // check for melee attack
+ if (actor->info->meleestate && P_FaceStabCheckMeleeRange(actor))
+ {
+ if (actor->info->attacksound)
+ S_StartAttackSound(actor, actor->info->attacksound);
+
+ P_SetMobjState(actor, actor->info->meleestate);
+ return;
+ }
+
+ // check for missile attack
+ if (actor->info->missilestate)
+ {
+ if (actor->movecount || !P_CheckMissileRange(actor))
+ goto nomissile;
+
+ P_SetMobjState(actor, actor->info->missilestate);
+ actor->flags2 |= MF2_JUSTATTACKED;
+ return;
+ }
+
+nomissile:
+ // possibly choose another target
+ if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+ && P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ // chase towards player
+ if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+ P_NewChaseDir(actor);
+}
+
+static void P_SharpDust(mobj_t *actor, mobjtype_t type, angle_t ang)
+{
+ mobj_t *dust;
+
+ if (!type || !P_IsObjectOnGround(actor))
+ return;
+
+ dust = P_SpawnMobjFromMobj(actor,
+ -P_ReturnThrustX(actor, ang, 16<angle, actor->radius),
+ -P_ReturnThrustY(actor, actor->angle, actor->radius),
+ actor->height/3,
+ MT_PARTICLE);
+ flume->destscale = actor->scale*3;
+ P_SetScale(flume, flume->destscale);
+ P_SetTarget(&flume->target, actor);
+ flume->sprite = SPR_JETF;
+ flume->frame = FF_FULLBRIGHT;
+ flume->tics = 2;
+}
+
+// Function: A_FaceStabRev
+//
+// Description: Facestabber rev action
+//
+// var1 = effective duration
+// var2 = effective nextstate
+//
+void A_FaceStabRev(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FaceStabRev", actor))
+ return;
+#endif
+
+ if (!actor->target)
+ {
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ actor->extravalue1 = 0;
+
+ if (!actor->reactiontime)
+ {
+ actor->reactiontime = locvar1;
+ S_StartSound(actor, actor->info->activesound);
+ }
+ else
+ {
+ if ((--actor->reactiontime) == 0)
+ {
+ S_StartSound(actor, actor->info->attacksound);
+ P_SetMobjState(actor, locvar2);
+ }
+ else
+ {
+ P_TryMove(actor, actor->x - P_ReturnThrustX(actor, actor->angle, 2<y - P_ReturnThrustY(actor, actor->angle, 2<target)
+ {
+ angle_t visang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ // Calculate new direction.
+ angle_t dirang = actor->angle;
+ angle_t diffang = visang - dirang;
+
+ if (locvar1) // Allow homing?
+ {
+ if (diffang > ANGLE_180)
+ {
+ angle_t workang = locvar1*(InvAngle(diffang)>>5);
+ diffang += InvAngle(workang);
+ }
+ else
+ diffang += (locvar1*(diffang>>5));
+ }
+ diffang += ANGLE_45;
+
+ // Check the sight cone.
+ if (diffang < ANGLE_90)
+ {
+ actor->angle = dirang;
+ if (++actor->extravalue2 < 4)
+ actor->extravalue2 = 4;
+ else if (actor->extravalue2 > 26)
+ actor->extravalue2 = 26;
+
+ if (P_TryMove(actor,
+ actor->x + P_ReturnThrustX(actor, dirang, actor->extravalue2<y + P_ReturnThrustY(actor, dirang, actor->extravalue2<extravalue1);
+ fixed_t basesize = FRACUNIT/MAXVAL;
+ mobj_t *hwork = actor;
+ INT32 dist = 113;
+ fixed_t xo = P_ReturnThrustX(actor, actor->angle, dist*basesize);
+ fixed_t yo = P_ReturnThrustY(actor, actor->angle, dist*basesize);
+
+ while (step > 0)
+ {
+ if (!hwork->hnext)
+ P_SetTarget(&hwork->hnext, P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_FACESTABBERSPEAR));
+ hwork = hwork->hnext;
+ hwork->angle = actor->angle + ANGLE_90;
+ hwork->destscale = FixedSqrt(step*basesize);
+ P_SetScale(hwork, hwork->destscale);
+ hwork->fuse = 2;
+ P_TeleportMove(hwork, actor->x + xo*(15-step), actor->y + yo*(15-step), actor->z + (actor->height - hwork->height)/2 + (P_MobjFlip(actor)*(8<extravalue1 >= MAXVAL)
+ actor->extravalue1 -= NUMGRADS;
+
+ if ((step % 5) == 0)
+ P_SharpDust(actor, MT_SPINDUST, actor->angle);
+
+ P_FaceStabFlume(actor);
+ return;
+#undef MAXVAL
+#undef NUMGRADS
+#undef NUMSTEPS
+ }
+ }
+ }
+
+ P_SetMobjState(actor, locvar2);
+ actor->reactiontime = actor->info->reactiontime;
+}
+
+// Function: A_FaceStabMiss
+//
+// Description: Facestabber miss action
+//
+// var1 = unused
+// var2 = effective nextstate
+//
+void A_FaceStabMiss(mobj_t *actor)
+{
+ //INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FaceStabMiss", actor))
+ return;
+#endif
+
+ if (++actor->extravalue1 >= 3)
+ {
+ actor->extravalue2 -= 2;
+ actor->extravalue1 = 0;
+ S_StartSound(actor, sfx_s3k47);
+ P_SharpDust(actor, MT_SPINDUST, actor->angle);
+ }
+
+ if (actor->extravalue2 <= 0 || !P_TryMove(actor,
+ actor->x + P_ReturnThrustX(actor, actor->angle, actor->extravalue2<y + P_ReturnThrustY(actor, actor->angle, actor->extravalue2<extravalue2 = 0;
+ P_SetMobjState(actor, locvar2);
+ }
+}
+
+// Function: A_StatueBurst
+//
+// Description: For suspicious statues only...
+//
+// var1 = object to create
+// var2 = effective nextstate for created object
+//
+void A_StatueBurst(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobjtype_t chunktype = (mobjtype_t)actor->info->raisestate;
+ mobj_t *new;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_StatueBurst", actor))
+ return;
+#endif
+
+ if (!locvar1 || !(new = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1)))
+ return;
+
+ new->angle = actor->angle;
+ new->target = actor->target;
+ if (locvar2)
+ P_SetMobjState(new, (statenum_t)locvar2);
+ S_StartSound(new, new->info->attacksound);
+ S_StopSound(actor);
+ S_StartSound(actor, sfx_s3k96);
+
+ {
+ fixed_t a, b;
+ fixed_t c = (actor->height>>2) - FixedMul(actor->scale, mobjinfo[chunktype].height>>1);
+ fixed_t v = 4<radius>>1);
+ mobj_t *spawned;
+ UINT8 i;
+ for (i = 0; i < 8; i++)
+ {
+ a = ((i & 1) ? r : (-r));
+ b = ((i & 2) ? r : (-r));
+ if (i == 4)
+ {
+ c += (actor->height>>1);
+ v = 8<fuse = 3*TICRATE;
+ }
+ }
+}
+
+// Function: A_JetJawRoam
+//
+// Description: Roaming routine for JetJaw
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetJawRoam(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_JetJawRoam", actor))
+ return;
+#endif
+ if (actor->reactiontime)
+ {
+ actor->reactiontime--;
+ P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed*FRACUNIT/4, actor->scale));
+ }
+ else
+ {
+ actor->reactiontime = actor->info->reactiontime;
+ actor->angle += ANGLE_180;
+ }
+
+ if (P_LookForPlayers(actor, false, false, actor->radius * 16))
+ P_SetMobjState(actor, actor->info->seestate);
+}
+
+// Function: A_JetJawChomp
+//
+// Description: Chase and chomp at the target, as long as it is in view
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetJawChomp(mobj_t *actor)
+{
+ INT32 delta;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_JetJawChomp", actor))
+ return;
+#endif
+
+ // turn towards movement direction if not there yet
+ if (actor->movedir < NUMDIRS)
+ {
+ actor->angle &= (7<<29);
+ delta = actor->angle - (actor->movedir << 29);
+
+ if (delta > 0)
+ actor->angle -= ANGLE_45;
+ else if (delta < 0)
+ actor->angle += ANGLE_45;
+ }
+
+ // Stop chomping if target's dead or you can't see it
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)
+ || actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+ {
+ P_SetMobjStateNF(actor, actor->info->spawnstate);
+ return;
+ }
+
+ // chase towards player
+ if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+ P_NewChaseDir(actor);
+}
+
+// Function: A_PointyThink
+//
+// Description: Thinker function for Pointy
+//
+// var1 = unused
+// var2 = unused
+//
+void A_PointyThink(mobj_t *actor)
+{
+ INT32 i;
+ player_t *player = NULL;
+ mobj_t *ball;
+ TVector v;
+ TVector *res;
+ angle_t fa;
+ fixed_t radius = FixedMul(actor->info->radius*actor->info->reactiontime, actor->scale);
+ boolean firsttime = true;
+ INT32 sign;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_PointyThink", actor))
+ return;
+#endif
+ actor->momx = actor->momy = actor->momz = 0;
+
+ // Find nearest player
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+
+ if (!players[i].mo)
+ continue;
+
+ if (!players[i].mo->health)
+ continue;
+
+ if (!P_CheckSight(actor, players[i].mo))
+ continue;
+
+ if (firsttime)
+ {
+ firsttime = false;
+ player = &players[i];
+ }
+ else
+ {
+ if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) <
+ P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y))
+ player = &players[i];
+ }
+ }
+
+ if (!player)
+ return;
+
+ // Okay, we found the closest player. Let's move based on his movement.
+ P_SetTarget(&actor->target, player->mo);
+ A_FaceTarget(actor);
+
+ if (P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y) < P_AproxDistance(player->mo->x + player->mo->momx - actor->x, player->mo->y + player->mo->momy - actor->y))
+ sign = -1; // Player is moving away
+ else
+ sign = 1; // Player is moving closer
+
+ if (player->mo->momx || player->mo->momy)
+ {
+ P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y), FixedMul(actor->info->speed*sign, actor->scale));
+
+ // Rotate our spike balls
+ actor->lastlook += actor->info->damage;
+ actor->lastlook %= FINEANGLES/4;
+ }
+
+ if (!actor->tracer) // For some reason we do not have spike balls...
+ return;
+
+ // Position spike balls relative to the value of 'lastlook'.
+ ball = actor->tracer;
+
+ i = 0;
+ while (ball)
+ {
+ fa = actor->lastlook+i;
+ v[0] = FixedMul(FINECOSINE(fa),radius);
+ v[1] = 0;
+ v[2] = FixedMul(FINESINE(fa),radius);
+ v[3] = FRACUNIT;
+
+ res = VectorMatrixMultiply(v, *RotateXMatrix(FixedAngle(actor->lastlook+i)));
+ M_Memcpy(&v, res, sizeof (v));
+ res = VectorMatrixMultiply(v, *RotateZMatrix(actor->angle+ANGLE_180));
+ M_Memcpy(&v, res, sizeof (v));
+
+ P_UnsetThingPosition(ball);
+ ball->x = actor->x + v[0];
+ ball->y = actor->y + v[1];
+ ball->z = actor->z + (actor->height>>1) + v[2];
+ P_SetThingPosition(ball);
+
+ ball = ball->tracer;
+ i += ANGLE_90 >> ANGLETOFINESHIFT;
+ }
+}
+
+// Function: A_CheckBuddy
+//
+// Description: Checks if target/tracer exists/has health. If not, the object removes itself.
+//
+// var1:
+// 0 = target
+// 1 = tracer
+// var2 = unused
+//
+void A_CheckBuddy(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckBuddy", actor))
+ return;
+#endif
+ if (locvar1 && (!actor->tracer || actor->tracer->health <= 0))
+ P_RemoveMobj(actor);
+ else if (!locvar1 && (!actor->target || actor->target->health <= 0))
+ P_RemoveMobj(actor);
+}
+
+// Helper function for the Robo Hood.
+// Don't ask me how it works. Nev3r made it with dark majyks.
+static void P_ParabolicMove(mobj_t *actor, fixed_t x, fixed_t y, fixed_t z, fixed_t speed)
+{
+ fixed_t dh;
+
+ x -= actor->x;
+ y -= actor->y;
+ z -= actor->z;
+
+ dh = P_AproxDistance(x, y);
+
+ actor->momx = FixedMul(FixedDiv(x, dh), speed);
+ actor->momy = FixedMul(FixedDiv(y, dh), speed);
+
+ if (!gravity)
+ return;
+
+ dh = FixedDiv(FixedMul(dh, gravity), speed);
+ actor->momz = (dh>>1) + FixedDiv(z, dh<<1);
+}
+
+// Function: A_HoodFire
+//
+// Description: Firing Robo-Hood
+//
+// var1 = object type to fire
+// var2 = unused
+//
+void A_HoodFire(mobj_t *actor)
+{
+ mobj_t *arrow;
+ INT32 locvar1 = var1;
+ //INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_HoodFire", actor))
+ return;
+#endif
+
+ // Check target first.
+ if (!actor->target)
+ {
+ actor->reactiontime = actor->info->reactiontime;
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ A_FaceTarget(actor);
+
+ if (!(arrow = P_SpawnMissile(actor, actor->target, (mobjtype_t)locvar1)))
+ return;
+
+ // Set a parabolic trajectory for the arrow.
+ P_ParabolicMove(arrow, actor->target->x, actor->target->y, actor->target->z, arrow->info->speed);
+}
+
+// Function: A_HoodThink
+//
+// Description: Thinker for Robo-Hood
+//
+// var1 = unused
+// var2 = unused
+//
+void A_HoodThink(mobj_t *actor)
+{
+ fixed_t dx, dy, dz, dm;
+ boolean checksight;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_HoodThink", actor))
+ return;
+#endif
+
+ // Check target first.
+ if (!actor->target)
+ {
+ actor->reactiontime = actor->info->reactiontime;
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ dx = (actor->target->x - actor->x), dy = (actor->target->y - actor->y), dz = (actor->target->z - actor->z);
+ dm = P_AproxDistance(dx, dy);
+ // Target dangerously close to robohood, retreat then.
+ if ((dm < 256<info->raisestate);
+ return;
+ }
+
+ // If target on sight, look at it.
+ if ((checksight = P_CheckSight(actor, actor->target)))
+ {
+ angle_t dang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ if (actor->angle >= ANGLE_180)
+ {
+ actor->angle = InvAngle(actor->angle)>>1;
+ actor->angle = InvAngle(actor->angle);
+ }
+ else
+ actor->angle >>= 1;
+
+ if (dang >= ANGLE_180)
+ {
+ dang = InvAngle(dang)>>1;
+ dang = InvAngle(dang);
+ }
+ else
+ dang >>= 1;
+
+ actor->angle += dang;
+ }
+
+ // Check whether to do anything.
+ if ((--actor->reactiontime) <= 0)
+ {
+ actor->reactiontime = actor->info->reactiontime;
+
+ // If way too far, don't shoot.
+ if ((dm < (3072<info->missilestate);
+ return;
+ }
+ }
+}
+
+// Function: A_HoodFall
+//
+// Description: Falling Robo-Hood
+//
+// var1 = unused
+// var2 = unused
+//
+void A_HoodFall(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_HoodFall", actor))
+ return;
+#endif
+
+ if (!P_IsObjectOnGround(actor))
+ return;
+
+ actor->momx = actor->momy = 0;
+ actor->reactiontime = actor->info->reactiontime;
+ P_SetMobjState(actor, actor->info->seestate);
+}
+
+// Function: A_ArrowBonks
+//
+// Description: Arrow momentum setting on collision
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ArrowBonks(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ArrowBonks", actor))
+ return;
+#endif
+
+ if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)
+ || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
+ actor->angle += ANGLE_180;
+
+ P_SetObjectMomZ(actor, 8*actor->scale, false);
+ P_InstaThrust(actor, actor->angle, -6*actor->scale);
+
+ actor->flags = (actor->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY;
+ actor->z += P_MobjFlip(actor);
+}
+
+// Function: A_SnailerThink
+//
+// Description: Thinker function for Snailer
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SnailerThink(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SnailerThink", actor))
+ return;
+#endif
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (!P_LookForPlayers(actor, true, false, 0))
+ return;
+ }
+
+ // We now have a target. Oh bliss, rapture, and contentment!
+
+ if (actor->target->z + actor->target->height > actor->z - FixedMul(32*FRACUNIT, actor->scale)
+ && actor->target->z < actor->z + actor->height + FixedMul(32*FRACUNIT, actor->scale)
+ && !(leveltime % (TICRATE*2)))
+ {
+ angle_t an;
+ fixed_t z;
+
+ // Actor shouldn't face target, so we'll do things a bit differently here
+
+ an = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle;
+
+ z = actor->z + actor->height/2;
+
+ if (an > ANGLE_45 && an < ANGLE_315) // fire as close as you can to the target, even if too sharp an angle from your front
+ {
+ fixed_t dist;
+ fixed_t dx, dy;
+
+ dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
+
+ if (an > ANGLE_45 && an <= ANGLE_90) // fire at 45 degrees to the left
+ {
+ dx = actor->x + P_ReturnThrustX(actor, actor->angle + ANGLE_45, dist);
+ dy = actor->y + P_ReturnThrustY(actor, actor->angle + ANGLE_45, dist);
+ }
+ else if (an >= ANGLE_270 && an < ANGLE_315) // fire at 45 degrees to the right
+ {
+ dx = actor->x + P_ReturnThrustX(actor, actor->angle - ANGLE_45, dist);
+ dy = actor->y + P_ReturnThrustY(actor, actor->angle - ANGLE_45, dist);
+ }
+ else // fire straight ahead
+ {
+ dx = actor->x + P_ReturnThrustX(actor, actor->angle, dist);
+ dy = actor->y + P_ReturnThrustY(actor, actor->angle, dist);
+ }
+
+ P_SpawnPointMissile(actor, dx, dy, actor->target->z, MT_ROCKET, actor->x, actor->y, z);
+ }
+ else
+ P_SpawnXYZMissile(actor, actor->target, MT_ROCKET, actor->x, actor->y, z);
+ }
+
+ if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z > actor->z)
+ || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) > (actor->z + actor->height)))
+ actor->momz += FixedMul(actor->info->speed, actor->scale);
+ else if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->target->z < actor->z)
+ || (actor->eflags & MFE_VERTICALFLIP && (actor->target->z + actor->target->height) < (actor->z + actor->height)))
+ actor->momz -= FixedMul(actor->info->speed, actor->scale);
+
+ actor->momz /= 2;
+}
+
+// Function: A_SharpChase
+//
+// Description: Thinker/Chase routine for Spincushions
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SharpChase(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SharpChase", actor))
+ return;
+#endif
+
+ if (actor->reactiontime)
+ {
+ INT32 delta;
+
+ actor->reactiontime--;
+
+ // turn towards movement direction if not there yet
+ if (actor->movedir < NUMDIRS)
+ {
+ actor->angle &= (7<<29);
+ delta = actor->angle - (actor->movedir << 29);
+
+ if (delta > 0)
+ actor->angle -= ANGLE_45;
+ else if (delta < 0)
+ actor->angle += ANGLE_45;
+ }
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ // chase towards player
+ if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+ P_NewChaseDir(actor);
+ }
+ else
+ {
+ actor->threshold = actor->info->painchance;
+ P_SetMobjState(actor, actor->info->missilestate);
+ S_StartSound(actor, actor->info->attacksound);
+ }
+}
+
+// Function: A_SharpSpin
+//
+// Description: Spin chase routine for Spincushions
+//
+// var1 = object # to spawn as dust (if not provided not done)
+// var2 = if nonzero, do the old-style spinning using this as the angle difference
+//
+void A_SharpSpin(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ angle_t oldang = actor->angle;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SharpSpin", actor))
+ return;
+#endif
+
+ if (actor->threshold && actor->target)
+ {
+ angle_t ang = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ P_Thrust(actor, ang, actor->info->speed*actor->scale);
+ if (locvar2)
+ actor->angle += locvar2; // ANGLE_22h;
+ else
+ actor->angle = ang;
+ actor->threshold--;
+ if (leveltime & 1)
+ S_StartSound(actor, actor->info->painsound);
+ }
+ else
+ {
+ actor->reactiontime = actor->info->reactiontime;
+ P_SetMobjState(actor, actor->info->meleestate);
+ }
+
+ P_SharpDust(actor, locvar1, oldang);
+}
+
+// Function: A_SharpDecel
+//
+// Description: Slow down the Spincushion
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SharpDecel(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SharpDecel", actor))
+ return;
+#endif
+
+ if (actor->momx > 2 || actor->momy > 2)
+ {
+ actor->momx >>= 1;
+ actor->momy >>= 1;
+ }
+ else
+ P_SetMobjState(actor, actor->info->xdeathstate);
+}
+
+// Function: A_CrushstaceanWalk
+//
+// Description: Crushstacean movement
+//
+// var1 = speed (actor info's speed if 0)
+// var2 = state to switch to when blocked (spawnstate if 0)
+//
+void A_CrushstaceanWalk(mobj_t *actor)
+{
+ INT32 locvar1 = (var1 ? var1 : (INT32)actor->info->speed);
+ INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate);
+ angle_t ang = actor->angle + ((actor->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CrushstaceanWalk", actor))
+ return;
+#endif
+
+ actor->reactiontime--;
+
+ if (!P_TryMove(actor,
+ actor->x + P_ReturnThrustX(actor, ang, locvar1*actor->scale),
+ actor->y + P_ReturnThrustY(actor, ang, locvar1*actor->scale),
+ false)
+ || (actor->reactiontime-- <= 0))
+ {
+ actor->flags2 ^= MF2_AMBUSH;
+ P_SetMobjState(actor, locvar2);
+ actor->reactiontime = actor->info->reactiontime;
+ }
+}
+
+// Function: A_CrushstaceanPunch
+//
+// Description: Crushstacean attack
+//
+// var1 = unused
+// var2 = state to go to if unsuccessful (spawnstate if 0)
+//
+void A_CrushstaceanPunch(mobj_t *actor)
+{
+ //INT32 locvar1 = var1;
+ INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CrushstaceanPunch", actor))
+ return;
+#endif
+
+ if (!actor->tracer)
+ return;
+
+ if (!actor->target)
+ {
+ P_SetMobjState(actor, locvar2);
+ return;
+ }
+
+ actor->tracer->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ P_SetMobjState(actor->tracer, actor->tracer->info->missilestate);
+ actor->tracer->extravalue1 = actor->tracer->extravalue2 = 0;
+ S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_CrushclawAim
+//
+// Description: Crushstacean claw aiming
+//
+// var1 = sideways offset
+// var2 = vertical offset
+//
+void A_CrushclawAim(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *crab = actor->tracer;
+ angle_t ang;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CrushclawAim", actor))
+ return;
+#endif
+
+ if (!crab)
+ {
+ P_RemoveMobj(actor);
+ return; // there is only one step and it is crab
+ }
+
+ if (crab->target || P_LookForPlayers(crab, true, false, 600*crab->scale))
+ ang = R_PointToAngle2(crab->x, crab->y, crab->target->x, crab->target->y);
+ else
+ ang = crab->angle + ((crab->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270);
+ ang -= actor->angle;
+
+#define anglimit ANGLE_22h
+#define angfactor 5
+ if (ang < ANGLE_180)
+ {
+ if (ang > anglimit)
+ ang = anglimit;
+ ang /= angfactor;
+ }
+ else
+ {
+ ang = InvAngle(ang);
+ if (ang > anglimit)
+ ang = anglimit;
+ ang = InvAngle(ang/angfactor);
+ }
+ actor->angle += ang;
+#undef anglimit
+#undef angfactor
+
+ P_TeleportMove(actor,
+ crab->x + P_ReturnThrustX(actor, actor->angle, locvar1*crab->scale),
+ crab->y + P_ReturnThrustY(actor, actor->angle, locvar1*crab->scale),
+ crab->z + locvar2*crab->scale);
+
+ if (!crab->target || !crab->info->missilestate || (statenum_t)(crab->state-states) == crab->info->missilestate)
+ return;
+
+ if (((ang + ANG1) < ANG2) || P_AproxDistance(crab->x - crab->target->x, crab->y - crab->target->y) < 333*crab->scale)
+ P_SetMobjState(crab, crab->info->missilestate);
+}
+
+// Function: A_CrushclawLaunch
+//
+// Description: Crushstacean claw launching
+//
+// var1:
+// 0 - forwards
+// anything else - backwards
+// var2 = state to change to when done
+//
+void A_CrushclawLaunch(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *crab = actor->tracer;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CrushclawLaunch", actor))
+ return;
+#endif
+
+ if (!crab)
+ {
+ mobj_t *chainnext;
+ while (actor)
+ {
+ chainnext = actor->target;
+ P_RemoveMobj(actor);
+ actor = chainnext;
+ }
+ return; // there is only one step and it is crab
+ }
+
+ if (!actor->extravalue1)
+ {
+ S_StartSound(actor, actor->info->activesound);
+ actor->extravalue1 = ((locvar1) ? -1 : 32);
+ }
+ else if (actor->extravalue1 != 1)
+ actor->extravalue1 -= 1;
+
+#define CSEGS 5
+ if (!actor->target)
+ {
+ mobj_t *prevchain = actor;
+ UINT8 i = 0;
+ for (i = 0; (i < CSEGS); i++)
+ {
+ mobj_t *newchain = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->info->raisestate);
+ prevchain->target = newchain;
+ prevchain = newchain;
+ }
+ actor->target->angle = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y);
+ }
+
+ if ((!locvar1) && crab->target)
+ {
+#define anglimit ANGLE_22h
+#define angfactor 7
+ angle_t ang = R_PointToAngle2(actor->target->x, actor->target->y, crab->target->x, crab->target->y) - actor->target->angle;
+ if (ang < ANGLE_180)
+ {
+ if (ang > anglimit)
+ ang = anglimit;
+ ang /= angfactor;
+ }
+ else
+ {
+ ang = InvAngle(ang);
+ if (ang > anglimit)
+ ang = anglimit;
+ ang /= angfactor;
+ ang = InvAngle(ang);
+ }
+ actor->target->angle += ang;
+ actor->angle = actor->target->angle;
+ }
+
+ actor->extravalue2 += actor->extravalue1;
+
+ if (!P_TryMove(actor,
+ actor->target->x + P_ReturnThrustX(actor, actor->target->angle, actor->extravalue2*actor->scale),
+ actor->target->y + P_ReturnThrustY(actor, actor->target->angle, actor->extravalue2*actor->scale),
+ true)
+ && !locvar1)
+ {
+ actor->extravalue1 = 0;
+ actor->extravalue2 = FixedHypot(actor->x - actor->target->x, actor->y - actor->target->y)>>FRACBITS;
+ P_SetMobjState(actor, locvar2);
+ S_StopSound(actor);
+ S_StartSound(actor, sfx_s3k49);
+ }
+ else
+ {
+ actor->z = actor->target->z;
+ if ((!locvar1 && (actor->extravalue2 > 256)) || (locvar1 && (actor->extravalue2 < 16)))
+ {
+ if (locvar1) // In case of retracting, resume crab and remove the chain.
+ {
+ mobj_t *chain = actor->target, *chainnext;
+ while (chain)
+ {
+ chainnext = chain->target;
+ P_RemoveMobj(chain);
+ chain = chainnext;
+ }
+ actor->extravalue2 = 0;
+ actor->angle = R_PointToAngle2(crab->x, crab->y, actor->x, actor->y);
+ P_SetTarget(&actor->target, NULL);
+ P_SetTarget(&crab->target, NULL);
+ P_SetMobjState(crab, crab->state->nextstate);
+ }
+ actor->extravalue1 = 0;
+ P_SetMobjState(actor, locvar2);
+ S_StopSound(actor);
+ if (!locvar1)
+ S_StartSound(actor, sfx_s3k64);
+ }
+ }
+
+ if (!actor->target)
+ return;
+
+ {
+ mobj_t *chain = actor->target->target;
+ fixed_t dx = (actor->x - actor->target->x)/CSEGS, dy = (actor->y - actor->target->y)/CSEGS, dz = (actor->z - actor->target->z)/CSEGS;
+ fixed_t idx = dx, idy = dy, idz = dz;
+ while (chain)
+ {
+ P_TeleportMove(chain, actor->target->x + idx, actor->target->y + idy, actor->target->z + idz);
+ chain->watertop = chain->z;
+ idx += dx;
+ idy += dy;
+ idz += dz;
+ chain = chain->target;
+ }
+ }
+#undef CSEGS
+}
+
+// Function: A_VultureVtol
+//
+// Description: Vulture rising up to match target's height
+//
+// var1 = unused
+// var2 = unused
+//
+void A_VultureVtol(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_VultureVtol", actor))
+ return;
+#endif
+
+ if (!actor->target)
+ return;
+
+ actor->flags |= MF_NOGRAVITY;
+ actor->flags |= MF_FLOAT;
+
+ A_FaceTarget(actor);
+
+ S_StopSound(actor);
+
+ if (actor->z < actor->target->z+(actor->target->height/4) && actor->z + actor->height < actor->ceilingz)
+ actor->momz = FixedMul(2*FRACUNIT, actor->scale);
+ else if (actor->z > (actor->target->z+(actor->target->height/4)*3) && actor->z > actor->floorz)
+ actor->momz = FixedMul(-2*FRACUNIT, actor->scale);
+ else
+ {
+ // Attack!
+ actor->momz = 0;
+ P_SetMobjState(actor, actor->info->missilestate);
+ S_StartSound(actor, actor->info->activesound);
+ }
+}
+
+// Function: A_VultureCheck
+//
+// Description: If the vulture is stopped, look for a new target
+//
+// var1 = unused
+// var2 = unused
+//
+void A_VultureCheck(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_VultureCheck", actor))
+ return;
+#endif
+
+ if (actor->momx || actor->momy)
+ return;
+
+ actor->flags &= ~MF_NOGRAVITY; // Fall down
+
+ if (actor->z <= actor->floorz)
+ {
+ actor->angle -= ANGLE_180; // turn around
+ P_SetMobjState(actor, actor->info->spawnstate);
+ }
+}
+
+// Function: A_SkimChase
+//
+// Description: Thinker/Chase routine for Skims
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SkimChase(mobj_t *actor)
+{
+ INT32 delta;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SkimChase", actor))
+ return;
+#endif
+ if (actor->reactiontime)
+ actor->reactiontime--;
+
+ // modify target threshold
+ if (actor->threshold)
+ {
+ if (!actor->target || actor->target->health <= 0)
+ actor->threshold = 0;
+ else
+ actor->threshold--;
+ }
+
+ // turn towards movement direction if not there yet
+ if (actor->movedir < NUMDIRS)
+ {
+ actor->angle &= (7<<29);
+ delta = actor->angle - (actor->movedir << 29);
+
+ if (delta > 0)
+ actor->angle -= ANGLE_45;
+ else if (delta < 0)
+ actor->angle += ANGLE_45;
+ }
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ P_LookForPlayers(actor, true, false, 0);
+
+ // the spawnstate for skims already calls this function so just return either way
+ // without changing state
+ return;
+ }
+
+ // do not attack twice in a row
+ if (actor->flags2 & MF2_JUSTATTACKED)
+ {
+ actor->flags2 &= ~MF2_JUSTATTACKED;
+ P_NewChaseDir(actor);
+ return;
+ }
+
+ // check for melee attack
+ if (actor->info->meleestate && P_SkimCheckMeleeRange(actor))
+ {
+ if (actor->info->attacksound)
+ S_StartAttackSound(actor, actor->info->attacksound);
+
+ P_SetMobjState(actor, actor->info->meleestate);
+ return;
+ }
+
+ // check for missile attack
+ if (actor->info->missilestate)
+ {
+ if (actor->movecount || !P_CheckMissileRange(actor))
+ goto nomissile;
+
+ P_SetMobjState(actor, actor->info->missilestate);
+ actor->flags2 |= MF2_JUSTATTACKED;
+ return;
+ }
+
+nomissile:
+ // possibly choose another target
+ if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+ && P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ // chase towards player
+ if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+ P_NewChaseDir(actor);
+}
+
+// Function: A_FaceTarget
+//
+// Description: Immediately turn to face towards your target.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_FaceTarget(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FaceTarget", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+}
+
+// Function: A_FaceTracer
+//
+// Description: Immediately turn to face towards your tracer.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_FaceTracer(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FaceTracer", actor))
+ return;
+#endif
+ if (!actor->tracer)
+ return;
+
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
+}
+
+// Function: A_LobShot
+//
+// Description: Lob an object at your target.
+//
+// var1 = object # to lob
+// var2:
+// var2 >> 16 = height offset
+// var2 & 65535 = airtime
+//
+void A_LobShot(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2 >> 16;
+ mobj_t *shot, *hitspot;
+ angle_t an;
+ fixed_t z;
+ fixed_t dist;
+ fixed_t vertical, horizontal;
+ fixed_t airtime = var2 & 65535;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_LobShot", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ A_FaceTarget(actor);
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ {
+ z = actor->z + actor->height - FixedMul(locvar2*FRACUNIT, actor->scale);
+ if (actor->type == MT_BLACKEGGMAN)
+ z -= FixedMul(mobjinfo[locvar1].height, actor->scale/2);
+ else
+ z -= FixedMul(mobjinfo[locvar1].height, actor->scale);
+ }
+ else
+ z = actor->z + FixedMul(locvar2*FRACUNIT, actor->scale);
+
+ shot = P_SpawnMobj(actor->x, actor->y, z, locvar1);
+
+ if (actor->type == MT_BLACKEGGMAN)
+ {
+ shot->destscale = actor->scale/2;
+ P_SetScale(shot, actor->scale/2);
+ }
+ else
+ {
+ shot->destscale = actor->scale;
+ P_SetScale(shot, actor->scale);
+ }
+
+ // Keep track of where it's going to land
+ hitspot = P_SpawnMobj(actor->target->x&(64*FRACUNIT-1), actor->target->y&(64*FRACUNIT-1), actor->target->subsector->sector->floorheight, MT_NULL);
+ hitspot->tics = airtime;
+ P_SetTarget(&shot->tracer, hitspot);
+
+ P_SetTarget(&shot->target, actor); // where it came from
+
+ shot->angle = an = actor->angle;
+ an >>= ANGLETOFINESHIFT;
+
+ dist = P_AproxDistance(actor->target->x - shot->x, actor->target->y - shot->y);
+
+ horizontal = dist / airtime;
+ vertical = FixedMul((gravity*airtime)/2, shot->scale);
+
+ shot->momx = FixedMul(horizontal, FINECOSINE(an));
+ shot->momy = FixedMul(horizontal, FINESINE(an));
+ shot->momz = vertical;
+
+/* Try to adjust when destination is not the same height
+ if (actor->z != actor->target->z)
+ {
+ fixed_t launchhyp;
+ fixed_t diff;
+ fixed_t orig;
+
+ diff = actor->z - actor->target->z;
+ {
+ launchhyp = P_AproxDistance(horizontal, vertical);
+
+ orig = FixedMul(FixedDiv(vertical, horizontal), diff);
+
+ CONS_Debug(DBG_GAMELOGIC, "orig: %d\n", (orig)>>FRACBITS);
+
+ horizontal = dist / airtime;
+ vertical = (gravity*airtime)/2;
+ }
+ dist -= orig;
+ shot->momx = FixedMul(horizontal, FINECOSINE(an));
+ shot->momy = FixedMul(horizontal, FINESINE(an));
+ shot->momz = vertical;
+*/
+
+ if (shot->info->seesound)
+ S_StartSound(shot, shot->info->seesound);
+
+ if (!(actor->flags & MF_BOSS))
+ {
+ if (ultimatemode)
+ actor->reactiontime = actor->info->reactiontime*TICRATE;
+ else
+ actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+ }
+}
+
+// Function: A_FireShot
+//
+// Description: Shoot an object at your target.
+//
+// var1 = object # to shoot
+// var2 = height offset
+//
+void A_FireShot(mobj_t *actor)
+{
+ fixed_t z;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FireShot", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ A_FaceTarget(actor);
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+
+ P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
+
+ if (!(actor->flags & MF_BOSS))
+ {
+ if (ultimatemode)
+ actor->reactiontime = actor->info->reactiontime*TICRATE;
+ else
+ actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+ }
+}
+
+// Function: A_SuperFireShot
+//
+// Description: Shoot an object at your target that will even stall Super Sonic.
+//
+// var1 = object # to shoot
+// var2 = height offset
+//
+void A_SuperFireShot(mobj_t *actor)
+{
+ fixed_t z;
+ mobj_t *mo;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SuperFireShot", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ A_FaceTarget(actor);
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+
+ mo = P_SpawnXYZMissile(actor, actor->target, locvar1, actor->x, actor->y, z);
+
+ if (mo)
+ mo->flags2 |= MF2_SUPERFIRE;
+
+ if (!(actor->flags & MF_BOSS))
+ {
+ if (ultimatemode)
+ actor->reactiontime = actor->info->reactiontime*TICRATE;
+ else
+ actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+ }
+}
+
+// Function: A_BossFireShot
+//
+// Description: Shoot an object at your target ala Bosses:
+//
+// var1 = object # to shoot
+// var2:
+// 0 - Boss 1 Left side
+// 1 - Boss 1 Right side
+// 2 - Boss 3 Left side upper
+// 3 - Boss 3 Left side lower
+// 4 - Boss 3 Right side upper
+// 5 - Boss 3 Right side lower
+//
+void A_BossFireShot(mobj_t *actor)
+{
+ fixed_t x, y, z;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BossFireShot", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ A_FaceTarget(actor);
+
+ switch (locvar2)
+ {
+ case 0:
+ x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+ y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
+ break;
+ case 1:
+ x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+ y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(48*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(48*FRACUNIT, actor->scale);
+ break;
+ case 2:
+ x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
+ y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
+ break;
+ case 3:
+ x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
+ y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
+ break;
+ case 4:
+ x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
+ y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(56*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(42*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(42*FRACUNIT, actor->scale);
+ break;
+ case 5:
+ x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
+ y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(58*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(30*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(30*FRACUNIT, actor->scale);
+ break;
+ default:
+ x = actor->x;
+ y = actor->y;
+ z = actor->z + actor->height/2;
+ break;
+ }
+
+ P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
+}
+
+// Function: A_Boss7FireMissiles
+//
+// Description: Shoot 4 missiles of a specific object type at your target ala Black Eggman
+//
+// var1 = object # to shoot
+// var2 = firing sound
+//
+void A_Boss7FireMissiles(mobj_t *actor)
+{
+ mobj_t dummymo;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss7FireMissiles", actor))
+ return;
+#endif
+
+ if (!actor->target)
+ {
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ A_FaceTarget(actor);
+
+ S_StartSound(NULL, locvar2);
+
+ // set dummymo's coordinates
+ dummymo.x = actor->target->x;
+ dummymo.y = actor->target->y;
+ dummymo.z = actor->target->z + FixedMul(16*FRACUNIT, actor->scale); // raised height
+
+ P_SpawnXYZMissile(actor, &dummymo, locvar1,
+ actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+ actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+ actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
+
+ P_SpawnXYZMissile(actor, &dummymo, locvar1,
+ actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+ actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+ actor->z + FixedDiv(actor->height, 3*FRACUNIT/2));
+
+ P_SpawnXYZMissile(actor, &dummymo, locvar1,
+ actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+ actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+ actor->z + actor->height/2);
+
+ P_SpawnXYZMissile(actor, &dummymo, locvar1,
+ actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+ actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedDiv(actor->radius, 3*FRACUNIT/2)+FixedMul(4*FRACUNIT, actor->scale)),
+ actor->z + actor->height/2);
+}
+
+// Function: A_Boss1Laser
+//
+// Description: Shoot an object at your target ala Bosses:
+//
+// var1 = object # to shoot
+// var2:
+// 0 - Boss 1 Left side
+// 1 - Boss 1 Right side
+//
+void A_Boss1Laser(mobj_t *actor)
+{
+ fixed_t x, y, z, floorz, speed;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ INT32 i;
+ angle_t angle;
+ mobj_t *point;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss1Laser", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ switch (locvar2)
+ {
+ case 0:
+ x = actor->x + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+ y = actor->y + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
+ else
+ z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
+ break;
+ case 1:
+ x = actor->x + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+ y = actor->y + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(43*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(56*FRACUNIT, actor->scale) - mobjinfo[locvar1].height;
+ else
+ z = actor->z + FixedMul(56*FRACUNIT, actor->scale);
+ break;
+ default:
+ x = actor->x;
+ y = actor->y;
+ z = actor->z + actor->height/2;
+ break;
+ }
+
+ if (!(actor->flags2 & MF2_FIRING))
+ {
+ actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);
+ if (mobjinfo[locvar1].seesound)
+ S_StartSound(actor, mobjinfo[locvar1].seesound);
+ if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
+ {
+ point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET);
+ point->angle = actor->angle;
+ point->fuse = actor->tics+1;
+ P_SetTarget(&point->target, actor->target);
+ P_SetTarget(&actor->target, point);
+ }
+ }
+ /* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path
+ else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
+ actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/
+
+ if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)
+ angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT);
+ else
+ angle = R_PointToAngle2(z + (mobjinfo[locvar1].height>>1), 0, actor->target->z, R_PointToDist2(x, y, actor->target->x, actor->target->y));
+ point = P_SpawnMobj(x, y, z, locvar1);
+ P_SetTarget(&point->target, actor);
+ point->angle = actor->angle;
+ speed = point->radius*2;
+ point->momz = FixedMul(FINECOSINE(angle>>ANGLETOFINESHIFT), speed);
+ point->momx = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(point->angle>>ANGLETOFINESHIFT), speed));
+ point->momy = FixedMul(FINESINE(angle>>ANGLETOFINESHIFT), FixedMul(FINESINE(point->angle>>ANGLETOFINESHIFT), speed));
+
+ for (i = 0; i < 256; i++)
+ {
+ mobj_t *mo = P_SpawnMobj(point->x, point->y, point->z, point->type);
+ mo->angle = point->angle;
+ P_UnsetThingPosition(mo);
+ mo->flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY;
+ P_SetThingPosition(mo);
+
+ x = point->x, y = point->y, z = point->z;
+ if (P_RailThinker(point))
+ break;
+ }
+
+ floorz = P_FloorzAtPos(x, y, z, mobjinfo[MT_EGGMOBILE_FIRE].height);
+ if (z - floorz < mobjinfo[MT_EGGMOBILE_FIRE].height>>1)
+ {
+ point = P_SpawnMobj(x, y, floorz+1, MT_EGGMOBILE_FIRE);
+ point->target = actor;
+ point->destscale = 3*FRACUNIT;
+ point->scalespeed = FRACUNIT>>2;
+ point->fuse = TICRATE;
+ }
+
+ if (actor->tics > 1)
+ actor->flags2 |= MF2_FIRING;
+ else
+ actor->flags2 &= ~MF2_FIRING;
+}
+
+// Function: A_FocusTarget
+//
+// Description: Home in on your target.
+//
+// var1:
+// 0 - accelerative focus with friction
+// 1 - steady focus with fixed movement speed
+// anything else - don't move
+// var2:
+// 0 - don't trace target, just move forwards
+// & 1 - change horizontal angle
+// & 2 - change vertical angle
+//
+void A_FocusTarget(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FocusTarget", actor))
+ return;
+#endif
+
+ if (actor->target)
+ {
+ fixed_t speed = FixedMul(actor->info->speed, actor->scale);
+ fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1);
+ angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle);
+ angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90);
+ switch(locvar1)
+ {
+ case 0:
+ {
+ actor->momx -= actor->momx>>4, actor->momy -= actor->momy>>4, actor->momz -= actor->momz>>4;
+ actor->momz += FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
+ actor->momx += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
+ actor->momy += FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
+ }
+ break;
+ case 1:
+ if (dist > speed)
+ {
+ actor->momz = FixedMul(FINECOSINE(vangle>>ANGLETOFINESHIFT), speed);
+ actor->momx = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(hangle>>ANGLETOFINESHIFT), speed));
+ actor->momy = FixedMul(FINESINE(vangle>>ANGLETOFINESHIFT), FixedMul(FINESINE(hangle>>ANGLETOFINESHIFT), speed));
+ }
+ else
+ {
+ actor->momx = 0, actor->momy = 0, actor->momz = 0;
+ actor->z = actor->target->z + (actor->target->height>>1);
+ P_TryMove(actor, actor->target->x, actor->target->y, true);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+// Function: A_Boss4Reverse
+//
+// Description: Reverse arms direction.
+//
+// var1 = sfx to play
+// var2 = unused
+//
+void A_Boss4Reverse(mobj_t *actor)
+{
+ sfxenum_t locvar1 = (sfxenum_t)var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss4Reverse", actor))
+ return;
+#endif
+ S_StartSound(NULL, locvar1);
+ actor->reactiontime = 0;
+ if (actor->movedir == 1)
+ actor->movedir = 2;
+ else
+ actor->movedir = 1;
+}
+
+// Function: A_Boss4SpeedUp
+//
+// Description: Speed up arms
+//
+// var1 = sfx to play
+// var2 = unused
+//
+void A_Boss4SpeedUp(mobj_t *actor)
+{
+ sfxenum_t locvar1 = (sfxenum_t)var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss4SpeedUp", actor))
+ return;
+#endif
+ S_StartSound(NULL, locvar1);
+ actor->reactiontime = 2;
+}
+
+// Function: A_Boss4Raise
+//
+// Description: Raise helmet
+//
+// var1 = sfx to play
+// var2 = unused
+//
+void A_Boss4Raise(mobj_t *actor)
+{
+ sfxenum_t locvar1 = (sfxenum_t)var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss4Raise", actor))
+ return;
+#endif
+ S_StartSound(NULL, locvar1);
+ actor->reactiontime = 1;
+}
+
+// Function: A_SkullAttack
+//
+// Description: Fly at the player like a missile.
+//
+// var1:
+// 0 - Fly at the player
+// 1 - Fly away from the player
+// 2 - Strafe in relation to the player
+// var2:
+// 0 - Fly horizontally and vertically
+// 1 - Fly horizontal-only (momz = 0)
+//
+#define SKULLSPEED (20*FRACUNIT)
+
+void A_SkullAttack(mobj_t *actor)
+{
+ mobj_t *dest;
+ angle_t an;
+ INT32 dist;
+ INT32 speed;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SkullAttack", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ speed = FixedMul(SKULLSPEED, actor->scale);
+
+ dest = actor->target;
+ actor->flags2 |= MF2_SKULLFLY;
+ if (actor->info->activesound)
+ S_StartSound(actor, actor->info->activesound);
+ A_FaceTarget(actor);
+
+ if (locvar1 == 1)
+ actor->angle += ANGLE_180;
+ else if (locvar1 == 2)
+ actor->angle += (P_RandomChance(FRACUNIT/2)) ? ANGLE_90 : -ANGLE_90;
+
+ an = actor->angle >> ANGLETOFINESHIFT;
+
+ actor->momx = FixedMul(speed, FINECOSINE(an));
+ actor->momy = FixedMul(speed, FINESINE(an));
+ dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
+ dist = dist / speed;
+
+ if (dist < 1)
+ dist = 1;
+
+ actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
+
+ if (locvar1 == 1)
+ actor->momz = -actor->momz;
+ if (locvar2 == 1)
+ actor->momz = 0;
+}
+
+// Function: A_BossZoom
+//
+// Description: Like A_SkullAttack, but used by Boss 1.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_BossZoom(mobj_t *actor)
+{
+ mobj_t *dest;
+ angle_t an;
+ INT32 dist;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BossZoom", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ dest = actor->target;
+ actor->flags2 |= MF2_SKULLFLY;
+ if (actor->info->attacksound)
+ S_StartAttackSound(actor, actor->info->attacksound);
+ A_FaceTarget(actor);
+ an = actor->angle >> ANGLETOFINESHIFT;
+ actor->momx = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINECOSINE(an));
+ actor->momy = FixedMul(FixedMul(actor->info->speed*5*FRACUNIT, actor->scale), FINESINE(an));
+ dist = P_AproxDistance(dest->x - actor->x, dest->y - actor->y);
+ dist = dist / FixedMul(actor->info->speed*5*FRACUNIT, actor->scale);
+
+ if (dist < 1)
+ dist = 1;
+ actor->momz = (dest->z + (dest->height>>1) - actor->z) / dist;
+}
+
+// Function: A_BossScream
+//
+// Description: Spawns explosions and plays appropriate sounds around the defeated boss.
+//
+// var1:
+// 0 - Use movecount to spawn explosions evenly
+// 1 - Use P_Random to spawn explosions at complete random
+// var2 = Object to spawn. Default is MT_BOSSEXPLODE.
+//
+void A_BossScream(mobj_t *actor)
+{
+ mobj_t *mo;
+ fixed_t x, y, z;
+ angle_t fa;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobjtype_t explodetype;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BossScream", actor))
+ return;
+#endif
+ switch (locvar1)
+ {
+ default:
+ case 0:
+ actor->movecount += 4*16;
+ actor->movecount %= 360;
+ fa = (FixedAngle(actor->movecount*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
+ break;
+ case 1:
+ fa = (FixedAngle(P_RandomKey(360)*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
+ break;
+ }
+ x = actor->x + FixedMul(FINECOSINE(fa),actor->radius);
+ y = actor->y + FixedMul(FINESINE(fa),actor->radius);
+
+ // Determine what mobj to spawn. If undefined or invalid, use MT_BOSSEXPLODE as default.
+ if (locvar2 <= 0 || locvar2 >= NUMMOBJTYPES)
+ explodetype = MT_BOSSEXPLODE;
+ else
+ explodetype = (mobjtype_t)locvar2;
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - mobjinfo[explodetype].height - FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul((P_RandomByte()<<(FRACBITS-2)) - 8*FRACUNIT, actor->scale);
+
+ mo = P_SpawnMobj(x, y, z, explodetype);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ mo->flags2 |= MF2_OBJECTFLIP;
+ mo->destscale = actor->scale;
+ P_SetScale(mo, mo->destscale);
+ if (actor->info->deathsound)
+ S_StartSound(mo, actor->info->deathsound);
+}
+
+// Function: A_Scream
+//
+// Description: Starts the death sound of the object.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Scream(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Scream", actor))
+ return;
+#endif
+ if (actor->tracer && (actor->tracer->type == MT_SHELL || actor->tracer->type == MT_FIREBALL))
+ S_StartScreamSound(actor, sfx_mario2);
+ else if (actor->info->deathsound)
+ S_StartScreamSound(actor, actor->info->deathsound);
+}
+
+// Function: A_Pain
+//
+// Description: Starts the pain sound of the object.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Pain(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Pain", actor))
+ return;
+#endif
+ if (actor->info->painsound)
+ S_StartSound(actor, actor->info->painsound);
+
+ actor->flags2 &= ~MF2_FIRING;
+ actor->flags2 &= ~MF2_SUPERFIRE;
+}
+
+// Function: A_Fall
+//
+// Description: Changes a dying object's flags to reflect its having fallen to the ground.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Fall(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Fall", actor))
+ return;
+#endif
+ // actor is on ground, it can be walked over
+ actor->flags &= ~MF_SOLID;
+
+ // fall through the floor
+ actor->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY;
+
+ // So change this if corpse objects
+ // are meant to be obstacles.
+}
+
+#define LIVESBOXDISPLAYPLAYER // Use displayplayer instead of closest player
+
+// Function: A_1upThinker
+//
+// Description: Used by the 1up box to show the player's face.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_1upThinker(mobj_t *actor)
+{
+ INT32 i;
+ fixed_t dist = INT32_MAX;
+ fixed_t temp;
+ INT32 closestplayer = -1;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_1upThinker", actor))
+ return;
+#endif
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].bot || players[i].spectator)
+ continue;
+
+ if (!players[i].mo)
+ continue;
+
+ if ((netgame || multiplayer) && players[i].playerstate != PST_LIVE)
+ continue;
+
+ temp = P_AproxDistance(players[i].mo->x-actor->x, players[i].mo->y-actor->y);
+
+ if (temp < dist)
+ {
+ closestplayer = i;
+ dist = temp;
+ }
+ }
+
+ if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0)
+ { // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite.
+ if (actor->tracer) {
+ P_RemoveMobj(actor->tracer);
+ actor->tracer = NULL;
+ }
+ return;
+ }
+
+ // We're using the overlay, so use the overlay 1up box (no text)
+ actor->sprite = SPR_TV1P;
+
+ if (!actor->tracer)
+ {
+ P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY));
+ P_SetTarget(&actor->tracer->target, actor);
+ actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame
+ P_SetMobjState(actor->tracer, actor->info->seestate);
+
+ // The overlay is going to be one tic early turning off and on
+ // because it's going to get its thinker run the frame we spawned it.
+ // So make it take one tic longer if it just spawned.
+ ++actor->tracer->tics;
+ }
+
+ actor->tracer->color = players[closestplayer].mo->color;
+ actor->tracer->skin = &skins[players[closestplayer].skin];
+}
+
+// Function: A_MonitorPop
+//
+// Description: Used by monitors when they explode.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MonitorPop(mobj_t *actor)
+{
+ mobjtype_t item = 0;
+ mobj_t *newmobj;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MonitorPop", actor))
+ return;
+#endif
+
+ // Spawn the "pop" explosion.
+ if (actor->info->deathsound)
+ S_StartSound(actor, actor->info->deathsound);
+ P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_EXPLODE);
+
+ // We're dead now. De-solidify.
+ actor->health = 0;
+ P_UnsetThingPosition(actor);
+ actor->flags &= ~MF_SOLID;
+ actor->flags |= MF_NOCLIP;
+ P_SetThingPosition(actor);
+
+ if (actor->info->damage == MT_UNKNOWN)
+ {
+ // MT_UNKNOWN is random. Because it's unknown to us... get it?
+ item = P_DoRandomBoxChances();
+
+ if (item == MT_NULL)
+ {
+ CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
+ return;
+ }
+ }
+ else
+ item = actor->info->damage;
+
+ if (item == 0)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_MonitorPop\n");
+ return;
+ }
+
+ newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 13*FRACUNIT, item);
+ P_SetTarget(&newmobj->target, actor->target); // Transfer target
+
+ if (item == MT_1UP_ICON)
+ {
+ if (actor->tracer) // Remove the old lives icon.
+ P_RemoveMobj(actor->tracer);
+
+ if (!newmobj->target
+ || !newmobj->target->player
+ || !newmobj->target->skin
+ || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
+ {} // No lives icon for this player, use the default.
+ else
+ { // Spawn the lives icon.
+ mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
+ P_SetTarget(&livesico->target, newmobj);
+ P_SetTarget(&newmobj->tracer, livesico);
+
+ livesico->color = newmobj->target->player->mo->color;
+ livesico->skin = &skins[newmobj->target->player->skin];
+ P_SetMobjState(livesico, newmobj->info->seestate);
+
+ // We're using the overlay, so use the overlay 1up sprite (no text)
+ newmobj->sprite = SPR_TV1P;
+ }
+ }
+
+ // Run a linedef executor immediately upon popping
+ // You may want to delay your effects by 18 tics to sync with the reward giving
+ if (actor->spawnpoint && actor->lastlook)
+ P_LinedefExecute(actor->lastlook, actor->target, NULL);
+}
+
+// Function: A_GoldMonitorPop
+//
+// Description: Used by repeating monitors when they turn off. They don't really pop, but, you know...
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoldMonitorPop(mobj_t *actor)
+{
+ mobjtype_t item = 0;
+ mobj_t *newmobj;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_GoldMonitorPop", actor))
+ return;
+#endif
+
+ // Don't spawn the "pop" explosion, because the monitor isn't broken.
+ if (actor->info->deathsound)
+ S_StartSound(actor, actor->info->deathsound);
+ //P_SpawnMobjFromMobj(actor, 0, 0, actor.height/4, MT_EXPLODE);
+
+ // Remove our flags for a bit.
+ // Players can now stand on top of us.
+ P_UnsetThingPosition(actor);
+ actor->flags &= ~(MF_MONITOR|MF_SHOOTABLE);
+ P_SetThingPosition(actor);
+
+ // Don't count this box in statistics. Sorry.
+ if (actor->target && actor->target->player)
+ --actor->target->player->numboxes;
+ actor->fuse = 0; // Don't let the monitor code screw us up.
+
+ if (actor->info->damage == MT_UNKNOWN)
+ {
+ // MT_UNKNOWN is random. Because it's unknown to us... get it?
+ item = P_DoRandomBoxChances();
+
+ if (item == MT_NULL)
+ {
+ CONS_Alert(CONS_WARNING, M_GetText("All monitors turned off.\n"));
+ return;
+ }
+ }
+ else
+ item = actor->info->damage;
+
+ if (item == 0)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup item not defined in 'damage' field for A_GoldMonitorPop\n");
+ return;
+ }
+
+ // Note: the icon spawns 1 fracunit higher
+ newmobj = P_SpawnMobjFromMobj(actor, 0, 0, 14*FRACUNIT, item);
+ P_SetTarget(&newmobj->target, actor->target); // Transfer target
+
+ if (item == MT_1UP_ICON)
+ {
+ if (actor->tracer) // Remove the old lives icon.
+ P_RemoveMobj(actor->tracer);
+
+ if (!newmobj->target
+ || !newmobj->target->player
+ || !newmobj->target->skin
+ || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
+ {} // No lives icon for this player, use the default.
+ else
+ { // Spawn the lives icon.
+ mobj_t *livesico = P_SpawnMobjFromMobj(newmobj, 0, 0, 0, MT_OVERLAY);
+ P_SetTarget(&livesico->target, newmobj);
+ P_SetTarget(&newmobj->tracer, livesico);
+
+ livesico->color = newmobj->target->player->mo->color;
+ livesico->skin = &skins[newmobj->target->player->skin];
+ P_SetMobjState(livesico, newmobj->info->seestate);
+
+ // We're using the overlay, so use the overlay 1up sprite (no text)
+ newmobj->sprite = SPR_TV1P;
+ }
+ }
+
+ // Run a linedef executor immediately upon popping
+ // You may want to delay your effects by 18 tics to sync with the reward giving
+ if (actor->spawnpoint && actor->lastlook)
+ P_LinedefExecute(actor->lastlook, actor->target, NULL);
+}
+
+// Function: A_GoldMonitorRestore
+//
+// Description: A repeating monitor is coming back to life. Reset monitor flags, etc.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoldMonitorRestore(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_GoldMonitorRestore", actor))
+ return;
+#endif
+
+ actor->flags |= MF_MONITOR|MF_SHOOTABLE;
+ actor->health = 1; // Just in case.
+}
+
+// Function: A_GoldMonitorSparkle
+//
+// Description: Spawns the little sparkly effect around big monitors. Looks pretty, doesn't it?
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoldMonitorSparkle(mobj_t *actor)
+{
+ fixed_t i, ngangle, xofs, yofs;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_GoldMonitorSparkle", actor))
+ return;
+#endif
+
+ ngangle = FixedAngle(((leveltime * 21) % 360) << FRACBITS);
+ xofs = FINESINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS);
+ yofs = FINECOSINE((ngangle>>ANGLETOFINESHIFT) & FINEMASK) * (actor->radius>>FRACBITS);
+
+ for (i = FRACUNIT*2; i <= FRACUNIT*3; i += FRACUNIT/2)
+ P_SetObjectMomZ(P_SpawnMobjFromMobj(actor, xofs, yofs, 0, MT_BOXSPARKLE), i, false);
+}
+
+// Function: A_Explode
+//
+// Description: Explodes an object, doing damage to any objects nearby. The target is used as the cause of the explosion. Damage value is used as explosion range.
+//
+// var1 = damagetype
+// var2 = unused
+//
+void A_Explode(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Explode", actor))
+ return;
+#endif
+ P_RadiusAttack(actor, actor->target, actor->info->damage, locvar1);
+}
+
+// Function: A_BossDeath
+//
+// Description: Possibly trigger special effects when boss dies.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_BossDeath(mobj_t *mo)
+{
+ thinker_t *th;
+ mobj_t *mo2;
+ line_t junk;
+ INT32 i;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BossDeath", mo))
+ return;
+#endif
+
+ P_LinedefExecute(LE_BOSSDEAD, mo, NULL);
+ mo->health = 0;
+
+ // Boss is dead (but not necessarily fleeing...)
+ // Lua may use this to ignore bosses after they start fleeing
+ mo->flags2 |= MF2_BOSSDEAD;
+
+ // make sure there is a player alive for victory
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i] && ((players[i].mo && players[i].mo->health)
+ || ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
+ break;
+
+ if (i == MAXPLAYERS)
+ return; // no one left alive, so do not end game
+
+ // scan the remaining thinkers to see
+ // if all bosses are dead
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo2 = (mobj_t *)th;
+ if (mo2 != mo && (mo2->flags & MF_BOSS) && mo2->health > 0)
+ goto bossjustdie; // other boss not dead - just go straight to dying!
+ }
+
+ // victory!
+ P_LinedefExecute(LE_ALLBOSSESDEAD, mo, NULL);
+ if (mo->flags2 & MF2_BOSSNOTRAP)
+ {
+ for (i = 0; i < MAXPLAYERS; i++)
+ P_DoPlayerExit(&players[i]);
+ }
+ else
+ {
+ // Bring the egg trap up to the surface
+ junk.tag = 680;
+ EV_DoElevator(&junk, elevateHighest, false);
+ junk.tag = 681;
+ EV_DoElevator(&junk, elevateUp, false);
+ junk.tag = 682;
+ EV_DoElevator(&junk, elevateHighest, false);
+ }
+
+bossjustdie:
+#ifdef HAVE_BLUA
+ if (LUAh_BossDeath(mo))
+ return;
+ else if (P_MobjWasRemoved(mo))
+ return;
+#endif
+ if (mo->type == MT_BLACKEGGMAN || mo->type == MT_CYBRAKDEMON)
+ {
+ mo->flags |= MF_NOCLIP;
+ mo->flags &= ~MF_SPECIAL;
+
+ S_StartSound(NULL, sfx_befall);
+ }
+ else if (mo->type == MT_KOOPA)
+ {
+ junk.tag = 650;
+ EV_DoCeiling(&junk, raiseToHighest);
+ return;
+ }
+ else // eggmobiles
+ {
+ // Stop exploding and prepare to run.
+ P_SetMobjState(mo, mo->info->xdeathstate);
+ if (P_MobjWasRemoved(mo))
+ return;
+
+ P_SetTarget(&mo->target, NULL);
+
+ // Flee! Flee! Find a point to escape to! If none, just shoot upward!
+ // scan the thinkers to find the runaway point
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo2 = (mobj_t *)th;
+
+ if (mo2->type == MT_BOSSFLYPOINT)
+ {
+ // If this one's closer then the last one, go for it.
+ if (!mo->target ||
+ P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) <
+ P_AproxDistance(P_AproxDistance(mo->x - mo->target->x, mo->y - mo->target->y), mo->z - mo->target->z))
+ P_SetTarget(&mo->target, mo2);
+ // Otherwise... Don't!
+ }
+ }
+
+ mo->flags |= MF_NOGRAVITY|MF_NOCLIP;
+ mo->flags |= MF_NOCLIPHEIGHT;
+
+ if (mo->target)
+ {
+ mo->angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y);
+ mo->flags2 |= MF2_BOSSFLEE;
+ mo->momz = FixedMul(FixedDiv(mo->target->z - mo->z, P_AproxDistance(mo->x-mo->target->x,mo->y-mo->target->y)), FixedMul(2*FRACUNIT, mo->scale));
+ }
+ else
+ mo->momz = FixedMul(2*FRACUNIT, mo->scale);
+ }
+
+ if (mo->type == MT_EGGMOBILE2)
+ {
+ mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
+ mo->y + P_ReturnThrustY(mo, mo->angle - ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
+ mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK1].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK1); // Right tank
+ mo2->angle = mo->angle;
+ mo2->destscale = mo->scale;
+ P_SetScale(mo2, mo2->destscale);
+ if (mo->eflags & MFE_VERTICALFLIP)
+ {
+ mo2->eflags |= MFE_VERTICALFLIP;
+ mo2->flags2 |= MF2_OBJECTFLIP;
+ }
+ P_InstaThrust(mo2, mo2->angle - ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale));
+ P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
+
+ mo2 = P_SpawnMobj(mo->x + P_ReturnThrustX(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
+ mo->y + P_ReturnThrustY(mo, mo->angle + ANGLE_90, FixedMul(32*FRACUNIT, mo->scale)),
+ mo->z + mo->height/2 + ((mo->eflags & MFE_VERTICALFLIP)? FixedMul(8*FRACUNIT, mo->scale)-mobjinfo[MT_BOSSTANK2].height : -FixedMul(8*FRACUNIT, mo->scale)), MT_BOSSTANK2); // Left tank
+ mo2->angle = mo->angle;
+ mo2->destscale = mo->scale;
+ P_SetScale(mo2, mo2->destscale);
+ if (mo->eflags & MFE_VERTICALFLIP)
+ {
+ mo2->eflags |= MFE_VERTICALFLIP;
+ mo2->flags2 |= MF2_OBJECTFLIP;
+ }
+ P_InstaThrust(mo2, mo2->angle + ANGLE_90, FixedMul(4*FRACUNIT, mo2->scale));
+ P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
+
+ mo2 = P_SpawnMobj(mo->x, mo->y,
+ mo->z + ((mo->eflags & MFE_VERTICALFLIP)? mobjinfo[MT_BOSSSPIGOT].height-FixedMul(32*FRACUNIT,mo->scale): mo->height + FixedMul(32*FRACUNIT, mo->scale)), MT_BOSSSPIGOT);
+ mo2->angle = mo->angle;
+ mo2->destscale = mo->scale;
+ P_SetScale(mo2, mo2->destscale);
+ if (mo->eflags & MFE_VERTICALFLIP)
+ {
+ mo2->eflags |= MFE_VERTICALFLIP;
+ mo2->flags2 |= MF2_OBJECTFLIP;
+ }
+ P_SetObjectMomZ(mo2, 4*FRACUNIT, false);
+ return;
+ }
+}
+
+// Function: A_CustomPower
+//
+// Description: Provides a custom powerup. Target (must be a player) is awarded the powerup. Reactiontime of the object is used as an index to the powers array.
+//
+// var1 = Power index #
+// var2 = Power duration in tics
+//
+void A_CustomPower(mobj_t *actor)
+{
+ player_t *player;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ boolean spawnshield = false;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CustomPower", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ if (locvar1 >= NUMPOWERS)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Power #%d out of range!\n", locvar1);
+ return;
+ }
+
+ player = actor->target->player;
+
+ if (locvar1 == pw_shield && player->powers[pw_shield] != locvar2)
+ spawnshield = true;
+
+ player->powers[locvar1] = (UINT16)locvar2;
+ if (actor->info->seesound)
+ S_StartSound(player->mo, actor->info->seesound);
+
+ if (spawnshield) //workaround for a bug
+ P_SpawnShieldOrb(player);
+}
+
+// Function: A_GiveWeapon
+//
+// Description: Gives the player the specified weapon panels.
+//
+// var1 = Weapon index #
+// var2 = unused
+//
+void A_GiveWeapon(mobj_t *actor)
+{
+ player_t *player;
+ INT32 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_GiveWeapon", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ if (locvar1 >= 1<<(NUM_WEAPONS-1))
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Weapon #%d out of range!\n", locvar1);
+ return;
+ }
+
+ player = actor->target->player;
+
+ player->ringweapons |= locvar1;
+ if (actor->info->seesound)
+ S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_RingBox
+//
+// Description: Awards the player 10 rings.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_RingBox(mobj_t *actor)
+{
+ player_t *player;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RingBox", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ player = actor->target->player;
+
+ P_GivePlayerRings(player, actor->info->reactiontime);
+ if (actor->info->seesound)
+ S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_Invincibility
+//
+// Description: Awards the player invincibility.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Invincibility(mobj_t *actor)
+{
+ player_t *player;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Invincibility", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ player = actor->target->player;
+ player->powers[pw_invulnerability] = invulntics + 1;
+
+ if (P_IsLocalPlayer(player) && !player->powers[pw_super])
+ {
+ S_StopMusic();
+ if (mariomode)
+ G_GhostAddColor(GHC_INVINCIBLE);
+ strlcpy(S_sfx[sfx_None].caption, "Invincibility", 14);
+ S_StartCaption(sfx_None, -1, player->powers[pw_invulnerability]);
+ S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
+ }
+}
+
+// Function: A_SuperSneakers
+//
+// Description: Awards the player super sneakers.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SuperSneakers(mobj_t *actor)
+{
+ player_t *player;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SuperSneakers", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ player = actor->target->player;
+
+ actor->target->player->powers[pw_sneakers] = sneakertics + 1;
+
+ if (P_IsLocalPlayer(player) && !player->powers[pw_super])
+ {
+ if (S_SpeedMusic(0.0f) && (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC))
+ S_SpeedMusic(1.4f);
+ else
+ {
+ S_StopMusic();
+ S_ChangeMusicInternal("_shoes", false);
+ }
+ strlcpy(S_sfx[sfx_None].caption, "Speed shoes", 12);
+ S_StartCaption(sfx_None, -1, player->powers[pw_sneakers]);
+ }
+}
+
+// Function: A_AwardScore
+//
+// Description: Adds a set amount of points to the player's score.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_AwardScore(mobj_t *actor)
+{
+ player_t *player;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_AwardScore", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ player = actor->target->player;
+
+ P_AddPlayerScore(player, actor->info->reactiontime);
+ if (actor->info->seesound)
+ S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_ExtraLife
+//
+// Description: Awards the player an extra life.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ExtraLife(mobj_t *actor)
+{
+ player_t *player;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ExtraLife", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ player = actor->target->player;
+
+ if (actor->type == MT_1UP_ICON && actor->tracer)
+ {
+ // We're using the overlay, so use the overlay 1up sprite (no text)
+ actor->sprite = SPR_TV1P;
+ }
+
+ if (ultimatemode) //I don't THINK so!
+ {
+ S_StartSound(player->mo, sfx_lose);
+ return;
+ }
+
+ P_GiveCoopLives(player, 1, true);
+}
+
+// Function: A_GiveShield
+//
+// Description: Awards the player a specified shield.
+//
+// var1 = Shield type (make with SH_ constants)
+// var2 = unused
+//
+void A_GiveShield(mobj_t *actor)
+{
+ player_t *player;
+ UINT16 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_GiveShield", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ player = actor->target->player;
+
+ P_SwitchShield(player, locvar1);
+ S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_GravityBox
+//
+// Description: Awards the player gravity boots.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GravityBox(mobj_t *actor)
+{
+ player_t *player;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_GravityBox", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ player = actor->target->player;
+
+ S_StartSound(player, actor->info->activesound);
+
+ player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
+}
+
+// Function: A_ScoreRise
+//
+// Description: Makes the little score logos rise. Speed value sets speed.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ScoreRise(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ScoreRise", actor))
+ return;
+#endif
+ // make logo rise!
+ P_SetObjectMomZ(actor, actor->info->speed, false);
+}
+
+// Function: A_BunnyHop
+//
+// Description: Makes object hop like a bunny.
+//
+// var1 = jump strength
+// var2 = horizontal movement
+//
+void A_BunnyHop(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BunnyHop", actor))
+ return;
+#endif
+ if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)
+ || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz))
+ {
+ P_SetObjectMomZ(actor, locvar1*FRACUNIT, false);
+ P_InstaThrust(actor, actor->angle, FixedMul(locvar2*FRACUNIT, actor->scale)); // Launch the hopping action! PHOOM!!
+ }
+}
+
+// Function: A_BubbleSpawn
+//
+// Description: Spawns a randomly sized bubble from the object's location. Only works underwater.
+//
+// var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
+// var2 = unused
+//
+void A_BubbleSpawn(mobj_t *actor)
+{
+ INT32 i, locvar1 = var1;
+ UINT8 prandom;
+ mobj_t *bubble = NULL;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BubbleSpawn", actor))
+ return;
+#endif
+ if (!(actor->eflags & MFE_UNDERWATER))
+ {
+ // Don't draw or spawn bubbles above water
+ actor->flags2 |= MF2_DONTDRAW;
+ return;
+ }
+ actor->flags2 &= ~MF2_DONTDRAW;
+
+ if (!(actor->flags2 & MF2_AMBUSH))
+ {
+ // Quick! Look through players!
+ // Don't spawn bubbles unless a player is relatively close by (var1).
+ for (i = 0; i < MAXPLAYERS; ++i)
+ if (playeringame[i] && players[i].mo
+ && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<x, actor->y, actor->z + (actor->height / 2), MT_EXTRALARGEBUBBLE);
+ else if (prandom > 128)
+ bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_SMALLBUBBLE);
+ else if (prandom < 128 && prandom > 96)
+ bubble = P_SpawnMobj(actor->x, actor->y, actor->z + (actor->height / 2), MT_MEDIUMBUBBLE);
+
+ if (bubble)
+ {
+ bubble->destscale = actor->scale;
+ P_SetScale(bubble, actor->scale);
+ }
+}
+
+// Function: A_FanBubbleSpawn
+//
+// Description: Spawns bubbles from fans, if they're underwater.
+//
+// var1 = Distance to look for players. If no player is in this distance, bubbles aren't spawned. (Ambush overrides)
+// var2 = unused
+//
+void A_FanBubbleSpawn(mobj_t *actor)
+{
+ INT32 i, locvar1 = var1;
+ UINT8 prandom;
+ mobj_t *bubble = NULL;
+ fixed_t hz = actor->z + (4*actor->height)/5;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FanBubbleSpawn", actor))
+ return;
+#endif
+ if (!(actor->eflags & MFE_UNDERWATER))
+ return;
+
+ if (!(actor->flags2 & MF2_AMBUSH))
+ {
+ // Quick! Look through players!
+ // Don't spawn bubbles unless a player is relatively close by (var2).
+ for (i = 0; i < MAXPLAYERS; ++i)
+ if (playeringame[i] && players[i].mo
+ && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (locvar1<x, actor->y, hz, MT_SMALLBUBBLE);
+ else if ((prandom & 0xF0) == 0xF0)
+ bubble = P_SpawnMobj(actor->x, actor->y, hz, MT_MEDIUMBUBBLE);
+
+ if (bubble)
+ {
+ bubble->destscale = actor->scale;
+ P_SetScale(bubble, actor->scale);
+ }
+}
+
+// Function: A_BubbleRise
+//
+// Description: Raises a bubble
+//
+// var1:
+// 0 = Bend around the water abit, looking more realistic
+// 1 = Rise straight up
+// var2 = rising speed
+//
+void A_BubbleRise(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BubbleRise", actor))
+ return;
+#endif
+ if (actor->type == MT_EXTRALARGEBUBBLE)
+ P_SetObjectMomZ(actor, FixedDiv(6*FRACUNIT,5*FRACUNIT), false); // make bubbles rise!
+ else
+ {
+ P_SetObjectMomZ(actor, locvar2, true); // make bubbles rise!
+
+ // Move around slightly to make it look like it's bending around the water
+ if (!locvar1)
+ {
+ UINT8 prandom = P_RandomByte();
+ if (!(prandom & 0x7)) // *****000
+ {
+ P_InstaThrust(actor, prandom & 0x70 ? actor->angle + ANGLE_90 : actor->angle,
+ FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
+ }
+ else if (!(prandom & 0x38)) // **000***
+ {
+ P_InstaThrust(actor, prandom & 0x70 ? actor->angle - ANGLE_90 : actor->angle - ANGLE_180,
+ FixedMul(prandom & 0xF0 ? FRACUNIT/2 : -FRACUNIT/2, actor->scale));
+ }
+ }
+ }
+}
+
+// Function: A_BubbleCheck
+//
+// Description: Checks if a bubble should be drawn or not. Bubbles are not drawn above water.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_BubbleCheck(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BubbleCheck", actor))
+ return;
+#endif
+ if (actor->eflags & MFE_UNDERWATER)
+ actor->flags2 &= ~MF2_DONTDRAW; // underwater so draw
+ else
+ actor->flags2 |= MF2_DONTDRAW; // above water so don't draw
+}
+
+// Function: A_AttractChase
+//
+// Description: Makes a ring chase after a player with a ring shield and also causes spilled rings to flicker.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_AttractChase(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_AttractChase", actor))
+ return;
+#endif
+ if (actor->flags2 & MF2_NIGHTSPULL || !actor->health)
+ return;
+
+ // spilled rings flicker before disappearing
+ if (leveltime & 1 && actor->type == (mobjtype_t)actor->info->reactiontime && actor->fuse && actor->fuse < 2*TICRATE)
+ actor->flags2 |= MF2_DONTDRAW;
+ else
+ actor->flags2 &= ~MF2_DONTDRAW;
+
+ // Turn flingrings back into regular rings if attracted.
+ if (actor->tracer && actor->tracer->player
+ && !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
+ {
+ mobj_t *newring;
+ newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime);
+ newring->momx = actor->momx;
+ newring->momy = actor->momy;
+ newring->momz = actor->momz;
+ P_RemoveMobj(actor);
+ return;
+ }
+
+ P_LookForShield(actor); // Go find 'em, boy!
+
+ if (!actor->tracer
+ || !actor->tracer->player
+ || !actor->tracer->health
+ || !P_CheckSight(actor, actor->tracer)) // You have to be able to SEE it...sorta
+ {
+ // Lost attracted rings don't through walls anymore.
+ actor->flags &= ~MF_NOCLIP;
+ P_SetTarget(&actor->tracer, NULL);
+ return;
+ }
+
+ // If a FlingRing gets attracted by a shield, change it into a normal ring.
+ if (actor->type == (mobjtype_t)actor->info->reactiontime)
+ {
+ P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance);
+ P_RemoveMobj(actor);
+ return;
+ }
+
+ // Keep stuff from going down inside floors and junk
+ actor->flags &= ~MF_NOCLIPHEIGHT;
+
+ // Let attracted rings move through walls and such.
+ actor->flags |= MF_NOCLIP;
+
+ P_Attract(actor, actor->tracer, false);
+}
+
+// Function: A_DropMine
+//
+// Description: Drops a mine. Raisestate specifies the object # to use for the mine.
+//
+// var1 = height offset
+// var2:
+// lower 16 bits = proximity check distance (0 disables)
+// upper 16 bits = 0 to check proximity with target, 1 for tracer
+//
+void A_DropMine(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ fixed_t z;
+ mobj_t *mine;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_DropMine", actor))
+ return;
+#endif
+
+ if (locvar2 & 65535)
+ {
+ fixed_t dist;
+ mobj_t *target;
+
+ if (locvar2 >> 16)
+ target = actor->tracer;
+ else
+ target = actor->target;
+
+ if (!target)
+ return;
+
+ dist = P_AproxDistance(actor->x-target->x, actor->y-target->y)>>FRACBITS;
+
+ if (dist > FixedMul((locvar2 & 65535), actor->scale))
+ return;
+ }
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - mobjinfo[actor->info->raisestate].height - FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul((locvar1*FRACUNIT) - 12*FRACUNIT, actor->scale);
+
+ // Use raisestate instead of MT_MINE
+ mine = P_SpawnMobj(actor->x, actor->y, z, (mobjtype_t)actor->info->raisestate);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ mine->eflags |= MFE_VERTICALFLIP;
+ mine->momz = actor->momz + actor->pmomz;
+
+ S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_FishJump
+//
+// Description: Makes the stupid harmless fish in Greenflower Zone jump.
+//
+// var1 = Jump strength (in FRACBITS), if specified. Otherwise, uses the angle value.
+// var2 = unused
+//
+void A_FishJump(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FishJump", actor))
+ return;
+#endif
+
+ if (locvar2)
+ {
+ fixed_t rad = actor->radius>>FRACBITS;
+ P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale)))
+ {
+ fixed_t jumpval;
+
+ if (locvar1)
+ jumpval = var1;
+ else
+ jumpval = FixedMul(AngleFixed(actor->angle)/4, actor->scale);
+
+ if (!jumpval) jumpval = FixedMul(44*(FRACUNIT/4), actor->scale);
+ actor->momz = jumpval;
+ P_SetMobjStateNF(actor, actor->info->seestate);
+ }
+
+ if (actor->momz < 0
+ && (actor->state < &states[actor->info->meleestate] || actor->state > &states[actor->info->xdeathstate]))
+ P_SetMobjStateNF(actor, actor->info->meleestate);
+}
+
+// Function:A_ThrownRing
+//
+// Description: Thinker for thrown rings/sparkle trail
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ThrownRing(mobj_t *actor)
+{
+ INT32 c = 0;
+ INT32 stop;
+ player_t *player;
+ fixed_t dist;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ThrownRing", actor))
+ return;
+#endif
+
+ if (leveltime % (TICRATE/7) == 0)
+ {
+ mobj_t *ring = NULL;
+
+ if (actor->flags2 & MF2_EXPLOSION)
+ {
+ if (actor->momx != 0 || actor->momy != 0)
+ ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE);
+ // Else spawn nothing because it's totally stationary and constantly smoking would be weird -SH
+ }
+ else if (actor->flags2 & MF2_AUTOMATIC)
+ ring = P_SpawnGhostMobj(actor);
+ else if (!(actor->flags2 & MF2_RAILRING))
+ ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPARK);
+
+ if (ring)
+ {
+ /*
+ P_SetTarget(&ring->target, actor);
+ ring->color = actor->color; //copy color
+ */
+ ring->destscale = actor->scale;
+ P_SetScale(ring, actor->scale);
+ }
+ }
+
+ // A_GrenadeRing beeping lives once moooooore -SH
+ if (actor->type == MT_THROWNGRENADE && actor->fuse % TICRATE == 0)
+ S_StartSound(actor, actor->info->attacksound);
+
+ // decrement bounce ring time
+ if (actor->flags2 & MF2_BOUNCERING)
+ {
+ if (actor->fuse)
+ actor->fuse--;
+ else {
+ P_RemoveMobj(actor);
+ return;
+ }
+ }
+
+ // spilled rings (and thrown bounce) flicker before disappearing
+ if (leveltime & 1 && actor->fuse > 0 && actor->fuse < 2*TICRATE
+ && actor->type != MT_THROWNGRENADE)
+ actor->flags2 |= MF2_DONTDRAW;
+ else
+ actor->flags2 &= ~MF2_DONTDRAW;
+
+ if (actor->tracer && actor->tracer->health <= 0)
+ P_SetTarget(&actor->tracer, NULL);
+
+ // Updated homing ring special capability
+ // If you have a ring shield, all rings thrown
+ // at you become homing (except rail)!
+ if (actor->tracer)
+ {
+ // A non-homing ring getting attracted by a
+ // magnetic player. If he gets too far away, make
+ // sure to stop the attraction!
+ if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC)
+ && P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x,
+ actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale)))
+ {
+ P_SetTarget(&actor->tracer, NULL);
+ }
+
+ if (actor->tracer && (actor->tracer->health)
+ && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow.
+ {
+ const INT32 temp = actor->threshold;
+ actor->threshold = 32000;
+ P_HomingAttack(actor, actor->tracer);
+ actor->threshold = temp;
+ return;
+ }
+ }
+
+ // first time init, this allow minimum lastlook changes
+ if (actor->lastlook < 0)
+ actor->lastlook = P_RandomByte();
+
+ actor->lastlook %= MAXPLAYERS;
+
+ stop = (actor->lastlook - 1) & PLAYERSMASK;
+
+ for (; ; actor->lastlook = (actor->lastlook + 1) & PLAYERSMASK)
+ {
+ // done looking
+ if (actor->lastlook == stop)
+ return;
+
+ if (!playeringame[actor->lastlook])
+ continue;
+
+ if (c++ == 2)
+ return;
+
+ player = &players[actor->lastlook];
+
+ if (!player->mo)
+ continue;
+
+ if (player->mo->health <= 0)
+ continue; // dead
+
+ if ((netgame || multiplayer) && player->spectator)
+ continue; // spectator
+
+ if (actor->target && actor->target->player)
+ {
+ if (player->mo == actor->target)
+ continue;
+
+ // Don't home in on teammates.
+ if (gametype == GT_CTF
+ && actor->target->player->ctfteam == player->ctfteam)
+ continue;
+ }
+
+ dist = P_AproxDistance(P_AproxDistance(player->mo->x-actor->x,
+ player->mo->y-actor->y), player->mo->z-actor->z);
+
+ // check distance
+ if (actor->flags2 & MF2_RAILRING)
+ {
+ if (dist > FixedMul(RING_DIST/2, player->mo->scale))
+ continue;
+ }
+ else if (dist > FixedMul(RING_DIST, player->mo->scale))
+ continue;
+
+ // do this after distance check because it's more computationally expensive
+ if (!P_CheckSight(actor, player->mo))
+ continue; // out of sight
+
+ if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
+ && dist < FixedMul(RING_DIST/4, player->mo->scale))
+ P_SetTarget(&actor->tracer, player->mo);
+ return;
+ }
+
+ return;
+}
+
+// Function: A_SetSolidSteam
+//
+// Description: Makes steam solid so it collides with the player to boost them.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SetSolidSteam(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetSolidSteam", actor))
+ return;
+#endif
+ actor->flags &= ~MF_NOCLIP;
+ actor->flags |= MF_SOLID;
+ if (!(actor->flags2 & MF2_AMBUSH))
+ {
+ if (P_RandomChance(FRACUNIT/8))
+ {
+ if (actor->info->deathsound)
+ S_StartSound(actor, actor->info->deathsound); // Hiss!
+ }
+ else
+ {
+ if (actor->info->painsound)
+ S_StartSound(actor, actor->info->painsound);
+ }
+ }
+
+ P_SetObjectMomZ (actor, 1, true);
+}
+
+// Function: A_UnsetSolidSteam
+//
+// Description: Makes an object non-solid and also noclip. Used by the steam.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_UnsetSolidSteam(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_UnsetSolidSteam", actor))
+ return;
+#endif
+ actor->flags &= ~MF_SOLID;
+ actor->flags |= MF_NOCLIP;
+}
+
+// Function: A_SignPlayer
+//
+// Description: Changes the state of a level end sign to reflect the player that hit it.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SignPlayer(mobj_t *actor)
+{
+ mobj_t *ov;
+ skin_t *skin;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SignPlayer", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ if (!actor->target->player)
+ return;
+
+ skin = &skins[actor->target->player->skin];
+
+ if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor?
+ {
+ actor->color = skin->prefoppositecolor;
+ /*
+ If you're here from the comment above Color_Opposite,
+ the following line is the one which is dependent on the
+ array being symmetrical. It gets the opposite of the
+ opposite of your desired colour just so it can get the
+ brightness frame for the End Sign. It's not a great
+ design choice, but it's constant time array access and
+ the idea that the colours should be OPPOSITES is kind
+ of in the name. If you have a better idea, feel free
+ to let me know. ~toast 2016/07/20
+ */
+ actor->frame += (15 - Color_Opposite[(Color_Opposite[(skin->prefoppositecolor - 1)*2] - 1)*2 + 1]);
+ }
+ else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor.
+ {
+ actor->color = Color_Opposite[(actor->target->player->skincolor - 1)*2];
+ actor->frame += (15 - Color_Opposite[(actor->target->player->skincolor - 1)*2 + 1]);
+ }
+
+ if (skin->sprites[SPR2_SIGN].numframes)
+ {
+ // spawn an overlay of the player's face.
+ ov = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
+ P_SetTarget(&ov->target, actor);
+ ov->color = actor->target->player->skincolor;
+ ov->skin = skin;
+ P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN
+ }
+}
+
+// Function: A_OverlayThink
+//
+// Description: Moves the overlay to the position of its target.
+//
+// var1 = unused
+// var2 = invert, z offset
+//
+void A_OverlayThink(mobj_t *actor)
+{
+ fixed_t destx, desty;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_OverlayThink", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ if (!splitscreen && rendermode != render_soft)
+ {
+ angle_t viewingangle;
+
+ if (players[displayplayer].awayviewtics)
+ viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y);
+ else if (!camera.chase && players[displayplayer].mo)
+ viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y);
+ else
+ viewingangle = R_PointToAngle2(actor->target->x, actor->target->y, camera.x, camera.y);
+
+ destx = actor->target->x + P_ReturnThrustX(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
+ desty = actor->target->y + P_ReturnThrustY(actor->target, viewingangle, FixedMul(FRACUNIT, actor->scale));
+ }
+ else
+ {
+ destx = actor->target->x;
+ desty = actor->target->y;
+ }
+ P_UnsetThingPosition(actor);
+ actor->x = destx;
+ actor->y = desty;
+ P_SetThingPosition(actor);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ actor->z = actor->target->z + actor->target->height - mobjinfo[actor->type].height - ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
+ else
+ actor->z = actor->target->z + ((var2>>16) ? -1 : 1)*(var2&0xFFFF)*FRACUNIT;
+ actor->angle = actor->target->angle;
+ actor->eflags = actor->target->eflags;
+
+ actor->momx = actor->target->momx;
+ actor->momy = actor->target->momy;
+ actor->momz = actor->target->momz; // assume target has correct momz! Do not use P_SetObjectMomZ!
+}
+
+// Function: A_JetChase
+//
+// Description: A_Chase for Jettysyns
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetChase(mobj_t *actor)
+{
+ fixed_t thefloor;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_JetChase", actor))
+ return;
+#endif
+
+ if (actor->flags2 & MF2_AMBUSH)
+ return;
+
+ if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
+ && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
+ thefloor = actor->watertop;
+ else
+ thefloor = actor->floorz;
+
+ if (actor->reactiontime)
+ actor->reactiontime--;
+
+ if (P_RandomChance(FRACUNIT/32))
+ {
+ actor->momx = actor->momx / 2;
+ actor->momy = actor->momy / 2;
+ actor->momz = actor->momz / 2;
+ }
+
+ // Bounce if too close to floor or ceiling -
+ // ideal for Jetty-Syns above you on 3d floors
+ if (actor->momz && ((actor->z - FixedMul((32<scale)) < thefloor) && !((thefloor + FixedMul(32*FRACUNIT, actor->scale) + actor->height) > actor->ceilingz))
+ actor->momz = -actor->momz/2;
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ actor->momx = actor->momy = actor->momz = 0;
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ // modify target threshold
+ if (actor->threshold)
+ {
+ if (!actor->target || actor->target->health <= 0)
+ actor->threshold = 0;
+ else
+ actor->threshold--;
+ }
+
+ // turn towards movement direction if not there yet
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+
+ if ((multiplayer || netgame) && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target)))
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ // If the player is over 3072 fracunits away, then look for another player
+ if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
+ actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
+ {
+ return; // got a new target
+ }
+
+ // chase towards player
+ if (ultimatemode)
+ P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/2, actor->scale));
+ else
+ P_Thrust(actor, actor->angle, FixedMul(actor->info->speed/4, actor->scale));
+
+ // must adjust height
+ if (ultimatemode)
+ {
+ if (actor->z < (actor->target->z + actor->target->height + FixedMul((64<scale)))
+ actor->momz += FixedMul(FRACUNIT/2, actor->scale);
+ else
+ actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
+ }
+ else
+ {
+ if (actor->z < (actor->target->z + actor->target->height + FixedMul((32<scale)))
+ actor->momz += FixedMul(FRACUNIT/2, actor->scale);
+ else
+ actor->momz -= FixedMul(FRACUNIT/2, actor->scale);
+ }
+}
+
+// Function: A_JetbThink
+//
+// Description: Thinker for Jetty-Syn bombers
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetbThink(mobj_t *actor)
+{
+ sector_t *nextsector;
+ fixed_t thefloor;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_JetbThink", actor))
+ return;
+#endif
+
+ if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
+ && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
+ thefloor = actor->watertop;
+ else
+ thefloor = actor->floorz;
+
+ if (actor->target)
+ {
+ A_JetChase(actor);
+ // check for melee attack
+ if (actor->info->raisestate
+ && (actor->z > (actor->floorz + FixedMul((32<scale)))
+ && P_JetbCheckMeleeRange(actor) && !actor->reactiontime
+ && (actor->target->z >= actor->floorz))
+ {
+ mobj_t *bomb;
+ if (actor->info->attacksound)
+ S_StartAttackSound(actor, actor->info->attacksound);
+
+ // use raisestate instead of MT_MINE
+ bomb = P_SpawnMobj(actor->x, actor->y, actor->z - FixedMul((32<scale), (mobjtype_t)actor->info->raisestate);
+
+ P_SetTarget(&bomb->target, actor);
+ bomb->destscale = actor->scale;
+ P_SetScale(bomb, actor->scale);
+ actor->reactiontime = TICRATE; // one second
+ S_StartSound(actor, actor->info->attacksound);
+ }
+ }
+ else if (((actor->z - FixedMul((32<scale)) < thefloor) && !((thefloor + FixedMul((32<scale) + actor->height) > actor->ceilingz))
+ actor->z = thefloor+FixedMul((32<scale);
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
+
+ // Move downwards or upwards to go through a passageway.
+ if (nextsector->ceilingheight < actor->z + actor->height)
+ actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
+ else if (nextsector->floorheight > actor->z)
+ actor->momz += FixedMul(5*FRACUNIT, actor->scale);
+}
+
+// Function: A_JetgShoot
+//
+// Description: Firing function for Jetty-Syn gunners.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetgShoot(mobj_t *actor)
+{
+ fixed_t dist;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_JetgShoot", actor))
+ return;
+#endif
+
+ if (!actor->target)
+ return;
+
+ if (actor->reactiontime)
+ return;
+
+ dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+
+ if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
+ return;
+
+ if (dist < FixedMul(64*FRACUNIT, actor->scale))
+ return;
+
+ A_FaceTarget(actor);
+ P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
+
+ if (ultimatemode)
+ actor->reactiontime = actor->info->reactiontime*TICRATE;
+ else
+ actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+
+ if (actor->info->attacksound)
+ S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_ShootBullet
+//
+// Description: Shoots a bullet. Raisestate defines object # to use as projectile.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ShootBullet(mobj_t *actor)
+{
+ fixed_t dist;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ShootBullet", actor))
+ return;
+#endif
+
+ if (!actor->target)
+ return;
+
+ dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z);
+
+ if (dist > FixedMul(actor->info->painchance*FRACUNIT, actor->scale))
+ return;
+
+ A_FaceTarget(actor);
+ P_SpawnMissile(actor, actor->target, (mobjtype_t)actor->info->raisestate);
+
+ if (actor->info->attacksound)
+ S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_MinusDigging
+//
+// Description: Minus digging in the ground.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MinusDigging(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MinusDigging", actor))
+ return;
+#endif
+ actor->flags &= ~MF_SPECIAL;
+ actor->flags &= ~MF_SHOOTABLE;
+
+ if (!actor->target)
+ {
+ A_Look(actor);
+ return;
+ }
+
+ if (actor->reactiontime)
+ {
+ actor->reactiontime--;
+ return;
+ }
+
+ // Dirt trail
+ P_SpawnGhostMobj(actor);
+
+ actor->flags |= MF_NOCLIPTHING;
+ var1 = 3;
+ A_Chase(actor);
+ actor->flags &= ~MF_NOCLIPTHING;
+
+ // Play digging sound
+ if (!(leveltime & 15))
+ S_StartSound(actor, actor->info->activesound);
+
+ // If we're close enough to our target, pop out of the ground
+ if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) < actor->radius
+ && abs(actor->target->z - actor->z) < 2*actor->height)
+ P_SetMobjState(actor, actor->info->missilestate);
+
+ // Snap to ground
+ if (actor->eflags & MFE_VERTICALFLIP)
+ actor->z = actor->ceilingz - actor->height;
+ else
+ actor->z = actor->floorz;
+}
+
+// Function: A_MinusPopup
+//
+// Description: Minus popping out of the ground.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MinusPopup(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MinusPopup", actor))
+ return;
+#endif
+ P_SetObjectMomZ(actor, 10*FRACUNIT, false);
+
+ actor->flags |= MF_SPECIAL;
+ actor->flags |= MF_SHOOTABLE;
+
+ // Sound for busting out of the ground.
+ S_StartSound(actor, actor->info->attacksound);
+}
+
+// Function: A_MinusCheck
+//
+// Description: If the minus hits the floor, dig back into the ground.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MinusCheck(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MinusCheck", actor))
+ return;
+#endif
+ if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+ || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz))
+ {
+ actor->flags &= ~MF_SPECIAL;
+ actor->flags &= ~MF_SHOOTABLE;
+ actor->reactiontime = TICRATE;
+ P_SetMobjState(actor, actor->info->seestate);
+ return;
+ }
+
+ // 'Falling' animation
+ if (P_MobjFlip(actor)*actor->momz < 0 && actor->state < &states[actor->info->meleestate])
+ P_SetMobjState(actor, actor->info->meleestate);
+}
+
+// Function: A_ChickenCheck
+//
+// Description: Resets the chicken once it hits the floor again.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ChickenCheck(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ChickenCheck", actor))
+ return;
+#endif
+ if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+ || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height >= actor->ceilingz))
+ {
+ if (!(actor->momx || actor->momy || actor->momz)
+ && actor->state > &states[actor->info->seestate])
+ {
+ A_Chase(actor);
+ P_SetMobjState(actor, actor->info->seestate);
+ }
+
+ actor->momx >>= 2;
+ actor->momy >>= 2;
+ }
+}
+
+// Function: A_JetgThink
+//
+// Description: Thinker for Jetty-Syn Gunners
+//
+// var1 = unused
+// var2 = unused
+//
+void A_JetgThink(mobj_t *actor)
+{
+ sector_t *nextsector;
+
+ fixed_t thefloor;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_JetgThink", actor))
+ return;
+#endif
+
+ if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
+ && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
+ thefloor = actor->watertop;
+ else
+ thefloor = actor->floorz;
+
+ if (actor->target)
+ {
+ if (P_RandomChance(FRACUNIT/8) && !actor->reactiontime)
+ P_SetMobjState(actor, actor->info->missilestate);
+ else
+ A_JetChase (actor);
+ }
+ else if (actor->z - FixedMul((32<scale) < thefloor && !(thefloor + FixedMul((32<scale)
+ + actor->height > actor->ceilingz))
+ {
+ actor->z = thefloor + FixedMul((32<scale);
+ }
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
+
+ // Move downwards or upwards to go through a passageway.
+ if (nextsector->ceilingheight < actor->z + actor->height)
+ actor->momz -= FixedMul(5*FRACUNIT, actor->scale);
+ else if (nextsector->floorheight > actor->z)
+ actor->momz += FixedMul(5*FRACUNIT, actor->scale);
+}
+
+// Function: A_MouseThink
+//
+// Description: Thinker for scurrying mice.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MouseThink(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MouseThink", actor))
+ return;
+#endif
+
+ if (actor->reactiontime)
+ actor->reactiontime--;
+
+ if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z == actor->floorz)
+ || (actor->eflags & MFE_VERTICALFLIP && actor->z + actor->height == actor->ceilingz))
+ && !actor->reactiontime)
+ {
+ if (twodlevel || actor->flags2 & MF2_TWOD)
+ {
+ if (P_RandomChance(FRACUNIT/2))
+ actor->angle += ANGLE_180;
+ }
+ else if (P_RandomChance(FRACUNIT/2))
+ actor->angle += ANGLE_90;
+ else
+ actor->angle -= ANGLE_90;
+
+ P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
+ actor->reactiontime = TICRATE/5;
+ }
+}
+
+// Function: A_DetonChase
+//
+// Description: Chases a Deton after a player.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_DetonChase(mobj_t *actor)
+{
+ angle_t exact;
+ fixed_t xydist, dist;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_DetonChase", actor))
+ return;
+#endif
+
+ // modify tracer threshold
+ if (!actor->tracer || actor->tracer->health <= 0)
+ actor->threshold = 0;
+ else
+ actor->threshold = 1;
+
+ if (!actor->tracer || !(actor->tracer->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, true, 0))
+ return; // got a new target
+
+ actor->momx = actor->momy = actor->momz = 0;
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ if (multiplayer && !actor->threshold && P_LookForPlayers(actor, true, true, 0))
+ return; // got a new target
+
+ // Face movement direction if not doing so
+ exact = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
+ actor->angle = exact;
+ /*if (exact != actor->angle)
+ {
+ if (exact - actor->angle > ANGLE_180)
+ {
+ actor->angle -= actor->info->raisestate;
+ if (exact - actor->angle < ANGLE_180)
+ actor->angle = exact;
+ }
+ else
+ {
+ actor->angle += actor->info->raisestate;
+ if (exact - actor->angle > ANGLE_180)
+ actor->angle = exact;
+ }
+ }*/
+ // movedir is up/down angle: how much it has to go up as it goes over to the player
+ xydist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
+ exact = R_PointToAngle2(0, 0, xydist, actor->tracer->z - actor->z);
+ actor->movedir = exact;
+ /*if (exact != actor->movedir)
+ {
+ if (exact - actor->movedir > ANGLE_180)
+ {
+ actor->movedir -= actor->info->raisestate;
+ if (exact - actor->movedir < ANGLE_180)
+ actor->movedir = exact;
+ }
+ else
+ {
+ actor->movedir += actor->info->raisestate;
+ if (exact - actor->movedir > ANGLE_180)
+ actor->movedir = exact;
+ }
+ }*/
+
+ // check for melee attack
+ if (actor->tracer)
+ {
+ if (P_AproxDistance(actor->tracer->x-actor->x, actor->tracer->y-actor->y) < actor->radius+actor->tracer->radius)
+ {
+ if (!((actor->tracer->z > actor->z + actor->height) || (actor->z > actor->tracer->z + actor->tracer->height)))
+ {
+ P_ExplodeMissile(actor);
+ return;
+ }
+ }
+ }
+
+ // chase towards player
+ if ((dist = P_AproxDistance(xydist, actor->tracer->z-actor->z))
+ > FixedMul((actor->info->painchance << FRACBITS), actor->scale))
+ {
+ P_SetTarget(&actor->tracer, NULL); // Too far away
+ return;
+ }
+
+ if (actor->reactiontime == 0)
+ {
+ actor->reactiontime = actor->info->reactiontime;
+ return;
+ }
+
+ if (actor->reactiontime > 1)
+ {
+ actor->reactiontime--;
+ return;
+ }
+
+ if (actor->reactiontime > 0)
+ {
+ actor->reactiontime = -42;
+
+ if (actor->info->seesound)
+ S_StartScreamSound(actor, actor->info->seesound);
+ }
+
+ if (actor->reactiontime == -42)
+ {
+ fixed_t xyspeed;
+
+ actor->reactiontime = -42;
+
+ exact = actor->movedir>>ANGLETOFINESHIFT;
+ xyspeed = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINECOSINE(exact));
+ actor->momz = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINESINE(exact));
+
+ exact = actor->angle>>ANGLETOFINESHIFT;
+ actor->momx = FixedMul(xyspeed, FINECOSINE(exact));
+ actor->momy = FixedMul(xyspeed, FINESINE(exact));
+
+ // Variable re-use
+ xyspeed = (P_AproxDistance(actor->tracer->x - actor->x, P_AproxDistance(actor->tracer->y - actor->y, actor->tracer->z - actor->z))>>(FRACBITS+6));
+
+ if (xyspeed < 1)
+ xyspeed = 1;
+
+ if (leveltime % xyspeed == 0)
+ S_StartSound(actor, sfx_deton);
+ }
+}
+
+// Function: A_CapeChase
+//
+// Description: Set an object's location to its target or tracer.
+//
+// var1:
+// 0 = Use target
+// 1 = Use tracer
+// upper 16 bits = Z offset
+// var2:
+// upper 16 bits = forward/backward offset
+// lower 16 bits = sideways offset
+//
+void A_CapeChase(mobj_t *actor)
+{
+ mobj_t *chaser;
+ fixed_t foffsetx, foffsety, boffsetx, boffsety;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ angle_t angle;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CapeChase", actor))
+ return;
+#endif
+
+ CONS_Debug(DBG_GAMELOGIC, "A_CapeChase called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+ if (locvar1 & 65535)
+ chaser = actor->tracer;
+ else
+ chaser = actor->target;
+
+ if (!chaser || (chaser->health <= 0))
+ {
+ if (chaser)
+ CONS_Debug(DBG_GAMELOGIC, "Hmm, the guy I'm chasing (object type %d) has no health.. so I'll die too!\n", chaser->type);
+
+ P_RemoveMobj(actor);
+ return;
+ }
+
+ angle = (chaser->player ? chaser->player->drawangle : chaser->angle);
+
+ foffsetx = P_ReturnThrustX(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
+ foffsety = P_ReturnThrustY(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
+
+ boffsetx = P_ReturnThrustX(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
+ boffsety = P_ReturnThrustY(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
+
+ P_UnsetThingPosition(actor);
+ actor->x = chaser->x + foffsetx + boffsetx;
+ actor->y = chaser->y + foffsety + boffsety;
+ if (chaser->eflags & MFE_VERTICALFLIP)
+ {
+ actor->eflags |= MFE_VERTICALFLIP;
+ actor->flags2 |= MF2_OBJECTFLIP;
+ actor->z = chaser->z + chaser->height - actor->height - FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
+ }
+ else
+ {
+ actor->eflags &= ~MFE_VERTICALFLIP;
+ actor->flags2 &= ~MF2_OBJECTFLIP;
+ actor->z = chaser->z + FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
+ }
+ actor->angle = angle;
+ P_SetThingPosition(actor);
+}
+
+// Function: A_RotateSpikeBall
+//
+// Description: Rotates a spike ball around its target/tracer.
+//
+// var1:
+// 0 = Use target
+// 1 = Use tracer
+// var2 = unused
+//
+void A_RotateSpikeBall(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ const fixed_t radius = FixedMul(12*actor->info->speed, actor->scale);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RotateSpikeBall", actor))
+ return;
+#endif
+
+ if (!((!locvar1 && (actor->target)) || (locvar1 && (actor->tracer))))// This should NEVER happen.
+ {
+ CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Spikeball has no target\n");
+ P_RemoveMobj(actor);
+ return;
+ }
+
+ if (!actor->info->speed)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "A_RotateSpikeBall: Object has no speed.\n");
+ return;
+ }
+
+ actor->angle += FixedAngle(actor->info->speed);
+ P_UnsetThingPosition(actor);
+ {
+ const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
+ if (!locvar1)
+ {
+ actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
+ actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
+ actor->z = actor->target->z + actor->target->height/2;
+ }
+ else
+ {
+ actor->x = actor->tracer->x + FixedMul(FINECOSINE(fa),radius);
+ actor->y = actor->tracer->y + FixedMul(FINESINE(fa),radius);
+ actor->z = actor->tracer->z + actor->tracer->height/2;
+ }
+ P_SetThingPosition(actor);
+ }
+}
+
+// Function: A_UnidusBall
+//
+// Description: Rotates a spike ball around its target.
+//
+// var1:
+// 0 = Don't throw
+// 1 = Throw
+// 2 = Throw when target leaves MF2_SKULLFLY.
+// var2 = unused
+//
+void A_UnidusBall(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ boolean canthrow = false;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_UnidusBall", actor))
+ return;
+#endif
+
+ actor->angle += ANGLE_11hh;
+
+ if (actor->movecount)
+ {
+ if (P_AproxDistance(actor->momx, actor->momy) < FixedMul(actor->info->damage/2, actor->scale))
+ P_ExplodeMissile(actor);
+ return;
+ }
+
+ if (!actor->target || !actor->target->health)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "A_UnidusBall: Removing unthrown spikeball from nonexistant Unidus\n");
+ P_RemoveMobj(actor);
+ return;
+ }
+
+ P_UnsetThingPosition(actor);
+ {
+ const angle_t angle = actor->movedir + FixedAngle(actor->info->speed*(leveltime%360));
+ const UINT16 fa = angle>>ANGLETOFINESHIFT;
+
+ actor->x = actor->target->x + FixedMul(FINECOSINE(fa),actor->threshold);
+ actor->y = actor->target->y + FixedMul( FINESINE(fa),actor->threshold);
+ actor->z = actor->target->z + actor->target->height/2 - actor->height/2;
+
+ if (locvar1 == 1 && actor->target->target)
+ {
+ const angle_t tang = R_PointToAngle2(actor->target->x, actor->target->y, actor->target->target->x, actor->target->target->y);
+ const angle_t mina = tang-ANGLE_11hh;
+ canthrow = (angle-mina < FixedAngle(actor->info->speed*3));
+ }
+ }
+ P_SetThingPosition(actor);
+
+ if (locvar1 == 1 && canthrow)
+ {
+ if (P_AproxDistance(actor->target->target->x - actor->target->x, actor->target->target->y - actor->target->y) > FixedMul(MISSILERANGE>>1, actor->scale)
+ || !P_CheckSight(actor, actor->target->target))
+ return;
+
+ actor->movecount = actor->info->damage>>FRACBITS;
+ actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
+ P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->target->target->x, actor->target->target->y), FixedMul(actor->info->damage, actor->scale));
+ }
+ else if (locvar1 == 2)
+ {
+ boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY;
+ if (actor->target->state == &states[actor->target->info->painstate])
+ {
+ P_KillMobj(actor, NULL, NULL, 0);
+ return;
+ }
+ switch(actor->extravalue2)
+ {
+ case 0: // at least one frame where not dashing
+ if (!skull) ++actor->extravalue2;
+ else break;
+ /* FALLTHRU */
+ case 1: // at least one frame where ARE dashing
+ if (skull) ++actor->extravalue2;
+ else break;
+ /* FALLTHRU */
+ case 2: // not dashing again?
+ if (skull) break;
+ // launch.
+ {
+ mobj_t *target = actor->target;
+ if (actor->target->target)
+ target = actor->target->target;
+ actor->movecount = actor->info->damage>>FRACBITS;
+ actor->flags &= ~(MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOCLIPTHING);
+ P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, target->x, target->y), FixedMul(actor->info->damage, actor->scale));
+ }
+ default: // from our compiler appeasement program (CAP).
+ break;
+ }
+ }
+}
+
+// Function: A_RockSpawn
+//
+// Spawns rocks at a specified interval
+//
+// var1 = unused
+// var2 = unused
+void A_RockSpawn(mobj_t *actor)
+{
+ mobj_t *mo;
+ mobjtype_t type;
+ INT32 i = P_FindSpecialLineFromTag(12, (INT16)actor->threshold, -1);
+ line_t *line;
+ fixed_t dist;
+ fixed_t randomoomph;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RockSpawn", actor))
+ return;
+#endif
+
+ if (i == -1)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: Unable to find parameter line 12 (tag %d)!\n", actor->threshold);
+ return;
+ }
+
+ line = &lines[i];
+
+ if (!(sides[line->sidenum[0]].textureoffset >> FRACBITS))
+ {
+ CONS_Debug(DBG_GAMELOGIC, "A_RockSpawn: No X-offset detected! (tag %d)!\n", actor->threshold);
+ return;
+ }
+
+ dist = P_AproxDistance(line->dx, line->dy)/16;
+
+ if (dist < 1)
+ dist = 1;
+
+ type = MT_ROCKCRUMBLE1 + (sides[line->sidenum[0]].rowoffset >> FRACBITS);
+
+ if (line->flags & ML_NOCLIMB)
+ randomoomph = P_RandomByte() * (FRACUNIT/32);
+ else
+ randomoomph = 0;
+
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z, MT_FALLINGROCK);
+ P_SetMobjState(mo, mobjinfo[type].spawnstate);
+ mo->angle = R_PointToAngle2(line->v2->x, line->v2->y, line->v1->x, line->v1->y);
+
+ P_InstaThrust(mo, mo->angle, dist + randomoomph);
+ mo->momz = dist + randomoomph;
+
+ var1 = sides[line->sidenum[0]].textureoffset >> FRACBITS;
+ A_SetTics(actor);
+}
+
+//
+// Function: A_SlingAppear
+//
+// Appears a sling.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SlingAppear(mobj_t *actor)
+{
+ UINT8 mlength = 4;
+ mobj_t *spawnee, *hprev;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SlingAppear", actor))
+ return;
+#endif
+
+ P_UnsetThingPosition(actor);
+ actor->flags &= ~(MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
+ P_SetThingPosition(actor);
+ actor->lastlook = 128;
+ actor->movecount = actor->lastlook;
+ actor->threshold = 0;
+ actor->movefactor = actor->threshold;
+ actor->friction = 128;
+
+ hprev = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLGRABCHAIN);
+ P_SetTarget(&hprev->tracer, actor);
+ P_SetTarget(&hprev->hprev, actor);
+ P_SetTarget(&actor->hnext, hprev);
+ hprev->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
+ hprev->movecount = mlength;
+
+ mlength--;
+
+ while (mlength > 0)
+ {
+ spawnee = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMALLMACECHAIN);
+ P_SetTarget(&spawnee->tracer, actor);
+ P_SetTarget(&spawnee->hprev, hprev);
+ P_SetTarget(&hprev->hnext, spawnee);
+ hprev = spawnee;
+
+ spawnee->flags |= MF_NOCLIP|MF_NOCLIPHEIGHT;
+ spawnee->movecount = mlength;
+
+ mlength--;
+ }
+}
+
+// Function: A_SetFuse
+//
+// Description: Sets the actor's fuse timer if not set already. May also change state when fuse reaches the last tic, otherwise by default the actor will die or disappear. (Replaces A_SnowBall)
+//
+// var1 = fuse timer duration (in tics).
+// var2:
+// lower 16 bits = if > 0, state to change to when fuse = 1
+// upper 16 bits: 0 = (default) don't set fuse unless 0, 1 = force change, 2 = force no change
+//
+void A_SetFuse(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetFuse", actor))
+ return;
+#endif
+
+ if ((!actor->fuse || (locvar2 >> 16)) && (locvar2 >> 16) != 2) // set the actor's fuse value
+ actor->fuse = locvar1;
+
+ if (actor->fuse == 1 && (locvar2 & 65535)) // change state on the very last tic (fuse is handled before actions in P_MobjThinker)
+ {
+ actor->fuse = 0; // don't die/disappear the next tic!
+ P_SetMobjState(actor, locvar2 & 65535);
+ }
+}
+
+// Function: A_CrawlaCommanderThink
+//
+// Description: Thinker for Crawla Commander.
+//
+// var1 = shoot bullets?
+// var2 = "pogo mode" speed
+//
+void A_CrawlaCommanderThink(mobj_t *actor)
+{
+ fixed_t dist;
+ sector_t *nextsector;
+ fixed_t thefloor;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ boolean hovermode = (actor->health > 1 || actor->fuse);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CrawlaCommanderThink", actor))
+ return;
+#endif
+
+ if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
+ && actor->z > actor->watertop - FixedMul(256*FRACUNIT, actor->scale))
+ thefloor = actor->watertop;
+ else
+ thefloor = actor->floorz;
+
+ if (!actor->fuse && actor->flags2 & MF2_FRET)
+ {
+ if (actor->info->painsound)
+ S_StartSound(actor, actor->info->painsound);
+
+ actor->fuse = TICRATE/2;
+ actor->momz = 0;
+
+ P_InstaThrust(actor, actor->angle-ANGLE_180, FixedMul(5*FRACUNIT, actor->scale));
+ }
+
+ if (actor->reactiontime > 0)
+ actor->reactiontime--;
+
+ if (actor->fuse < 2)
+ {
+ actor->fuse = 0;
+ actor->flags2 &= ~MF2_FRET;
+ }
+
+ // Hover mode
+ if (hovermode)
+ {
+ if (actor->z < thefloor + FixedMul(16*FRACUNIT, actor->scale))
+ actor->momz += FixedMul(FRACUNIT, actor->scale);
+ else if (actor->z < thefloor + FixedMul(32*FRACUNIT, actor->scale))
+ actor->momz += FixedMul(FRACUNIT/2, actor->scale);
+ else
+ actor->momz += FixedMul(16, actor->scale);
+ }
+
+ if (!actor->target)
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ if (actor->state != &states[actor->info->spawnstate])
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ dist = P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y);
+
+ if (actor->target->player && (!hovermode || actor->reactiontime <= 2*TICRATE))
+ {
+ if (dist < FixedMul(64<<(FRACBITS+(hovermode ? 1 : 0)), actor->scale)
+ && ((actor->target->player->pflags & PF_JUMPED) || (actor->target->player->pflags & PF_SPINNING)))
+ {
+ // Auugh! She's trying to kill you! Strafe! STRAAAAFFEEE!!
+ P_InstaThrust(actor, actor->angle - ANGLE_180, FixedMul(20*FRACUNIT, actor->scale));
+ return;
+ }
+ }
+
+ if (locvar1)
+ {
+ if (actor->health < 2 && P_RandomChance(FRACUNIT/128))
+ P_SpawnMissile(actor, actor->target, locvar1);
+ }
+
+ // Face the player
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+
+ if (actor->threshold && dist > FixedMul(256*FRACUNIT, actor->scale))
+ actor->momx = actor->momy = 0;
+
+ if (actor->reactiontime && actor->reactiontime <= 2*TICRATE && dist > actor->target->radius - FixedMul(FRACUNIT, actor->scale))
+ {
+ actor->threshold = 0;
+
+ // Roam around, somewhat in the player's direction.
+ actor->angle += (P_RandomByte()<<10);
+ actor->angle -= (P_RandomByte()<<10);
+
+ if (hovermode)
+ {
+ fixed_t mom;
+ P_Thrust(actor, actor->angle, 2*actor->scale);
+ mom = P_AproxDistance(actor->momx, actor->momy);
+ if (mom > 20*actor->scale)
+ {
+ mom += 20*actor->scale;
+ mom >>= 1;
+ P_InstaThrust(actor, R_PointToAngle2(0, 0, actor->momx, actor->momy), mom);
+ }
+ }
+ }
+ else if (!actor->reactiontime)
+ {
+ if (hovermode && !(actor->flags2 & MF2_FRET)) // Hover Mode
+ {
+ if (dist < FixedMul(512*FRACUNIT, actor->scale))
+ {
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ P_InstaThrust(actor, actor->angle, FixedMul(40*FRACUNIT, actor->scale));
+ actor->threshold = 1;
+ if (actor->info->attacksound)
+ S_StartSound(actor, actor->info->attacksound);
+ }
+ }
+ actor->reactiontime = 3*TICRATE + (P_RandomByte()>>2);
+ }
+
+ if (actor->health == 1)
+ P_Thrust(actor, actor->angle, 1);
+
+ // Pogo Mode
+ if (!hovermode && actor->z <= actor->floorz)
+ {
+ if (actor->info->activesound)
+ S_StartSound(actor, actor->info->activesound);
+
+ if (dist < FixedMul(256*FRACUNIT, actor->scale))
+ {
+ actor->momz = FixedMul(locvar2, actor->scale);
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ P_InstaThrust(actor, actor->angle, FixedMul(locvar2/8, actor->scale));
+ // pogo on player
+ }
+ else
+ {
+ UINT8 prandom = P_RandomByte();
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
+ P_InstaThrust(actor, actor->angle, FixedDiv(FixedMul(locvar2, actor->scale), 3*FRACUNIT/2));
+ actor->momz = FixedMul(locvar2, actor->scale); // Bounce up in air
+ }
+ }
+
+ nextsector = R_PointInSubsector(actor->x + actor->momx, actor->y + actor->momy)->sector;
+
+ // Move downwards or upwards to go through a passageway.
+ if (nextsector->floorheight > actor->z && nextsector->floorheight - actor->z < FixedMul(128*FRACUNIT, actor->scale))
+ actor->momz += (nextsector->floorheight - actor->z) / 4;
+}
+
+// Function: A_RingExplode
+//
+// Description: An explosion ring exploding
+//
+// var1 = unused
+// var2 = unused
+//
+void A_RingExplode(mobj_t *actor)
+{
+ mobj_t *mo2;
+ thinker_t *th;
+ angle_t d;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RingExplode", actor))
+ return;
+#endif
+
+ for (d = 0; d < 16; d++)
+ P_SpawnParaloop(actor->x, actor->y, actor->z + actor->height, FixedMul(actor->info->painchance, actor->scale), 16, MT_NIGHTSPARKLE, S_NULL, d*(ANGLE_22h), true);
+
+ S_StartSound(actor, sfx_prloop);
+
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo2 = (mobj_t *)th;
+
+ if (mo2 == actor) // Don't explode yourself! Endless loop!
+ continue;
+
+ if (P_AproxDistance(P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y), mo2->z - actor->z) > FixedMul(actor->info->painchance, actor->scale))
+ continue;
+
+ if (mo2->flags & MF_SHOOTABLE)
+ {
+ actor->flags2 |= MF2_DEBRIS;
+ P_DamageMobj(mo2, actor, actor->target, 1, 0);
+ continue;
+ }
+ }
+ return;
+}
+
+// Function: A_OldRingExplode
+//
+// Description: An explosion ring exploding, 1.09.4 style
+//
+// var1 = object # to explode as debris
+// var2 = unused
+//
+void A_OldRingExplode(mobj_t *actor) {
+ UINT8 i;
+ mobj_t *mo;
+ const fixed_t ns = FixedMul(20 * FRACUNIT, actor->scale);
+ INT32 locvar1 = var1;
+ //INT32 locvar2 = var2;
+ boolean changecolor = (actor->target && actor->target->player);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_OldRingExplode", actor))
+ return;
+#endif
+
+ for (i = 0; i < 32; i++)
+ {
+ const angle_t fa = (i*FINEANGLES/16) & FINEMASK;
+
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+ P_SetTarget(&mo->target, actor->target); // Transfer target so player gets the points
+
+ mo->momx = FixedMul(FINECOSINE(fa),ns);
+ mo->momy = FixedMul(FINESINE(fa),ns);
+
+ if (i > 15)
+ {
+ if (i & 1)
+ mo->momz = ns;
+ else
+ mo->momz = -ns;
+ }
+
+ mo->flags2 |= MF2_DEBRIS;
+ mo->fuse = TICRATE/5;
+
+ if (changecolor)
+ {
+ if (gametype != GT_CTF)
+ mo->color = actor->target->color; //copy color
+ else if (actor->target->player->ctfteam == 2)
+ mo->color = skincolor_bluering;
+ }
+ }
+
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+
+ P_SetTarget(&mo->target, actor->target);
+ mo->momz = ns;
+ mo->flags2 |= MF2_DEBRIS;
+ mo->fuse = TICRATE/5;
+
+ if (changecolor)
+ {
+ if (gametype != GT_CTF)
+ mo->color = actor->target->color; //copy color
+ else if (actor->target->player->ctfteam == 2)
+ mo->color = skincolor_bluering;
+ }
+
+ mo = P_SpawnMobj(actor->x, actor->y, actor->z, locvar1);
+
+ P_SetTarget(&mo->target, actor->target);
+ mo->momz = -ns;
+ mo->flags2 |= MF2_DEBRIS;
+ mo->fuse = TICRATE/5;
+
+ if (changecolor)
+ {
+ if (gametype != GT_CTF)
+ mo->color = actor->target->color; //copy color
+ else if (actor->target->player->ctfteam == 2)
+ mo->color = skincolor_bluering;
+ }
+}
+
+// Function: A_MixUp
+//
+// Description: Mix up all of the player positions.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MixUp(mobj_t *actor)
+{
+ boolean teleported[MAXPLAYERS];
+ INT32 i, numplayers = 0, prandom = 0;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MixUp", actor))
+ return;
+#else
+ (void)actor;
+#endif
+
+ if (!multiplayer)
+ return;
+
+ // No mix-up monitors in hide and seek or time only race.
+ // The random factor is okay for other game modes, but in these, it is cripplingly unfair.
+ if (gametype == GT_HIDEANDSEEK || gametype == GT_RACE)
+ {
+ S_StartSound(actor, sfx_lose);
+ return;
+ }
+
+ numplayers = 0;
+ memset(teleported, 0, sizeof (teleported));
+
+ // Count the number of players in the game
+ // and grab their xyz coords
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
+ && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+ {
+ if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
+ continue;
+
+ numplayers++;
+ }
+
+ if (numplayers <= 1) // Not enough players to mix up.
+ {
+ S_StartSound(actor, sfx_lose);
+ return;
+ }
+ else if (numplayers == 2) // Special case -- simple swap
+ {
+ fixed_t x, y, z;
+ angle_t angle;
+ INT32 one = -1, two = 0; // default value 0 to make the compiler shut up
+
+ // Zoom tube stuff
+ mobj_t *tempthing = NULL; //tracer
+ UINT16 carry1,carry2; //carry
+ INT32 transspeed; //player speed
+
+ // Starpost stuff
+ INT16 starpostx, starposty, starpostz;
+ INT32 starpostnum;
+ tic_t starposttime;
+ angle_t starpostangle;
+
+ INT32 mflags2;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
+ && !players[i].exiting && !players[i].powers[pw_super])
+ {
+ if ((netgame || multiplayer) && players[i].spectator) // Ignore spectators
+ continue;
+
+ if (one == -1)
+ one = i;
+ else
+ {
+ two = i;
+ break;
+ }
+ }
+
+ //get this done first!
+ tempthing = players[one].mo->tracer;
+ P_SetTarget(&players[one].mo->tracer, players[two].mo->tracer);
+ P_SetTarget(&players[two].mo->tracer, tempthing);
+
+ //zoom tubes use player->speed to determine direction and speed
+ transspeed = players[one].speed;
+ players[one].speed = players[two].speed;
+ players[two].speed = transspeed;
+
+ //set flags variables now but DON'T set them.
+ carry1 = (players[one].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[one].powers[pw_carry]);
+ carry2 = (players[two].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[two].powers[pw_carry]);
+
+ x = players[one].mo->x;
+ y = players[one].mo->y;
+ z = players[one].mo->z;
+ angle = players[one].mo->angle;
+
+ starpostx = players[one].starpostx;
+ starposty = players[one].starposty;
+ starpostz = players[one].starpostz;
+ starpostangle = players[one].starpostangle;
+ starpostnum = players[one].starpostnum;
+ starposttime = players[one].starposttime;
+
+ mflags2 = players[one].mo->flags2;
+
+ P_MixUp(players[one].mo, players[two].mo->x, players[two].mo->y, players[two].mo->z, players[two].mo->angle,
+ players[two].starpostx, players[two].starposty, players[two].starpostz,
+ players[two].starpostnum, players[two].starposttime, players[two].starpostangle,
+ players[two].mo->flags2);
+
+ P_MixUp(players[two].mo, x, y, z, angle, starpostx, starposty, starpostz,
+ starpostnum, starposttime, starpostangle,
+ mflags2);
+
+ //carry set after mixup. Stupid P_ResetPlayer() takes away some of the stuff we look for...
+ //but not all of it! So we need to make sure they aren't set wrong or anything.
+ players[one].powers[pw_carry] = carry2;
+ players[two].powers[pw_carry] = carry1;
+
+ teleported[one] = true;
+ teleported[two] = true;
+ }
+ else
+ {
+ fixed_t position[MAXPLAYERS][3];
+ angle_t anglepos[MAXPLAYERS];
+ INT32 pindex[MAXPLAYERS], counter = 0, teleportfrom = 0;
+
+ // Zoom tube stuff
+ mobj_t *transtracer[MAXPLAYERS]; //tracer
+ //pflags_t transflag[MAXPLAYERS]; //cyan pink white pink cyan
+ UINT16 transcarry[MAXPLAYERS]; //player carry
+ INT32 transspeed[MAXPLAYERS]; //player speed
+
+ // Star post stuff
+ INT16 spposition[MAXPLAYERS][3];
+ INT32 starpostnum[MAXPLAYERS];
+ tic_t starposttime[MAXPLAYERS];
+ angle_t starpostangle[MAXPLAYERS];
+
+ INT32 flags2[MAXPLAYERS];
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ position[i][0] = position[i][1] = position[i][2] = anglepos[i] = pindex[i] = -1;
+ teleported[i] = false;
+ }
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i] && players[i].playerstate == PST_LIVE
+ && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+ {
+ if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
+ continue;
+
+ position[counter][0] = players[i].mo->x;
+ position[counter][1] = players[i].mo->y;
+ position[counter][2] = players[i].mo->z;
+ pindex[counter] = i;
+ anglepos[counter] = players[i].mo->angle;
+ players[i].mo->momx = players[i].mo->momy = players[i].mo->momz =
+ players[i].rmomx = players[i].rmomy = 1;
+ players[i].cmomx = players[i].cmomy = 0;
+
+ transcarry[counter] = (players[i].powers[pw_carry] == CR_PLAYER ? CR_NONE : players[i].powers[pw_carry]);
+ transspeed[counter] = players[i].speed;
+ transtracer[counter] = players[i].mo->tracer;
+
+ spposition[counter][0] = players[i].starpostx;
+ spposition[counter][1] = players[i].starposty;
+ spposition[counter][2] = players[i].starpostz;
+ starpostnum[counter] = players[i].starpostnum;
+ starposttime[counter] = players[i].starposttime;
+ starpostangle[counter] = players[i].starpostangle;
+
+ flags2[counter] = players[i].mo->flags2;
+
+ counter++;
+ }
+ }
+
+ counter = 0;
+
+ // Mix them up!
+ for (;;)
+ {
+ if (counter > 255) // fail-safe to avoid endless loop
+ break;
+ prandom = P_RandomByte();
+ prandom %= numplayers; // I love modular arithmetic, don't you?
+ if (prandom) // Make sure it's not a useless mix
+ break;
+ counter++;
+ }
+
+ counter = 0;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i] && players[i].playerstate == PST_LIVE
+ && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+ {
+ if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
+ continue;
+
+ teleportfrom = (counter + prandom) % numplayers;
+
+ //speed and tracer come before...
+ players[i].speed = transspeed[teleportfrom];
+ P_SetTarget(&players[i].mo->tracer, transtracer[teleportfrom]);
+
+ P_MixUp(players[i].mo, position[teleportfrom][0], position[teleportfrom][1], position[teleportfrom][2], anglepos[teleportfrom],
+ spposition[teleportfrom][0], spposition[teleportfrom][1], spposition[teleportfrom][2],
+ starpostnum[teleportfrom], starposttime[teleportfrom], starpostangle[teleportfrom],
+ flags2[teleportfrom]);
+
+ //...carry after. same reasoning.
+ players[i].powers[pw_carry] = transcarry[teleportfrom];
+
+ teleported[i] = true;
+ counter++;
+ }
+ }
+ }
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (teleported[i])
+ {
+ if (playeringame[i] && players[i].playerstate == PST_LIVE
+ && players[i].mo && players[i].mo->health > 0 && !players[i].exiting && !players[i].powers[pw_super] && players[i].powers[pw_carry] != CR_NIGHTSMODE)
+ {
+ if ((netgame || multiplayer) && players[i].spectator)// Ignore spectators
+ continue;
+
+ P_SetThingPosition(players[i].mo);
+
+#ifdef ESLOPE
+ players[i].mo->floorz = P_GetFloorZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
+ players[i].mo->ceilingz = P_GetCeilingZ(players[i].mo, players[i].mo->subsector->sector, players[i].mo->x, players[i].mo->y, NULL);
+#else
+ players[i].mo->floorz = players[i].mo->subsector->sector->floorheight;
+ players[i].mo->ceilingz = players[i].mo->subsector->sector->ceilingheight;
+#endif
+
+ P_CheckPosition(players[i].mo, players[i].mo->x, players[i].mo->y);
+ }
+ }
+ }
+
+ // Play the 'bowrwoosh!' sound
+ S_StartSound(NULL, sfx_mixup);
+}
+
+// Function: A_RecyclePowers
+//
+// Description: Take all player's powers, and swap 'em.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_RecyclePowers(mobj_t *actor)
+{
+ INT32 i, j, k, numplayers = 0;
+
+#ifdef WEIGHTEDRECYCLER
+ UINT8 beneficiary = 255;
+#endif
+ UINT8 playerslist[MAXPLAYERS];
+ UINT8 postscramble[MAXPLAYERS];
+
+ UINT16 powers[MAXPLAYERS][NUMPOWERS];
+ INT32 weapons[MAXPLAYERS];
+ INT32 weaponheld[MAXPLAYERS];
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RecyclePowers", actor))
+ return;
+#endif
+
+#if !defined(WEIGHTEDRECYCLER) && !defined(HAVE_BLUA)
+ // actor is used in all scenarios but this one, funny enough
+ (void)actor;
+#endif
+
+ if (!multiplayer)
+ {
+ S_StartSound(actor, sfx_lose);
+ return;
+ }
+
+ numplayers = 0;
+
+ // Count the number of players in the game
+ for (i = 0, j = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i] && players[i].mo && players[i].mo->health > 0 && players[i].playerstate == PST_LIVE
+ && !players[i].exiting && !((netgame || multiplayer) && players[i].spectator))
+ {
+#ifndef WEIGHTEDRECYCLER
+ if (players[i].powers[pw_super])
+ continue; // Ignore super players
+#endif
+
+ numplayers++;
+ postscramble[j] = playerslist[j] = (UINT8)i;
+
+#ifdef WEIGHTEDRECYCLER
+ // The guy who started the recycle gets the best result
+ if (actor && actor->target && actor->target->player && &players[i] == actor->target->player)
+ beneficiary = (UINT8)i;
+#endif
+
+ // Save powers
+ for (k = 0; k < NUMPOWERS; k++)
+ powers[i][k] = players[i].powers[k];
+ //1.1: ring weapons too
+ weapons[i] = players[i].ringweapons;
+ weaponheld[i] = players[i].currentweapon;
+
+ j++;
+ }
+ }
+
+ if (numplayers <= 1)
+ {
+ S_StartSound(actor, sfx_lose);
+ return; //nobody to touch!
+ }
+
+ //shuffle the post scramble list, whee!
+ // hardcoded 0-1 to 1-0 for two players
+ if (numplayers == 2)
+ {
+ postscramble[0] = playerslist[1];
+ postscramble[1] = playerslist[0];
+ }
+ else
+ for (j = 0; j < numplayers; j++)
+ {
+ UINT8 tempint;
+
+ i = j + ((P_RandomByte() + leveltime) % (numplayers - j));
+ tempint = postscramble[j];
+ postscramble[j] = postscramble[i];
+ postscramble[i] = tempint;
+ }
+
+#ifdef WEIGHTEDRECYCLER
+ //the joys of qsort...
+ if (beneficiary != 255) {
+ qsort(playerslist, numplayers, sizeof(UINT8), P_RecycleCompare);
+
+ // now, make sure the benificiary is in the best slot
+ // swap out whatever poor sap was going to get the best items
+ for (i = 0; i < numplayers; i++)
+ {
+ if (postscramble[i] == beneficiary)
+ {
+ postscramble[i] = postscramble[0];
+ postscramble[0] = beneficiary;
+ break;
+ }
+ }
+ }
+#endif
+
+ // now assign!
+ for (i = 0; i < numplayers; i++)
+ {
+ UINT8 send_pl = playerslist[i];
+ UINT8 recv_pl = postscramble[i];
+
+ // debugF
+ CONS_Debug(DBG_GAMELOGIC, "sending player %hu's items to %hu\n", (UINT16)send_pl, (UINT16)recv_pl);
+
+ for (j = 0; j < NUMPOWERS; j++)
+ {
+ if (j == pw_flashing || j == pw_underwater || j == pw_spacetime || j == pw_carry
+ || j == pw_tailsfly || j == pw_extralife || j == pw_nocontrol || j == pw_super)
+ continue;
+ players[recv_pl].powers[j] = powers[send_pl][j];
+ }
+
+ //1.1: weapon rings too
+ players[recv_pl].ringweapons = weapons[send_pl];
+ players[recv_pl].currentweapon = weaponheld[send_pl];
+
+ P_SpawnShieldOrb(&players[recv_pl]);
+ if (P_IsLocalPlayer(&players[recv_pl]))
+ P_RestoreMusic(&players[recv_pl]);
+ P_FlashPal(&players[recv_pl], PAL_RECYCLE, 10);
+ }
+
+ S_StartSound(NULL, sfx_gravch); //heh, the sound effect I used is already in
+}
+
+// Function: A_Boss1Chase
+//
+// Description: Like A_Chase, but for Boss 1.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss1Chase(mobj_t *actor)
+{
+ INT32 delta;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss1Chase", actor))
+ return;
+#endif
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ P_SetMobjStateNF(actor, actor->info->spawnstate);
+ return;
+ }
+
+ if (actor->reactiontime)
+ actor->reactiontime--;
+
+ // turn towards movement direction if not there yet
+ if (actor->movedir < NUMDIRS)
+ {
+ actor->angle &= (7<<29);
+ delta = actor->angle - (actor->movedir << 29);
+
+ if (delta > 0)
+ actor->angle -= ANGLE_45;
+ else if (delta < 0)
+ actor->angle += ANGLE_45;
+ }
+
+ // do not attack twice in a row
+ if (actor->flags2 & MF2_JUSTATTACKED)
+ {
+ actor->flags2 &= ~MF2_JUSTATTACKED;
+ P_NewChaseDir(actor);
+ return;
+ }
+
+ if (actor->movecount)
+ goto nomissile;
+
+ if (!P_CheckMissileRange(actor))
+ goto nomissile;
+
+ if (actor->reactiontime <= 0)
+ {
+ if (actor->health > actor->info->damage)
+ {
+ if (P_RandomChance(FRACUNIT/2))
+ P_SetMobjState(actor, actor->info->missilestate);
+ else
+ P_SetMobjState(actor, actor->info->meleestate);
+ }
+ else
+ {
+ P_LinedefExecute(LE_PINCHPHASE, actor, NULL);
+ P_SetMobjState(actor, actor->info->raisestate);
+ }
+
+ actor->flags2 |= MF2_JUSTATTACKED;
+ actor->reactiontime = actor->info->reactiontime;
+ return;
+ }
+
+ // ?
+nomissile:
+ // possibly choose another target
+ if (multiplayer && P_RandomChance(FRACUNIT/128))
+ {
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+ }
+
+ if (actor->flags & MF_FLOAT && !(actor->flags2 & MF2_SKULLFLY))
+ { // Float up/down to your target's position. Stay above them, but not out of jump range.
+ fixed_t target_min = actor->target->floorz+FixedMul(64*FRACUNIT, actor->scale);
+ if (target_min < actor->target->z - actor->height)
+ target_min = actor->target->z - actor->height;
+ if (target_min < actor->floorz+FixedMul(33*FRACUNIT, actor->scale))
+ target_min = actor->floorz+FixedMul(33*FRACUNIT, actor->scale);
+ if (actor->z > target_min+FixedMul(16*FRACUNIT, actor->scale))
+ actor->momz = FixedMul((-actor->info->speed<<(FRACBITS-1)), actor->scale);
+ else if (actor->z < target_min)
+ actor->momz = FixedMul(actor->info->speed<<(FRACBITS-1), actor->scale);
+ else
+ actor->momz = FixedMul(actor->momz,7*FRACUNIT/8);
+ }
+
+ // chase towards player
+ if (P_AproxDistance(actor->target->x-actor->x, actor->target->y-actor->y) > actor->radius+actor->target->radius)
+ {
+ if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+ P_NewChaseDir(actor);
+ }
+ // too close, don't want to chase.
+ else if (--actor->movecount < 0)
+ {
+ // A mini-A_FaceTarget based on P_NewChaseDir.
+ // Yes, it really is this simple when you get down to it.
+ fixed_t deltax, deltay;
+
+ deltax = actor->target->x - actor->x;
+ deltay = actor->target->y - actor->y;
+
+ actor->movedir = diags[((deltay < 0)<<1) + (deltax > 0)];
+ actor->movecount = P_RandomByte() & 15;
+ }
+}
+
+// Function: A_Boss2Chase
+//
+// Description: Really doesn't 'chase', but rather goes in a circle.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss2Chase(mobj_t *actor)
+{
+ fixed_t radius;
+ boolean reverse = false;
+ INT32 speedvar;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss2Chase", actor))
+ return;
+#endif
+
+ if (actor->health <= 0)
+ return;
+
+ // Startup randomness
+ if (actor->reactiontime <= -666)
+ actor->reactiontime = 2*TICRATE + P_RandomByte();
+
+ // When reactiontime hits zero, he will go the other way
+ if (--actor->reactiontime <= 0)
+ {
+ reverse = true;
+ actor->reactiontime = 2*TICRATE + P_RandomByte();
+ }
+
+ P_SetTarget(&actor->target, P_GetClosestAxis(actor));
+
+ if (!actor->target) // This should NEVER happen.
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Boss2 has no target!\n");
+ A_BossDeath(actor);
+ return;
+ }
+
+ radius = actor->target->radius;
+
+ if (reverse)
+ {
+ actor->watertop = -actor->watertop;
+ actor->extravalue1 = 18;
+ if (actor->flags2 & MF2_AMBUSH)
+ actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2;
+ actor->extravalue2 = actor->extravalue1;
+ }
+
+ // Turnaround
+ if (actor->extravalue1 > 0)
+ {
+ --actor->extravalue1;
+
+ // Set base angle
+ {
+ const angle_t fa = (actor->target->angle + FixedAngle(actor->watertop))>>ANGLETOFINESHIFT;
+ const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
+ const fixed_t fs = FixedMul(FINESINE(fa),radius);
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
+ }
+
+ // Now turn you around!
+ // Note that the start position is the final position, we move it back around
+ // to intermediary positions...
+ actor->angle -= FixedAngle(FixedMul(FixedDiv(180<extravalue2<extravalue1<flags2 & MF2_AMBUSH)
+ speedvar = actor->health;
+ else
+ speedvar = actor->info->spawnhealth;
+
+ actor->target->angle += // Don't use FixedAngleC!
+ FixedAngle(FixedDiv(FixedMul(actor->watertop, (actor->info->spawnhealth*(FRACUNIT/4)*3)), speedvar*FRACUNIT));
+
+ P_UnsetThingPosition(actor);
+ {
+ const angle_t fa = actor->target->angle>>ANGLETOFINESHIFT;
+ const fixed_t fc = FixedMul(FINECOSINE(fa),radius);
+ const fixed_t fs = FixedMul(FINESINE(fa),radius);
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x + fc, actor->target->y + fs);
+ actor->x = actor->target->x + fc;
+ actor->y = actor->target->y + fs;
+ }
+ P_SetThingPosition(actor);
+
+ // Spray goo once every second
+ if (leveltime % (speedvar*15/10)-1 == 0)
+ {
+ const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
+ mobj_t *goop;
+ fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
+ angle_t fa;
+ // actor->movedir is used to determine the last
+ // direction goo was sprayed in. There are 8 possible
+ // directions to spray. (45-degree increments)
+
+ actor->movedir++;
+ actor->movedir %= NUMDIRS;
+ fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
+
+ goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
+ goop->momx = FixedMul(FINECOSINE(fa),ns);
+ goop->momy = FixedMul(FINESINE(fa),ns);
+ goop->momz = FixedMul(4*FRACUNIT, actor->scale);
+ goop->fuse = 10*TICRATE;
+
+ if (actor->info->attacksound)
+ S_StartAttackSound(actor, actor->info->attacksound);
+
+ if (P_RandomChance(FRACUNIT/2))
+ {
+ goop->momx *= 2;
+ goop->momy *= 2;
+ }
+ else if (P_RandomChance(129*FRACUNIT/256))
+ {
+ goop->momx *= 3;
+ goop->momy *= 3;
+ }
+
+ actor->flags2 |= MF2_JUSTATTACKED;
+ }
+ }
+}
+
+// Function: A_Boss2Pogo
+//
+// Description: Pogo part of Boss 2 AI.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss2Pogo(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss2Pogo", actor))
+ return;
+#endif
+ if (actor->z <= actor->floorz + FixedMul(8*FRACUNIT, actor->scale) && actor->momz <= 0)
+ {
+ if (actor->state != &states[actor->info->raisestate])
+ P_SetMobjState(actor, actor->info->raisestate);
+ // Pogo Mode
+ }
+ else if (actor->momz < 0 && actor->reactiontime)
+ {
+ const fixed_t ns = FixedMul(3 * FRACUNIT, actor->scale);
+ mobj_t *goop;
+ fixed_t fz = actor->z+actor->height+FixedMul(24*FRACUNIT, actor->scale);
+ angle_t fa;
+ INT32 i;
+ // spray in all 8 directions!
+ for (i = 0; i < 8; i++)
+ {
+ actor->movedir++;
+ actor->movedir %= NUMDIRS;
+ fa = (actor->movedir*FINEANGLES/8) & FINEMASK;
+
+ goop = P_SpawnMobj(actor->x, actor->y, fz, actor->info->painchance);
+ goop->momx = FixedMul(FINECOSINE(fa),ns);
+ goop->momy = FixedMul(FINESINE(fa),ns);
+ goop->momz = FixedMul(4*FRACUNIT, actor->scale);
+
+ goop->fuse = 10*TICRATE;
+ }
+ actor->reactiontime = 0; // we already shot goop, so don't do it again!
+ if (actor->info->attacksound)
+ S_StartAttackSound(actor, actor->info->attacksound);
+ actor->flags2 |= MF2_JUSTATTACKED;
+ }
+}
+
+// Function: A_Boss2TakeDamage
+//
+// Description: Special function for Boss 2 so you can't just sit and destroy him.
+//
+// var1 = Invincibility duration
+// var2 = unused
+//
+void A_Boss2TakeDamage(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss2TakeDamage", actor))
+ return;
+#endif
+ A_Pain(actor);
+ actor->reactiontime = 1; // turn around
+ if (locvar1 == 0) // old A_Invincibilerize behavior
+ actor->movecount = TICRATE;
+ else
+ actor->movecount = locvar1; // become flashing invulnerable for this long.
+}
+
+// Function: A_Boss7Chase
+//
+// Description: Like A_Chase, but for Black Eggman
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss7Chase(mobj_t *actor)
+{
+ INT32 delta;
+ INT32 i;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss7Chase", actor))
+ return;
+#endif
+
+ if (actor->z != actor->floorz)
+ return;
+
+ // Self-adjust if stuck on the edge
+ if (actor->tracer)
+ {
+ if (P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y) > 128*FRACUNIT - actor->radius)
+ P_InstaThrust(actor, R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y), FRACUNIT);
+ }
+
+ if (actor->flags2 & MF2_FRET)
+ {
+ P_SetMobjState(actor, S_BLACKEGG_DESTROYPLAT1);
+ S_StartSound(0, sfx_s3k53);
+ actor->flags2 &= ~MF2_FRET;
+ return;
+ }
+
+ // turn towards movement direction if not there yet
+ if (actor->movedir < NUMDIRS)
+ {
+ actor->angle &= (7<<29);
+ delta = actor->angle - (actor->movedir << 29);
+
+ if (delta > 0)
+ actor->angle -= ANGLE_45;
+ else if (delta < 0)
+ actor->angle += ANGLE_45;
+ }
+
+ // Is a player on top of us?
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+
+ if (!players[i].mo)
+ continue;
+
+ if (players[i].mo->health <= 0)
+ continue;
+
+ if (P_AproxDistance(players[i].mo->x - actor->x, players[i].mo->y - actor->y) > actor->radius)
+ continue;
+
+ if (players[i].mo->z > actor->z + actor->height - 2*FRACUNIT
+ && players[i].mo->z < actor->z + actor->height + 32*FRACUNIT)
+ {
+ // Punch him!
+ P_SetMobjState(actor, actor->info->meleestate);
+ S_StartSound(0, sfx_begrnd); // warning sound
+ return;
+ }
+ }
+
+ if (actor->health <= actor->info->damage
+ && actor->target
+ && actor->target->player
+ && (actor->target->player->powers[pw_carry] == CR_GENERIC))
+ {
+ A_FaceTarget(actor);
+ P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
+ actor->movecount = TICRATE + P_RandomByte()/2;
+ return;
+ }
+
+ if (actor->reactiontime)
+ actor->reactiontime--;
+
+ if (actor->reactiontime <= 0 && actor->z == actor->floorz)
+ {
+ // Here, we'll call P_RandomByte() and decide what kind of attack to do
+ switch(actor->threshold)
+ {
+ case 0: // Lob cannon balls
+ if (actor->z < 1056*FRACUNIT)
+ {
+ A_FaceTarget(actor);
+ P_SetMobjState(actor, actor->info->xdeathstate);
+ actor->movecount = 7*TICRATE + P_RandomByte();
+ break;
+ }
+ actor->threshold++;
+ /* FALLTHRU */
+ case 1: // Chaingun Goop
+ A_FaceTarget(actor);
+ P_SetMobjState(actor, S_BLACKEGG_SHOOT1);
+
+ if (actor->health > actor->info->damage)
+ actor->movecount = TICRATE + P_RandomByte()/3;
+ else
+ actor->movecount = TICRATE + P_RandomByte()/2;
+ break;
+ case 2: // Homing Missile
+ A_FaceTarget(actor);
+ P_SetMobjState(actor, actor->info->missilestate);
+ S_StartSound(0, sfx_beflap);
+ break;
+ }
+
+ actor->threshold++;
+ actor->threshold %= 3;
+ return;
+ }
+
+ // possibly choose another target
+ if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+ && P_BossTargetPlayer(actor, false))
+ return; // got a new target
+
+ if (leveltime & 1)
+ {
+ // chase towards player
+ if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+ P_NewChaseDir(actor);
+ }
+}
+
+// Function: A_GoopSplat
+//
+// Description: Black Eggman goop hits a target and sticks around for awhile.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GoopSplat(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_GoopSplat", actor))
+ return;
+#endif
+ P_UnsetThingPosition(actor);
+ if (sector_list)
+ {
+ P_DelSeclist(sector_list);
+ sector_list = NULL;
+ }
+ actor->flags = MF_SPECIAL; // Not a typo
+ P_SetThingPosition(actor);
+}
+
+// Function: A_Boss2PogoSFX
+//
+// Description: Pogoing for Boss 2
+//
+// var1 = pogo jump strength
+// var2 = idle pogo speed
+//
+void A_Boss2PogoSFX(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss2PogoSFX", actor))
+ return;
+#endif
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ return;
+ }
+
+ // Boing!
+ if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(256*FRACUNIT, actor->scale))
+ {
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ P_InstaThrust(actor, actor->angle, FixedMul(actor->info->speed, actor->scale));
+ // pogo on player
+ }
+ else
+ {
+ UINT8 prandom = P_RandomByte();
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom);
+ P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale));
+ }
+ if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
+ actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
+ actor->reactiontime = 1;
+}
+
+// Function: A_Boss2PogoTarget
+//
+// Description: Pogoing for Boss 2, tries to actually land on the player directly.
+//
+// var1 = pogo jump strength
+// var2 = idle pogo speed
+//
+void A_Boss2PogoTarget(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss2PogoTarget", actor))
+ return;
+#endif
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || (actor->target->player && actor->target->player->powers[pw_flashing])
+ || P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) >= FixedMul(512*FRACUNIT, actor->scale))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 512*FRACUNIT))
+ ; // got a new target
+ else if (P_LookForPlayers(actor, true, false, 0))
+ ; // got a new target
+ else
+ return;
+ }
+
+ // Target hit, retreat!
+ if (actor->target->player->powers[pw_flashing] > TICRATE || actor->flags2 & MF2_FRET)
+ {
+ UINT8 prandom = P_RandomByte();
+ actor->z++; // unstick from the floor
+ actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
+ P_InstaThrust(actor, actor->angle+ANGLE_180, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
+ }
+ // Try to land on top of the player.
+ else if (P_AproxDistance(actor->x-actor->target->x, actor->y-actor->target->y) < FixedMul(512*FRACUNIT, actor->scale))
+ {
+ fixed_t airtime, gravityadd, zoffs;
+
+ // check gravity in the sector (for later math)
+ P_CheckGravity(actor, true);
+ gravityadd = actor->momz;
+
+ actor->z++; // unstick from the floor
+ actor->momz = FixedMul(locvar1 + (locvar1>>2), actor->scale); // Bounce up in air
+
+ /*badmath = 0;
+ airtime = 0;
+ do {
+ badmath += momz;
+ momz += gravityadd;
+ airtime++;
+ } while(badmath > 0);
+ airtime = 2*airtime<momz<<1, gravityadd)<<1; // going from 0 to 0 is much simpler
+ zoffs = (P_GetPlayerHeight(actor->target->player)>>1) + (actor->target->floorz - actor->floorz); // offset by the difference in floor height plus half the player height,
+ airtime = FixedDiv((-actor->momz - FixedSqrt(FixedMul(actor->momz,actor->momz)+zoffs)), gravityadd)<<1; // to try and land on their head rather than on their feet
+
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ P_InstaThrust(actor, actor->angle, FixedDiv(P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y), airtime));
+ }
+ // Wander semi-randomly towards the player to get closer.
+ else
+ {
+ UINT8 prandom = P_RandomByte();
+ actor->z++; // unstick from the floor
+ actor->momz = FixedMul(locvar1, actor->scale); // Bounce up in air
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) + (P_RandomChance(FRACUNIT/2) ? -prandom : +prandom); // Pick a direction, and randomize it.
+ P_InstaThrust(actor, actor->angle, FixedMul(FixedMul(actor->info->speed,(locvar2)), actor->scale)); // Move at wandering speed
+ }
+ // Boing!
+ if (actor->info->activesound) S_StartSound(actor, actor->info->activesound);
+
+ if (actor->info->missilestate) // spawn the pogo stick collision box
+ {
+ mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate);
+ pogo->target = actor;
+ }
+
+ actor->reactiontime = 1;
+}
+
+// Function: A_EggmanBox
+//
+// Description: Harms the player
+//
+// var1 = unused
+// var2 = unused
+//
+void A_EggmanBox(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_EggmanBox", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ P_DamageMobj(actor->target, actor, actor, 1, 0); // Ow!
+}
+
+// Function: A_TurretFire
+//
+// Description: Initiates turret fire.
+//
+// var1 = object # to repeatedly fire
+// var2 = distance threshold
+//
+void A_TurretFire(mobj_t *actor)
+{
+ INT32 count = 0;
+ fixed_t dist;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_TurretFire", actor))
+ return;
+#endif
+
+ if (locvar2)
+ dist = FixedMul(locvar2*FRACUNIT, actor->scale);
+ else
+ dist = FixedMul(2048*FRACUNIT, actor->scale);
+
+ if (!locvar1)
+ locvar1 = MT_TURRETLASER;
+
+ while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
+ {
+ if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
+ {
+ actor->flags2 |= MF2_FIRING;
+ actor->extravalue1 = locvar1;
+ break;
+ }
+
+ count++;
+ }
+}
+
+// Function: A_SuperTurretFire
+//
+// Description: Initiates turret fire that even stops Super Sonic.
+//
+// var1 = object # to repeatedly fire
+// var2 = distance threshold
+//
+void A_SuperTurretFire(mobj_t *actor)
+{
+ INT32 count = 0;
+ fixed_t dist;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SuperTurretFire", actor))
+ return;
+#endif
+
+ if (locvar2)
+ dist = FixedMul(locvar2*FRACUNIT, actor->scale);
+ else
+ dist = FixedMul(2048*FRACUNIT, actor->scale);
+
+ if (!locvar1)
+ locvar1 = MT_TURRETLASER;
+
+ while (P_SupermanLook4Players(actor) && count < MAXPLAYERS)
+ {
+ if (P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) < dist)
+ {
+ actor->flags2 |= MF2_FIRING;
+ actor->flags2 |= MF2_SUPERFIRE;
+ actor->extravalue1 = locvar1;
+ break;
+ }
+
+ count++;
+ }
+}
+
+// Function: A_TurretStop
+//
+// Description: Stops the turret fire.
+//
+// var1 = Don't play activesound?
+// var2 = unused
+//
+void A_TurretStop(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_TurretStop", actor))
+ return;
+#endif
+
+ actor->flags2 &= ~MF2_FIRING;
+ actor->flags2 &= ~MF2_SUPERFIRE;
+
+ if (actor->target && actor->info->activesound && !locvar1)
+ S_StartSound(actor, actor->info->activesound);
+}
+
+// Function: A_SparkFollow
+//
+// Description: Used by the hyper sparks to rotate around their target.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SparkFollow(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SparkFollow", actor))
+ return;
+#endif
+
+ if ((!actor->target || (actor->target->health <= 0))
+ || (actor->target->player && !actor->target->player->powers[pw_super]))
+ {
+ P_RemoveMobj(actor);
+ return;
+ }
+
+ actor->angle += FixedAngle(actor->info->damage*FRACUNIT);
+ P_UnsetThingPosition(actor);
+ {
+ const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
+ actor->x = actor->target->x + FixedMul(FINECOSINE(fa),FixedMul(actor->info->speed, actor->scale));
+ actor->y = actor->target->y + FixedMul(FINESINE(fa),FixedMul(actor->info->speed, actor->scale));
+ if (actor->target->eflags & MFE_VERTICALFLIP)
+ actor->z = actor->target->z + actor->target->height - FixedDiv(actor->target->height,3*FRACUNIT);
+ else
+ actor->z = actor->target->z + FixedDiv(actor->target->height,3*FRACUNIT) - actor->height;
+ }
+ P_SetThingPosition(actor);
+}
+
+// Function: A_BuzzFly
+//
+// Description: Makes an object slowly fly after a player, in the manner of a Buzz.
+//
+// var1 = sfx to play
+// var2 = length of sfx, set to threshold if played
+//
+void A_BuzzFly(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BuzzFly", actor))
+ return;
+#endif
+ if (actor->flags2 & MF2_AMBUSH)
+ return;
+
+ if (actor->reactiontime)
+ actor->reactiontime--;
+
+ // modify target threshold
+ if (actor->threshold)
+ {
+ if (!actor->target || actor->target->health <= 0)
+ actor->threshold = 0;
+ else
+ actor->threshold--;
+ }
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ actor->momz = actor->momy = actor->momx = 0;
+ P_SetMobjState(actor, actor->info->spawnstate);
+ return;
+ }
+
+ // turn towards movement direction if not there yet
+ actor->angle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+
+ if (actor->target->health <= 0 || (!actor->threshold && !P_CheckSight(actor, actor->target)))
+ {
+ if ((multiplayer || netgame) && P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)))
+ return; // got a new target
+
+ actor->momx = actor->momy = actor->momz = 0;
+ P_SetMobjState(actor, actor->info->spawnstate); // Go back to looking around
+ return;
+ }
+
+ // If the player is over 3072 fracunits away, then look for another player
+ if (P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y),
+ actor->target->z - actor->z) > FixedMul(3072*FRACUNIT, actor->scale))
+ {
+ if (multiplayer || netgame)
+ P_LookForPlayers(actor, true, false, FixedMul(3072*FRACUNIT, actor->scale)); // maybe get a new target
+
+ return;
+ }
+
+ // chase towards player
+ {
+ INT32 dist, realspeed;
+ const fixed_t mf = 5*(FRACUNIT/4);
+
+ if (ultimatemode)
+ realspeed = FixedMul(FixedMul(actor->info->speed,mf), actor->scale);
+ else
+ realspeed = FixedMul(actor->info->speed, actor->scale);
+
+ dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x,
+ actor->target->y - actor->y), actor->target->z - actor->z);
+
+ if (dist < 1)
+ dist = 1;
+
+ actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), realspeed);
+ actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), realspeed);
+ actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), realspeed);
+
+ if (actor->z+actor->momz >= actor->waterbottom && actor->watertop > actor->floorz
+ && actor->z+actor->momz > actor->watertop - FixedMul(256*FRACUNIT, actor->scale)
+ && actor->z+actor->momz <= actor->watertop)
+ {
+ actor->momz = 0;
+ actor->z = actor->watertop;
+ }
+ }
+
+ if (locvar1 != sfx_None && !actor->threshold)
+ {
+ S_StartSound(actor, locvar1);
+ actor->threshold = locvar2;
+ }
+}
+
+// Function: A_GuardChase
+//
+// Description: Modified A_Chase for Egg Guard
+//
+// var1 = unused
+// var2 = unused
+//
+void A_GuardChase(mobj_t *actor)
+{
+ INT32 delta;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_GuardChase", actor))
+ return;
+#endif
+
+ if (actor->reactiontime)
+ actor->reactiontime--;
+
+ if (actor->threshold != 42) // In formation...
+ {
+ fixed_t speed;
+
+ if (!actor->tracer || !actor->tracer->health)
+ {
+ P_SetTarget(&actor->tracer, NULL);
+ actor->threshold = 42;
+ P_SetMobjState(actor, actor->info->painstate);
+ actor->flags |= MF_SPECIAL|MF_SHOOTABLE;
+ return;
+ }
+
+ speed = actor->extravalue1*actor->scale;
+
+ if (actor->flags2 & MF2_AMBUSH)
+ speed <<= 1;
+
+ if (speed
+ && !P_TryMove(actor,
+ actor->x + P_ReturnThrustX(actor, actor->angle, speed),
+ actor->y + P_ReturnThrustY(actor, actor->angle, speed),
+ false)
+ && speed > 0) // can't be the same check as previous so that P_TryMove gets to happen.
+ {
+ if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_OBJECTSPECIAL))
+ actor->angle += ANGLE_90;
+ else if (actor->spawnpoint && ((actor->spawnpoint->options & (MTF_EXTRA|MTF_OBJECTSPECIAL)) == MTF_EXTRA))
+ actor->angle -= ANGLE_90;
+ else
+ actor->angle += ANGLE_180;
+ }
+
+ if (actor->extravalue1 < actor->info->speed)
+ actor->extravalue1++;
+ }
+ else // Break ranks!
+ {
+ // turn towards movement direction if not there yet
+ if (actor->movedir < NUMDIRS)
+ {
+ actor->angle &= (7<<29);
+ delta = actor->angle - (actor->movedir << 29);
+
+ if (delta > 0)
+ actor->angle -= ANGLE_45;
+ else if (delta < 0)
+ actor->angle += ANGLE_45;
+ }
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ P_SetMobjStateNF(actor, actor->info->spawnstate);
+ return;
+ }
+
+ // possibly choose another target
+ if (multiplayer && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+ && P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ // chase towards player
+ if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
+ {
+ P_NewChaseDir(actor);
+ actor->movecount += 5; // Increase tics before change in direction allowed.
+ }
+ }
+
+ // Now that we've moved, its time for our shield to move!
+ // Otherwise it'll never act as a proper overlay.
+ if (actor->tracer && actor->tracer->state
+ && actor->tracer->state->action.acp1)
+ {
+ var1 = actor->tracer->state->var1, var2 = actor->tracer->state->var2;
+ actor->tracer->state->action.acp1(actor->tracer);
+ }
+}
+
+// Function: A_EggShield
+//
+// Description: Modified A_Chase for Egg Guard's shield
+//
+// var1 = unused
+// var2 = unused
+//
+void A_EggShield(mobj_t *actor)
+{
+ INT32 i;
+ player_t *player;
+ fixed_t blockdist;
+ fixed_t newx, newy;
+ fixed_t movex, movey;
+ angle_t angle;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_EggShield", actor))
+ return;
+#endif
+
+ if (!actor->target || !actor->target->health)
+ {
+ P_RemoveMobj(actor);
+ return;
+ }
+
+ newx = actor->target->x + P_ReturnThrustX(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
+ newy = actor->target->y + P_ReturnThrustY(actor, actor->target->angle, FixedMul(FRACUNIT, actor->scale));
+
+ movex = newx - actor->x;
+ movey = newy - actor->y;
+
+ actor->angle = actor->target->angle;
+ if (actor->target->eflags & MFE_VERTICALFLIP)
+ {
+ actor->eflags |= MFE_VERTICALFLIP;
+ actor->z = actor->target->z + actor->target->height - actor->height;
+ }
+ else
+ actor->z = actor->target->z;
+
+ actor->destscale = actor->target->destscale;
+ P_SetScale(actor, actor->target->scale);
+
+ actor->floorz = actor->target->floorz;
+ actor->ceilingz = actor->target->ceilingz;
+
+ if (!movex && !movey)
+ return;
+
+ P_UnsetThingPosition(actor);
+ actor->x = newx;
+ actor->y = newy;
+ P_SetThingPosition(actor);
+
+ // Search for players to push
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+
+ player = &players[i];
+
+ if (!player->mo)
+ continue;
+
+ if (player->mo->z > actor->z + actor->height)
+ continue;
+
+ if (player->mo->z + player->mo->height < actor->z)
+ continue;
+
+ blockdist = actor->radius + player->mo->radius;
+
+ if (abs(actor->x - player->mo->x) >= blockdist || abs(actor->y - player->mo->y) >= blockdist)
+ continue; // didn't hit it
+
+ angle = R_PointToAngle2(actor->x, actor->y, player->mo->x, player->mo->y) - actor->angle;
+
+ if (angle > ANGLE_90 && angle < ANGLE_270)
+ continue;
+
+ // Blocked by the shield
+ player->mo->momx += movex;
+ player->mo->momy += movey;
+ return;
+ }
+}
+
+
+// Function: A_SetReactionTime
+//
+// Description: Sets the object's reaction time.
+//
+// var1 = 1 (use value in var2); 0 (use info table value)
+// var2 = if var1 = 1, then value to set
+//
+void A_SetReactionTime(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetReactionTime", actor))
+ return;
+#endif
+ if (var1)
+ actor->reactiontime = var2;
+ else
+ actor->reactiontime = actor->info->reactiontime;
+}
+
+// Function: A_Boss1Spikeballs
+//
+// Description: Boss 1 spikeball spawning loop.
+//
+// var1 = ball number
+// var2 = total balls
+//
+void A_Boss1Spikeballs(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *ball;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss1Spikeballs", actor))
+ return;
+#endif
+
+ ball = P_SpawnMobj(actor->x, actor->y, actor->z, MT_EGGMOBILE_BALL);
+ P_SetTarget(&ball->target, actor);
+ ball->movedir = FixedAngle(FixedMul(FixedDiv(locvar1<threshold = ball->radius + actor->radius + ball->info->painchance;
+
+ S_StartSound(ball, ball->info->seesound);
+ var1 = ball->state->var1, var2 = ball->state->var2;
+ ball->state->action.acp1(ball);
+}
+
+// Function: A_Boss3TakeDamage
+//
+// Description: Called when Boss 3 takes damage.
+//
+// var1 = movecount value
+// var2 = unused
+//
+void A_Boss3TakeDamage(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss3TakeDamage", actor))
+ return;
+#endif
+ actor->movecount = var1;
+
+ if (actor->target && actor->target->spawnpoint)
+ actor->threshold = actor->target->spawnpoint->extrainfo;
+}
+
+// Function: A_Boss3Path
+//
+// Description: Does pathfinding along Boss 3's nodes.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss3Path(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss3Path", actor))
+ return;
+#endif
+
+ if (actor->tracer && actor->tracer->health && actor->tracer->movecount)
+ actor->movecount |= 1;
+ else if (actor->movecount & 1)
+ actor->movecount = 0;
+
+ if (actor->movecount & 2) // We've reached a firing point?
+ {
+ // Wait here and pretend to be angry or something.
+ actor->momx = 0;
+ actor->momy = 0;
+ actor->momz = 0;
+ P_SetTarget(&actor->target, actor->tracer->target);
+ var1 = 0, var2 = 0;
+ A_FaceTarget(actor);
+ if (actor->tracer->state == &states[actor->tracer->info->missilestate])
+ P_SetMobjState(actor, actor->info->missilestate);
+ return;
+ }
+ else if (actor->threshold >= 0) // Traveling mode
+ {
+ thinker_t *th;
+ mobj_t *mo2;
+ fixed_t dist, dist2;
+ fixed_t speed;
+
+ P_SetTarget(&actor->target, NULL);
+
+ // scan the thinkers
+ // to find a point that matches
+ // the number
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo2 = (mobj_t *)th;
+ if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint && mo2->spawnpoint->angle == actor->threshold)
+ {
+ P_SetTarget(&actor->target, mo2);
+ break;
+ }
+ }
+
+ if (!actor->target) // Should NEVER happen
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy was unable to find specified waypoint: %d\n", actor->threshold);
+ return;
+ }
+
+ dist = P_AproxDistance(P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y), actor->target->z - actor->z);
+
+ if (dist < 1)
+ dist = 1;
+
+ if (actor->tracer && ((actor->tracer->movedir)
+ || (actor->tracer->health <= actor->tracer->info->damage)))
+ speed = actor->info->speed * 2;
+ else
+ speed = actor->info->speed;
+
+ actor->momx = FixedMul(FixedDiv(actor->target->x - actor->x, dist), speed);
+ actor->momy = FixedMul(FixedDiv(actor->target->y - actor->y, dist), speed);
+ actor->momz = FixedMul(FixedDiv(actor->target->z - actor->z, dist), speed);
+
+ if (actor->momx != 0 || actor->momy != 0)
+ actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy);
+
+ dist2 = P_AproxDistance(P_AproxDistance(actor->target->x - (actor->x + actor->momx), actor->target->y - (actor->y + actor->momy)), actor->target->z - (actor->z + actor->momz));
+
+ if (dist2 < 1)
+ dist2 = 1;
+
+ if ((dist >> FRACBITS) <= (dist2 >> FRACBITS))
+ {
+ // If further away, set XYZ of mobj to waypoint location
+ P_UnsetThingPosition(actor);
+ actor->x = actor->target->x;
+ actor->y = actor->target->y;
+ actor->z = actor->target->z;
+ actor->momx = actor->momy = actor->momz = 0;
+ P_SetThingPosition(actor);
+
+ if (actor->threshold == 0)
+ {
+ P_RemoveMobj(actor); // Cycle completed. Dummy removed.
+ return;
+ }
+
+ // Set to next waypoint in sequence
+ if (actor->target->spawnpoint)
+ {
+ // From the center point, choose one of the five paths
+ if (actor->target->spawnpoint->angle == 0)
+ {
+ P_RemoveMobj(actor); // Cycle completed. Dummy removed.
+ return;
+ }
+ else
+ actor->threshold = actor->target->spawnpoint->extrainfo;
+
+ // If the deaf flag is set, go into firing mode
+ if (actor->target->spawnpoint->options & MTF_AMBUSH)
+ actor->movecount |= 2;
+ }
+ else // This should never happen, as well
+ CONS_Debug(DBG_GAMELOGIC, "Error: Boss 3 Dummy waypoint has no spawnpoint associated with it.\n");
+ }
+ }
+}
+
+// Function: A_LinedefExecute
+//
+// Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well.
+//
+// var1 = tag
+// var2 = add angle to tag (optional)
+//
+void A_LinedefExecute(mobj_t *actor)
+{
+ INT32 tagnum;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_LinedefExecute", actor))
+ return;
+#endif
+
+ tagnum = locvar1;
+ // state numbers option is no more, custom states cannot be guaranteed to stay the same number anymore, now that they can be defined by names instead
+
+ if (locvar2)
+ tagnum += locvar2*(AngleFixed(actor->angle)>>FRACBITS);
+
+ CONS_Debug(DBG_GAMELOGIC, "A_LinedefExecute: Running mobjtype %d's sector with tag %d\n", actor->type, tagnum);
+
+ // tag 32768 displayed in map editors is actually tag -32768, tag 32769 is -32767, 65535 is -1 etc.
+ P_LinedefExecute((INT16)tagnum, actor, actor->subsector->sector);
+}
+
+// Function: A_PlaySeeSound
+//
+// Description: Plays the object's seesound.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_PlaySeeSound(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_PlaySeeSound", actor))
+ return;
+#endif
+ if (actor->info->seesound)
+ S_StartScreamSound(actor, actor->info->seesound);
+}
+
+// Function: A_PlayAttackSound
+//
+// Description: Plays the object's attacksound.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_PlayAttackSound(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_PlayAttackSound", actor))
+ return;
+#endif
+ if (actor->info->attacksound)
+ S_StartAttackSound(actor, actor->info->attacksound);
+}
+
+// Function: A_PlayActiveSound
+//
+// Description: Plays the object's activesound.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_PlayActiveSound(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_PlayActiveSound", actor))
+ return;
+#endif
+ if (actor->info->activesound)
+ S_StartSound(actor, actor->info->activesound);
+}
+
+// Function: A_SmokeTrailer
+//
+// Description: Adds smoke trails to an object.
+//
+// var1 = object # to spawn as smoke
+// var2 = unused
+//
+void A_SmokeTrailer(mobj_t *actor)
+{
+ mobj_t *th;
+ INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SmokeTrailer", actor))
+ return;
+#endif
+
+ if (leveltime % 4)
+ return;
+
+ // add the smoke behind the rocket
+ if (actor->eflags & MFE_VERTICALFLIP)
+ {
+ th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z + actor->height - FixedMul(mobjinfo[locvar1].height, actor->scale), locvar1);
+ th->flags2 |= MF2_OBJECTFLIP;
+ }
+ else
+ th = P_SpawnMobj(actor->x-actor->momx, actor->y-actor->momy, actor->z, locvar1);
+ P_SetObjectMomZ(th, FRACUNIT, false);
+ th->destscale = actor->scale;
+ P_SetScale(th, actor->scale);
+ th->tics -= P_RandomByte() & 3;
+ if (th->tics < 1)
+ th->tics = 1;
+}
+
+// Function: A_SpawnObjectAbsolute
+//
+// Description: Spawns an object at an absolute position
+//
+// var1:
+// var1 >> 16 = x
+// var1 & 65535 = y
+// var2:
+// var2 >> 16 = z
+// var2 & 65535 = type
+//
+void A_SpawnObjectAbsolute(mobj_t *actor)
+{
+ INT16 x, y, z; // Want to be sure we can use negative values
+ mobjtype_t type;
+ mobj_t *mo;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SpawnObjectAbsolute", actor))
+ return;
+#endif
+
+ x = (INT16)(locvar1>>16);
+ y = (INT16)(locvar1&65535);
+ z = (INT16)(locvar2>>16);
+ type = (mobjtype_t)(locvar2&65535);
+
+ mo = P_SpawnMobj(x<angle = actor->angle;
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ mo->flags2 |= MF2_OBJECTFLIP;
+}
+
+// Function: A_SpawnObjectRelative
+//
+// Description: Spawns an object relative to the location of the actor
+//
+// var1:
+// var1 >> 16 = x
+// var1 & 65535 = y
+// var2:
+// var2 >> 16 = z
+// var2 & 65535 = type
+//
+void A_SpawnObjectRelative(mobj_t *actor)
+{
+ INT16 x, y, z; // Want to be sure we can use negative values
+ mobjtype_t type;
+ mobj_t *mo;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SpawnObjectRelative", actor))
+ return;
+#endif
+
+ CONS_Debug(DBG_GAMELOGIC, "A_SpawnObjectRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+ x = (INT16)(locvar1>>16);
+ y = (INT16)(locvar1&65535);
+ z = (INT16)(locvar2>>16);
+ type = (mobjtype_t)(locvar2&65535);
+
+ // Spawn objects correctly in reverse gravity.
+ // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame
+ mo = P_SpawnMobj(actor->x + FixedMul(x<scale),
+ actor->y + FixedMul(y<scale),
+ (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[type].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), type);
+
+ // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
+ mo->angle = actor->angle;
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ mo->flags2 |= MF2_OBJECTFLIP;
+
+}
+
+// Function: A_ChangeAngleRelative
+//
+// Description: Changes the angle to a random relative value between the min and max. Set min and max to the same value to eliminate randomness
+//
+// var1 = min
+// var2 = max
+//
+void A_ChangeAngleRelative(mobj_t *actor)
+{
+ // Oh god, the old code /sucked/. Changed this and the absolute version to get a random range using amin and amax instead of
+ // getting a random angle from the _entire_ spectrum and then clipping. While we're at it, do the angle conversion to the result
+ // rather than the ranges, so <0 and >360 work as possible values. -Red
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ //angle_t angle = (P_RandomByte()+1)<<24;
+ const fixed_t amin = locvar1*FRACUNIT;
+ const fixed_t amax = locvar2*FRACUNIT;
+ //const angle_t amin = FixedAngle(locvar1*FRACUNIT);
+ //const angle_t amax = FixedAngle(locvar2*FRACUNIT);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ChangeAngleRelative", actor))
+ return;
+#endif
+
+#ifdef PARANOIA
+ if (amin > amax)
+ I_Error("A_ChangeAngleRelative: var1 is greater then var2");
+#endif
+/*
+ if (angle < amin)
+ angle = amin;
+ if (angle > amax)
+ angle = amax;*/
+
+ actor->angle += FixedAngle(P_RandomRange(amin, amax));
+}
+
+// Function: A_ChangeAngleAbsolute
+//
+// Description: Changes the angle to a random absolute value between the min and max. Set min and max to the same value to eliminate randomness
+//
+// var1 = min
+// var2 = max
+//
+void A_ChangeAngleAbsolute(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ //angle_t angle = (P_RandomByte()+1)<<24;
+ const fixed_t amin = locvar1*FRACUNIT;
+ const fixed_t amax = locvar2*FRACUNIT;
+ //const angle_t amin = FixedAngle(locvar1*FRACUNIT);
+ //const angle_t amax = FixedAngle(locvar2*FRACUNIT);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ChangeAngleAbsolute", actor))
+ return;
+#endif
+
+#ifdef PARANOIA
+ if (amin > amax)
+ I_Error("A_ChangeAngleAbsolute: var1 is greater then var2");
+#endif
+/*
+ if (angle < amin)
+ angle = amin;
+ if (angle > amax)
+ angle = amax;*/
+
+ actor->angle = FixedAngle(P_RandomRange(amin, amax));
+}
+
+// Function: A_PlaySound
+//
+// Description: Plays a sound
+//
+// var1 = sound # to play
+// var2:
+// 0 = Play sound without an origin
+// 1 = Play sound using calling object as origin
+//
+void A_PlaySound(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_PlaySound", actor))
+ return;
+#endif
+
+ S_StartSound(locvar2 ? actor : NULL, locvar1);
+}
+
+// Function: A_FindTarget
+//
+// Description: Finds the nearest/furthest mobj of the specified type and sets actor->target to it.
+//
+// var1 = mobj type
+// var2 = if (0) nearest; else furthest;
+//
+void A_FindTarget(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *targetedmobj = NULL;
+ thinker_t *th;
+ mobj_t *mo2;
+ fixed_t dist1 = 0, dist2 = 0;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FindTarget", actor))
+ return;
+#endif
+
+ CONS_Debug(DBG_GAMELOGIC, "A_FindTarget called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+ // scan the thinkers
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo2 = (mobj_t *)th;
+
+ if (mo2->type == (mobjtype_t)locvar1)
+ {
+ if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
+ continue; // Ignore spectators
+ if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
+ continue; // Ignore dead things
+ if (targetedmobj == NULL)
+ {
+ targetedmobj = mo2;
+ dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+ }
+ else
+ {
+ dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+
+ if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
+ {
+ targetedmobj = mo2;
+ dist2 = dist1;
+ }
+ }
+ }
+ }
+
+ if (!targetedmobj)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Unable to find the specified object to target.\n");
+ return; // Oops, nothing found..
+ }
+
+ CONS_Debug(DBG_GAMELOGIC, "A_FindTarget: Found a target.\n");
+
+ P_SetTarget(&actor->target, targetedmobj);
+}
+
+// Function: A_FindTracer
+//
+// Description: Finds the nearest/furthest mobj of the specified type and sets actor->tracer to it.
+//
+// var1 = mobj type
+// var2 = if (0) nearest; else furthest;
+//
+void A_FindTracer(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *targetedmobj = NULL;
+ thinker_t *th;
+ mobj_t *mo2;
+ fixed_t dist1 = 0, dist2 = 0;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FindTracer", actor))
+ return;
+#endif
+
+ CONS_Debug(DBG_GAMELOGIC, "A_FindTracer called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+ // scan the thinkers
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo2 = (mobj_t *)th;
+
+ if (mo2->type == (mobjtype_t)locvar1)
+ {
+ if (mo2->player && (mo2->player->spectator || mo2->player->pflags & PF_INVIS))
+ continue; // Ignore spectators
+ if ((mo2->player || mo2->flags & MF_ENEMY) && mo2->health <= 0)
+ continue; // Ignore dead things
+ if (targetedmobj == NULL)
+ {
+ targetedmobj = mo2;
+ dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+ }
+ else
+ {
+ dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+
+ if ((!locvar2 && dist1 < dist2) || (locvar2 && dist1 > dist2))
+ {
+ targetedmobj = mo2;
+ dist2 = dist1;
+ }
+ }
+ }
+ }
+
+ if (!targetedmobj)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Unable to find the specified object to target.\n");
+ return; // Oops, nothing found..
+ }
+
+ CONS_Debug(DBG_GAMELOGIC, "A_FindTracer: Found a target.\n");
+
+ P_SetTarget(&actor->tracer, targetedmobj);
+}
+
+// Function: A_SetTics
+//
+// Description: Sets the animation tics of an object
+//
+// var1 = tics to set to
+// var2 = if this is set, and no var1 is supplied, the mobj's threshold value will be used.
+//
+void A_SetTics(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetTics", actor))
+ return;
+#endif
+
+ if (locvar1)
+ actor->tics = locvar1;
+ else if (locvar2)
+ actor->tics = actor->threshold;
+}
+
+// Function: A_SetRandomTics
+//
+// Description: Sets the animation tics of an object to a random value
+//
+// var1 = lower bound
+// var2 = upper bound
+//
+void A_SetRandomTics(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetRandomTics", actor))
+ return;
+#endif
+
+ actor->tics = P_RandomRange(locvar1, locvar2);
+}
+
+// Function: A_ChangeColorRelative
+//
+// Description: Changes the color of an object
+//
+// var1 = if (var1 > 0), find target and add its color value to yours
+// var2 = if (var1 = 0), color value to add
+//
+void A_ChangeColorRelative(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ChangeColorRelative", actor))
+ return;
+#endif
+
+ if (locvar1)
+ {
+ // Have you ever seen anything so hideous?
+ if (actor->target)
+ actor->color = (UINT8)(actor->color + actor->target->color);
+ }
+ else
+ actor->color = (UINT8)(actor->color + locvar2);
+}
+
+// Function: A_ChangeColorAbsolute
+//
+// Description: Changes the color of an object by an absolute value. Note: 0 is default colormap.
+//
+// var1 = if (var1 > 0), set your color to your target's color
+// var2 = if (var1 = 0), color value to set to
+//
+void A_ChangeColorAbsolute(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ChangeColorAbsolute", actor))
+ return;
+#endif
+
+ if (locvar1)
+ {
+ if (actor->target)
+ actor->color = actor->target->color;
+ }
+ else
+ actor->color = (UINT8)locvar2;
+}
+
+// Function: A_MoveRelative
+//
+// Description: Moves an object (wrapper for P_Thrust)
+//
+// var1 = angle
+// var2 = force
+//
+void A_MoveRelative(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MoveRelative", actor))
+ return;
+#endif
+
+ P_Thrust(actor, actor->angle+FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
+}
+
+// Function: A_MoveAbsolute
+//
+// Description: Moves an object (wrapper for P_InstaThrust)
+//
+// var1 = angle
+// var2 = force
+//
+void A_MoveAbsolute(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MoveAbsolute", actor))
+ return;
+#endif
+
+ P_InstaThrust(actor, FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale));
+}
+
+// Function: A_Thrust
+//
+// Description: Pushes the object horizontally at its current angle.
+//
+// var1 = amount of force
+// var2 = If 1, xy momentum is lost. If 0, xy momentum is kept
+//
+void A_Thrust(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Thrust", actor))
+ return;
+#endif
+
+ if (!locvar1)
+ CONS_Debug(DBG_GAMELOGIC, "A_Thrust: Var1 not specified!\n");
+
+ if (locvar2)
+ P_InstaThrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
+ else
+ P_Thrust(actor, actor->angle, FixedMul(locvar1*FRACUNIT, actor->scale));
+}
+
+// Function: A_ZThrust
+//
+// Description: Pushes the object up or down.
+//
+// var1 = amount of force
+// var2:
+// lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
+// upper 16 bits = If 1, z momentum is lost. If 0, z momentum is kept
+//
+void A_ZThrust(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ZThrust", actor))
+ return;
+#endif
+
+ if (!locvar1)
+ CONS_Debug(DBG_GAMELOGIC, "A_ZThrust: Var1 not specified!\n");
+
+ if (locvar2 & 65535)
+ actor->momx = actor->momy = 0;
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ actor->z--;
+ else
+ actor->z++;
+
+ P_SetObjectMomZ(actor, locvar1*FRACUNIT, !(locvar2 >> 16));
+}
+
+// Function: A_SetTargetsTarget
+//
+// Description: Sets your target to the object who your target is targeting. Yikes! If it happens to be NULL, you're just out of luck.
+//
+// var1: (Your target)
+// 0 = target
+// 1 = tracer
+// var2: (Your target's target)
+// 0 = target/tracer's target
+// 1 = target/tracer's tracer
+//
+void A_SetTargetsTarget(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *oldtarg = NULL, *newtarg = NULL;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetTargetsTarget", actor))
+ return;
+#endif
+
+ // actor's target
+ if (locvar1) // or tracer
+ oldtarg = actor->tracer;
+ else
+ oldtarg = actor->target;
+
+ if (P_MobjWasRemoved(oldtarg))
+ return;
+
+ // actor's target's target!
+ if (locvar2) // or tracer
+ newtarg = oldtarg->tracer;
+ else
+ newtarg = oldtarg->target;
+
+ if (P_MobjWasRemoved(newtarg))
+ return;
+
+ // set actor's new target
+ if (locvar1) // or tracer
+ P_SetTarget(&actor->tracer, newtarg);
+ else
+ P_SetTarget(&actor->target, newtarg);
+}
+
+// Function: A_SetObjectFlags
+//
+// Description: Sets the flags of an object
+//
+// var1 = flag value to set
+// var2:
+// if var2 == 2, add the flag to the current flags
+// else if var2 == 1, remove the flag from the current flags
+// else if var2 == 0, set the flags to the exact value
+//
+void A_SetObjectFlags(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ boolean unlinkthings = false;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetObjectFlags", actor))
+ return;
+#endif
+
+ if (locvar2 == 2)
+ locvar1 = actor->flags | locvar1;
+ else if (locvar2 == 1)
+ locvar1 = actor->flags & ~locvar1;
+
+ if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
+ unlinkthings = true;
+
+ if (unlinkthings) {
+ P_UnsetThingPosition(actor);
+ if (sector_list)
+ {
+ P_DelSeclist(sector_list);
+ sector_list = NULL;
+ }
+ }
+
+ actor->flags = locvar1;
+
+ if (unlinkthings)
+ P_SetThingPosition(actor);
+}
+
+// Function: A_SetObjectFlags2
+//
+// Description: Sets the flags2 of an object
+//
+// var1 = flag value to set
+// var2:
+// if var2 == 2, add the flag to the current flags
+// else if var2 == 1, remove the flag from the current flags
+// else if var2 == 0, set the flags to the exact value
+//
+void A_SetObjectFlags2(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetObjectFlags2", actor))
+ return;
+#endif
+
+ if (locvar2 == 2)
+ actor->flags2 |= locvar1;
+ else if (locvar2 == 1)
+ actor->flags2 &= ~locvar1;
+ else
+ actor->flags2 = locvar1;
+}
+
+// Function: A_BossJetFume
+//
+// Description: Spawns jet fumes/other attachment miscellany for the boss. To only be used when he is spawned.
+//
+// var1:
+// 0 - Triple jet fume pattern
+// 1 - Boss 3's propeller
+// 2 - Metal Sonic jet fume
+// 3 - Boss 4 jet flame
+// var2 = unused
+//
+void A_BossJetFume(mobj_t *actor)
+{
+ mobj_t *filler;
+ INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BossJetFume", actor))
+ return;
+#endif
+
+ if (locvar1 == 0) // Boss1 jet fumes
+ {
+ fixed_t jetx, jety, jetz;
+
+ jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
+ jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(64*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ jetz = actor->z + actor->height - FixedMul(38*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
+ else
+ jetz = actor->z + FixedMul(38*FRACUNIT, actor->scale);
+
+ filler = P_SpawnMobj(jetx, jety, jetz, MT_JETFUME1);
+ P_SetTarget(&filler->target, actor);
+ filler->destscale = actor->scale;
+ P_SetScale(filler, filler->destscale);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ filler->flags2 |= MF2_OBJECTFLIP;
+ filler->fuse = 56;
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ jetz = actor->z + actor->height - FixedMul(12*FRACUNIT + mobjinfo[MT_JETFUME1].height, actor->scale);
+ else
+ jetz = actor->z + FixedMul(12*FRACUNIT, actor->scale);
+
+ filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
+ jety + P_ReturnThrustY(actor, actor->angle-ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
+ jetz, MT_JETFUME1);
+ P_SetTarget(&filler->target, actor);
+ filler->destscale = actor->scale;
+ P_SetScale(filler, filler->destscale);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ filler->flags2 |= MF2_OBJECTFLIP;
+ filler->fuse = 57;
+
+ filler = P_SpawnMobj(jetx + P_ReturnThrustX(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
+ jety + P_ReturnThrustY(actor, actor->angle+ANGLE_90, FixedMul(24*FRACUNIT, actor->scale)),
+ jetz, MT_JETFUME1);
+ P_SetTarget(&filler->target, actor);
+ filler->destscale = actor->scale;
+ P_SetScale(filler, filler->destscale);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ filler->flags2 |= MF2_OBJECTFLIP;
+ filler->fuse = 58;
+
+ P_SetTarget(&actor->tracer, filler);
+ }
+ else if (locvar1 == 1) // Boss 3 propeller
+ {
+ fixed_t jetx, jety, jetz;
+
+ jetx = actor->x + P_ReturnThrustX(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale));
+ jety = actor->y + P_ReturnThrustY(actor, actor->angle, -FixedMul(60*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ jetz = actor->z + actor->height - FixedMul(17*FRACUNIT + mobjinfo[MT_PROPELLER].height, actor->scale);
+ else
+ jetz = actor->z + FixedMul(17*FRACUNIT, actor->scale);
+
+ filler = P_SpawnMobj(jetx, jety, jetz, MT_PROPELLER);
+ P_SetTarget(&filler->target, actor);
+ filler->destscale = actor->scale;
+ P_SetScale(filler, filler->destscale);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ filler->flags2 |= MF2_OBJECTFLIP;
+ filler->angle = actor->angle - ANGLE_180;
+
+ P_SetTarget(&actor->tracer, filler);
+ }
+ else if (locvar1 == 2) // Metal Sonic jet fumes
+ {
+ filler = P_SpawnMobj(actor->x, actor->y, actor->z, MT_JETFUME1);
+ P_SetTarget(&filler->target, actor);
+ filler->fuse = 59;
+ P_SetTarget(&actor->tracer, filler);
+ filler->destscale = actor->scale/2;
+ P_SetScale(filler, filler->destscale);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ filler->flags2 |= MF2_OBJECTFLIP;
+ }
+ else if (locvar1 == 3) // Boss 4 jet flame
+ {
+ fixed_t jetz;
+ if (actor->eflags & MFE_VERTICALFLIP)
+ jetz = actor->z + actor->height + FixedMul(50*FRACUNIT - mobjinfo[MT_JETFLAME].height, actor->scale);
+ else
+ jetz = actor->z - FixedMul(50*FRACUNIT, actor->scale);
+ filler = P_SpawnMobj(actor->x, actor->y, jetz, MT_JETFLAME);
+ P_SetTarget(&filler->target, actor);
+ // Boss 4 already uses its tracer for other things
+ filler->destscale = actor->scale;
+ P_SetScale(filler, filler->destscale);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ filler->flags2 |= MF2_OBJECTFLIP;
+ }
+}
+
+// Function: A_RandomState
+//
+// Description: Chooses one of the two state numbers supplied randomly.
+//
+// var1 = state number 1
+// var2 = state number 2
+//
+void A_RandomState(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RandomState", actor))
+ return;
+#endif
+
+ P_SetMobjState(actor, P_RandomChance(FRACUNIT/2) ? locvar1 : locvar2);
+}
+
+// Function: A_RandomStateRange
+//
+// Description: Chooses a random state within the range supplied.
+//
+// var1 = Minimum state number to choose.
+// var2 = Maximum state number to use.
+//
+void A_RandomStateRange(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RandomStateRange", actor))
+ return;
+#endif
+
+ P_SetMobjState(actor, P_RandomRange(locvar1, locvar2));
+}
+
+// Function: A_DualAction
+//
+// Description: Calls two actions. Be careful, if you reference the same state this action is called from, you can create an infinite loop.
+//
+// var1 = state # to use 1st action from
+// var2 = state # to use 2nd action from
+//
+void A_DualAction(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_DualAction", actor))
+ return;
+#endif
+
+ CONS_Debug(DBG_GAMELOGIC, "A_DualAction called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+ var1 = states[locvar1].var1;
+ var2 = states[locvar1].var2;
+#ifdef HAVE_BLUA
+ astate = &states[locvar1];
+#endif
+
+ CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling First Action (state %d)...\n", locvar1);
+ states[locvar1].action.acp1(actor);
+
+ var1 = states[locvar2].var1;
+ var2 = states[locvar2].var2;
+#ifdef HAVE_BLUA
+ astate = &states[locvar2];
+#endif
+
+ CONS_Debug(DBG_GAMELOGIC, "A_DualAction: Calling Second Action (state %d)...\n", locvar2);
+ states[locvar2].action.acp1(actor);
+}
+
+// Function: A_RemoteAction
+//
+// Description: var1 is the remote object. var2 is the state reference for calling the action called on var1. var1 becomes the actor's target, the action (var2) is called on var1. actor's target is restored
+//
+// var1 = remote object (-2 uses tracer, -1 uses target)
+// var2 = state reference for calling an action
+//
+void A_RemoteAction(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *originaltarget = actor->target; // Hold on to the target for later.
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RemoteAction", actor))
+ return;
+#endif
+
+ // If >=0, find the closest target.
+ if (locvar1 >= 0)
+ {
+ ///* DO A_FINDTARGET STUFF *///
+ mobj_t *targetedmobj = NULL;
+ thinker_t *th;
+ mobj_t *mo2;
+ fixed_t dist1 = 0, dist2 = 0;
+
+ // scan the thinkers
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo2 = (mobj_t *)th;
+
+ if (mo2->type == (mobjtype_t)locvar1)
+ {
+ if (targetedmobj == NULL)
+ {
+ targetedmobj = mo2;
+ dist2 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+ }
+ else
+ {
+ dist1 = R_PointToDist2(actor->x, actor->y, mo2->x, mo2->y);
+
+ if ((locvar2 && dist1 < dist2) || (!locvar2 && dist1 > dist2))
+ {
+ targetedmobj = mo2;
+ dist2 = dist1;
+ }
+ }
+ }
+ }
+
+ if (!targetedmobj)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Unable to find the specified object to target.\n");
+ return; // Oops, nothing found..
+ }
+
+ CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Found a target.\n");
+
+ P_SetTarget(&actor->target, targetedmobj);
+
+ ///* END A_FINDTARGET STUFF *///
+ }
+
+ // If -2, use the tracer as the target
+ else if (locvar1 == -2)
+ P_SetTarget(&actor->target, actor->tracer);
+ // if -1 or anything else, just use the target.
+
+ if (actor->target)
+ {
+ // Steal the var1 and var2 from "locvar2"
+ var1 = states[locvar2].var1;
+ var2 = states[locvar2].var2;
+#ifdef HAVE_BLUA
+ astate = &states[locvar2];
+#endif
+
+ CONS_Debug(DBG_GAMELOGIC, "A_RemoteAction: Calling action on %p\n"
+ "var1 is %d\nvar2 is %d\n", actor->target, var1, var2);
+ states[locvar2].action.acp1(actor->target);
+ }
+
+ P_SetTarget(&actor->target, originaltarget); // Restore the original target.
+}
+
+// Function: A_ToggleFlameJet
+//
+// Description: Turns a flame jet on and off.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ToggleFlameJet(mobj_t* actor)
+{
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ToggleFlameJet", actor))
+ return;
+#endif
+ // threshold - off delay
+ // movecount - on timer
+
+ if (actor->flags2 & MF2_FIRING)
+ {
+ actor->flags2 &= ~MF2_FIRING;
+
+ if (actor->threshold)
+ actor->tics = actor->threshold;
+ }
+ else
+ {
+ actor->flags2 |= MF2_FIRING;
+
+ if (actor->movecount)
+ actor->tics = actor->movecount;
+ }
+}
+
+// Function: A_OrbitNights
+//
+// Description: Used by Chaos Emeralds to orbit around Nights (aka Super Sonic.)
+//
+// var1 = Angle adjustment (aka orbit speed)
+// var2:
+// Bits 1-10: height offset, max 1023
+// Bits 11-16: X radius factor (max 63, default 20)
+// Bit 17: set if object is Nightopian Helper
+// Bit 18: set to define X/Y/Z rotation factor
+// Bits 19-20: Unused
+// Bits 21-26: Y radius factor (max 63, default 32)
+// Bits 27-32: Z radius factor (max 63, default 32)
+//
+// If MF_GRENADEBOUNCE is flagged on mobj, use actor->threshold to define X/Y/Z radius factor, max 1023 each:
+// Bits 1-10: X factor
+// Bits 11-20: Y factor
+// Bits 21-30: Z factor
+void A_OrbitNights(mobj_t* actor)
+{
+ INT32 ofs = (var2 & 0x3FF);
+ boolean ishelper = (var2 & 0x10000);
+ boolean donotrescale = (var2 & 0x40000);
+ INT32 xfactor = 32, yfactor = 32, zfactor = 20;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_OrbitNights", actor))
+ return;
+#endif
+
+ if (actor->flags & MF_GRENADEBOUNCE)
+ {
+ xfactor = (actor->threshold & 0x3FF);
+ yfactor = (actor->threshold & 0xFFC00) >> 10;
+ zfactor = (actor->threshold & 0x3FF00000) >> 20;
+ }
+ else if (var2 & 0x20000)
+ {
+ xfactor = (var2 & 0xFC00) >> 10;
+ yfactor = (var2 & 0x3F00000) >> 20;
+ zfactor = (var2 & 0xFC000000) >> 26;
+ }
+
+ if (!actor->target
+ || (actor->target->player &&
+ // if NiGHTS special stage and not NiGHTSmode.
+ (((maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && !(actor->target->player->powers[pw_carry] == CR_NIGHTSMODE))
+ // Also remove this object if they no longer have a NiGHTS helper
+ || (ishelper && !actor->target->player->powers[pw_nights_helper]))))
+ {
+ P_RemoveMobj(actor);
+ return;
+ }
+ else
+ {
+ actor->extravalue1 += var1;
+ P_UnsetThingPosition(actor);
+ {
+ const angle_t fa = (angle_t)actor->extravalue1 >> ANGLETOFINESHIFT;
+ const angle_t ofa = ((angle_t)actor->extravalue1 + (ofs*ANG1)) >> ANGLETOFINESHIFT;
+
+ const fixed_t fc = FixedMul(FINECOSINE(fa),FixedMul(xfactor*FRACUNIT, actor->scale));
+ const fixed_t fh = FixedMul(FINECOSINE(ofa),FixedMul(zfactor*FRACUNIT, actor->scale));
+ const fixed_t fs = FixedMul(FINESINE(fa),FixedMul(yfactor*FRACUNIT, actor->scale));
+
+ actor->x = actor->target->x + fc;
+ actor->y = actor->target->y + fs;
+ actor->z = actor->target->z + fh + FixedMul(16*FRACUNIT, actor->scale);
+
+ // Semi-lazy hack
+ actor->angle = (angle_t)actor->extravalue1 + ANGLE_90;
+ }
+ P_SetThingPosition(actor);
+
+ if (ishelper && actor->target->player) // Flash a helper that's about to be removed.
+ {
+ if ((actor->target->player->powers[pw_nights_helper] < TICRATE)
+ && (actor->target->player->powers[pw_nights_helper] & 1))
+ actor->flags2 |= MF2_DONTDRAW;
+ else
+ actor->flags2 &= ~MF2_DONTDRAW;
+ }
+
+ if (!donotrescale && actor->destscale != actor->target->destscale)
+ actor->destscale = actor->target->destscale;
+ }
+}
+
+// Function: A_GhostMe
+//
+// Description: Spawns a "ghost" mobj of this actor, ala spindash trails and the minus's digging "trails"
+//
+// var1 = duration in tics
+// var2 = unused
+//
+void A_GhostMe(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ mobj_t *ghost;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_GhostMe", actor))
+ return;
+#endif
+ ghost = P_SpawnGhostMobj(actor);
+ if (ghost && locvar1 > 0)
+ ghost->fuse = locvar1;
+}
+
+// Function: A_SetObjectState
+//
+// Description: Changes the state of an object's target/tracer.
+//
+// var1 = state number
+// var2:
+// 0 = target
+// 1 = tracer
+//
+void A_SetObjectState(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *target;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetObjectState", actor))
+ return;
+#endif
+
+ if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer))
+ {
+ if (cv_debug)
+ CONS_Printf("A_SetObjectState: No target to change state!\n");
+ return;
+ }
+
+ if (!locvar2) // target
+ target = actor->target;
+ else // tracer
+ target = actor->tracer;
+
+ if (target->health > 0)
+ {
+ if (!target->player)
+ P_SetMobjState(target, locvar1);
+ else
+ P_SetPlayerMobjState(target, locvar1);
+ }
+}
+
+// Function: A_SetObjectTypeState
+//
+// Description: Changes the state of all active objects of a certain type in a certain range of the actor.
+//
+// var1 = state number
+// var2:
+// lower 16 bits = type
+// upper 16 bits = range (if == 0, across whole map)
+//
+void A_SetObjectTypeState(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
+ const UINT16 loc2up = (UINT16)(locvar2 >> 16);
+
+ thinker_t *th;
+ mobj_t *mo2;
+ fixed_t dist = 0;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetObjectTypeState", actor))
+ return;
+#endif
+
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo2 = (mobj_t *)th;
+
+ if (mo2->type == (mobjtype_t)loc2lw)
+ {
+ dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
+
+ if (mo2->health > 0)
+ {
+ if (loc2up == 0)
+ P_SetMobjState(mo2, locvar1);
+ else
+ {
+ if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
+ P_SetMobjState(mo2, locvar1);
+ }
+ }
+ }
+ }
+}
+
+// Function: A_KnockBack
+//
+// Description: Knocks back the object's target at its current speed.
+//
+// var1:
+// 0 = target
+// 1 = tracer
+// var2 = unused
+//
+void A_KnockBack(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ mobj_t *target;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_KnockBack", actor))
+ return;
+#endif
+
+ if (!locvar1)
+ target = actor->target;
+ else
+ target = actor->tracer;
+
+ if (!target)
+ {
+ if(cv_debug)
+ CONS_Printf("A_KnockBack: No target!\n");
+ return;
+ }
+
+ target->momx *= -1;
+ target->momy *= -1;
+}
+
+// Function: A_PushAway
+//
+// Description: Pushes an object's target away from the calling object.
+//
+// var1 = amount of force
+// var2:
+// lower 16 bits = If 1, xy momentum is lost. If 0, xy momentum is kept
+// upper 16 bits = 0 - target, 1 - tracer
+//
+void A_PushAway(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *target; // target
+ angle_t an; // actor to target angle
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_PushAway", actor))
+ return;
+#endif
+
+ if ((!(locvar2 >> 16) && !actor->target) || ((locvar2 >> 16) && !actor->tracer))
+ return;
+
+ if (!locvar1)
+ CONS_Printf("A_Thrust: Var1 not specified!\n");
+
+ if (!(locvar2 >> 16)) // target
+ target = actor->target;
+ else // tracer
+ target = actor->tracer;
+
+ an = R_PointToAngle2(actor->x, actor->y, target->x, target->y);
+
+ if (locvar2 & 65535)
+ P_InstaThrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
+ else
+ P_Thrust(target, an, FixedMul(locvar1*FRACUNIT, actor->scale));
+}
+
+// Function: A_RingDrain
+//
+// Description: Drain targeted player's rings.
+//
+// var1 = ammount of drained rings
+// var2 = unused
+//
+void A_RingDrain(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ player_t *player;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RingDrain", actor))
+ return;
+#endif
+
+ if (!actor->target || !actor->target->player)
+ {
+ if(cv_debug)
+ CONS_Printf("A_RingDrain: No player targeted!\n");
+ return;
+ }
+
+ player = actor->target->player;
+ P_GivePlayerRings(player, -min(locvar1, player->rings));
+}
+
+// Function: A_SplitShot
+//
+// Description: Shoots 2 missiles that hit next to the player.
+//
+// var1 = target x-y-offset
+// var2:
+// lower 16 bits = missile type
+// upper 16 bits = height offset
+//
+void A_SplitShot(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
+ const UINT16 loc2up = (UINT16)(locvar2 >> 16);
+ const fixed_t offs = (fixed_t)(locvar1*FRACUNIT);
+ const fixed_t hoffs = (fixed_t)(loc2up*FRACUNIT);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SplitShot", actor))
+ return;
+#endif
+
+ A_FaceTarget(actor);
+ {
+ const angle_t an = (actor->angle + ANGLE_90) >> ANGLETOFINESHIFT;
+ const fixed_t fasin = FINESINE(an);
+ const fixed_t facos = FINECOSINE(an);
+ fixed_t xs = FixedMul(facos,FixedMul(offs, actor->scale));
+ fixed_t ys = FixedMul(fasin,FixedMul(offs, actor->scale));
+ fixed_t z;
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(hoffs, actor->scale);
+ else
+ z = actor->z + FixedMul(hoffs, actor->scale);
+
+ P_SpawnPointMissile(actor, actor->target->x+xs, actor->target->y+ys, actor->target->z, loc2lw, actor->x, actor->y, z);
+ P_SpawnPointMissile(actor, actor->target->x-xs, actor->target->y-ys, actor->target->z, loc2lw, actor->x, actor->y, z);
+ }
+}
+
+// Function: A_MissileSplit
+//
+// Description: If the object is a missile it will create a new missile with an alternate flight path owned by the one who shot the former missile.
+//
+// var1 = splitting missile type
+// var2 = splitting angle
+//
+void A_MissileSplit(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MissileSplit", actor))
+ return;
+#endif
+ if (actor->eflags & MFE_VERTICALFLIP)
+ P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z+actor->height, locvar2);
+ else
+ P_SpawnAlteredDirectionMissile(actor, locvar1, actor->x, actor->y, actor->z, locvar2);
+}
+
+// Function: A_MultiShot
+//
+// Description: Shoots objects horizontally that spread evenly in all directions.
+//
+// var1:
+// lower 16 bits = number of missiles
+// upper 16 bits = missile type #
+// var2 = height offset
+//
+void A_MultiShot(mobj_t *actor)
+{
+ fixed_t z, xr, yr;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
+ const UINT16 loc1up = (UINT16)(locvar1 >> 16);
+ INT32 count = 0;
+ fixed_t ad;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MultiShot", actor))
+ return;
+#endif
+
+ if (actor->target)
+ A_FaceTarget(actor);
+
+ if(loc1lw > 90)
+ ad = FixedMul(90*FRACUNIT, actor->scale);
+ else
+ ad = FixedMul(loc1lw*FRACUNIT, actor->scale);
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(48*FRACUNIT + locvar2*FRACUNIT, actor->scale);
+ xr = FixedMul((P_SignedRandom()/3)<scale); // please note p_mobj.c's P_Rand() abuse
+ yr = FixedMul((P_SignedRandom()/3)<scale); // of rand(), RAND_MAX and signness mess
+
+ while(count <= loc1lw && loc1lw >= 1)
+ {
+ const angle_t fa = FixedAngleC(count*FRACUNIT*360, ad)>>ANGLETOFINESHIFT;
+ const fixed_t rc = FINECOSINE(fa);
+ const fixed_t rs = FINESINE(fa);
+ const fixed_t xrc = FixedMul(xr, rc);
+ const fixed_t yrs = FixedMul(yr, rs);
+ const fixed_t xrs = FixedMul(xr, rs);
+ const fixed_t yrc = FixedMul(yr, rc);
+
+ P_SpawnPointMissile(actor, xrc-yrs+actor->x, xrs+yrc+actor->y, z, loc1up, actor->x, actor->y, z);
+ count++;
+ }
+
+ if (!(actor->flags & MF_BOSS))
+ {
+ if (ultimatemode)
+ actor->reactiontime = actor->info->reactiontime*TICRATE;
+ else
+ actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+ }
+}
+
+// Function: A_InstaLoop
+//
+// Description: Makes the object move along a 2d (view angle, z) polygon.
+//
+// var1:
+// lower 16 bits = current step
+// upper 16 bits = maximum step #
+// var2 = force
+//
+void A_InstaLoop(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ fixed_t force = max(locvar2, 1)*FRACUNIT; // defaults to 1 if var2 < 1
+ const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
+ const UINT16 loc1up = (UINT16)(locvar1 >> 16);
+ const angle_t fa = FixedAngleC(loc1lw*FRACUNIT*360, loc1up*FRACUNIT)>>ANGLETOFINESHIFT;
+ const fixed_t ac = FINECOSINE(fa);
+ const fixed_t as = FINESINE(fa);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_InstaLoop", actor))
+ return;
+#endif
+
+ P_InstaThrust(actor, actor->angle, FixedMul(ac, FixedMul(force, actor->scale)));
+ P_SetObjectMomZ(actor, FixedMul(as, force), false);
+}
+
+// Function: A_Custom3DRotate
+//
+// Description: Rotates the actor around its target in 3 dimensions.
+//
+// var1:
+// lower 16 bits = radius in fracunits
+// upper 16 bits = vertical offset
+// var2:
+// lower 16 bits = vertical rotation speed in 1/10 fracunits per tic
+// upper 16 bits = horizontal rotation speed in 1/10 fracunits per tic
+//
+void A_Custom3DRotate(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+ const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
+ const UINT16 loc1up = (UINT16)(locvar1 >> 16);
+ const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
+ const UINT16 loc2up = (UINT16)(locvar2 >> 16);
+
+ const fixed_t radius = FixedMul(loc1lw*FRACUNIT, actor->scale);
+ const fixed_t hOff = FixedMul(loc1up*FRACUNIT, actor->scale);
+ const fixed_t hspeed = FixedMul(loc2up*FRACUNIT/10, actor->scale);
+ const fixed_t vspeed = FixedMul(loc2lw*FRACUNIT/10, actor->scale);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Custom3DRotate", actor))
+ return;
+#endif
+
+ if (actor->target->health == 0)
+ {
+ P_RemoveMobj(actor);
+ return;
+ }
+
+ if (!actor->target) // This should NEVER happen.
+ {
+ if (cv_debug)
+ CONS_Printf("Error: Object has no target\n");
+ P_RemoveMobj(actor);
+ return;
+ }
+ if (hspeed==0 && vspeed==0)
+ {
+ CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n");
+ return;
+ }
+
+ actor->angle += FixedAngle(hspeed);
+ actor->movedir += FixedAngle(vspeed);
+ P_UnsetThingPosition(actor);
+ {
+ const angle_t fa = actor->angle>>ANGLETOFINESHIFT;
+
+ if (vspeed == 0 && hspeed != 0)
+ {
+ actor->x = actor->target->x + FixedMul(FINECOSINE(fa),radius);
+ actor->y = actor->target->y + FixedMul(FINESINE(fa),radius);
+ actor->z = actor->target->z + actor->target->height/2 - actor->height/2 + hOff;
+ }
+ else
+ {
+ const angle_t md = actor->movedir>>ANGLETOFINESHIFT;
+ actor->x = actor->target->x + FixedMul(FixedMul(FINESINE(md),FINECOSINE(fa)),radius);
+ actor->y = actor->target->y + FixedMul(FixedMul(FINESINE(md),FINESINE(fa)),radius);
+ actor->z = actor->target->z + FixedMul(FINECOSINE(md),radius) + actor->target->height/2 - actor->height/2 + hOff;
+ }
+ }
+ P_SetThingPosition(actor);
+}
+
+// Function: A_SearchForPlayers
+//
+// Description: Checks if the actor has targeted a vulnerable player. If not a new player will be searched all around. If no players are available the object can call a specific state. (Useful for not moving enemies)
+//
+// var1:
+// if var1 == 0, if necessary call state with same state number as var2
+// else, do not call a specific state if no players are available
+// var2 = state number
+//
+void A_SearchForPlayers(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SearchForPlayers", actor))
+ return;
+#endif
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ if(locvar1==0)
+ {
+ P_SetMobjStateNF(actor, locvar2);
+ return;
+ }
+ }
+}
+
+// Function: A_CheckRandom
+//
+// Description: Calls a state by chance.
+//
+// var1:
+// lower 16 bits = denominator
+// upper 16 bits = numerator (defaults to 1 if zero)
+// var2 = state number
+//
+void A_CheckRandom(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ fixed_t chance = FRACUNIT;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckRandom", actor))
+ return;
+#endif
+ if ((locvar1 & 0xFFFF) == 0)
+ return;
+
+ // The PRNG doesn't suck anymore, OK?
+ if (locvar1 >> 16)
+ chance *= (locvar1 >> 16);
+ chance /= (locvar1 & 0xFFFF);
+
+ if (P_RandomChance(chance))
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckTargetRings
+//
+// Description: Calls a state depending on the ammount of rings currently owned by targeted players.
+//
+// var1 = if player rings >= var1 call state
+// var2 = state number
+//
+void A_CheckTargetRings(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckTargetRings", actor))
+ return;
+#endif
+
+ if (!(actor->target) || !(actor->target->player))
+ return;
+
+ if (actor->target->player->rings >= locvar1)
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckRings
+//
+// Description: Calls a state depending on the ammount of rings currently owned by all players.
+//
+// var1 = if player rings >= var1 call state
+// var2 = state number
+//
+void A_CheckRings(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ INT32 i, cntr = 0;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckRings", actor))
+ return;
+#endif
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ cntr += players[i].rings;
+
+ if (cntr >= locvar1)
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckTotalRings
+//
+// Description: Calls a state depending on the maximum ammount of rings owned by all players during this try.
+//
+// var1 = if total player rings >= var1 call state
+// var2 = state number
+//
+void A_CheckTotalRings(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+ INT32 i, cntr = 0;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckTotalRings", actor))
+ return;
+#endif
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ cntr += players[i].totalring;
+
+ if (cntr >= locvar1)
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckHealth
+//
+// Description: Calls a state depending on the object's current health.
+//
+// var1 = if health <= var1 call state
+// var2 = state number
+//
+void A_CheckHealth(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckHealth", actor))
+ return;
+#endif
+
+ if (actor->health <= locvar1)
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckRange
+//
+// Description: Calls a state if the object's target is in range.
+//
+// var1:
+// lower 16 bits = range
+// upper 16 bits = 0 - target, 1 - tracer
+// var2 = state number
+//
+void A_CheckRange(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ fixed_t dist;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckRange", actor))
+ return;
+#endif
+
+ if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
+ return;
+
+ if (!(locvar1 >> 16)) //target
+ dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+ else //tracer
+ dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
+
+ if (dist <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckHeight
+//
+// Description: Calls a state if the object and it's target have a height offset <= var1 compared to each other.
+//
+// var1:
+// lower 16 bits = height offset
+// upper 16 bits = 0 - target, 1 - tracer
+// var2 = state number
+//
+void A_CheckHeight(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ fixed_t height;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckHeight", actor))
+ return;
+#endif
+
+ if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
+ return;
+
+ if (!(locvar1 >> 16)) // target
+ height = abs(actor->target->z - actor->z);
+ else // tracer
+ height = abs(actor->tracer->z - actor->z);
+
+ if (height <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckTrueRange
+//
+// Description: Calls a state if the object's target is in true range. (Checks height, too.)
+//
+// var1:
+// lower 16 bits = range
+// upper 16 bits = 0 - target, 1 - tracer
+// var2 = state number
+//
+void A_CheckTrueRange(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ fixed_t height; // vertical range
+ fixed_t dist; // horizontal range
+ fixed_t l; // true range
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckTrueRange", actor))
+ return;
+#endif
+
+ if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
+ return;
+
+ if (!(locvar1 >> 16)) // target
+ {
+ height = actor->target->z - actor->z;
+ dist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+
+ }
+ else // tracer
+ {
+ height = actor->tracer->z - actor->z;
+ dist = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
+ }
+
+ l = P_AproxDistance(dist, height);
+
+ if (l <= FixedMul((locvar1 & 65535)*FRACUNIT, actor->scale))
+ P_SetMobjState(actor, locvar2);
+
+}
+
+// Function: A_CheckThingCount
+//
+// Description: Calls a state depending on the number of active things in range.
+//
+// var1:
+// lower 16 bits = number of things
+// upper 16 bits = thing type
+// var2:
+// lower 16 bits = state to call
+// upper 16 bits = range (if == 0, check whole map)
+//
+void A_CheckThingCount(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+ const UINT16 loc1lw = (UINT16)(locvar1 & 65535);
+ const UINT16 loc1up = (UINT16)(locvar1 >> 16);
+ const UINT16 loc2lw = (UINT16)(locvar2 & 65535);
+ const UINT16 loc2up = (UINT16)(locvar2 >> 16);
+
+ INT32 count = 0;
+ thinker_t *th;
+ mobj_t *mo2;
+ fixed_t dist = 0;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckThingCount", actor))
+ return;
+#endif
+
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo2 = (mobj_t *)th;
+
+ if (mo2->type == (mobjtype_t)loc1up)
+ {
+ dist = P_AproxDistance(mo2->x - actor->x, mo2->y - actor->y);
+
+ if (loc2up == 0)
+ count++;
+ else
+ {
+ if (dist <= FixedMul(loc2up*FRACUNIT, actor->scale))
+ count++;
+ }
+ }
+ }
+
+ if(loc1lw <= count)
+ P_SetMobjState(actor, loc2lw);
+}
+
+// Function: A_CheckAmbush
+//
+// Description: Calls a state if the actor is behind its targeted player.
+//
+// var1:
+// 0 = target
+// 1 = tracer
+// var2 = state number
+//
+void A_CheckAmbush(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ angle_t at; // angle target is currently facing
+ angle_t atp; // actor to target angle
+ angle_t an; // angle between at and atp
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckAmbush", actor))
+ return;
+#endif
+
+ if ((!locvar1 && !actor->target) || (locvar1 && !actor->tracer))
+ return;
+
+ if (!locvar1) // target
+ {
+ at = actor->target->angle;
+ atp = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ }
+ else // tracer
+ {
+ at = actor->tracer->angle;
+ atp = R_PointToAngle2(actor->x, actor->y, actor->tracer->x, actor->tracer->y);
+ }
+
+ an = atp - at;
+
+ if (an > ANGLE_180) // flip angle if bigger than 180
+ an = InvAngle(an);
+
+ if (an < ANGLE_90+ANGLE_22h) // within an angle of 112.5 from each other?
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckCustomValue
+//
+// Description: Calls a state depending on the object's custom value.
+//
+// var1 = if custom value >= var1, call state
+// var2 = state number
+//
+void A_CheckCustomValue(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckCustomValue", actor))
+ return;
+#endif
+
+ if (actor->cusval >= locvar1)
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_CheckCusValMemo
+//
+// Description: Calls a state depending on the object's custom memory value.
+//
+// var1 = if memory value >= var1, call state
+// var2 = state number
+//
+void A_CheckCusValMemo(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckCusValMemo", actor))
+ return;
+#endif
+
+ if (actor->cvmem >= locvar1)
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_SetCustomValue
+//
+// Description: Changes the custom value of an object.
+//
+// var1 = manipulating value
+// var2:
+// if var2 == 5, multiply the custom value by var1
+// else if var2 == 4, divide the custom value by var1
+// else if var2 == 3, apply modulo var1 to the custom value
+// else if var2 == 2, add var1 to the custom value
+// else if var2 == 1, substract var1 from the custom value
+// else if var2 == 0, replace the custom value with var1
+//
+void A_SetCustomValue(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetCustomValue", actor))
+ return;
+#endif
+
+ if (cv_debug)
+ CONS_Printf("Init custom value is %d\n", actor->cusval);
+
+ if (locvar1 == 0 && locvar2 == 4)
+ return; // DON'T DIVIDE BY ZERO
+
+ // no need for a "temp" value here, just modify the cusval directly
+ if (locvar2 == 5) // multiply
+ actor->cusval *= locvar1;
+ else if (locvar2 == 4) // divide
+ actor->cusval /= locvar1;
+ else if (locvar2 == 3) // modulo
+ actor->cusval %= locvar1;
+ else if (locvar2 == 2) // add
+ actor->cusval += locvar1;
+ else if (locvar2 == 1) // subtract
+ actor->cusval -= locvar1;
+ else // replace
+ actor->cusval = locvar1;
+
+ if(cv_debug)
+ CONS_Printf("New custom value is %d\n", actor->cusval);
+}
+
+// Function: A_UseCusValMemo
+//
+// Description: Memorizes or recalls a current custom value.
+//
+// var1:
+// if var1 == 1, manipulate memory value
+// else, recall memory value replacing the custom value
+// var2:
+// if var2 == 5, mem = mem*cv || cv = cv*mem
+// else if var2 == 4, mem = mem/cv || cv = cv/mem
+// else if var2 == 3, mem = mem%cv || cv = cv%mem
+// else if var2 == 2, mem += cv || cv += mem
+// else if var2 == 1, mem -= cv || cv -= mem
+// else mem = cv || cv = mem
+//
+void A_UseCusValMemo(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+ INT32 temp = actor->cusval; // value being manipulated
+ INT32 tempM = actor->cvmem; // value used to manipulate temp with
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_UseCusValMemo", actor))
+ return;
+#endif
+
+ if (locvar1 == 1) // cvmem being changed using cusval
+ {
+ temp = actor->cvmem;
+ tempM = actor->cusval;
+ }
+ else // cusval being changed with cvmem
+ {
+ temp = actor->cusval;
+ tempM = actor->cvmem;
+ }
+
+ if (tempM == 0 && locvar2 == 4)
+ return; // DON'T DIVIDE BY ZERO
+
+ // now get new value for cusval/cvmem using the other
+ if (locvar2 == 5) // multiply
+ temp *= tempM;
+ else if (locvar2 == 4) // divide
+ temp /= tempM;
+ else if (locvar2 == 3) // modulo
+ temp %= tempM;
+ else if (locvar2 == 2) // add
+ temp += tempM;
+ else if (locvar2 == 1) // subtract
+ temp -= tempM;
+ else // replace
+ temp = tempM;
+
+ // finally, give cusval/cvmem the new value!
+ if (locvar1 == 1)
+ actor->cvmem = temp;
+ else
+ actor->cusval = temp;
+}
+
+// Function: A_RelayCustomValue
+//
+// Description: Manipulates the custom value of the object's target/tracer.
+//
+// var1:
+// lower 16 bits:
+// if var1 == 0, use own custom value
+// else, use var1 value
+// upper 16 bits = 0 - target, 1 - tracer
+// var2:
+// if var2 == 5, multiply the target's custom value by var1
+// else if var2 == 4, divide the target's custom value by var1
+// else if var2 == 3, apply modulo var1 to the target's custom value
+// else if var2 == 2, add var1 to the target's custom value
+// else if var2 == 1, substract var1 from the target's custom value
+// else if var2 == 0, replace the target's custom value with var1
+//
+void A_RelayCustomValue(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+ INT32 temp; // reference value - var1 lower 16 bits changes this
+ INT32 tempT; // target's value - changed to tracer if var1 upper 16 bits set, then modified to become final value
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RelayCustomValue", actor))
+ return;
+#endif
+
+ if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer))
+ return;
+
+ // reference custom value
+ if ((locvar1 & 65535) == 0)
+ temp = actor->cusval; // your own custom value
+ else
+ temp = (locvar1 & 65535); // var1 value
+
+ if (!(locvar1 >> 16)) // target's custom value
+ tempT = actor->target->cusval;
+ else // tracer's custom value
+ tempT = actor->tracer->cusval;
+
+ if (temp == 0 && locvar2 == 4)
+ return; // DON'T DIVIDE BY ZERO
+
+ // now get new cusval using target's and the reference
+ if (locvar2 == 5) // multiply
+ tempT *= temp;
+ else if (locvar2 == 4) // divide
+ tempT /= temp;
+ else if (locvar2 == 3) // modulo
+ tempT %= temp;
+ else if (locvar2 == 2) // add
+ tempT += temp;
+ else if (locvar2 == 1) // subtract
+ tempT -= temp;
+ else // replace
+ tempT = temp;
+
+ // finally, give target/tracer the new cusval!
+ if (!(locvar1 >> 16)) // target
+ actor->target->cusval = tempT;
+ else // tracer
+ actor->tracer->cusval = tempT;
+}
+
+// Function: A_CusValAction
+//
+// Description: Calls an action from a reference state applying custom value parameters.
+//
+// var1 = state # to use action from
+// var2:
+// if var2 == 5, only replace new action's var2 with memory value
+// else if var2 == 4, only replace new action's var1 with memory value
+// else if var2 == 3, replace new action's var2 with custom value and var1 with memory value
+// else if var2 == 2, replace new action's var1 with custom value and var2 with memory value
+// else if var2 == 1, only replace new action's var2 with custom value
+// else if var2 == 0, only replace new action's var1 with custom value
+//
+void A_CusValAction(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CusValAction", actor))
+ return;
+#endif
+
+ if (locvar2 == 5)
+ {
+ var1 = states[locvar1].var1;
+ var2 = (INT32)actor->cvmem;
+ }
+ else if (locvar2 == 4)
+ {
+ var1 = (INT32)actor->cvmem;
+ var2 = states[locvar1].var2;
+ }
+ else if (locvar2 == 3)
+ {
+ var1 = (INT32)actor->cvmem;
+ var2 = (INT32)actor->cusval;
+ }
+ else if (locvar2 == 2)
+ {
+ var1 = (INT32)actor->cusval;
+ var2 = (INT32)actor->cvmem;
+ }
+ else if (locvar2 == 1)
+ {
+ var1 = states[locvar1].var1;
+ var2 = (INT32)actor->cusval;
+ }
+ else
+ {
+ var1 = (INT32)actor->cusval;
+ var2 = states[locvar1].var2;
+ }
+
+#ifdef HAVE_BLUA
+ astate = &states[locvar1];
+#endif
+ states[locvar1].action.acp1(actor);
+}
+
+// Function: A_ForceStop
+//
+// Description: Actor immediately stops its current movement.
+//
+// var1:
+// if var1 == 0, stop x-y-z-movement
+// else, stop x-y-movement only
+// var2 = unused
+//
+void A_ForceStop(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ForceStop", actor))
+ return;
+#endif
+
+ actor->momx = actor->momy = 0;
+ if (locvar1 == 0)
+ actor->momz = 0;
+}
+
+// Function: A_ForceWin
+//
+// Description: Makes all players win the level.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ForceWin(mobj_t *actor)
+{
+ INT32 i;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ForceWin", actor))
+ return;
+#else
+ (void)actor;
+#endif
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (playeringame[i] && ((players[i].mo && players[i].mo->health)
+ || ((netgame || multiplayer) && (players[i].lives || players[i].continues))))
+ break;
+ }
+
+ if (i == MAXPLAYERS)
+ return;
+
+ for (i = 0; i < MAXPLAYERS; i++)
+ P_DoPlayerExit(&players[i]);
+}
+
+// Function: A_SpikeRetract
+//
+// Description: Toggles actor solid flag.
+//
+// var1:
+// if var1 == 0, actor no collide
+// else, actor solid
+// var2 = unused
+//
+void A_SpikeRetract(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SpikeRetract", actor))
+ return;
+#endif
+
+ if (actor->flags & MF_NOBLOCKMAP)
+ return;
+
+ if (locvar1 == 0)
+ {
+ actor->flags &= ~MF_SOLID;
+ actor->flags |= MF_NOCLIPTHING;
+ }
+ else
+ {
+ actor->flags |= MF_SOLID;
+ actor->flags &= ~MF_NOCLIPTHING;
+ }
+ if (actor->flags & MF_SOLID)
+ P_CheckPosition(actor, actor->x, actor->y);
+}
+
+// Function: A_InfoState
+//
+// Description: Set mobj state to one predefined in mobjinfo.
+//
+// var1:
+// if var1 == 0, set actor to spawnstate
+// else if var1 == 1, set actor to seestate
+// else if var1 == 2, set actor to meleestate
+// else if var1 == 3, set actor to missilestate
+// else if var1 == 4, set actor to deathstate
+// else if var1 == 5, set actor to xdeathstate
+// else if var1 == 6, set actor to raisestate
+// var2 = unused
+//
+void A_InfoState(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ switch (locvar1)
+ {
+ case 0:
+ if (actor->state != &states[actor->info->spawnstate])
+ P_SetMobjState(actor, actor->info->spawnstate);
+ break;
+ case 1:
+ if (actor->state != &states[actor->info->seestate])
+ P_SetMobjState(actor, actor->info->seestate);
+ break;
+ case 2:
+ if (actor->state != &states[actor->info->meleestate])
+ P_SetMobjState(actor, actor->info->meleestate);
+ break;
+ case 3:
+ if (actor->state != &states[actor->info->missilestate])
+ P_SetMobjState(actor, actor->info->missilestate);
+ break;
+ case 4:
+ if (actor->state != &states[actor->info->deathstate])
+ P_SetMobjState(actor, actor->info->deathstate);
+ break;
+ case 5:
+ if (actor->state != &states[actor->info->xdeathstate])
+ P_SetMobjState(actor, actor->info->xdeathstate);
+ break;
+ case 6:
+ if (actor->state != &states[actor->info->raisestate])
+ P_SetMobjState(actor, actor->info->raisestate);
+ break;
+ default:
+ break;
+ }
+}
+
+// Function: A_Repeat
+//
+// Description: Returns to state var2 until animation has been used var1 times, then continues to nextstate.
+//
+// var1 = repeat count
+// var2 = state to return to if extravalue2 > 0
+//
+void A_Repeat(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Repeat", actor))
+ return;
+#endif
+
+ if (locvar1 && (!actor->extravalue2 || actor->extravalue2 > locvar1))
+ actor->extravalue2 = locvar1;
+
+ if (--actor->extravalue2 > 0)
+ P_SetMobjState(actor, locvar2);
+}
+
+// Function: A_SetScale
+//
+// Description: Changes the scale of the actor or its target/tracer
+//
+// var1 = new scale (1*FRACUNIT = 100%)
+// var2:
+// upper 16 bits: 0 = actor, 1 = target, 2 = tracer
+// lower 16 bits: 0 = instant change, 1 = smooth change
+//
+void A_SetScale(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *target;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SetScale", actor))
+ return;
+#endif
+
+ if (locvar1 <= 0)
+ {
+ if(cv_debug)
+ CONS_Printf("A_SetScale: Valid scale not specified!\n");
+ return;
+ }
+
+ if ((locvar2>>16) == 1)
+ target = actor->target;
+ else if ((locvar2>>16) == 2)
+ target = actor->tracer;
+ else // default to yourself!
+ target = actor;
+
+ if (!target)
+ {
+ if(cv_debug)
+ CONS_Printf("A_SetScale: No target!\n");
+ return;
+ }
+
+ target->destscale = locvar1; // destination scale
+ if (!(locvar2 & 65535))
+ P_SetScale(target, locvar1); // this instantly changes current scale to var1 if used, if not destscale will alter scale to var1 anyway
+}
+
+// Function: A_RemoteDamage
+//
+// Description: Damages, kills or even removes either the actor or its target/tracer. Actor acts as the inflictor/source unless harming itself
+//
+// var1 = Mobj affected: 0 - actor, 1 - target, 2 - tracer
+// var2 = Action: 0 - Damage, 1 - Kill, 2 - Remove
+//
+void A_RemoteDamage(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *target; // we MUST have a target
+ mobj_t *source = NULL; // on the other hand we don't necessarily need a source
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_RemoteDamage", actor))
+ return;
+#endif
+ if (locvar1 == 1)
+ target = actor->target;
+ else if (locvar1 == 2)
+ target = actor->tracer;
+ else // default to yourself!
+ target = actor;
+
+ if (locvar1 == 1 || locvar1 == 2)
+ source = actor;
+
+ if (!target)
+ {
+ if(cv_debug)
+ CONS_Printf("A_RemoteDamage: No target!\n");
+ return;
+ }
+
+ if (locvar2 == 1) // Kill mobj!
+ {
+ if (target->player) // players die using P_DamageMobj instead for some reason
+ P_DamageMobj(target, source, source, 1, DMG_INSTAKILL);
+ else
+ P_KillMobj(target, source, source, 0);
+ }
+ else if (locvar2 == 2) // Remove mobj!
+ {
+ if (target->player) //don't remove players!
+ return;
+
+ P_RemoveMobj(target);
+ }
+ else // default: Damage mobj!
+ P_DamageMobj(target, source, source, 1, 0);
+}
+
+// Function: A_HomingChase
+//
+// Description: Actor chases directly towards its destination object
+//
+// var1 = speed multiple
+// var2 = destination: 0 = target, 1 = tracer
+//
+void A_HomingChase(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *dest;
+ fixed_t dist;
+ fixed_t speedmul;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_HomingChase", actor))
+ return;
+#endif
+
+ if (locvar2 == 1)
+ dest = actor->tracer;
+ else //default
+ dest = actor->target;
+
+ if (!dest || !dest->health)
+ return;
+
+ actor->angle = R_PointToAngle2(actor->x, actor->y, dest->x, dest->y);
+
+ dist = P_AproxDistance(P_AproxDistance(dest->x - actor->x, dest->y - actor->y), dest->z - actor->z);
+
+ if (dist < 1)
+ dist = 1;
+
+ speedmul = FixedMul(locvar1, actor->scale);
+
+ actor->momx = FixedMul(FixedDiv(dest->x - actor->x, dist), speedmul);
+ actor->momy = FixedMul(FixedDiv(dest->y - actor->y, dist), speedmul);
+ actor->momz = FixedMul(FixedDiv(dest->z - actor->z, dist), speedmul);
+}
+
+// Function: A_TrapShot
+//
+// Description: Fires a missile in a particular direction and angle rather than AT something, Trapgoyle-style!
+//
+// var1:
+// lower 16 bits = object # to fire
+// upper 16 bits = front offset
+// var2:
+// lower 15 bits = vertical angle variable
+// 16th bit:
+// - 0: use vertical angle variable as vertical angle in degrees
+// - 1: mimic P_SpawnXYZMissile
+// use z of actor minus z of missile as vertical distance to cover during momz calculation
+// use vertical angle variable as horizontal distance to cover during momz calculation
+// upper 16 bits = height offset
+//
+void A_TrapShot(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ boolean oldstyle = (locvar2 & 32768) ? true : false;
+ mobjtype_t type = (mobjtype_t)(locvar1 & 65535);
+ mobj_t *missile;
+ INT16 frontoff = (INT16)(locvar1 >> 16);
+ INT16 vertoff = (INT16)(locvar2 >> 16);
+ fixed_t x, y, z;
+ fixed_t speed;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_TrapShot", actor))
+ return;
+#endif
+
+ x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
+ y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale);
+ else
+ z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale);
+
+ CONS_Debug(DBG_GAMELOGIC, "A_TrapShot: missile no. = %d, front offset = %d, vertical angle = %d, z offset = %d\n",
+ type, frontoff, (INT16)(locvar2 & 65535), vertoff);
+
+ missile = P_SpawnMobj(x, y, z, type);
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ missile->flags2 |= MF2_OBJECTFLIP;
+
+ missile->destscale = actor->scale;
+ P_SetScale(missile, actor->scale);
+
+ if (missile->info->seesound)
+ S_StartSound(missile, missile->info->seesound);
+
+ P_SetTarget(&missile->target, actor);
+ missile->angle = actor->angle;
+
+ speed = FixedMul(missile->info->speed, missile->scale);
+
+ if (oldstyle)
+ {
+ missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed);
+ missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed);
+ // The below line basically mimics P_SpawnXYZMissile's momz calculation.
+ missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed);
+ P_CheckMissileSpawn(missile);
+ }
+ else
+ {
+ angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ vertang = InvAngle(vertang); // flip firing angle
+ missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed));
+ missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed));
+ missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed);
+ }
+}
+
+// Function: A_VileTarget
+//
+// Description: Spawns an object directly on the target, and sets this object as the actor's tracer.
+// Originally used by Archviles to summon a pillar of hellfire, hence the name.
+//
+// var1 = mobj to spawn
+// var2 = If 0, target only the actor's target. Else, target every player, period.
+//
+void A_VileTarget(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *fog;
+ mobjtype_t fogtype;
+ INT32 i;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_VileTarget", actor))
+ return;
+#endif
+
+ if (!actor->target)
+ return;
+
+ A_FaceTarget(actor);
+
+ // Determine object to spawn
+ if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
+ fogtype = MT_CYBRAKDEMON_TARGET_RETICULE;
+ else
+ fogtype = (mobjtype_t)locvar1;
+
+ if (!locvar2)
+ {
+ fog = P_SpawnMobj(actor->target->x,
+ actor->target->y,
+ actor->target->z + ((actor->target->eflags & MFE_VERTICALFLIP) ? actor->target->height - mobjinfo[fogtype].height : 0),
+ fogtype);
+ if (actor->target->eflags & MFE_VERTICALFLIP)
+ {
+ fog->eflags |= MFE_VERTICALFLIP;
+ fog->flags2 |= MF2_OBJECTFLIP;
+ }
+ fog->destscale = actor->target->scale;
+ P_SetScale(fog, fog->destscale);
+
+ P_SetTarget(&actor->tracer, fog);
+ P_SetTarget(&fog->target, actor);
+ P_SetTarget(&fog->tracer, actor->target);
+ A_VileFire(fog);
+ }
+ else
+ {
+ // Our "Archvile" here is actually Oprah. "YOU GET A TARGET! YOU GET A TARGET! YOU ALL GET A TARGET!"
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+
+ if (!players[i].mo)
+ continue;
+
+ if (!players[i].mo->health)
+ continue;
+
+ fog = P_SpawnMobj(players[i].mo->x,
+ players[i].mo->y,
+ players[i].mo->z + ((players[i].mo->eflags & MFE_VERTICALFLIP) ? players[i].mo->height - mobjinfo[fogtype].height : 0),
+ fogtype);
+ if (players[i].mo->eflags & MFE_VERTICALFLIP)
+ {
+ fog->eflags |= MFE_VERTICALFLIP;
+ fog->flags2 |= MF2_OBJECTFLIP;
+ }
+ fog->destscale = players[i].mo->scale;
+ P_SetScale(fog, fog->destscale);
+
+ if (players[i].mo == actor->target) // We only care to track the fog targeting who we REALLY hate right now
+ P_SetTarget(&actor->tracer, fog);
+ P_SetTarget(&fog->target, actor);
+ P_SetTarget(&fog->tracer, players[i].mo);
+ A_VileFire(fog);
+ }
+ }
+}
+
+// Function: A_VileAttack
+//
+// Description: Instantly hurts the actor's target, if it's in the actor's line of sight.
+// Originally used by Archviles to cause explosions where their hellfire pillars were, hence the name.
+//
+// var1 = sound to play
+// var2:
+// Lower 16 bits = optional explosion object
+// Upper 16 bits = If 0, attack only the actor's target. Else, attack all the players. All of them.
+//
+void A_VileAttack(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ sfxenum_t soundtoplay;
+ mobjtype_t explosionType = MT_NULL;
+ mobj_t *fire;
+ INT32 i;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_VileAttack", actor))
+ return;
+#endif
+
+ if (!actor->target)
+ return;
+
+ A_FaceTarget(actor);
+
+ if (locvar1 <= 0 || locvar1 >= NUMSFX)
+ soundtoplay = sfx_brakrx;
+ else
+ soundtoplay = (sfxenum_t)locvar1;
+
+ if ((locvar2 & 0xFFFF) > 0 && (locvar2 & 0xFFFF) <= NUMMOBJTYPES)
+ {
+ explosionType = (mobjtype_t)(locvar2 & 0xFFFF);
+ }
+
+ if (!(locvar2 & 0xFFFF0000)) {
+ if (!P_CheckSight(actor, actor->target))
+ return;
+
+ S_StartSound(actor, soundtoplay);
+ P_DamageMobj(actor->target, actor, actor, 1, 0);
+ //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
+ actor->target->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(actor->target); // How we're doing it
+ if (explosionType != MT_NULL)
+ {
+ P_SpawnMobj(actor->target->x, actor->target->y, actor->target->z, explosionType);
+ }
+
+ // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
+ fire = actor->tracer;
+
+ if (!fire)
+ return;
+
+ // move the fire between the vile and the player
+ //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
+ //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
+ P_TeleportMove(fire,
+ actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
+ actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
+ fire->z);
+ P_RadiusAttack(fire, actor, 70*FRACUNIT, 0);
+ }
+ else
+ {
+ // Oprahvile strikes again, but this time, she brings HOT PAIN
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (!playeringame[i] || players[i].spectator)
+ continue;
+
+ if (!players[i].mo)
+ continue;
+
+ if (!players[i].mo->health)
+ continue;
+
+ if (!P_CheckSight(actor, players[i].mo))
+ continue;
+
+ S_StartSound(actor, soundtoplay);
+ P_DamageMobj(players[i].mo, actor, actor, 1, 0);
+ //actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
+ players[i].mo->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(players[i].mo); // How we're doing it
+ if (explosionType != MT_NULL)
+ {
+ P_SpawnMobj(players[i].mo->x, players[i].mo->y, players[i].mo->z, explosionType);
+ }
+
+ // Extra attack. This was for additional damage in Doom. Doesn't really belong in SRB2, but the heck with it, it's here anyway.
+ // However, it ONLY applies to the actor's target. Nobody else matters!
+ if (actor->target != players[i].mo)
+ continue;
+
+ fire = actor->tracer;
+
+ if (!fire)
+ continue;
+
+ // move the fire between the vile and the player
+ //fire->x = actor->target->x - FixedMul (24*FRACUNIT, finecosine[an]);
+ //fire->y = actor->target->y - FixedMul (24*FRACUNIT, finesine[an]);
+ P_TeleportMove(fire,
+ actor->target->x - P_ReturnThrustX(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
+ actor->target->y - P_ReturnThrustY(fire, actor->angle, FixedMul(24*FRACUNIT, fire->scale)),
+ fire->z);
+ P_RadiusAttack(fire, actor, 70*FRACUNIT, 0);
+ }
+ }
+
+}
+
+// Function: A_VileFire
+//
+// Description: Kind of like A_CapeChase; keeps this object in front of its tracer, unless its target can't see it.
+// Originally used by Archviles to keep their hellfire pillars on top of the player, hence the name (although it was just "A_Fire" there; added "Vile" to make it more specific).
+// Added some functionality to optionally draw a line directly to the enemy doing the targetting. Y'know, to hammer things in a bit.
+//
+// var1 = sound to play
+// var2:
+// Lower 16 bits = mobj to spawn (0 doesn't spawn a line at all)
+// Upper 16 bits = # to spawn (default is 8)
+//
+void A_VileFire(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ mobj_t *dest;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_VileFire", actor))
+ return;
+#endif
+
+ dest = actor->tracer;
+ if (!dest)
+ return;
+
+ // don't move it if the vile lost sight
+ if (!P_CheckSight(actor->target, dest))
+ return;
+
+ // keep to same scale and gravity as tracer ALWAYS
+ actor->destscale = dest->scale;
+ P_SetScale(actor, actor->destscale);
+ if (dest->eflags & MFE_VERTICALFLIP)
+ {
+ actor->eflags |= MFE_VERTICALFLIP;
+ actor->flags2 |= MF2_OBJECTFLIP;
+ }
+ else
+ {
+ actor->eflags &= ~MFE_VERTICALFLIP;
+ actor->flags2 &= ~MF2_OBJECTFLIP;
+ }
+
+ P_UnsetThingPosition(actor);
+ actor->x = dest->x + P_ReturnThrustX(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
+ actor->y = dest->y + P_ReturnThrustY(actor, dest->angle, FixedMul(24*FRACUNIT, actor->scale));
+ actor->z = dest->z + ((actor->eflags & MFE_VERTICALFLIP) ? dest->height-actor->height : 0);
+ P_SetThingPosition(actor);
+
+ // Play sound, if one's specified
+ if (locvar1 > 0 && locvar1 < NUMSFX)
+ S_StartSound(actor, (sfxenum_t)locvar1);
+
+ // Now draw the line to the actor's target
+ if (locvar2 & 0xFFFF)
+ {
+ mobjtype_t lineMobj;
+ UINT16 numLineMobjs;
+ fixed_t distX;
+ fixed_t distY;
+ fixed_t distZ;
+ UINT16 i;
+
+ lineMobj = (mobjtype_t)(locvar2 & 0xFFFF);
+ numLineMobjs = (UINT16)(locvar2 >> 16);
+ if (numLineMobjs == 0) {
+ numLineMobjs = 8;
+ }
+
+ // Get distance for each step
+ distX = (actor->target->x - actor->x) / numLineMobjs;
+ distY = (actor->target->y - actor->y) / numLineMobjs;
+ distZ = ((actor->target->z + FixedMul(actor->target->height/2, actor->target->scale)) - (actor->z + FixedMul(actor->height/2, actor->scale))) / numLineMobjs;
+
+ for (i = 1; i <= numLineMobjs; i++)
+ {
+ P_SpawnMobj(actor->x + (distX * i), actor->y + (distY * i), actor->z + (distZ * i) + FixedMul(actor->height/2, actor->scale), lineMobj);
+ }
+ }
+}
+
+// Function: A_BrakChase
+//
+// Description: Chase after your target, but speed and attack are tied to health.
+//
+// Every time this is called, generate a random number from a 1/4 to 3/4 of mobj's spawn health.
+// If health is above that value, use missilestate to attack.
+// If health is at or below that value, use meleestate to attack (default to missile state if not available).
+//
+// Likewise, state will linearly speed up as health goes down.
+// Upper bound will be the frame's normal length.
+// Lower bound defaults to 1 tic (technically 0, but we round up), unless a lower bound is specified in var1.
+//
+// var1 = lower-bound of frame length, in tics
+// var2 = optional sound to play
+//
+void A_BrakChase(mobj_t *actor)
+{
+ INT32 delta;
+ INT32 lowerbound;
+ INT32 newtics;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BrakChase", actor))
+ return;
+#endif
+
+ // Set new tics NOW, in case the state changes while we're doing this and we try applying this to the painstate or something silly
+ if (actor->tics > 1 && locvar1 < actor->tics) // Not much point, otherwise
+ {
+ if (locvar1 < 0)
+ lowerbound = 0;
+ else
+ lowerbound = locvar1;
+
+ newtics = (((actor->tics - lowerbound) * actor->health) / actor->info->spawnhealth) + lowerbound;
+ if (newtics < 1)
+ newtics = 1;
+
+ actor->tics = newtics;
+ }
+
+ if (actor->reactiontime)
+ {
+ actor->reactiontime--;
+ if (actor->reactiontime == 0 && actor->type == MT_CYBRAKDEMON)
+ S_StartSound(0, sfx_bewar1 + P_RandomKey(4));
+ }
+
+ // modify target threshold
+ if (actor->threshold)
+ {
+ if (!actor->target || actor->target->health <= 0)
+ actor->threshold = 0;
+ else
+ actor->threshold--;
+ }
+
+ // turn towards movement direction if not there yet
+ if (actor->movedir < NUMDIRS)
+ {
+ actor->angle &= (7<<29);
+ delta = actor->angle - (actor->movedir << 29);
+
+ if (delta > 0)
+ actor->angle -= ANGLE_45;
+ else if (delta < 0)
+ actor->angle += ANGLE_45;
+ }
+
+ if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
+ {
+ // look for a new target
+ if (P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ P_SetMobjStateNF(actor, actor->info->spawnstate);
+ return;
+ }
+
+ // do not attack twice in a row
+ if (actor->flags2 & MF2_JUSTATTACKED)
+ {
+ actor->flags2 &= ~MF2_JUSTATTACKED;
+ P_NewChaseDir(actor);
+ return;
+ }
+
+ // Check if we can attack
+ if (P_CheckMissileRange(actor) && !actor->movecount)
+ {
+ // Check if we should use "melee" attack first. (Yes, this still runs outside of melee range. Quiet, you.)
+ if (actor->info->meleestate
+ && actor->health <= P_RandomRange(actor->info->spawnhealth/4, (actor->info->spawnhealth * 3)/4)) // Guaranteed true if <= 1/4 health, guaranteed false if > 3/4 health
+ {
+ if (actor->info->attacksound)
+ S_StartAttackSound(actor, actor->info->attacksound);
+
+ P_SetMobjState(actor, actor->info->meleestate);
+ actor->flags2 |= MF2_JUSTATTACKED;
+ return;
+ }
+ // Else, check for missile attack.
+ else if (actor->info->missilestate)
+ {
+ P_SetMobjState(actor, actor->info->missilestate);
+ actor->flags2 |= MF2_JUSTATTACKED;
+ return;
+ }
+ }
+
+ // possibly choose another target
+ if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
+ && P_LookForPlayers(actor, true, false, 0))
+ return; // got a new target
+
+ // chase towards player
+ if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
+ P_NewChaseDir(actor);
+
+ // Optionally play a sound effect
+ if (locvar2 > 0 && locvar2 < NUMSFX)
+ S_StartSound(actor, (sfxenum_t)locvar2);
+
+ // make active sound
+ if (actor->type != MT_CYBRAKDEMON && actor->info->activesound && P_RandomChance(3*FRACUNIT/256))
+ {
+ S_StartSound(actor, actor->info->activesound);
+ }
+}
+
+// Function: A_BrakFireShot
+//
+// Description: Shoot an object at your target, offset to match where Brak's gun is.
+// Also, sets Brak's reaction time; behaves normally otherwise.
+//
+// var1 = object # to shoot
+// var2 = unused
+//
+void A_BrakFireShot(mobj_t *actor)
+{
+ fixed_t x, y, z;
+ INT32 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BrakFireShot", actor))
+ return;
+#endif
+ if (!actor->target)
+ return;
+
+ A_FaceTarget(actor);
+
+ x = actor->x
+ + P_ReturnThrustX(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
+ + P_ReturnThrustX(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
+ y = actor->y
+ + P_ReturnThrustY(actor, actor->angle, FixedMul(64*FRACUNIT, actor->scale))
+ + P_ReturnThrustY(actor, actor->angle+ANGLE_270, FixedMul(32*FRACUNIT, actor->scale));
+ if (actor->eflags & MFE_VERTICALFLIP)
+ z = actor->z + actor->height - FixedMul(144*FRACUNIT, actor->scale);
+ else
+ z = actor->z + FixedMul(144*FRACUNIT, actor->scale);
+
+ P_SpawnXYZMissile(actor, actor->target, locvar1, x, y, z);
+
+ if (!(actor->flags & MF_BOSS))
+ {
+ if (ultimatemode)
+ actor->reactiontime = actor->info->reactiontime*TICRATE;
+ else
+ actor->reactiontime = actor->info->reactiontime*TICRATE*2;
+ }
+}
+
+// Function: A_BrakLobShot
+//
+// Description: Lobs an object at the floor about a third of the way toward your target.
+// Implication is it'll bounce the rest of the way.
+// (You can also just aim straight at the target, but whatever)
+// Formula grabbed from http://en.wikipedia.org/wiki/Trajectory_of_a_projectile#Angle_required_to_hit_coordinate_.28x.2Cy.29
+//
+// var1 = object # to lob
+// var2:
+// Lower 16 bits: height offset to shoot from, from the actor's bottom (none that "airtime" malarky)
+// Upper 16 bits: if 0, aim 1/3 of the way. Else, aim directly at target.
+//
+
+void A_BrakLobShot(mobj_t *actor)
+{
+ fixed_t v; // Velocity to shoot object
+ fixed_t a1, a2, aToUse; // Velocity squared
+ fixed_t g; // Gravity
+ fixed_t x; // Horizontal difference
+ INT32 x_int; // x! But in integer form!
+ fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
+ INT32 y_int; // y! But in integer form!
+ INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
+ fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
+ angle_t theta; // Angle of attack
+ mobjtype_t typeOfShot;
+ mobj_t *shot; // Object to shoot
+ fixed_t newTargetX; // If not aiming directly
+ fixed_t newTargetY; // If not aiming directly
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2 & 0x0000FFFF;
+ INT32 aimDirect = var2 & 0xFFFF0000;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BrakLobShot", actor))
+ return;
+#endif
+
+ if (!actor->target)
+ return; // Don't even bother if we've got nothing to aim at.
+
+ // Look up actor's current gravity situation
+ if (actor->subsector->sector->gravity)
+ g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
+ else
+ g = gravity;
+
+ // Look up distance between actor and its target
+ x = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+ if (!aimDirect)
+ {
+ // Distance should actually be a third of the way over
+ x = FixedDiv(x, 3<x + P_ReturnThrustX(actor, actor->angle, x);
+ newTargetY = actor->y + P_ReturnThrustY(actor, actor->angle, x);
+ x = P_AproxDistance(newTargetX - actor->x, newTargetY - actor->y);
+ // Look up height difference between actor and the ground 1/3 of the way to its target
+ y = P_FloorzAtPos(newTargetX, newTargetY, actor->target->z, actor->target->height) - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
+ }
+ else
+ {
+ // Look up height difference between actor and its target
+ y = actor->target->z - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
+ }
+
+ // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
+ x_int = x>>FRACBITS;
+ y_int = y>>FRACBITS;
+ intHypotenuse = (x_int*x_int) + (y_int*y_int);
+ fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
+
+ // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
+ a1 = FixedMul(g,y+fixedHypotenuse);
+ a2 = FixedMul(g,y-fixedHypotenuse);
+
+ // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
+ if (a1 < 0 || a2 < 0)
+ {
+ if (a1 < 0 && a2 < 0)
+ {
+ //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
+ return;
+ }
+ // Just find which one's NOT negative, and use that
+ aToUse = max(a1,a2);
+ }
+ else
+ {
+ // Both are positive; use whichever's smaller so it can decay faster
+ aToUse = min(a1,a2);
+ }
+ v = FixedSqrt(aToUse);
+ // Okay, so we know the velocity. Let's actually find theta.
+ // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
+ //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
+ theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
+
+ // Okay, complicated math done. Let's fire our object already, sheesh.
+ A_FaceTarget(actor);
+ if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
+ typeOfShot = MT_CANNONBALL;
+ else typeOfShot = (mobjtype_t)locvar1;
+ shot = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(locvar2*FRACUNIT, actor->scale), typeOfShot);
+ if (shot->info->seesound)
+ S_StartSound(shot, shot->info->seesound);
+ P_SetTarget(&shot->target, actor); // where it came from
+
+ shot->angle = actor->angle;
+
+ // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
+ shot->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(shot->angle >> ANGLETOFINESHIFT));
+ shot->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINESINE(shot->angle >> ANGLETOFINESHIFT));
+ // Then the vertical axis. No angle-correction needed here.
+ shot->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
+ // I hope that's all that's needed, ugh
+}
+
+// Function: A_NapalmScatter
+//
+// Description: Scatters a specific number of projectiles around in a circle.
+// Intended for use with objects that are affected by gravity; would be kind of silly otherwise.
+//
+// var1:
+// Lower 16 bits: object # to lob (TODO: come up with a default)
+// Upper 16 bits: Number to lob (default 8)
+// var2:
+// Lower 16 bits: distance to toss them (No default - 0 does just that - but negatives will revert to 128)
+// Upper 16 bits: airtime in tics (default 16)
+//
+void A_NapalmScatter(mobj_t *actor)
+{
+ mobjtype_t typeOfShot = var1 & 0x0000FFFF; // Type
+ INT32 numToShoot = (var1 & 0xFFFF0000) >> 16; // How many
+ fixed_t distance = (var2 & 0x0000FFFF) << FRACBITS; // How far
+ fixed_t airtime = var2 & 0xFFFF0000; // How long until impact (assuming no obstacles)
+ fixed_t vx; // Horizontal momentum
+ fixed_t vy; // Vertical momentum
+ fixed_t g; // Gravity
+ INT32 i; // for-loop cursor
+ mobj_t *mo; // each and every spawned napalm burst
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_NapalmScatter", actor))
+ return;
+#endif
+
+ // Some quick sanity-checking
+ if (typeOfShot >= NUMMOBJTYPES) // I'd add a <0 check, too, but 0x0000FFFF isn't negative in this case
+ typeOfShot = MT_NULL;
+ if (numToShoot <= 0) // Presumably you forgot to set var1 up; else, why are you calling this to shoot nothing?
+ numToShoot = 8;
+ else if (numToShoot > 8192) // If you seriously need this many objects spawned, stop and ask yourself "Why am I doing this?"
+ numToShoot = 8192;
+ if (distance < 0) // Presumably you thought this was an unsigned integer, you naive fool
+ distance = 32767<subsector->sector->gravity)
+ g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
+ else
+ g = gravity;
+
+ // vy = (g*(airtime-1))/2
+ vy = FixedMul(g,(airtime-(1<>1;
+ // vx = distance/airtime
+ vx = FixedDiv(distance, airtime);
+
+ for (i = 0; ix, actor->y, actor->z, typeOfShot);
+ P_SetTarget(&mo->target, actor->target); // Transfer target so Brak doesn't hit himself like an idiot
+
+ mo->angle = fa << ANGLETOFINESHIFT;
+ mo->momx = FixedMul(FINECOSINE(fa),vx);
+ mo->momy = FixedMul(FINESINE(fa),vx);
+ mo->momz = vy;
+ }
+}
+
+// Function: A_SpawnFreshCopy
+//
+// Description: Spawns a copy of the mobj. x, y, z, angle, scale, target and tracer carry over; everything else starts anew.
+// Mostly writing this because I want to do multiple actions to pass these along in a single frame instead of several.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_SpawnFreshCopy(mobj_t *actor)
+{
+ mobj_t *newObject;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SpawnFreshCopy", actor))
+ return;
+#endif
+
+ newObject = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->type);
+ newObject->flags2 = actor->flags2 & MF2_AMBUSH;
+ newObject->angle = actor->angle;
+ newObject->color = actor->color;
+ P_SetTarget(&newObject->target, actor->target);
+ P_SetTarget(&newObject->tracer, actor->tracer);
+
+ if (newObject->info->seesound)
+ S_StartSound(newObject, newObject->info->seesound);
+}
+
+// Internal Flicky spawning function.
+mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers)
+{
+ mobj_t *flicky;
+
+ if (!flickytype)
+ {
+ if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
+ return NULL;
+ else
+ {
+ INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies);
+ flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
+ }
+ }
+
+ flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype);
+ flicky->angle = actor->angle;
+
+ if (flickytype == MT_SEED)
+ flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2;
+
+ if (actor->eflags & MFE_UNDERWATER)
+ momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
+
+ P_SetObjectMomZ(flicky, momz, false);
+ flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1);
+ flicky->fuse = P_RandomRange(595, 700); // originally 300, 350
+ flicky->threshold = 0;
+
+ if (lookforplayers)
+ P_LookForPlayers(flicky, true, false, 0);
+
+ return flicky;
+}
+
+// Function: A_FlickySpawn
+//
+// Description: Flicky spawning function.
+//
+// var1:
+// lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
+// upper 16 bits: if 0, no sound is played. Else, A_Scream is called.
+// var2 = upwards thrust for spawned flicky. If zero, default value is provided.
+//
+void A_FlickySpawn(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickySpawn", actor))
+ return;
+#endif
+
+ if (locvar1 >> 16) {
+ A_Scream(actor); // A shortcut for the truly lazy.
+ locvar1 &= 65535;
+ }
+
+ P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true);
+}
+
+// Internal Flicky color setting
+void P_InternalFlickySetColor(mobj_t *actor, UINT8 extrainfo)
+{
+ UINT8 flickycolors[] = {
+ SKINCOLOR_RED,
+ SKINCOLOR_CYAN,
+ SKINCOLOR_BLUE,
+ SKINCOLOR_VAPOR,
+ SKINCOLOR_PURPLE,
+ SKINCOLOR_BUBBLEGUM,
+ SKINCOLOR_NEON,
+ SKINCOLOR_BLACK,
+ SKINCOLOR_BEIGE,
+ SKINCOLOR_LAVENDER,
+ SKINCOLOR_RUBY,
+ SKINCOLOR_SALMON,
+ SKINCOLOR_SUNSET,
+ SKINCOLOR_ORANGE,
+ SKINCOLOR_YELLOW,
+ };
+
+ if (extrainfo == 0)
+ // until we can customize flicky colors by level header, just stick to SRB2's defaults
+ actor->color = flickycolors[P_RandomKey(2)]; //flickycolors[P_RandomKey(sizeof(flickycolors))];
+ else
+ actor->color = flickycolors[min(extrainfo-1, 14)]; // sizeof(flickycolors)-1
+}
+
+// Function: A_FlickyCenter
+//
+// Description: Place flickies in-level.
+//
+// var1:
+// Lower 16 bits = if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
+// Bits 17-20 = Flicky color, up to 15. Applies to fish.
+// Bit 21 = Flag MF_SLIDEME (see below)
+// Bit 22 = Flag MF_GRENADEBOUNCE (see below)
+// Bit 23 = Flag MF_NOCLIPTHING (see below)
+//
+// If actor is placed from a spawnpoint (map Thing), the Thing's properties take precedence.
+//
+// var2 = maximum default distance away from spawn the flickies are allowed to travel. If angle != 0, then that's the radius.
+//
+// If MTF_EXTRA (MF_SLIDEME): is flagged, Flickies move aimlessly. Else, orbit around the target.
+// If MTF_OBJECTSPECIAL (MF_GRENADEBOUNCE): Flickies stand in-place without gravity (unless they hop, then gravity is applied.)
+// If MTF_AMBUSH (MF_NOCLIPTHING): is flagged, Flickies hop.
+//
+void A_FlickyCenter(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ UINT16 flickytype = (locvar1 & 0xFFFF);
+ UINT8 flickycolor = ((locvar1 >> 16) & 0xFF);
+ UINT8 flickyflags = ((locvar1 >> 20) & 0xF);
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyCenter", actor))
+ return;
+#endif
+
+ if (!actor->tracer)
+ {
+ mobj_t *flicky = P_InternalFlickySpawn(actor, locvar1, 1, false);
+ P_SetTarget(&flicky->target, actor);
+ P_SetTarget(&actor->tracer, flicky);
+
+ if (actor->spawnpoint)
+ {
+ actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
+ actor->flags |= (
+ ((actor->spawnpoint->options & MTF_EXTRA) ? MF_SLIDEME : 0)
+ | ((actor->spawnpoint->options & MTF_OBJECTSPECIAL) ? MF_GRENADEBOUNCE : 0)
+ | ((actor->spawnpoint->options & MTF_AMBUSH) ? MF_NOCLIPTHING : 0)
+ );
+ actor->extravalue1 = actor->spawnpoint->angle ? abs(actor->spawnpoint->angle) * FRACUNIT
+ : locvar2 ? abs(locvar2) : 384 * FRACUNIT;
+ actor->extravalue2 = actor->spawnpoint->extrainfo;
+ actor->friction = actor->spawnpoint->x*FRACUNIT;
+ actor->movefactor = actor->spawnpoint->y*FRACUNIT;
+ actor->watertop = actor->spawnpoint->z*FRACUNIT;
+ }
+ else
+ {
+ actor->flags &= ~(MF_SLIDEME|MF_GRENADEBOUNCE|MF_NOCLIPTHING);
+ actor->flags |= (
+ ((flickyflags & 1) ? MF_SLIDEME : 0)
+ | ((flickyflags & 2) ? MF_GRENADEBOUNCE : 0)
+ | ((flickyflags & 4) ? MF_NOCLIPTHING : 0)
+ );
+ actor->extravalue1 = abs(locvar2);
+ actor->extravalue2 = flickycolor;
+ actor->friction = actor->x;
+ actor->movefactor = actor->y;
+ actor->watertop = actor->z;
+ locvar1 = flickytype;
+ }
+
+ if (actor->flags & MF_GRENADEBOUNCE) // in-place
+ actor->tracer->fuse = 0;
+ else if (actor->flags & MF_SLIDEME) // aimless
+ {
+ actor->tracer->fuse = 0; // less than 2*TICRATE means move aimlessly.
+ actor->tracer->angle = P_RandomKey(180)*ANG2;
+ }
+ else //orbit
+ actor->tracer->fuse = FRACUNIT;
+
+ if (locvar1 == MT_FLICKY_08)
+ P_InternalFlickySetColor(actor->tracer, actor->extravalue2);
+
+ actor->extravalue2 = 0;
+ }
+
+ if (!(actor->flags & MF_SLIDEME) && !(actor->flags & MF_GRENADEBOUNCE))
+ {
+ fixed_t originx = actor->friction;
+ fixed_t originy = actor->movefactor;
+ fixed_t originz = actor->watertop;
+
+ actor->tracer->fuse = FRACUNIT;
+
+ // Impose default home radius if flicky orbits around player
+ if (!actor->extravalue1)
+ actor->extravalue1 = locvar2 ? abs(locvar2) : 384 * FRACUNIT;
+
+ P_LookForPlayers(actor, true, false, actor->extravalue1);
+
+ if (actor->target && P_AproxDistance(actor->target->x - originx, actor->target->y - originy) < actor->extravalue1)
+ {
+ actor->extravalue2 = 1;
+ P_TeleportMove(actor, actor->target->x, actor->target->y, actor->target->z);
+ }
+ else if(actor->extravalue2)
+ {
+ actor->extravalue2 = 0;
+ P_TeleportMove(actor, originx, originy, originz);
+ }
+ }
+}
+
+// Internal Flicky bubbling function.
+void P_InternalFlickyBubble(mobj_t *actor)
+{
+ if (actor->eflags & MFE_UNDERWATER)
+ {
+ mobj_t *overlay;
+
+ if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer)
+ return;
+
+ overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
+ P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate);
+ P_SetTarget(&actor->tracer, overlay);
+ P_SetTarget(&overlay->target, actor);
+ return;
+ }
+
+ if (!actor->tracer || P_MobjWasRemoved(actor->tracer))
+ return;
+
+ P_RemoveMobj(actor->tracer);
+ P_SetTarget(&actor->tracer, NULL);
+}
+
+// Function: A_FlickyAim
+//
+// Description: Flicky aiming function.
+//
+// var1 = how far around the target (in angle constants) the flicky should look
+// var2 = distance from target to aim for
+//
+void A_FlickyAim(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ boolean flickyhitwall = false;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyAim", actor))
+ return;
+#endif
+
+ if ((actor->momx == actor->momy && actor->momy == 0)
+ || (actor->target && P_IsFlickyCenter(actor->target->type)
+ && actor->target->extravalue1 && (actor->target->flags & MF_SLIDEME)
+ && P_AproxDistance(actor->x - actor->target->x, actor->y - actor->target->y) >= actor->target->extravalue1))
+ flickyhitwall = true;
+
+ P_InternalFlickyBubble(actor);
+ P_InstaThrust(actor, 0, 0);
+
+ if (!actor->target)
+ {
+ P_LookForPlayers(actor, true, false, 0);
+ actor->angle = P_RandomKey(36)*ANG10;
+ return;
+ }
+
+ if (actor->fuse > 2*TICRATE)
+ {
+ angle_t posvar;
+ fixed_t chasevar, chasex, chasey;
+
+ if (flickyhitwall)
+ actor->movedir *= -1;
+
+ posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK;
+ chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2;
+
+ chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar);
+ chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar);
+
+ if (P_AproxDistance(chasex - actor->x, chasey - actor->y))
+ actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey);
+ }
+ else if (flickyhitwall)
+ {
+ if (actor->target && P_IsFlickyCenter(actor->target->type))
+ actor->angle = R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + P_RandomRange(112, 248) * ANG1;
+ else
+ actor->angle += P_RandomRange(112, 248)*ANG1;
+ actor->threshold = 0;
+ }
+}
+
+//Internal Flicky flying function. Also usuable as an underwater swim thrust.
+void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez)
+{
+ angle_t vertangle;
+
+ flyspeed = FixedMul(flyspeed, actor->scale);
+ actor->flags |= MF_NOGRAVITY;
+
+ var1 = ANG30;
+ var2 = 32*FRACUNIT;
+ A_FlickyAim(actor);
+
+ chasez *= 8;
+ if (!actor->target || !(actor->fuse > 2*TICRATE))
+ chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT);
+ else
+ {
+ fixed_t add = actor->target->z + (actor->target->height - actor->height)/2;
+ if (add > (actor->ceilingz - 24*actor->scale - actor->height))
+ add = actor->ceilingz - 24*actor->scale - actor->height;
+ else if (add < (actor->floorz + 24*actor->scale))
+ add = actor->floorz + 24*actor->scale;
+ chasez += add;
+ }
+
+ if (!targetdist)
+ targetdist = 16*FRACUNIT; //Default!
+
+ if (actor->target && abs(chasez - actor->z) > targetdist)
+ targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+
+ if (actor->target
+ && P_IsFlickyCenter(actor->target->type)
+ && (actor->target->flags & MF_SLIDEME))
+ vertangle = 0;
+ else
+ vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK;
+
+ P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed));
+ actor->momz = FixedMul(FINESINE(vertangle), flyspeed);
+}
+
+// Function: A_FlickyFly
+//
+// Description: Flicky flying function.
+//
+// var1 = how fast to fly
+// var2 = how far ahead the target should be considered
+//
+void A_FlickyFly(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyFly", actor))
+ return;
+#endif
+ P_InternalFlickyFly(actor, locvar1, locvar2,
+ FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK)
+ );
+}
+
+// Function: A_FlickySoar
+//
+// Description: Flicky soaring function - specific to puffin.
+//
+// var1 = how fast to fly
+// var2 = how far ahead the target should be considered
+//
+void A_FlickySoar(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickySoar", actor))
+ return;
+#endif
+ P_InternalFlickyFly(actor, locvar1, locvar2,
+ 2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK)))
+ );
+
+ if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10)
+ actor->frame = 3;
+}
+
+//Function: A_FlickyCoast
+//
+// Description: Flicky swim-coasting function.
+//
+// var1 = speed to change state upon reaching
+// var2 = state to change to upon slowing down
+// the spawnstate of the mobj = state to change to when above water
+//
+void A_FlickyCoast(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyCoast", actor))
+ return;
+#endif
+ if (actor->eflags & MFE_UNDERWATER)
+ {
+ actor->momx = (11*actor->momx)/12;
+ actor->momy = (11*actor->momy)/12;
+ actor->momz = (11*actor->momz)/12;
+
+ if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1)
+ P_SetMobjState(actor, locvar2);
+
+ return;
+ }
+
+ actor->flags &= ~MF_NOGRAVITY;
+ P_SetMobjState(actor, mobjinfo[actor->type].spawnstate);
+}
+
+// Internal Flicky hopping function.
+void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle)
+{
+ if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+ || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
+ {
+ if (momz)
+ {
+ if (actor->eflags & MFE_UNDERWATER)
+ momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
+ P_SetObjectMomZ(actor, momz, false);
+ }
+ P_InstaThrust(actor, angle, FixedMul(momh, actor->scale));
+ }
+}
+
+// Function: A_FlickyHop
+//
+// Description: Flicky hopping function.
+//
+// var1 = vertical thrust
+// var2 = horizontal thrust
+//
+void A_FlickyHop(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyHop", actor))
+ return;
+#endif
+ P_InternalFlickyHop(actor, locvar1, locvar2, actor->angle);
+}
+
+// Function: A_FlickyFlounder
+//
+// Description: Flicky floundering function.
+//
+// var1 = intended vertical thrust
+// var2 = intended horizontal thrust
+//
+void A_FlickyFlounder(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ angle_t hopangle;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyFlounder", actor))
+ return;
+#endif
+ locvar1 *= (P_RandomKey(2) + 1);
+ locvar2 *= (P_RandomKey(2) + 1);
+ hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2);
+ P_InternalFlickyHop(actor, locvar1, locvar2, hopangle);
+}
+
+// Function: A_FlickyCheck
+//
+// Description: Flicky airtime check function.
+//
+// var1 = state to change to upon touching the floor
+// var2 = state to change to upon falling
+// the meleestate of the mobj = state to change to when underwater
+//
+void A_FlickyCheck(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyCheck", actor))
+ return;
+#endif
+ if (actor->target
+ && P_IsFlickyCenter(actor->target->type)
+ && (actor->target->flags & MF_GRENADEBOUNCE))
+ {
+ if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping
+ {
+ actor->momz = 0;
+ actor->flags |= MF_NOGRAVITY;
+ }
+ actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY;
+ P_SetMobjState(actor, mobjinfo[actor->type].seestate);
+ }
+ else if (locvar2 && P_MobjFlip(actor)*actor->momz < 1)
+ P_SetMobjState(actor, locvar2);
+ else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+ || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
+ P_SetMobjState(actor, locvar1);
+ else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER))
+ P_SetMobjState(actor, mobjinfo[actor->type].meleestate);
+ P_InternalFlickyBubble(actor);
+}
+
+// Function: A_FlickyHeightCheck
+//
+// Description: Flicky height check function.
+//
+// var1 = state to change to when falling below height relative to target
+// var2 = height relative to target to change state at
+//
+void A_FlickyHeightCheck(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyHeightCheck", actor))
+ return;
+#endif
+ if (actor->target
+ && P_IsFlickyCenter(actor->target->type)
+ && (actor->target->flags & MF_GRENADEBOUNCE))
+ {
+ if (!(actor->target->flags & MF_NOCLIPTHING)) // no hopping
+ {
+ actor->momz = 0;
+ actor->flags |= MF_NOGRAVITY;
+ }
+ actor->flags |= MF_NOCLIP | MF_NOBLOCKMAP | MF_SCENERY;
+ P_SetMobjState(actor, mobjinfo[actor->type].seestate);
+ }
+ else if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1
+ && ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2)
+ || (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz)))
+ P_SetMobjState(actor, locvar1);
+ P_InternalFlickyBubble(actor);
+}
+
+// Function: A_FlickyFlutter
+//
+// Description: Flicky fluttering function - specific to chicken.
+//
+// var1 = state to change to upon touching the floor
+// var2 = state to change to upon falling
+// the meleestate of the mobj = state to change to when underwater
+//
+void A_FlickyFlutter(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyFlutter", actor))
+ return;
+#endif
+ var1 = locvar1;
+ var2 = locvar2;
+ A_FlickyCheck(actor);
+
+ var1 = ANG30;
+ var2 = 32*FRACUNIT;
+ A_FlickyAim(actor);
+
+ P_InstaThrust(actor, actor->angle, 2*actor->scale);
+ if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2)
+ actor->momz = -P_MobjFlip(actor)*actor->scale/2;
+}
+
+#undef FLICKYHITWALL
+
+// Function: A_FlameParticle
+//
+// Description: Creates the mobj's painchance at a random position around the object's radius.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_FlameParticle(mobj_t *actor)
+{
+ mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance);
+ fixed_t rad, hei;
+ mobj_t *particle;
+ //INT32 locvar1 = var1;
+ //INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlameParticle", actor))
+ return;
+#endif
+
+ if (!type)
+ return;
+
+ rad = actor->radius>>FRACBITS;
+ hei = actor->height>>FRACBITS;
+ particle = P_SpawnMobjFromMobj(actor,
+ P_RandomRange(rad, -rad)<frame = actor->frame;
+
+ if (!(locvar1 & 1))
+ {
+ fade->fuse = 15;
+ fade->flags2 |= MF2_BOSSNOTRAP;
+ }
+ else
+ fade->fuse = 20;
+
+ if (!(locvar1 & 2))
+ P_SetTarget(&actor->tracer, fade);
+}
+
+// Function: A_Boss5Jump
+//
+// Description: Makes an object jump in an arc to land on their tracer precicely.
+// Adapted from A_BrakLobShot, see there for explanation.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss5Jump(mobj_t *actor)
+{
+ fixed_t v; // Velocity to jump at
+ fixed_t a1, a2, aToUse; // Velocity squared
+ fixed_t g; // Gravity
+ fixed_t x; // Horizontal difference
+ INT32 x_int; // x! But in integer form!
+ fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
+ INT32 y_int; // y! But in integer form!
+ INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
+ fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
+ angle_t theta; // Angle of attack
+ // INT32 locvar1 = var1;
+ // INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_Boss5Jump", actor))
+ return;
+#endif
+
+ if (!actor->tracer)
+ return; // Don't even bother if we've got nothing to aim at.
+
+ // Look up actor's current gravity situation
+ if (actor->subsector->sector->gravity)
+ g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
+ else
+ g = gravity;
+
+ // Look up distance between actor and its tracer
+ x = P_AproxDistance(actor->tracer->x - actor->x, actor->tracer->y - actor->y);
+ // Look up height difference between actor and its tracer
+ y = actor->tracer->z - actor->z;
+
+ // Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
+ x_int = x>>FRACBITS;
+ y_int = y>>FRACBITS;
+ intHypotenuse = (x_int*x_int) + (y_int*y_int);
+ fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
+
+ // a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
+ a1 = FixedMul(g,y+fixedHypotenuse);
+ a2 = FixedMul(g,y-fixedHypotenuse);
+
+ // Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
+ if (a1 < 0 || a2 < 0)
+ {
+ if (a1 < 0 && a2 < 0)
+ {
+ //Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
+ return;
+ }
+ // Just find which one's NOT negative, and use that
+ aToUse = max(a1,a2);
+ }
+ else
+ {
+ // Both are positive; use whichever's smaller so it can decay faster
+ aToUse = min(a1,a2);
+ }
+ v = FixedSqrt(aToUse);
+ // Okay, so we know the velocity. Let's actually find theta.
+ // We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
+ //theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
+ theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
+
+ // Okay, complicated math done. Let's make this object jump already.
+ A_FaceTracer(actor);
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ actor->z--;
+ else
+ actor->z++;
+
+ // Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
+ fixedHypotenuse = FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)); // variable reuse
+ actor->momx = FixedMul(fixedHypotenuse, FINECOSINE(actor->angle >> ANGLETOFINESHIFT));
+ actor->momy = FixedMul(fixedHypotenuse, FINESINE(actor->angle >> ANGLETOFINESHIFT));
+ // Then the vertical axis. No angle-correction needed here.
+ actor->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
+ // I hope that's all that's needed, ugh
+}
+
+// Function: A_LightBeamReset
+// Description: Resets momentum and position for DSZ's projecting light beams
+//
+// var1 = unused
+// var2 = unused
+//
+void A_LightBeamReset(mobj_t *actor)
+{
+ // INT32 locvar1 = var1;
+ // INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_LightBeamReset", actor))
+ return;
+#endif
+
+ actor->destscale = FRACUNIT + P_SignedRandom()*FRACUNIT/256;
+ P_SetScale(actor, actor->destscale);
+
+ if (!actor->spawnpoint)
+ return; // this can't work properly welp
+
+ actor->momx = -(P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128;
+ actor->momy = (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/128;
+ actor->momz = (P_SignedRandom()*FRACUNIT)/128;
+
+ P_TeleportMove(actor,
+ actor->spawnpoint->x*FRACUNIT - (P_SignedRandom()*FINESINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2,
+ actor->spawnpoint->y*FRACUNIT + (P_SignedRandom()*FINECOSINE(((actor->spawnpoint->angle*ANG1)>>ANGLETOFINESHIFT) & FINEMASK))/2,
+ actor->spawnpoint->z*FRACUNIT + (P_SignedRandom()*FRACUNIT)/2);
+}
+
+// Function: A_MineExplode
+// Description: Handles the explosion of a DSZ mine.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_MineExplode(mobj_t *actor)
+{
+ // INT32 locvar1 = var1;
+ // INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MineExplode", actor))
+ return;
+#endif
+
+ A_Scream(actor);
+ actor->flags = MF_NOGRAVITY|MF_NOCLIP;
+
+ quake.epicenter = NULL;
+ quake.radius = 512*FRACUNIT;
+ quake.intensity = 8*FRACUNIT;
+ quake.time = TICRATE/3;
+
+ P_RadiusAttack(actor, actor->tracer, 192*FRACUNIT, DMG_CANHURTSELF);
+ P_MobjCheckWater(actor);
+
+ {
+#define dist 64
+ UINT8 i;
+ mobjtype_t type = ((actor->eflags & MFE_UNDERWATER) ? MT_UWEXPLODE : MT_BOSSEXPLODE);
+ S_StartSound(actor, ((actor->eflags & MFE_UNDERWATER) ? sfx_s3k57 : sfx_s3k4e));
+ P_SpawnMobj(actor->x, actor->y, actor->z, type);
+ for (i = 0; i < 16; i++)
+ {
+ mobj_t *b = P_SpawnMobj(actor->x+P_RandomRange(-dist, dist)*FRACUNIT,
+ actor->y+P_RandomRange(-dist, dist)*FRACUNIT,
+ actor->z+P_RandomRange(((actor->eflags & MFE_UNDERWATER) ? -dist : 0), dist)*FRACUNIT,
+ type);
+ fixed_t dx = b->x - actor->x, dy = b->y - actor->y, dz = b->z - actor->z;
+ fixed_t dm = P_AproxDistance(dz, P_AproxDistance(dy, dx));
+ b->momx = FixedDiv(dx, dm)*3;
+ b->momy = FixedDiv(dy, dm)*3;
+ b->momz = FixedDiv(dz, dm)*3;
+ if ((actor->watertop == INT32_MAX) || (b->z + b->height > actor->watertop))
+ b->flags &= ~MF_NOGRAVITY;
+ }
+#undef dist
+
+ if (actor->watertop != INT32_MAX)
+ P_SpawnMobj(actor->x, actor->y, actor->watertop, MT_SPLISH);
+ }
+}
+
+// Function: A_MineRange
+// Description: If the target gets too close, change the state to meleestate.
+//
+// var1 = Distance to alert at
+// var2 = unused
+//
+void A_MineRange(mobj_t *actor)
+{
+ fixed_t dm;
+ INT32 locvar1 = var1;
+ // INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MineRange", actor))
+ return;
+#endif
+
+ if (!actor->target)
+ return;
+
+ dm = P_AproxDistance(actor->z - actor->target->z, P_AproxDistance(actor->y - actor->target->y, actor->x - actor->target->x));
+ if ((dm>>FRACBITS) < locvar1)
+ P_SetMobjState(actor, actor->info->meleestate);
+}
+
+// Function: A_ConnectToGround
+// Description: Create a palm tree trunk/mine chain.
+//
+// var1 = Object type to connect to ground
+// var2 = Object type to place on ground
+//
+void A_ConnectToGround(mobj_t *actor)
+{
+ mobj_t *work;
+ fixed_t workz;
+ fixed_t workh;
+ SINT8 dir;
+ angle_t ang;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ConnectToGround", actor))
+ return;
+#endif
+
+ if (actor->subsector->sector->ffloors)
+ P_AdjustMobjFloorZ_FFloors(actor, actor->subsector->sector, 2);
+
+ if (actor->flags2 & MF2_OBJECTFLIP)
+ {
+ workz = actor->ceilingz - (actor->z + actor->height);
+ dir = -1;
+ }
+ else
+ {
+ workz = actor->floorz - actor->z;
+ dir = 1;
+ }
+
+ if (locvar2)
+ {
+ workh = FixedMul(mobjinfo[locvar2].height, actor->scale);
+ if (actor->flags2 & MF2_OBJECTFLIP)
+ workz -= workh;
+ work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar2);
+ workz += dir*workh;
+ }
+
+ if (!locvar1)
+ return;
+
+ if (!(workh = FixedMul(mobjinfo[locvar1].height, actor->scale)))
+ return;
+
+ if (actor->flags2 & MF2_OBJECTFLIP)
+ workz -= workh;
+
+ ang = actor->angle + ANGLE_45;
+ while (dir*workz < 0)
+ {
+ work = P_SpawnMobjFromMobj(actor, 0, 0, workz, locvar1);
+ if (work)
+ work->angle = ang;
+ ang += ANGLE_90;
+ workz += dir*workh;
+ }
+
+ if (workz != 0)
+ actor->z += workz;
+}
+
+// Function: A_SpawnParticleRelative
+//
+// Description: Spawns a particle effect relative to the location of the actor
+//
+// var1:
+// var1 >> 16 = x
+// var1 & 65535 = y
+// var2:
+// var2 >> 16 = z
+// var2 & 65535 = state
+//
+void A_SpawnParticleRelative(mobj_t *actor)
+{
+ INT16 x, y, z; // Want to be sure we can use negative values
+ statenum_t state;
+ mobj_t *mo;
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_SpawnParticleRelative", actor))
+ return;
+#endif
+
+ CONS_Debug(DBG_GAMELOGIC, "A_SpawnParticleRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2);
+
+ x = (INT16)(locvar1>>16);
+ y = (INT16)(locvar1&65535);
+ z = (INT16)(locvar2>>16);
+ state = (statenum_t)(locvar2&65535);
+
+ // Spawn objects correctly in reverse gravity.
+ // NOTE: Doing actor->z + actor->height is the bottom of the object while the object has reverse gravity. - Flame
+ mo = P_SpawnMobj(actor->x + FixedMul(x<scale),
+ actor->y + FixedMul(y<scale),
+ (actor->eflags & MFE_VERTICALFLIP) ? ((actor->z + actor->height - mobjinfo[MT_PARTICLE].height) - FixedMul(z<scale)) : (actor->z + FixedMul(z<scale)), MT_PARTICLE);
+
+ // Spawn objects with an angle matching the spawner's, rather than spawning Eastwards - Monster Iestyn
+ mo->angle = actor->angle;
+
+ if (actor->eflags & MFE_VERTICALFLIP)
+ mo->flags2 |= MF2_OBJECTFLIP;
+
+ P_SetMobjState(mo, state);
+}
+
+// Function: A_MultiShotDist
+//
+// Description: Spawns multiple shots based on player proximity
+//
+// var1 = same as A_MultiShot
+// var2 = same as A_MultiShot
+//
+void A_MultiShotDist(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_MultiShotDist", actor))
+ return;
+#endif
+
+ {
+ UINT8 i;
+ // Quick! Look through players!
+ // Don't spawn dust unless a player is relatively close by (var1).
+ for (i = 0; i < MAXPLAYERS; ++i)
+ if (playeringame[i] && players[i].mo
+ && P_AproxDistance(actor->x - players[i].mo->x, actor->y - players[i].mo->y) < (1600<> 16 = mobjtype of child
+// var2 & 65535 = vertical momentum
+// var2:
+// var2 >> 16 = forward offset
+// var2 & 65535 = vertical offset
+//
+void A_WhoCaresIfYourSonIsABee(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ fixed_t foffsetx;
+ fixed_t foffsety;
+ mobj_t *son;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_WhoCaresIfYourSonIsABee", actor))
+ return;
+#endif
+
+ A_FaceTarget(actor);
+
+ if (actor->extravalue1)
+ actor->extravalue1--;
+
+ if (actor->info->attacksound)
+ S_StartSound(actor, actor->info->attacksound);
+
+ foffsetx = P_ReturnThrustX(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
+ foffsety = P_ReturnThrustY(actor, actor->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
+
+ if (!(son = P_SpawnMobjFromMobj(actor, foffsetx, foffsety, (locvar2&65535)*FRACUNIT, (mobjtype_t)(locvar1 >> 16))))
+ return;
+
+ P_SetObjectMomZ(son, (locvar1 & 65535)<tracer, actor);
+ P_SetTarget(&son->target, actor->target);
+}
+
+// Function: A_ParentTriesToSleep
+//
+// Description: If extravalue1 is less than or equal to var1, go to var2.
+//
+// var1 = state to go to when extravalue1
+// var2 = unused
+//
+void A_ParentTriesToSleep(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ //INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ParentTriesToSleep", actor))
+ return;
+#endif
+
+ if (actor->extravalue1)
+ {
+ if (actor->info->seesound)
+ S_StartSound(actor, actor->info->seesound);
+ actor->reactiontime = 0;
+ P_SetMobjState(actor, locvar1);
+ }
+ else if (!actor->reactiontime)
+ {
+ actor->reactiontime = 1;
+ if (actor->info->activesound) // more like INactivesound doy hoy hoy
+ S_StartSound(actor, actor->info->activesound);
+ }
+}
+
+
+// Function: A_CryingToMomma
+//
+// Description: If you're a child, let your parent know something's happened to you through extravalue1. Also, prepare to die.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_CryingToMomma(mobj_t *actor)
+{
+ //INT32 locvar1 = var1;
+ //INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CryingToMomma", actor))
+ return;
+#endif
+
+ if (actor->tracer)
+ actor->tracer->extravalue1++;
+
+ actor->momx = actor->momy = actor->momz = 0;
+
+ P_UnsetThingPosition(actor);
+ if (sector_list)
+ {
+ P_DelSeclist(sector_list);
+ sector_list = NULL;
+ }
+ actor->flags = MF_NOBLOCKMAP|MF_NOCLIPTHING;
+ P_SetThingPosition(actor);
+}
+
+// Function: A_CheckFlags2
+//
+// Description: If actor->flags2 & var1, goto var2.
+//
+// var1 = mask
+// var2 = state to go
+//
+void A_CheckFlags2(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_CheckFlags2", actor))
+ return;
+#endif
+
+ if (actor->flags2 & locvar1)
+ P_SetMobjState(actor, (statenum_t)locvar2);
+}
diff --git a/src/p_floor.c b/src/p_floor.c
index 65e4b9537..a1b1c45fc 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_inter.c b/src/p_inter.c
index 5f2875252..177b8d16e 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -2756,8 +2756,18 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
if (oldnightstime > 10*TICRATE
&& player->nightstime < 10*TICRATE)
{
- //S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
- S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false);
+ if ((mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
+#ifdef _WIN32
+ // win32 MIDI volume hack means we cannot fade down the music
+ && S_MusicType() != MU_MID
+#endif
+ )
+ {
+ S_FadeMusic(0, 10*MUSICRATE);
+ S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS.
+ }
+ else
+ S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false);
}
}
}
@@ -2940,7 +2950,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
}
}
-static inline void P_SuperDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage)
+static void P_SuperDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage)
{
fixed_t fallbackspeed;
angle_t ang;
@@ -3137,8 +3147,13 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source)
if (oldnightstime > 10*TICRATE
&& player->nightstime < 10*TICRATE)
{
- //S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
- S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false);
+ if (mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
+ {
+ S_FadeMusic(0, 10*MUSICRATE);
+ S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS.
+ }
+ else
+ S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false);
}
}
diff --git a/src/p_lights.c b/src/p_lights.c
index c0b46f74c..67ec55e80 100644
--- a/src/p_lights.c
+++ b/src/p_lights.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_local.h b/src/p_local.h
index b98eeae1c..b686b9f09 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -39,9 +39,6 @@
// Convenience macro to fix issue with collision along bottom/left edges of blockmap -Red
#define BMBOUNDFIX(xl, xh, yl, yh) {if (xl > xh) xl = 0; if (yl > yh) yl = 0;}
-// player radius used only in am_map.c
-#define PLAYERRADIUS (16*FRACUNIT)
-
// MAXRADIUS is for precalculated sector block boxes
// the spider demon is larger,
// but we do not have any moving sectors nearby
diff --git a/src/p_map.c b/src/p_map.c
index b4ace61c7..ceaa6ca24 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -2183,18 +2183,6 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
return true;
}
-//
-// CheckMissileImpact
-//
-static void CheckMissileImpact(mobj_t *mobj)
-{
- if (!(mobj->flags & MF_MISSILE) || !mobj->target)
- return;
-
- if (!mobj->target->player)
- return;
-}
-
// The highest the camera will "step up" onto another floor.
#define MAXCAMERASTEPMOVE MAXSTEPMOVE
@@ -2419,11 +2407,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
}
if (!P_CheckPosition(thing, tryx, tryy))
- {
- if (!P_MobjWasRemoved(thing))
- CheckMissileImpact(thing);
return false; // solid wall or thing
- }
if (!(thing->flags & MF_NOCLIP))
{
@@ -2449,7 +2433,6 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
if (tmceilingz - tmfloorz < thing->height)
{
- CheckMissileImpact(thing);
if (tmfloorthing)
tmhitthing = tmfloorthing;
return false; // doesn't fit
@@ -2460,16 +2443,10 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
if (thing->eflags & MFE_VERTICALFLIP)
{
if (thing->z < tmfloorz)
- {
- CheckMissileImpact(thing);
return false; // mobj must raise itself to fit
- }
}
else if (tmceilingz < thingtop)
- {
- CheckMissileImpact(thing);
return false; // mobj must lower itself to fit
- }
// Ramp test
if (maxstep > 0 && !(
@@ -2521,7 +2498,6 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
{
if (thingtop - tmceilingz > maxstep)
{
- CheckMissileImpact(thing);
if (tmfloorthing)
tmhitthing = tmfloorthing;
return false; // too big a step up
@@ -2529,18 +2505,11 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
}
else if (tmfloorz - thing->z > maxstep)
{
- CheckMissileImpact(thing);
if (tmfloorthing)
tmhitthing = tmfloorthing;
return false; // too big a step up
}
- if (tmfloorz > thing->z)
- {
- if (thing->flags & MF_MISSILE)
- CheckMissileImpact(thing);
- }
-
if (!allowdropoff && !(thing->flags & MF_FLOAT) && thing->type != MT_SKIM && !tmfloorthing)
{
if (thing->eflags & MFE_VERTICALFLIP)
diff --git a/src/p_maputl.c b/src/p_maputl.c
index 46b033386..1be57399c 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_maputl.h b/src/p_maputl.h
index 3d74e927b..1fcb68d4c 100644
--- a/src/p_maputl.h
+++ b/src/p_maputl.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 7f871064a..5457c4f68 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -35,6 +35,7 @@
#include "p_slopes.h"
#endif
#include "f_finale.h"
+#include "m_cond.h"
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
consvar_t cv_movebob = {"movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -3729,7 +3730,7 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
if (player->pflags & PF_FLIPCAM && !(player->powers[pw_carry] == CR_NIGHTSMODE) && player->mo->eflags & MFE_VERTICALFLIP)
postimg = postimg_flip;
- else if (player->awayviewtics && player->awayviewmobj != NULL) // Camera must obviously exist
+ else if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist
{
camera_t dummycam;
dummycam.subsector = player->awayviewmobj->subsector;
@@ -6199,17 +6200,18 @@ void P_MaceRotate(mobj_t *center, INT32 baserot, INT32 baseprevrot)
boolean dosound = false;
mobj_t *mobj = center->hnext, *hnext = NULL;
- INT32 rot = (baserot &= FINEMASK);
- INT32 prevrot = (baseprevrot &= FINEMASK);
-
- INT32 lastthreshold = FINEMASK; // needs to never be equal at start of loop
+ INT32 lastthreshold = -1; // needs to never be equal at start of loop
fixed_t lastfriction = INT32_MIN; // ditto; almost certainly never, but...
- dist = pos_sideways[0] = pos_sideways[1] = pos_sideways[2] = pos_sideways[3] = unit_sideways[3] = pos_lengthways[0] = pos_lengthways[1] = pos_lengthways[2] = pos_lengthways[3] = 0;
+ INT32 rot;
+ INT32 prevrot;
+
+ dist = pos_sideways[0] = pos_sideways[1] = pos_sideways[2] = pos_sideways[3] = unit_sideways[3] =\
+ pos_lengthways[0] = pos_lengthways[1] = pos_lengthways[2] = pos_lengthways[3] = 0;
while (mobj)
{
- if (!mobj->health)
+ if (P_MobjWasRemoved(mobj) || !mobj->health)
{
mobj = mobj->hnext;
continue;
@@ -6796,10 +6798,67 @@ void P_MobjThinker(mobj_t *mobj)
case MT_FIREBARPOINT:
case MT_CUSTOMMACEPOINT:
case MT_HIDDEN_SLING:
- // The following was pretty good, but liked breaking whenever mobj->lastlook changed.
- //P_MaceRotate(mobj, ((leveltime + 1) * mobj->lastlook), (leveltime * mobj->lastlook));
- P_MaceRotate(mobj, mobj->movedir + mobj->lastlook, mobj->movedir);
- mobj->movedir = (mobj->movedir + mobj->lastlook) & FINEMASK;
+ {
+ angle_t oldmovedir = mobj->movedir;
+
+ // Always update movedir to prevent desyncing (in the traditional sense, not the netplay sense).
+ mobj->movedir = (mobj->movedir + mobj->lastlook) & FINEMASK;
+
+ // If too far away and not deliberately spitting in the face of optimisation, don't think!
+ if (!(mobj->flags2 & MF2_BOSSNOTRAP))
+ {
+ UINT8 i;
+ // Quick! Look through players! Don't move unless a player is relatively close by.
+ // The below is selected based on CEZ2's first room. I promise you it is a coincidence that it looks like the weed number.
+ for (i = 0; i < MAXPLAYERS; ++i)
+ if (playeringame[i] && players[i].mo
+ && P_AproxDistance(P_AproxDistance(mobj->x - players[i].mo->x, mobj->y - players[i].mo->y), mobj->z - players[i].mo->z) < (4200<flags2 & MF2_BEYONDTHEGRAVE))
+ {
+ mobj_t *ref = mobj;
+
+ // stop/hide all your babies
+ while ((ref = ref->hnext))
+ {
+ ref->eflags = (((ref->flags & MF_NOTHINK) ? 0 : 1)
+ | ((ref->flags & MF_NOCLIPTHING) ? 0 : 2)
+ | ((ref->flags2 & MF2_DONTDRAW) ? 0 : 4)); // oh my god this is nasty.
+ ref->flags |= MF_NOTHINK|MF_NOCLIPTHING;
+ ref->flags2 |= MF2_DONTDRAW;
+ ref->momx = ref->momy = ref->momz = 0;
+ }
+
+ mobj->flags2 |= MF2_BEYONDTHEGRAVE;
+ }
+
+ break; // don't make bubble!
+ }
+ else if (mobj->flags2 & MF2_BEYONDTHEGRAVE)
+ {
+ mobj_t *ref = mobj;
+
+ // start/show all your babies
+ while ((ref = ref->hnext))
+ {
+ if (ref->eflags & 1)
+ ref->flags &= ~MF_NOTHINK;
+ if (ref->eflags & 2)
+ ref->flags &= ~MF_NOCLIPTHING;
+ if (ref->eflags & 4)
+ ref->flags2 &= ~MF2_DONTDRAW;
+ ref->eflags = 0; // le sign
+ }
+
+ mobj->flags2 &= ~MF2_BEYONDTHEGRAVE;
+ }
+ }
+
+ // Okay, time to MOVE
+ P_MaceRotate(mobj, mobj->movedir, oldmovedir);
+ }
break;
case MT_HOOP:
if (mobj->fuse > 1)
@@ -8084,7 +8143,6 @@ void P_MobjThinker(mobj_t *mobj)
// Invisible/bouncing mode.
else
{
- fixed_t droneboxmandiff = max(mobj->height - droneman->height, 0);
INT32 i;
boolean bonustime = false;
fixed_t zcomp;
@@ -8476,6 +8534,8 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
// Assumedly in splitscreen players will be on opposing teams
if (players[consoleplayer].ctfteam == 1 || splitscreen)
S_StartSound(NULL, sfx_hoop1);
+ else if (players[consoleplayer].ctfteam == 2)
+ S_StartSound(NULL, sfx_hoop3);
redflag = flagmo;
}
@@ -8487,6 +8547,8 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
// Assumedly in splitscreen players will be on opposing teams
if (players[consoleplayer].ctfteam == 2 || splitscreen)
S_StartSound(NULL, sfx_hoop1);
+ else if (players[consoleplayer].ctfteam == 1)
+ S_StartSound(NULL, sfx_hoop3);
blueflag = flagmo;
}
@@ -9995,7 +10057,7 @@ void P_SpawnMapThing(mapthing_t *mthing)
|| mthing->type == mobjinfo[MT_REDTEAMRING].doomednum || mthing->type == mobjinfo[MT_BLUETEAMRING].doomednum
|| mthing->type == mobjinfo[MT_BLUESPHERE].doomednum || mthing->type == mobjinfo[MT_BOMBSPHERE].doomednum
|| (mthing->type >= 600 && mthing->type <= 609) // circles and diagonals
- || mthing->type == 1705 || mthing->type == 1713 || mthing->type == 1800) // hoops
+ || mthing->type == 1705 || mthing->type == 1713) // hoops
{
// Don't spawn hoops, wings, or rings yet!
return;
@@ -10170,6 +10232,9 @@ You should think about modifying the deathmatch starts to take full advantage of
if (i == MT_TOKEN && ((gametype != GT_COOP && gametype != GT_COMPETITION) || ultimatemode || tokenbits == 30 || tokenlist & (1 << tokenbits++)))
return; // you already got this token, or there are too many, or the gametype's not right
+ if (i == MT_EMBLEM && (netgame || multiplayer || (modifiedgame && !savemoddata))) // No cheating!!
+ return;
+
// Objectplace landing point
noreturns:
@@ -10186,7 +10251,7 @@ You should think about modifying the deathmatch starts to take full advantage of
ss->sector->floorheight) + ((mthing->options >> ZSHIFT) << FRACBITS);
else if (i == MT_AXIS || i == MT_AXISTRANSFER || i == MT_AXISTRANSFERLINE)
z = ONFLOORZ;
- else if (i == MT_SPIKEBALL || P_WeaponOrPanel(i) || i == MT_EMERALDSPAWN || i == MT_TOKEN)
+ else if (i == MT_SPIKEBALL || P_WeaponOrPanel(i) || i == MT_EMERALDSPAWN || i == MT_TOKEN || i == MT_EMBLEM)
{
if (mthing->options & MTF_OBJECTFLIP)
{
@@ -10288,6 +10353,62 @@ You should think about modifying the deathmatch starts to take full advantage of
#endif
switch(mobj->type)
{
+ case MT_EMBLEM:
+ {
+ INT32 j;
+ emblem_t *emblem = M_GetLevelEmblems(gamemap);
+ skincolors_t emcolor;
+
+ while (emblem)
+ {
+ if ((emblem->type == ET_GLOBAL || emblem->type == ET_SKIN) && emblem->tag == mthing->angle)
+ break;
+
+ emblem = M_GetLevelEmblems(-1);
+ }
+
+ if (!emblem)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "No map emblem for map %d with tag %d found!\n", gamemap, mthing->angle);
+ break;
+ }
+
+ j = emblem - emblemlocations;
+
+ I_Assert(emblemlocations[j].sprite >= 'A' && emblemlocations[j].sprite <= 'Z');
+ P_SetMobjState(mobj, mobj->info->spawnstate + (emblemlocations[j].sprite - 'A'));
+
+ mobj->health = j + 1;
+ emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting
+ mobj->color = (UINT8)emcolor;
+
+ if (emblemlocations[j].collected
+ || (emblemlocations[j].type == ET_SKIN && emblemlocations[j].var != players[0].skin))
+ {
+ P_UnsetThingPosition(mobj);
+ mobj->flags |= MF_NOCLIP;
+ mobj->flags &= ~MF_SPECIAL;
+ mobj->flags |= MF_NOBLOCKMAP;
+ mobj->frame |= (tr_trans50 << FF_TRANSSHIFT);
+ P_SetThingPosition(mobj);
+ }
+ else
+ {
+ mobj->frame &= ~FF_TRANSMASK;
+
+ if (emblemlocations[j].type == ET_GLOBAL)
+ {
+ mobj->reactiontime = emblemlocations[j].var;
+ if (emblemlocations[j].var & GE_NIGHTSITEM)
+ {
+ mobj->flags |= MF_NIGHTSITEM;
+ mobj->flags &= ~MF_SPECIAL;
+ mobj->flags2 |= MF2_DONTDRAW;
+ }
+ }
+ }
+ break;
+ }
case MT_SKYBOX:
if (mthing->options & MTF_OBJECTSPECIAL)
skyboxcenterpnts[mthing->extrainfo] = mobj;
@@ -10421,8 +10542,9 @@ ML_NOCLIMB :
anything else - no functionality
ML_EFFECT1 : Swings instead of spins
ML_EFFECT2 : Linktype is replaced with macetype for all spokes not ending in chains (inverted for MT_FIREBARPOINT)
-ML_EFFECT3 : Spawn a bonus macetype at the hinge point
+ML_EFFECT3 : Spawn a bonus linktype at the hinge point
ML_EFFECT4 : Don't clip inside the ground
+ML_EFFECT5 : Don't stop thinking when too far away
*/
mlength = abs(lines[line].dx >> FRACBITS);
mspeed = abs(lines[line].dy >> (FRACBITS - 4));
@@ -10548,6 +10670,10 @@ ML_EFFECT4 : Don't clip inside the ground
else
mmin = mnumspokes;
+ // If over distance away, don't move UNLESS this flag is applied
+ if (lines[line].flags & ML_EFFECT5)
+ mobj->flags2 |= MF2_BOSSNOTRAP;
+
// Make the links the same type as the end - repeated below
if ((mobj->type != MT_CHAINPOINT) && (((lines[line].flags & ML_EFFECT2) == ML_EFFECT2) != (mobj->type == MT_FIREBARPOINT))) // exclusive or
{
@@ -10880,35 +11006,37 @@ ML_EFFECT4 : Don't clip inside the ground
}
// spawn visual elements
- mobj_t *goalpost = P_SpawnMobjFromMobj(mobj, 0, 0, goaloffset, MT_NIGHTSDRONE_GOAL);
- mobj_t *sparkle = P_SpawnMobjFromMobj(mobj, 0, 0, sparkleoffset, MT_NIGHTSDRONE_SPARKLING);
- mobj_t *droneman = P_SpawnMobjFromMobj(mobj, 0, 0, dronemanoffset, MT_NIGHTSDRONE_MAN);
-
- P_SetTarget(&mobj->target, goalpost);
- P_SetTarget(&goalpost->target, sparkle);
- P_SetTarget(&goalpost->tracer, droneman);
-
- // correct Z position
- if (flip)
{
- P_TeleportMove(goalpost, goalpost->x, goalpost->y, mobj->z + goaloffset);
- P_TeleportMove(sparkle, sparkle->x, sparkle->y, mobj->z + sparkleoffset);
- P_TeleportMove(droneman, droneman->x, droneman->y, mobj->z + dronemanoffset);
+ mobj_t *goalpost = P_SpawnMobjFromMobj(mobj, 0, 0, goaloffset, MT_NIGHTSDRONE_GOAL);
+ mobj_t *sparkle = P_SpawnMobjFromMobj(mobj, 0, 0, sparkleoffset, MT_NIGHTSDRONE_SPARKLING);
+ mobj_t *droneman = P_SpawnMobjFromMobj(mobj, 0, 0, dronemanoffset, MT_NIGHTSDRONE_MAN);
+
+ P_SetTarget(&mobj->target, goalpost);
+ P_SetTarget(&goalpost->target, sparkle);
+ P_SetTarget(&goalpost->tracer, droneman);
+
+ // correct Z position
+ if (flip)
+ {
+ P_TeleportMove(goalpost, goalpost->x, goalpost->y, mobj->z + goaloffset);
+ P_TeleportMove(sparkle, sparkle->x, sparkle->y, mobj->z + sparkleoffset);
+ P_TeleportMove(droneman, droneman->x, droneman->y, mobj->z + dronemanoffset);
+ }
+
+ // Remember position preference for later
+ mobj->flags &= ~(MF_SLIDEME | MF_GRENADEBOUNCE);
+ if (topaligned)
+ mobj->flags |= MF_SLIDEME;
+ else if (middlealigned)
+ mobj->flags |= MF_GRENADEBOUNCE;
+ else if (!bottomoffsetted)
+ mobj->flags |= MF_SLIDEME | MF_GRENADEBOUNCE;
+
+ // Remember old Z position and flags for correction detection
+ goalpost->movefactor = mobj->z;
+ goalpost->friction = mobj->height;
+ goalpost->threshold = mobj->flags & (MF_SLIDEME | MF_GRENADEBOUNCE);
}
-
- // Remember position preference for later
- mobj->flags &= ~(MF_SLIDEME | MF_GRENADEBOUNCE);
- if (topaligned)
- mobj->flags |= MF_SLIDEME;
- else if (middlealigned)
- mobj->flags |= MF_GRENADEBOUNCE;
- else if (!bottomoffsetted)
- mobj->flags |= MF_SLIDEME | MF_GRENADEBOUNCE;
-
- // Remember old Z position and flags for correction detection
- goalpost->movefactor = mobj->z;
- goalpost->friction = mobj->height;
- goalpost->threshold = mobj->flags & (MF_SLIDEME | MF_GRENADEBOUNCE);
}
break;
case MT_HIVEELEMENTAL:
diff --git a/src/p_mobj.h b/src/p_mobj.h
index eac5118b8..936be3bb0 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_polyobj.c b/src/p_polyobj.c
index 875210435..dfa9906ba 100644
--- a/src/p_polyobj.c
+++ b/src/p_polyobj.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2006 by James Haley
-// Copyright (C) 2006-2016 by Sonic Team Junior.
+// Copyright (C) 2006-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -28,6 +28,10 @@
#include "r_state.h"
#include "r_defs.h"
+
+#define POLYOBJECTS
+
+
#ifdef POLYOBJECTS
/*
@@ -1150,7 +1154,7 @@ static INT32 Polyobj_clipThings(polyobj_t *po, line_t *line)
//
// Moves a polyobject on the x-y plane.
//
-static boolean Polyobj_moveXY(polyobj_t *po, fixed_t x, fixed_t y)
+static boolean Polyobj_moveXY(polyobj_t *po, fixed_t x, fixed_t y, boolean checkmobjs)
{
size_t i;
vertex_t vec;
@@ -1171,9 +1175,12 @@ static boolean Polyobj_moveXY(polyobj_t *po, fixed_t x, fixed_t y)
for (i = 0; i < po->numLines; ++i)
Polyobj_bboxAdd(po->lines[i]->bbox, &vec);
- // check for blocking things (yes, it needs to be done separately)
- for (i = 0; i < po->numLines; ++i)
- hitflags |= Polyobj_clipThings(po, po->lines[i]);
+ if (checkmobjs)
+ {
+ // check for blocking things (yes, it needs to be done separately)
+ for (i = 0; i < po->numLines; ++i)
+ hitflags |= Polyobj_clipThings(po, po->lines[i]);
+ }
if (hitflags & 2)
{
@@ -1191,7 +1198,8 @@ static boolean Polyobj_moveXY(polyobj_t *po, fixed_t x, fixed_t y)
po->spawnSpot.x += vec.x;
po->spawnSpot.y += vec.y;
- Polyobj_carryThings(po, x, y);
+ if (checkmobjs)
+ Polyobj_carryThings(po, x, y);
Polyobj_removeFromBlockmap(po); // unlink it from the blockmap
Polyobj_removeFromSubsec(po); // unlink it from its subsector
Polyobj_linkToBlockmap(po); // relink to blockmap
@@ -1358,7 +1366,7 @@ static void Polyobj_rotateThings(polyobj_t *po, vertex_t origin, angle_t delta,
//
// Rotates a polyobject around its start point.
//
-static boolean Polyobj_rotate(polyobj_t *po, angle_t delta, UINT8 turnthings)
+static boolean Polyobj_rotate(polyobj_t *po, angle_t delta, UINT8 turnthings, boolean checkmobjs)
{
size_t i;
angle_t angle;
@@ -1390,11 +1398,14 @@ static boolean Polyobj_rotate(polyobj_t *po, angle_t delta, UINT8 turnthings)
for (i = 0; i < po->numLines; ++i)
Polyobj_rotateLine(po->lines[i]);
- // check for blocking things
- for (i = 0; i < po->numLines; ++i)
- hitflags |= Polyobj_clipThings(po, po->lines[i]);
+ if (checkmobjs)
+ {
+ // check for blocking things
+ for (i = 0; i < po->numLines; ++i)
+ hitflags |= Polyobj_clipThings(po, po->lines[i]);
- Polyobj_rotateThings(po, origin, delta, turnthings);
+ Polyobj_rotateThings(po, origin, delta, turnthings);
+ }
if (hitflags & 2)
{
@@ -1610,19 +1621,23 @@ void Polyobj_InitLevel(void)
// Called when a savegame is being loaded. Rotates and translates an
// existing polyobject to its position when the game was saved.
//
+// Monster Iestyn 05/04/19: Please do not interact with mobjs! You
+// can cause I_Error crashes that way, and all the important mobjs are
+// going to be deleted afterwards anyway.
+//
void Polyobj_MoveOnLoad(polyobj_t *po, angle_t angle, fixed_t x, fixed_t y)
{
fixed_t dx, dy;
// first, rotate to the saved angle
- Polyobj_rotate(po, angle, false);
+ Polyobj_rotate(po, angle, false, false);
// determine component distances to translate
dx = x - po->spawnSpot.x;
dy = y - po->spawnSpot.y;
// translate
- Polyobj_moveXY(po, dx, dy);
+ Polyobj_moveXY(po, dx, dy, false);
}
// Thinker Functions
@@ -1662,7 +1677,7 @@ void T_PolyObjRotate(polyrotate_t *th)
// rotate by 'speed' angle per frame
// if distance == -1, this polyobject rotates perpetually
- if (Polyobj_rotate(po, th->speed, th->turnobjs) && th->distance != -1)
+ if (Polyobj_rotate(po, th->speed, th->turnobjs, true) && th->distance != -1)
{
INT32 avel = abs(th->speed);
@@ -1746,7 +1761,7 @@ void T_PolyObjMove(polymove_t *th)
}
// move the polyobject one step along its movement angle
- if (Polyobj_moveXY(po, th->momx, th->momy))
+ if (Polyobj_moveXY(po, th->momx, th->momy, true))
{
INT32 avel = abs(th->speed);
@@ -1860,12 +1875,15 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
fixed_t diffz;
amtx = (target->x - th->diffx) - po->centerPt.x;
amty = (target->y - th->diffy) - po->centerPt.y;
- Polyobj_moveXY(po, amtx, amty);
+ Polyobj_moveXY(po, amtx, amty, true);
// TODO: use T_MovePlane
amtz = (po->lines[0]->backsector->ceilingheight - po->lines[0]->backsector->floorheight)/2;
diffz = po->lines[0]->backsector->floorheight - (target->z - amtz);
po->lines[0]->backsector->floorheight = target->z - amtz;
po->lines[0]->backsector->ceilingheight = target->z + amtz;
+ // Sal: Remember to check your sectors!
+ P_CheckSector(po->lines[0]->frontsector, (boolean)(po->damage));
+ P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage));
// Apply action to mirroring polyobjects as well
start = 0;
while ((po = Polyobj_GetChild(oldpo, &start)))
@@ -1873,10 +1891,13 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
if (po->isBad)
continue;
- Polyobj_moveXY(po, amtx, amty);
+ Polyobj_moveXY(po, amtx, amty, true);
// TODO: use T_MovePlane
po->lines[0]->backsector->floorheight += diffz; // move up/down by same amount as the parent did
po->lines[0]->backsector->ceilingheight += diffz;
+ // Sal: Remember to check your sectors!
+ P_CheckSector(po->lines[0]->frontsector, (boolean)(po->damage));
+ P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage));
}
po = oldpo;
@@ -2033,10 +2054,13 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
}
// Move the polyobject
- Polyobj_moveXY(po, momx, momy);
+ Polyobj_moveXY(po, momx, momy, true);
// TODO: use T_MovePlane
po->lines[0]->backsector->floorheight += momz;
po->lines[0]->backsector->ceilingheight += momz;
+ // Sal: Remember to check your sectors!
+ P_CheckSector(po->lines[0]->frontsector, (boolean)(po->damage)); // frontsector is NEEDED for crushing
+ P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); // backsector may not be necessary, but just in case
// Apply action to mirroring polyobjects as well
start = 0;
@@ -2045,10 +2069,13 @@ void T_PolyObjWaypoint(polywaypoint_t *th)
if (po->isBad)
continue;
- Polyobj_moveXY(po, momx, momy);
+ Polyobj_moveXY(po, momx, momy, true);
// TODO: use T_MovePlane
po->lines[0]->backsector->floorheight += momz;
po->lines[0]->backsector->ceilingheight += momz;
+ // Sal: Remember to check your sectors!
+ P_CheckSector(po->lines[0]->frontsector, (boolean)(po->damage));
+ P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage));
}
}
@@ -2091,7 +2118,7 @@ void T_PolyDoorSlide(polyslidedoor_t *th)
}
// move the polyobject one step along its movement angle
- if (Polyobj_moveXY(po, th->momx, th->momy))
+ if (Polyobj_moveXY(po, th->momx, th->momy, true))
{
INT32 avel = abs(th->speed);
@@ -2197,7 +2224,7 @@ void T_PolyDoorSwing(polyswingdoor_t *th)
// rotate by 'speed' angle per frame
// if distance == -1, this polyobject rotates perpetually
- if (Polyobj_rotate(po, th->speed, false) && th->distance != -1)
+ if (Polyobj_rotate(po, th->speed, false, true) && th->distance != -1)
{
INT32 avel = abs(th->speed);
@@ -2253,7 +2280,7 @@ void T_PolyDoorSwing(polyswingdoor_t *th)
}
}
-// T_PolyObjDisplace: shift a polyobject based on a control sector's heights. -Red
+// T_PolyObjDisplace: shift a polyobject based on a control sector's heights.
void T_PolyObjDisplace(polydisplace_t *th)
{
polyobj_t *po = Polyobj_GetForNum(th->polyObjNum);
@@ -2262,10 +2289,10 @@ void T_PolyObjDisplace(polydisplace_t *th)
if (!po)
#ifdef RANGECHECK
- I_Error("T_PolyDoorSwing: thinker has invalid id %d\n", th->polyObjNum);
+ I_Error("T_PolyObjDisplace: thinker has invalid id %d\n", th->polyObjNum);
#else
{
- CONS_Debug(DBG_POLYOBJ, "T_PolyDoorSwing: thinker with invalid id %d removed.\n", th->polyObjNum);
+ CONS_Debug(DBG_POLYOBJ, "T_PolyObjDisplace: thinker with invalid id %d removed.\n", th->polyObjNum);
P_RemoveThinkerDelayed(&th->thinker);
return;
}
@@ -2289,7 +2316,46 @@ void T_PolyObjDisplace(polydisplace_t *th)
dx = FixedMul(th->dx, delta);
dy = FixedMul(th->dy, delta);
- if (Polyobj_moveXY(po, dx, dy))
+ if (Polyobj_moveXY(po, dx, dy, true))
+ th->oldHeights = newheights;
+}
+
+// T_PolyObjRotDisplace: rotate a polyobject based on a control sector's heights.
+void T_PolyObjRotDisplace(polyrotdisplace_t *th)
+{
+ polyobj_t *po = Polyobj_GetForNum(th->polyObjNum);
+ fixed_t newheights, delta;
+ fixed_t rotangle;
+
+ if (!po)
+#ifdef RANGECHECK
+ I_Error("T_PolyObjRotDisplace: thinker has invalid id %d\n", th->polyObjNum);
+#else
+ {
+ CONS_Debug(DBG_POLYOBJ, "T_PolyObjRotDisplace: thinker with invalid id %d removed.\n", th->polyObjNum);
+ P_RemoveThinkerDelayed(&th->thinker);
+ return;
+ }
+#endif
+
+ // check for displacement due to override and reattach when possible
+ if (po->thinker == NULL)
+ {
+ po->thinker = &th->thinker;
+
+ // reset polyobject's thrust
+ po->thrust = FRACUNIT;
+ }
+
+ newheights = th->controlSector->floorheight+th->controlSector->ceilingheight;
+ delta = newheights-th->oldHeights;
+
+ if (!delta)
+ return;
+
+ rotangle = FixedMul(th->rotscale, delta);
+
+ if (Polyobj_rotate(po, FixedAngle(rotangle), th->turnobjs, true))
th->oldHeights = newheights;
}
@@ -2752,6 +2818,52 @@ INT32 EV_DoPolyObjDisplace(polydisplacedata_t *prdata)
return 1;
}
+INT32 EV_DoPolyObjRotDisplace(polyrotdisplacedata_t *prdata)
+{
+ polyobj_t *po;
+ polyobj_t *oldpo;
+ polyrotdisplace_t *th;
+ INT32 start;
+
+ if (!(po = Polyobj_GetForNum(prdata->polyObjNum)))
+ {
+ CONS_Debug(DBG_POLYOBJ, "EV_DoPolyObjRotate: bad polyobj %d\n", prdata->polyObjNum);
+ return 0;
+ }
+
+ // don't allow line actions to affect bad polyobjects
+ if (po->isBad)
+ return 0;
+
+ // create a new thinker
+ th = Z_Malloc(sizeof(polyrotdisplace_t), PU_LEVSPEC, NULL);
+ th->thinker.function.acp1 = (actionf_p1)T_PolyObjRotDisplace;
+ PolyObj_AddThinker(&th->thinker);
+ po->thinker = &th->thinker;
+
+ // set fields
+ th->polyObjNum = prdata->polyObjNum;
+
+ th->controlSector = prdata->controlSector;
+ th->oldHeights = th->controlSector->floorheight+th->controlSector->ceilingheight;
+
+ th->rotscale = prdata->rotscale;
+ th->turnobjs = prdata->turnobjs;
+
+ oldpo = po;
+
+ // apply action to mirroring polyobjects as well
+ start = 0;
+ while ((po = Polyobj_GetChild(oldpo, &start)))
+ {
+ prdata->polyObjNum = po->id; // change id to match child polyobject's
+ EV_DoPolyObjRotDisplace(prdata);
+ }
+
+ // action was successful
+ return 1;
+}
+
void T_PolyObjFlag(polymove_t *th)
{
polyobj_t *po = Polyobj_GetForNum(th->polyObjNum);
diff --git a/src/p_polyobj.h b/src/p_polyobj.h
index 524518f2a..73a11d237 100644
--- a/src/p_polyobj.h
+++ b/src/p_polyobj.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2006 by James Haley
-// Copyright (C) 2006-2016 by Sonic Team Junior.
+// Copyright (C) 2006-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -207,6 +207,17 @@ typedef struct polydisplace_s
fixed_t oldHeights;
} polydisplace_t;
+typedef struct polyrotdisplace_s
+{
+ thinker_t thinker; // must be first
+
+ INT32 polyObjNum;
+ struct sector_s *controlSector;
+ fixed_t rotscale;
+ UINT8 turnobjs;
+ fixed_t oldHeights;
+} polyrotdisplace_t;
+
typedef struct polyfade_s
{
thinker_t thinker; // must be first
@@ -280,6 +291,14 @@ typedef struct polydisplacedata_s
fixed_t dy;
} polydisplacedata_t;
+typedef struct polyrotdisplacedata_s
+{
+ INT32 polyObjNum;
+ struct sector_s *controlSector;
+ fixed_t rotscale;
+ UINT8 turnobjs;
+} polyrotdisplacedata_t;
+
typedef struct polyfadedata_s
{
INT32 polyObjNum;
@@ -310,6 +329,7 @@ void T_PolyObjWaypoint (polywaypoint_t *);
void T_PolyDoorSlide(polyslidedoor_t *);
void T_PolyDoorSwing(polyswingdoor_t *);
void T_PolyObjDisplace (polydisplace_t *);
+void T_PolyObjRotDisplace (polyrotdisplace_t *);
void T_PolyObjFlag (polymove_t *);
void T_PolyObjFade (polyfade_t *);
@@ -318,6 +338,7 @@ INT32 EV_DoPolyObjMove(polymovedata_t *);
INT32 EV_DoPolyObjWaypoint(polywaypointdata_t *);
INT32 EV_DoPolyObjRotate(polyrotdata_t *);
INT32 EV_DoPolyObjDisplace(polydisplacedata_t *);
+INT32 EV_DoPolyObjRotDisplace(polyrotdisplacedata_t *);
INT32 EV_DoPolyObjFlag(struct line_s *);
INT32 EV_DoPolyObjFade(polyfadedata_t *);
diff --git a/src/p_pspr.h b/src/p_pspr.h
index 0734b78ec..c9c66b961 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 10ed68ddf..0d58387b9 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -1312,6 +1312,7 @@ typedef enum
tc_polyswingdoor,
tc_polyflag,
tc_polydisplace,
+ tc_polyrotdisplace,
tc_polyfade,
#endif
tc_end
@@ -2088,6 +2089,17 @@ static void SavePolydisplaceThinker(const thinker_t *th, const UINT8 type)
WRITEFIXED(save_p, ht->oldHeights);
}
+static void SavePolyrotdisplaceThinker(const thinker_t *th, const UINT8 type)
+{
+ const polyrotdisplace_t *ht = (const void *)th;
+ WRITEUINT8(save_p, type);
+ WRITEINT32(save_p, ht->polyObjNum);
+ WRITEUINT32(save_p, SaveSector(ht->controlSector));
+ WRITEFIXED(save_p, ht->rotscale);
+ WRITEUINT8(save_p, ht->turnobjs);
+ WRITEFIXED(save_p, ht->oldHeights);
+}
+
static void SavePolyfadeThinker(const thinker_t *th, const UINT8 type)
{
const polyfade_t *ht = (const void *)th;
@@ -2333,6 +2345,11 @@ static void P_NetArchiveThinkers(void)
SavePolydisplaceThinker(th, tc_polydisplace);
continue;
}
+ else if (th->function.acp1 == (actionf_p1)T_PolyObjRotDisplace)
+ {
+ SavePolyrotdisplaceThinker(th, tc_polyrotdisplace);
+ continue;
+ }
else if (th->function.acp1 == (actionf_p1)T_PolyObjFade)
{
SavePolyfadeThinker(th, tc_polyfade);
@@ -3217,6 +3234,18 @@ static inline void LoadPolydisplaceThinker(actionf_p1 thinker)
P_AddThinker(&ht->thinker);
}
+static inline void LoadPolyrotdisplaceThinker(actionf_p1 thinker)
+{
+ polyrotdisplace_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+ ht->thinker.function.acp1 = thinker;
+ ht->polyObjNum = READINT32(save_p);
+ ht->controlSector = LoadSector(READUINT32(save_p));
+ ht->rotscale = READFIXED(save_p);
+ ht->turnobjs = READUINT8(save_p);
+ ht->oldHeights = READFIXED(save_p);
+ P_AddThinker(&ht->thinker);
+}
+
//
// LoadPolyfadeThinker
//
@@ -3446,6 +3475,10 @@ static void P_NetUnArchiveThinkers(void)
LoadPolydisplaceThinker((actionf_p1)T_PolyObjDisplace);
break;
+ case tc_polyrotdisplace:
+ LoadPolyrotdisplaceThinker((actionf_p1)T_PolyObjRotDisplace);
+ break;
+
case tc_polyfade:
LoadPolyfadeThinker((actionf_p1)T_PolyObjFade);
break;
diff --git a/src/p_saveg.h b/src/p_saveg.h
index 5960660ab..2f5e88046 100644
--- a/src/p_saveg.h
+++ b/src/p_saveg.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_setup.c b/src/p_setup.c
index 9686585bb..0602865a9 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -212,6 +212,9 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
snprintf(mapheaderinfo[num]->musname, 7, "%sM", G_BuildMapName(i));
mapheaderinfo[num]->musname[6] = 0;
mapheaderinfo[num]->mustrack = 0;
+ mapheaderinfo[num]->muspos = 0;
+ mapheaderinfo[num]->musinterfadeout = 0;
+ mapheaderinfo[num]->musintername[0] = '\0';
mapheaderinfo[num]->forcecharacter[0] = '\0';
mapheaderinfo[num]->weather = 0;
mapheaderinfo[num]->skynum = 1;
@@ -391,30 +394,26 @@ static inline void P_LoadVertexes(lumpnum_t lumpnum)
Z_Free(data);
}
-
-//
-// Computes the line length in fracunits, the OpenGL render needs this
-//
-
/** Computes the length of a seg in fracunits.
- * This is needed for splats.
*
* \param seg Seg to compute length for.
* \return Length in fracunits.
*/
fixed_t P_SegLength(seg_t *seg)
{
- fixed_t dx, dy;
-
- // make a vector (start at origin)
- dx = seg->v2->x - seg->v1->x;
- dy = seg->v2->y - seg->v1->y;
-
- return FixedHypot(dx, dy);
+ INT64 dx = (seg->v2->x - seg->v1->x)>>1;
+ INT64 dy = (seg->v2->y - seg->v1->y)>>1;
+ return FixedHypot(dx, dy)<<1;
}
#ifdef HWRENDER
-static inline float P_SegLengthf(seg_t *seg)
+/** Computes the length of a seg as a float.
+ * This is needed for OpenGL.
+ *
+ * \param seg Seg to compute length for.
+ * \return Length as a float.
+ */
+static inline float P_SegLengthFloat(seg_t *seg)
{
float dx, dy;
@@ -450,11 +449,11 @@ static void P_LoadRawSegs(UINT8 *data, size_t i)
li->v1 = &vertexes[SHORT(ml->v1)];
li->v2 = &vertexes[SHORT(ml->v2)];
-#ifdef HWRENDER // not win32 only 19990829 by Kin
- // used for the hardware render
- if (rendermode != render_soft && rendermode != render_none)
+ li->length = P_SegLength(li);
+#ifdef HWRENDER
+ if (rendermode == render_opengl)
{
- li->flength = P_SegLengthf(li);
+ li->flength = P_SegLengthFloat(li);
//Hurdler: 04/12/2000: for now, only used in hardware mode
li->lightmaps = NULL; // list of static lightmap for this seg
}
@@ -965,10 +964,6 @@ void P_ScanThings(INT16 mapnum, INT16 wadnum, INT16 lumpnum)
}
#endif
-//
-// P_LoadThings
-//
-
static void P_PrepareRawThings(UINT8 *data, size_t i)
{
mapthing_t *mt;
@@ -1011,7 +1006,7 @@ static void P_PrepareThings(lumpnum_t lumpnum)
Z_Free(data);
}
-static void P_LoadThings(void)
+static void P_LoadThings(boolean loademblems)
{
size_t i;
mapthing_t *mt;
@@ -1036,6 +1031,9 @@ static void P_LoadThings(void)
|| mt->type == 1702) // MT_AXISTRANSFERLINE
continue; // These were already spawned
+ if (!loademblems && mt->type == mobjinfo[MT_EMBLEM].doomednum)
+ continue;
+
mt->mobj = NULL;
P_SpawnMapThing(mt);
}
@@ -1097,7 +1095,7 @@ static void P_LoadThings(void)
|| mt->type == mobjinfo[MT_REDTEAMRING].doomednum || mt->type == mobjinfo[MT_BLUETEAMRING].doomednum
|| mt->type == mobjinfo[MT_BLUESPHERE].doomednum || mt->type == mobjinfo[MT_BOMBSPHERE].doomednum
|| (mt->type >= 600 && mt->type <= 609) // circles and diagonals
- || mt->type == 1705 || mt->type == 1713 || mt->type == 1800) // hoops
+ || mt->type == 1705 || mt->type == 1713) // hoops
{
mt->mobj = NULL;
@@ -1110,65 +1108,6 @@ static void P_LoadThings(void)
}
}
-static inline void P_SpawnEmblems(void)
-{
- INT32 i, color;
- mobj_t *emblemmobj;
-
- for (i = 0; i < numemblems; i++)
- {
- if (emblemlocations[i].level != gamemap || emblemlocations[i].type > ET_SKIN)
- continue;
-
- emblemmobj = P_SpawnMobj(emblemlocations[i].x<= 'A' && emblemlocations[i].sprite <= 'Z');
- P_SetMobjStateNF(emblemmobj, emblemmobj->info->spawnstate + (emblemlocations[i].sprite - 'A'));
-
- emblemmobj->health = i+1;
- color = M_GetEmblemColor(&emblemlocations[i]);
-
- emblemmobj->color = (UINT8)color;
-
- if (emblemlocations[i].collected
- || (emblemlocations[i].type == ET_SKIN && emblemlocations[i].var != players[0].skin))
- {
- P_UnsetThingPosition(emblemmobj);
- emblemmobj->flags |= MF_NOCLIP;
- emblemmobj->flags &= ~MF_SPECIAL;
- emblemmobj->flags |= MF_NOBLOCKMAP;
- emblemmobj->frame |= (tr_trans50<frame &= ~FF_TRANSMASK;
-
- if (emblemlocations[i].type == ET_GLOBAL)
- {
- emblemmobj->reactiontime = emblemlocations[i].var;
- if (emblemlocations[i].var & GE_NIGHTSITEM)
- {
- emblemmobj->flags |= MF_NIGHTSITEM;
- emblemmobj->flags &= ~MF_SPECIAL;
- emblemmobj->flags2 |= MF2_DONTDRAW;
- }
- }
- }
- }
-}
-
-static void P_SpawnSecretItems(boolean loademblems)
-{
- // Now let's spawn those funky emblem things! Tails 12-08-2002
- if (netgame || multiplayer || (modifiedgame && !savemoddata)) // No cheating!!
- return;
-
- if (loademblems)
- P_SpawnEmblems();
-}
-
// Experimental groovy write function!
void P_WriteThings(lumpnum_t lumpnum)
{
@@ -1492,19 +1431,33 @@ static void P_LoadRawSideDefs2(void *data)
{
M_Memcpy(process,msd->bottomtexture,8);
process[8] = '\0';
- sd->bottomtexture = get_number(process)-1;
+ sd->bottomtexture = get_number(process);
}
- M_Memcpy(process,msd->toptexture,8);
- process[8] = '\0';
- sd->text = Z_Malloc(7, PU_LEVEL, NULL);
- // If they type in O_ or D_ and their music name, just shrug,
- // then copy the rest instead.
- if ((process[0] == 'O' || process[0] == 'D') && process[7])
- M_Memcpy(sd->text, process+2, 6);
- else // Assume it's a proper music name.
- M_Memcpy(sd->text, process, 6);
- sd->text[6] = 0;
+ if (!(msd->midtexture[0] == '-' && msd->midtexture[1] == '\0') || msd->midtexture[1] != '\0')
+ {
+ M_Memcpy(process,msd->midtexture,8);
+ process[8] = '\0';
+ sd->midtexture = get_number(process);
+ }
+
+ // always process if back sidedef, because we need that - symbol
+ sd->text = Z_Malloc(7, PU_LEVEL, NULL);
+ if (i == 1 || msd->toptexture[0] != '-' || msd->toptexture[1] != '\0')
+ {
+ M_Memcpy(process,msd->toptexture,8);
+ process[8] = '\0';
+
+ // If they type in O_ or D_ and their music name, just shrug,
+ // then copy the rest instead.
+ if ((process[0] == 'O' || process[0] == 'D') && process[7])
+ M_Memcpy(sd->text, process+2, 6);
+ else // Assume it's a proper music name.
+ M_Memcpy(sd->text, process, 6);
+ sd->text[6] = 0;
+ }
+ else
+ sd->text[0] = 0;
break;
}
@@ -1966,7 +1919,7 @@ static boolean P_LoadRawBlockMap(UINT8 *data, size_t count, const char *lumpname
if (!count || count >= 0x20000)
return false;
- CONS_Printf("Reading blockmap lump for pk3...\n");
+ //CONS_Printf("Reading blockmap lump for pk3...\n");
// no need to malloc anything, assume the data is uncompressed for now
count /= 2;
@@ -2368,14 +2321,11 @@ void P_LoadThingsOnly(void)
}
else // phew it's just a WAD
P_PrepareThings(lastloadedmaplumpnum + ML_THINGS);
-
- P_LoadThings();
-
+ P_LoadThings(true);
// restore skybox viewpoint/centerpoint if necessary, set them to defaults if we can't do that
skyboxmo[0] = skyboxviewpnts[(viewid >= 0) ? viewid : 0];
skyboxmo[1] = skyboxcenterpnts[(centerid >= 0) ? centerid : 0];
- P_SpawnSecretItems(true);
}
/** Compute MD5 message digest for bytes read from memory source
@@ -2867,7 +2817,6 @@ boolean P_SetupLevel(boolean skipprecip)
P_MakeMapMD5(lastloadedmaplumpnum, &mapmd5);
-
// HACK ALERT: Cache the WAD, get the map data into the tables, free memory.
// As it is implemented right now, we're assuming an uncompressed WAD.
// (As in, a normal PWAD, not ZWAD or anything. The lump itself can be compressed.)
@@ -2974,12 +2923,10 @@ boolean P_SetupLevel(boolean skipprecip)
P_ResetDynamicSlopes();
#endif
- P_LoadThings();
+ P_LoadThings(loademblems);
skyboxmo[0] = skyboxviewpnts[0];
skyboxmo[1] = skyboxcenterpnts[0];
- P_SpawnSecretItems(loademblems);
-
for (numcoopstarts = 0; numcoopstarts < MAXPLAYERS; numcoopstarts++)
if (!playerstarts[numcoopstarts])
break;
@@ -3355,6 +3302,8 @@ boolean P_AddWadFile(const char *wadfilename)
UINT16 numlumps, wadnum;
char *name;
lumpinfo_t *lumpinfo;
+
+ //boolean texturechange = false; ///\todo Useless; broken when back-frontporting PK3 changes?
boolean mapsadded = false;
boolean replacedcurrentmap = false;
@@ -3372,7 +3321,7 @@ boolean P_AddWadFile(const char *wadfilename)
// UINT16 mapPos, mapNum = 0;
// Init file.
- if ((numlumps = W_InitFile(wadfilename)) == INT16_MAX)
+ if ((numlumps = W_InitFile(wadfilename, false)) == INT16_MAX)
{
refreshdirmenu |= REFRESHDIR_NOTLOADED;
CONS_Printf(M_GetText("Errors occurred while loading %s; not added.\n"), wadfilename);
@@ -3463,7 +3412,10 @@ boolean P_AddWadFile(const char *wadfilename)
if (!devparm && digmreplaces)
CONS_Printf(M_GetText("%s digital musics replaced\n"), sizeu1(digmreplaces));
- // Search for sprite replacements.
+
+ //
+ // search for sprite replacements
+ //
R_AddSpriteDefs(wadnum);
// Reload it all anyway, just in case they
diff --git a/src/p_setup.h b/src/p_setup.h
index 158e0110b..7e8a5d7e6 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_sight.c b/src/p_sight.c
index 132f993cf..556041585 100644
--- a/src/p_sight.c
+++ b/src/p_sight.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_slopes.c b/src/p_slopes.c
index b7cd597aa..5a6874196 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2004 by Stephen McGranahan
-// Copyright (C) 2015-2016 by Sonic Team Junior.
+// Copyright (C) 2015-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -264,7 +264,7 @@ void P_SpawnSlope_Line(int linenum)
if(!line->frontsector || !line->backsector)
{
- CONS_Printf("P_SpawnSlope_Line used on a line without two sides.\n");
+ CONS_Debug(DBG_SETUP, "P_SpawnSlope_Line used on a line without two sides. (line number %i)\n", linenum);
return;
}
diff --git a/src/p_slopes.h b/src/p_slopes.h
index 252bbdb3c..b802ec25f 100644
--- a/src/p_slopes.h
+++ b/src/p_slopes.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2004 by Stephen McGranahan
-// Copyright (C) 2015-2016 by Sonic Team Junior.
+// Copyright (C) 2015-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_spec.c b/src/p_spec.c
index 57380c8b5..f8aefb9c8 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -1412,6 +1412,34 @@ static boolean PolyDisplace(line_t *line)
return EV_DoPolyObjDisplace(&pdd);
}
+
+/** Similar to PolyDisplace().
+ */
+static boolean PolyRotDisplace(line_t *line)
+{
+ polyrotdisplacedata_t pdd;
+ fixed_t anginter, distinter;
+
+ pdd.polyObjNum = line->tag;
+ pdd.controlSector = line->frontsector;
+
+ // Rotate 'anginter' interval for each 'distinter' interval from the control sector.
+ // Use default values if not provided as fallback.
+ anginter = sides[line->sidenum[0]].rowoffset ? sides[line->sidenum[0]].rowoffset : 90*FRACUNIT;
+ distinter = sides[line->sidenum[0]].textureoffset ? sides[line->sidenum[0]].textureoffset : 128*FRACUNIT;
+ pdd.rotscale = FixedDiv(anginter, distinter);
+
+ // Same behavior as other rotators when carrying things.
+ if (line->flags & ML_NOCLIMB)
+ pdd.turnobjs = 0;
+ else if (line->flags & ML_EFFECT4)
+ pdd.turnobjs = 2;
+ else
+ pdd.turnobjs = 1;
+
+ return EV_DoPolyObjRotDisplace(&pdd);
+}
+
#endif // ifdef POLYOBJECTS
/** Changes a sector's tag.
@@ -2593,16 +2621,68 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
// console player only unless NOCLIMB is set
if ((line->flags & ML_NOCLIMB) || (mo && mo->player && P_IsLocalPlayer(mo->player)) || titlemapinaction)
{
- UINT16 tracknum = (UINT16)sides[line->sidenum[0]].bottomtexture;
+ boolean musicsame = (!sides[line->sidenum[0]].text[0] || !strnicmp(sides[line->sidenum[0]].text, S_MusicName(), 7));
+ UINT16 tracknum = (UINT16)max(sides[line->sidenum[0]].bottomtexture, 0);
+ INT32 position = (INT32)max(sides[line->sidenum[0]].midtexture, 0);
+ UINT32 prefadems = (UINT32)max(sides[line->sidenum[0]].textureoffset >> FRACBITS, 0);
+ UINT32 postfadems = (UINT32)max(sides[line->sidenum[0]].rowoffset >> FRACBITS, 0);
+ UINT8 fadetarget = (UINT8)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].textureoffset >> FRACBITS : 0, 0);
+ INT16 fadesource = (INT16)max((line->sidenum[1] != 0xffff) ? sides[line->sidenum[1]].rowoffset >> FRACBITS : -1, -1);
- strncpy(mapmusname, sides[line->sidenum[0]].text, 7);
- mapmusname[6] = 0;
+ // Seek offset from current song position
+ if (line->flags & ML_EFFECT1)
+ {
+ // adjust for loop point if subtracting
+ if (position < 0 && S_GetMusicLength() &&
+ S_GetMusicPosition() > S_GetMusicLoopPoint() &&
+ S_GetMusicPosition() + position < S_GetMusicLoopPoint())
+ position = max(S_GetMusicLength() - (S_GetMusicLoopPoint() - (S_GetMusicPosition() + position)), 0);
+ else
+ position = max(S_GetMusicPosition() + position, 0);
+ }
- mapmusflags = tracknum & MUSIC_TRACKMASK;
- if (!(line->flags & ML_BLOCKMONSTERS))
- mapmusflags |= MUSIC_RELOADRESET;
+ // Fade current music to target volume (if music won't be changed)
+ if ((line->flags & ML_EFFECT2) && fadetarget && musicsame)
+ {
+ // 0 fadesource means fade from current volume.
+ // meaning that we can't specify volume 0 as the source volume -- this starts at 1.
+ if (!fadesource)
+ fadesource = -1;
- S_ChangeMusic(mapmusname, mapmusflags, !(line->flags & ML_EFFECT4));
+ if (!postfadems)
+ S_SetInternalMusicVolume(fadetarget);
+ else
+ S_FadeMusicFromVolume(fadetarget, fadesource, postfadems);
+
+ if (position)
+ S_SetMusicPosition(position);
+ }
+ // Change the music and apply position/fade operations
+ else
+ {
+ strncpy(mapmusname, sides[line->sidenum[0]].text, 7);
+ mapmusname[6] = 0;
+
+ mapmusflags = tracknum & MUSIC_TRACKMASK;
+ if (!(line->flags & ML_BLOCKMONSTERS))
+ mapmusflags |= MUSIC_RELOADRESET;
+ if (line->flags & ML_BOUNCY)
+ mapmusflags |= MUSIC_FORCERESET;
+
+ mapmusposition = position;
+
+ S_ChangeMusicEx(mapmusname, mapmusflags, !(line->flags & ML_EFFECT4), position,
+ !(line->flags & ML_EFFECT2) ? prefadems : 0,
+ !(line->flags & ML_EFFECT2) ? postfadems : 0);
+
+ if ((line->flags & ML_EFFECT2) && fadetarget)
+ {
+ if (!postfadems)
+ S_SetInternalMusicVolume(fadetarget);
+ else
+ S_FadeMusicFromVolume(fadetarget, fadesource, postfadems);
+ }
+ }
// Except, you can use the ML_BLOCKMONSTERS flag to change this behavior.
// if (mapmusflags & MUSIC_RELOADRESET) then it will reset the music in G_PlayerReborn.
@@ -7319,6 +7399,10 @@ void P_SpawnSpecials(INT32 fromnetsave)
case 31: // Polyobj_Displace
PolyDisplace(&lines[i]);
break;
+
+ case 32: // Polyobj_RotDisplace
+ PolyRotDisplace(&lines[i]);
+ break;
}
}
#endif
@@ -7662,11 +7746,22 @@ static void P_SpawnScrollers(void)
for (i = 0; i < numlines; i++, l++)
{
- fixed_t dx = l->dx >> SCROLL_SHIFT; // direction and speed of scrolling
- fixed_t dy = l->dy >> SCROLL_SHIFT;
+ fixed_t dx = l->dx; // direction and speed of scrolling
+ fixed_t dy = l->dy;
INT32 control = -1, accel = 0; // no control sector or acceleration
INT32 special = l->special;
+ // If front texture X offset provided, override the amount with it.
+ if (sides[l->sidenum[0]].textureoffset != 0)
+ {
+ fixed_t len = sides[l->sidenum[0]].textureoffset;
+ fixed_t h = FixedHypot(dx, dy);
+ dx = FixedMul(FixedDiv(dx, h), len);
+ dy = FixedMul(FixedDiv(dy, h), len);
+ }
+ dx = dx >> SCROLL_SHIFT;
+ dy = dy >> SCROLL_SHIFT;
+
// These types are same as the ones they get set to except that the
// first side's sector's heights cause scrolling when they change, and
// this linedef controls the direction and speed of the scrolling. The
@@ -7701,8 +7796,14 @@ static void P_SpawnScrollers(void)
case 513: // scroll effect ceiling
case 533: // scroll and carry objects on ceiling
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Scroller(sc_ceiling, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB);
+ if (l->tag == 0)
+ Add_Scroller(sc_ceiling, -dx, dy, control, l->frontsector - sectors, accel, l->flags & ML_NOCLIMB);
+ else
+ {
+ for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
+ Add_Scroller(sc_ceiling, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB);
+ }
+
if (special != 533)
break;
/* FALLTHRU */
@@ -7710,14 +7811,26 @@ static void P_SpawnScrollers(void)
case 523: // carry objects on ceiling
dx = FixedMul(dx, CARRYFACTOR);
dy = FixedMul(dy, CARRYFACTOR);
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Scroller(sc_carry_ceiling, dx, dy, control, s, accel, l->flags & ML_NOCLIMB);
+
+ if (l->tag == 0)
+ Add_Scroller(sc_carry_ceiling, dx, dy, control, l->frontsector - sectors, accel, l->flags & ML_NOCLIMB);
+ else
+ {
+ for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
+ Add_Scroller(sc_carry_ceiling, dx, dy, control, s, accel, l->flags & ML_NOCLIMB);
+ }
break;
case 510: // scroll effect floor
case 530: // scroll and carry objects on floor
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Scroller(sc_floor, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB);
+ if (l->tag == 0)
+ Add_Scroller(sc_floor, -dx, dy, control, l->frontsector - sectors, accel, l->flags & ML_NOCLIMB);
+ else
+ {
+ for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
+ Add_Scroller(sc_floor, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB);
+ }
+
if (special != 530)
break;
/* FALLTHRU */
@@ -7725,8 +7838,14 @@ static void P_SpawnScrollers(void)
case 520: // carry objects on floor
dx = FixedMul(dx, CARRYFACTOR);
dy = FixedMul(dy, CARRYFACTOR);
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Scroller(sc_carry, dx, dy, control, s, accel, l->flags & ML_NOCLIMB);
+
+ if (l->tag == 0)
+ Add_Scroller(sc_carry, dx, dy, control, l->frontsector - sectors, accel, l->flags & ML_NOCLIMB);
+ else
+ {
+ for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
+ Add_Scroller(sc_carry, dx, dy, control, s, accel, l->flags & ML_NOCLIMB);
+ }
break;
// scroll wall according to linedef
@@ -9089,43 +9208,77 @@ static void P_SpawnPushers(void)
line_t *l = lines;
register INT32 s;
mobj_t *thing;
+ pushertype_e pushertype;
+ fixed_t dx, dy;
for (i = 0; i < numlines; i++, l++)
+ {
switch (l->special)
{
- case 541: // wind
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Pusher(p_wind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
- break;
- case 544: // current
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Pusher(p_current, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
- break;
- case 547: // push/pull
+ case 541: // wind
+ pushertype = p_wind;
+ break;
+
+ case 544: // current
+ pushertype = p_current;
+ break;
+ case 547: // push/pull
+ if (l->tag == 0)
+ {
+ s = l->frontsector - sectors;
+ if ((thing = P_GetPushThing(s)) != NULL) // No MT_P* means no effect
+ Add_Pusher(p_push, l->dx, l->dy, thing, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
+ }
+ else
for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
{
- thing = P_GetPushThing(s);
- if (thing) // No MT_P* means no effect
+ if ((thing = P_GetPushThing(s)) != NULL) // No MT_P* means no effect
Add_Pusher(p_push, l->dx, l->dy, thing, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
}
- break;
- case 545: // current up
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Pusher(p_upcurrent, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
- break;
- case 546: // current down
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Pusher(p_downcurrent, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
- break;
- case 542: // wind up
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Pusher(p_upwind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
- break;
- case 543: // wind down
- for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
- Add_Pusher(p_downwind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
- break;
+
+ continue;
+
+ case 545: // current up
+ pushertype = p_upcurrent;
+ break;
+
+ case 546: // current down
+ pushertype = p_downcurrent;
+ break;
+
+ case 542: // wind up
+ pushertype = p_upwind;
+ break;
+
+ case 543: // wind down
+ pushertype = p_downwind;
+ break;
+
+ default:
+ continue;
}
+
+ dx = l->dx;
+ dy = l->dy;
+
+ // Obtain versor and scale it up according to texture offset, if provided; line length is ignored in this case.
+
+ if (sides[l->sidenum[0]].textureoffset != 0)
+ {
+ fixed_t len = sides[l->sidenum[0]].textureoffset;
+ fixed_t h = FixedHypot(dx, dy);
+ dx = FixedMul(FixedDiv(dx, h), len);
+ dy = FixedMul(FixedDiv(dy, h), len);
+ }
+
+ if (l->tag == 0)
+ Add_Pusher(pushertype, dx, dy, NULL, l->frontsector - sectors, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
+ else
+ {
+ for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
+ Add_Pusher(pushertype, dx, dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4);
+ }
+ }
}
static void P_SearchForDisableLinedefs(void)
diff --git a/src/p_spec.h b/src/p_spec.h
index f04e6590f..d7176afee 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_telept.c b/src/p_telept.c
index f70c4664c..2e070d14b 100644
--- a/src/p_telept.c
+++ b/src/p_telept.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_tick.c b/src/p_tick.c
index eb47e0c36..6f7c96ead 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_tick.h b/src/p_tick.h
index 75868fdd8..169c54c8e 100644
--- a/src/p_tick.h
+++ b/src/p_tick.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/p_user.c b/src/p_user.c
index 3a8b09c76..ca14c64d4 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -629,6 +629,14 @@ static void P_DeNightserizePlayer(player_t *player)
player->marescore = player->spheres =\
player->rings = 0;
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
+
+ // Reset music to beginning if MIXNIGHTSCOUNTDOWN
+ if ((mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
+#ifdef _WIN32
+ && S_MusicType() != MU_MID
+#endif
+ )
+ S_SetMusicPosition(0);
}
break;
@@ -639,7 +647,24 @@ static void P_DeNightserizePlayer(player_t *player)
player->oldscale = 0;
// Restore from drowning music
- P_RestoreMusic(player);
+ if ((mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
+#ifdef _WIN32
+ && S_MusicType() != MU_MID
+#endif
+ )
+ {
+ S_StopSoundByNum(sfx_timeup);
+ S_StopFadingMusic();
+ S_SetInternalMusicVolume(100);
+
+ // Reset the music if you did not destroy all the capsules, because you failed.
+ // Why make the all-capsules exception: because it's your reward for nearly finishing the level!
+ // (unless the player auto-loses upon denightserizing; for death case, see above.)
+ if (P_FindLowestMare() != UINT8_MAX || G_IsSpecialStage(gamemap))
+ S_SetMusicPosition(0);
+ }
+ else
+ P_RestoreMusic(player);
P_RunDeNightserizeExecutors(player->mo);
}
@@ -687,7 +712,16 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
player->nightstime = player->startedtime = player->lapstartedtime = nighttime*TICRATE;
player->bonustime = false;
- P_RestoreMusic(player);
+ // Restore from drowning music
+ if (mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
+ {
+ S_StopSoundByNum(sfx_timeup);
+ S_StopFadingMusic();
+ S_SetInternalMusicVolume(100);
+ }
+ else
+ P_RestoreMusic(player);
+
P_SetPlayerMobjState(player->mo, S_PLAY_NIGHTS_TRANS1);
if (gametype == GT_RACE || gametype == GT_COMPETITION)
@@ -1292,13 +1326,13 @@ void P_RestoreMusic(player_t *player)
if (mapheaderinfo[gamemap-1]->levelflags & LF_SPEEDMUSIC)
{
S_SpeedMusic(1.4f);
- S_ChangeMusic(mapmusname, mapmusflags, true);
+ S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
}
else
S_ChangeMusicInternal("_shoes", true);
}
else
- S_ChangeMusic(mapmusname, mapmusflags, true);
+ S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
}
//
@@ -6234,15 +6268,17 @@ static void P_DoNiGHTSCapsule(player_t *player)
//
static void P_MoveNiGHTSToDrone(player_t *player)
{
+ boolean flip, topaligned, middlealigned, bottomoffsetted;
+ fixed_t droneboxmandiff, zofs;
+
if (!player->drone)
return;
- boolean flip = player->drone->flags2 & MF2_OBJECTFLIP;
- boolean topaligned = (player->drone->flags & MF_SLIDEME) && !(player->drone->flags & MF_GRENADEBOUNCE);
- boolean middlealigned = (player->drone->flags & MF_GRENADEBOUNCE) && !(player->drone->flags & MF_SLIDEME);
- boolean bottomoffsetted = !(player->drone->flags & MF_SLIDEME) && !(player->drone->flags & MF_GRENADEBOUNCE);
- fixed_t droneboxmandiff = max(player->drone->height - player->mo->height, 0);
- fixed_t zofs;
+ flip = player->drone->flags2 & MF2_OBJECTFLIP;
+ topaligned = (player->drone->flags & MF_SLIDEME) && !(player->drone->flags & MF_GRENADEBOUNCE);
+ middlealigned = (player->drone->flags & MF_GRENADEBOUNCE) && !(player->drone->flags & MF_SLIDEME);
+ bottomoffsetted = !(player->drone->flags & MF_SLIDEME) && !(player->drone->flags & MF_GRENADEBOUNCE);
+ droneboxmandiff = max(player->drone->height - player->mo->height, 0);
if (!flip)
{
@@ -6342,14 +6378,18 @@ static void P_NiGHTSMovement(player_t *player)
{
P_DeNightserizePlayer(player);
S_StartScreamSound(player->mo, sfx_s3k66);
-// S_StopSoundByNum(sfx_timeup); // Kill the "out of time" music, if it's playing. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
- P_RestoreMusic(player); // I have my doubts that this is the right place for this...
-
return;
}
else if (P_IsLocalPlayer(player) && player->nightstime == 10*TICRATE)
-// S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS. Dummied out, as some on the dev team thought it wasn't Sonic-y enough (Mystic, notably). Uncomment to restore. -SH
- S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false);
+ {
+ if (mapheaderinfo[gamemap-1]->levelflags & LF_MIXNIGHTSCOUNTDOWN)
+ {
+ S_FadeMusic(0, 10*MUSICRATE);
+ S_StartSound(NULL, sfx_timeup); // that creepy "out of time" music from NiGHTS.
+ }
+ else
+ S_ChangeMusicInternal((((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)) ? "_ntime" : "_drown"), false);
+ }
if (player->mo->z < player->mo->floorz)
player->mo->z = player->mo->floorz;
@@ -8689,6 +8729,9 @@ static void P_DeathThink(player_t *player)
if (player->deadtimer < INT32_MAX)
player->deadtimer++;
+ if (player->bot) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already
+ goto notrealplayer;
+
// continue logic
if (!(netgame || multiplayer) && player->lives <= 0)
{
@@ -8815,6 +8858,8 @@ static void P_DeathThink(player_t *player)
P_RestoreMultiMusic(player);
}
+notrealplayer:
+
if (!player->mo)
return;
@@ -8912,7 +8957,7 @@ void P_ResetCamera(player_t *player, camera_t *thiscam)
boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled)
{
angle_t angle = 0, focusangle = 0, focusaiming = 0;
- fixed_t x, y, z, dist, height, checkdist, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight;
+ fixed_t x, y, z, dist, checkdist, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight;
INT32 camrotate;
boolean camstill, cameranoclip;
mobj_t *mo;
@@ -9092,8 +9137,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
}
- height = camheight;
-
// sets ideal cam pos
if (twodlevel || (mo->flags2 & MF2_TWOD))
dist = 480<climbing || player->exiting || player->playerstate == PST_DEAD || (player->powers[pw_carry] == CR_ROPEHANG || player->powers[pw_carry] == CR_GENERIC || player->powers[pw_carry] == CR_MACESPIN))
@@ -9165,9 +9208,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
pviewheight = FixedMul(41*player->height/48, mo->scale);
if (mo->eflags & MFE_VERTICALFLIP)
- z = mo->z + mo->height - pviewheight - height;
+ z = mo->z + mo->height - pviewheight - camheight;
else
- z = mo->z + pviewheight + height;
+ z = mo->z + pviewheight + camheight;
// move camera down to move under lower ceilings
newsubsec = R_IsPointInSubsector(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1));
@@ -9365,7 +9408,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
if (mo->type == MT_EGGTRAP)
- z = mo->z + 128*FRACUNIT + pviewheight + height;
+ z = mo->z + 128*FRACUNIT + pviewheight + camheight;
if (thiscam->z < thiscam->floorz && !cameranoclip)
thiscam->z = thiscam->floorz;
@@ -9442,7 +9485,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (!(multiplayer || netgame) && !splitscreen)
{
fixed_t vx = thiscam->x, vy = thiscam->y;
- if (player->awayviewtics && player->awayviewmobj != NULL) // Camera must obviously exist
+ if (player->awayviewtics && player->awayviewmobj != NULL && !P_MobjWasRemoved(player->awayviewmobj)) // Camera must obviously exist
{
vx = player->awayviewmobj->x;
vy = player->awayviewmobj->y;
@@ -9598,7 +9641,7 @@ static void P_CalcPostImg(player_t *player)
else
pviewheight = player->mo->z + player->viewheight;
- if (player->awayviewtics)
+ if (player->awayviewtics && player->awayviewmobj && !P_MobjWasRemoved(player->awayviewmobj))
{
sector = player->awayviewmobj->subsector->sector;
pviewheight = player->awayviewmobj->z + 20*FRACUNIT;
@@ -9733,8 +9776,11 @@ void P_PlayerThink(player_t *player)
if (player->bot)
{
- if (player->playerstate == PST_LIVE && B_CheckRespawn(player))
- player->playerstate = PST_REBORN;
+ if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD)
+ {
+ if (B_CheckRespawn(player))
+ player->playerstate = PST_REBORN;
+ }
if (player->playerstate == PST_REBORN)
return;
}
@@ -9766,12 +9812,23 @@ void P_PlayerThink(player_t *player)
}
#endif
+ if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
+ {
+ P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid
+ player->awayviewtics = 0; // reset to zero
+ }
+
if (player->flashcount)
player->flashcount--;
- // By the time P_MoveChaseCamera is called, this might be zero. Do not do it here.
- //if (player->awayviewtics)
- // player->awayviewtics--;
+ // Re-fixed by Jimita (11-12-2018)
+ if (player->awayviewtics)
+ {
+ player->awayviewtics--;
+ if (!player->awayviewtics)
+ player->awayviewtics = -1;
+ // The timer might've reached zero, but we'll run the remote view camera anyway by setting it to -1.
+ }
/// \note do this in the cheat code
if (player->pflags & PF_NOCLIP)
@@ -10744,8 +10801,8 @@ void P_PlayerAfterThink(player_t *player)
}
}
- if (player->awayviewtics)
- player->awayviewtics--;
+ if (player->awayviewtics < 0)
+ player->awayviewtics = 0;
// spectator invisibility and nogravity.
if ((netgame || multiplayer) && player->spectator)
diff --git a/src/r_bsp.c b/src/r_bsp.c
index cbb012b28..22abaeb88 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -388,18 +388,18 @@ static void R_AddLine(seg_t *line)
{
INT32 x1, x2;
angle_t angle1, angle2, span, tspan;
- static sector_t tempsec; // ceiling/water hack
+ static sector_t tempsec;
boolean bothceilingssky = false, bothfloorssky = false;
+ portalline = false;
+
if (line->polyseg && !(line->polyseg->flags & POF_RENDERSIDES))
return;
+ // big room fix
+ angle1 = R_PointToAngleEx(viewx, viewy, line->v1->x, line->v1->y);
+ angle2 = R_PointToAngleEx(viewx, viewy, line->v2->x, line->v2->y);
curline = line;
- portalline = false;
-
- // OPTIMIZE: quickly reject orthogonal back sides.
- angle1 = R_PointToAngle(line->v1->x, line->v1->y);
- angle2 = R_PointToAngle(line->v2->x, line->v2->y);
// Clip to view edges.
span = angle1 - angle2;
@@ -608,69 +608,35 @@ INT32 checkcoord[12][4] =
{2, 1, 3, 0}
};
-static boolean R_CheckBBox(fixed_t *bspcoord)
+static boolean R_CheckBBox(const fixed_t *bspcoord)
{
- INT32 boxpos, sx1, sx2;
- fixed_t px1, py1, px2, py2;
- angle_t angle1, angle2, span, tspan;
+ angle_t angle1, angle2;
+ INT32 sx1, sx2, boxpos;
+ const INT32* check;
cliprange_t *start;
// Find the corners of the box that define the edges from current viewpoint.
- if (viewx <= bspcoord[BOXLEFT])
- boxpos = 0;
- else if (viewx < bspcoord[BOXRIGHT])
- boxpos = 1;
- else
- boxpos = 2;
-
- if (viewy >= bspcoord[BOXTOP])
- boxpos |= 0;
- else if (viewy > bspcoord[BOXBOTTOM])
- boxpos |= 1<<2;
- else
- boxpos |= 2<<2;
-
- if (boxpos == 5)
+ if ((boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 : viewx < bspcoord[BOXRIGHT] ? 1 : 2) + (viewy >= bspcoord[BOXTOP] ? 0 : viewy > bspcoord[BOXBOTTOM] ? 4 : 8)) == 5)
return true;
- px1 = bspcoord[checkcoord[boxpos][0]];
- py1 = bspcoord[checkcoord[boxpos][1]];
- px2 = bspcoord[checkcoord[boxpos][2]];
- py2 = bspcoord[checkcoord[boxpos][3]];
+ check = checkcoord[boxpos];
- // check clip list for an open space
- angle1 = R_PointToAngle2(viewx>>1, viewy>>1, px1>>1, py1>>1) - viewangle;
- angle2 = R_PointToAngle2(viewx>>1, viewy>>1, px2>>1, py2>>1) - viewangle;
+ // big room fix
+ angle1 = R_PointToAngleEx(viewx, viewy, bspcoord[check[0]], bspcoord[check[1]]) - viewangle;
+ angle2 = R_PointToAngleEx(viewx, viewy, bspcoord[check[2]], bspcoord[check[3]]) - viewangle;
- span = angle1 - angle2;
-
- // Sitting on a line?
- if (span >= ANGLE_180)
- return true;
-
- tspan = angle1 + clipangle;
-
- if (tspan > doubleclipangle)
+ if ((signed)angle1 < (signed)angle2)
{
- tspan -= doubleclipangle;
-
- // Totally off the left edge?
- if (tspan >= span)
- return false;
-
- angle1 = clipangle;
+ if ((angle1 >= ANGLE_180) && (angle1 < ANGLE_270))
+ angle1 = ANGLE_180-1;
+ else
+ angle2 = ANGLE_180;
}
- tspan = clipangle - angle2;
- if (tspan > doubleclipangle)
- {
- tspan -= doubleclipangle;
- // Totally off the left edge?
- if (tspan >= span)
- return false;
-
- angle2 = -(signed)clipangle;
- }
+ if ((signed)angle2 >= (signed)clipangle) return false;
+ if ((signed)angle1 <= -(signed)clipangle) return false;
+ if ((signed)angle1 >= (signed)clipangle) angle1 = clipangle;
+ if ((signed)angle2 <= -(signed)clipangle) angle2 = 0-clipangle;
// Find the first clippost that touches the source post (adjacent pixels are touching).
angle1 = (angle1+ANGLE_90)>>ANGLETOFINESHIFT;
@@ -679,9 +645,7 @@ static boolean R_CheckBBox(fixed_t *bspcoord)
sx2 = viewangletox[angle2];
// Does not cross a pixel.
- if (sx1 == sx2)
- return false;
- sx2--;
+ if (sx1 >= sx2) return false;
start = solidsegs;
while (start->last < sx2)
@@ -1122,7 +1086,6 @@ static void R_Subsector(size_t num)
&& (viewz < polysec->floorheight))
{
light = R_GetPlaneLight(frontsector, polysec->floorheight, viewz < polysec->floorheight);
- light = 0;
ffloor[numffloors].plane = R_FindPlane(polysec->floorheight, polysec->floorpic,
polysec->lightlevel, polysec->floor_xoffs, polysec->floor_yoffs,
polysec->floorpic_angle-po->angle,
@@ -1152,7 +1115,6 @@ static void R_Subsector(size_t num)
&& (viewz > polysec->ceilingheight))
{
light = R_GetPlaneLight(frontsector, polysec->ceilingheight, viewz < polysec->ceilingheight);
- light = 0;
ffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic,
polysec->lightlevel, polysec->ceiling_xoffs, polysec->ceiling_yoffs, polysec->ceilingpic_angle-po->angle,
NULL, NULL, po
diff --git a/src/r_bsp.h b/src/r_bsp.h
index 80824831b..e3662e2e6 100644
--- a/src/r_bsp.h
+++ b/src/r_bsp.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_data.c b/src/r_data.c
index 08e3d91b6..838bc160c 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -1198,7 +1198,7 @@ static void R_InitExtraColormaps(void)
}
#endif
-// Search for flat name through all
+// Search for flat name.
lumpnum_t R_GetFlatNumForName(const char *name)
{
INT32 i;
@@ -1209,39 +1209,30 @@ lumpnum_t R_GetFlatNumForName(const char *name)
// Scan wad files backwards so patched flats take preference.
for (i = numwadfiles - 1; i >= 0; i--)
{
-
- if (wadfiles[i]->type == RET_PK3)
+ switch (wadfiles[i]->type)
{
- start = W_CheckNumForFolderStartPK3("Flats/", i, 0);
- if (start == INT16_MAX)
- continue;
- end = W_CheckNumForFolderEndPK3("Flats/", i, start);
- if (end == INT16_MAX)
- continue;
- }
- else // WAD type? use markers.
- {
- // Find the ranges to work with.
- start = W_CheckNumForNamePwad("F_START", (UINT16)i, 0);
- if (start == INT16_MAX)
+ case RET_WAD:
+ if ((start = W_CheckNumForNamePwad("F_START", (UINT16)i, 0)) == INT16_MAX)
{
- start = W_CheckNumForNamePwad("FF_START", (UINT16)i, 0);
- if (start == INT16_MAX)
+ if ((start = W_CheckNumForNamePwad("FF_START", (UINT16)i, 0)) == INT16_MAX)
+ continue;
+ else if ((end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start)) == INT16_MAX)
continue;
- else
- {
- end = W_CheckNumForNamePwad("FF_END", (UINT16)i, start);
- if (end == INT16_MAX)
- continue;
- }
}
else
- {
- end = W_CheckNumForNamePwad("F_END", (UINT16)i, start);
- if (end == INT16_MAX)
+ if ((end = W_CheckNumForNamePwad("F_END", (UINT16)i, start)) == INT16_MAX)
continue;
- }
+ break;
+ case RET_PK3:
+ if ((start = W_CheckNumForFolderStartPK3("Flats/", i, 0)) == INT16_MAX)
+ continue;
+ if ((end = W_CheckNumForFolderEndPK3("Flats/", i, start)) == INT16_MAX)
+ continue;
+ break;
+ default:
+ continue;
}
+
// Now find lump with specified name in that range.
lump = W_CheckNumForNamePwad(name, (UINT16)i, start);
if (lump < end)
diff --git a/src/r_data.h b/src/r_data.h
index 54857661a..b6b0a16a1 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_defs.h b/src/r_defs.h
index 0e63eec64..e7315b35c 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -589,6 +589,7 @@ typedef struct seg_s
sector_t *frontsector;
sector_t *backsector;
+ fixed_t length; // precalculated seg length
#ifdef HWRENDER
// new pointers so that AdjustSegs doesn't mess with v1/v2
void *pv1; // polyvertex_t
diff --git a/src/r_draw.c b/src/r_draw.c
index 40945f4e0..13ac691d5 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_draw.h b/src/r_draw.h
index 4a105b5b0..0c04fee2a 100644
--- a/src/r_draw.h
+++ b/src/r_draw.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_draw16.c b/src/r_draw16.c
index a922f4d0a..918dd356c 100644
--- a/src/r_draw16.c
+++ b/src/r_draw16.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_draw8.c b/src/r_draw8.c
index bda146546..8a2d37fb3 100644
--- a/src/r_draw8.c
+++ b/src/r_draw8.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_local.h b/src/r_local.h
index a3dfe7df6..1d3187750 100644
--- a/src/r_local.h
+++ b/src/r_local.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_main.c b/src/r_main.c
index ab5993c5b..f86c89b00 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -353,6 +353,29 @@ fixed_t R_PointToDist(fixed_t x, fixed_t y)
return R_PointToDist2(viewx, viewy, x, y);
}
+angle_t R_PointToAngleEx(INT32 x2, INT32 y2, INT32 x1, INT32 y1)
+{
+ INT64 dx = x1-x2;
+ INT64 dy = y1-y2;
+ if (dx < INT32_MIN || dx > INT32_MAX || dy < INT32_MIN || dy > INT32_MAX)
+ {
+ x1 = (int)(dx / 2 + x2);
+ y1 = (int)(dy / 2 + y2);
+ }
+ return (y1 -= y2, (x1 -= x2) || y1) ?
+ x1 >= 0 ?
+ y1 >= 0 ?
+ (x1 > y1) ? tantoangle[SlopeDivEx(y1,x1)] : // octant 0
+ ANGLE_90-tantoangle[SlopeDivEx(x1,y1)] : // octant 1
+ x1 > (y1 = -y1) ? 0-tantoangle[SlopeDivEx(y1,x1)] : // octant 8
+ ANGLE_270+tantoangle[SlopeDivEx(x1,y1)] : // octant 7
+ y1 >= 0 ? (x1 = -x1) > y1 ? ANGLE_180-tantoangle[SlopeDivEx(y1,x1)] : // octant 3
+ ANGLE_90 + tantoangle[SlopeDivEx(x1,y1)] : // octant 2
+ (x1 = -x1) > (y1 = -y1) ? ANGLE_180+tantoangle[SlopeDivEx(y1,x1)] : // octant 4
+ ANGLE_270-tantoangle[SlopeDivEx(x1,y1)] : // octant 5
+ 0;
+}
+
//
// R_ScaleFromGlobalAngle
// Returns the texture mapping scale for the current line (horizontal span)
@@ -549,13 +572,11 @@ void R_SetViewSize(void)
//
void R_ExecuteSetViewSize(void)
{
- fixed_t cosadj;
fixed_t dy;
INT32 i;
INT32 j;
INT32 level;
INT32 startmapl;
- INT32 aspectx; //added : 02-02-98 : for aspect ratio calc. below...
setsizeneeded = false;
@@ -595,31 +616,22 @@ void R_ExecuteSetViewSize(void)
for (i = 0; i < viewwidth; i++)
screenheightarray[i] = (INT16)viewheight;
- // setup sky scaling (uses pspriteyscale)
+ // setup sky scaling
R_SetSkyScale();
// planes
- //aspectx = (((vid.height*centerx*BASEVIDWIDTH)/BASEVIDHEIGHT)/vid.width);
- aspectx = centerx;
-
if (rendermode == render_soft)
{
// this is only used for planes rendering in software mode
- j = viewheight*4;
+ j = viewheight*16;
for (i = 0; i < j; i++)
{
- dy = ((i - viewheight*2)<>ANGLETOFINESHIFT));
- distscale[i] = FixedDiv(FRACUNIT, cosadj);
- }
-
memset(scalelight, 0xFF, sizeof(scalelight));
// Calculate the light levels to use for each level/scale combination.
@@ -732,9 +744,136 @@ static mobj_t *viewmobj;
// WARNING: a should be unsigned but to add with 2048, it isn't!
#define AIMINGTODY(a) ((FINETANGENT((2048+(((INT32)a)>>ANGLETOFINESHIFT)) & FINEMASK)*160)>>FRACBITS)
-void R_SkyboxFrame(player_t *player)
+// recalc necessary stuff for mouseaiming
+// slopes are already calculated for the full possible view (which is 4*viewheight).
+// 18/08/18: (No it's actually 16*viewheight, thanks Jimita for finding this out)
+static void R_SetupFreelook(void)
{
INT32 dy = 0;
+ if (rendermode == render_soft)
+ {
+ // clip it in the case we are looking a hardware 90 degrees full aiming
+ // (lmps, network and use F12...)
+ G_SoftwareClipAimingPitch((INT32 *)&aimingangle);
+ dy = AIMINGTODY(aimingangle) * viewwidth/BASEVIDWIDTH;
+ yslope = &yslopetab[viewheight*8 - (viewheight/2 + dy)];
+ }
+ centery = (viewheight/2) + dy;
+ centeryfrac = centery<climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode)
+ chasecam = true; // force chasecam on
+ else if (player->spectator) // no spectator chasecam
+ chasecam = false; // force chasecam off
+
+ if (chasecam && !thiscam->chase)
+ {
+ P_ResetCamera(player, thiscam);
+ thiscam->chase = true;
+ }
+ else if (!chasecam)
+ thiscam->chase = false;
+
+ viewsky = !skybox;
+ if (player->awayviewtics)
+ {
+ // cut-away view stuff
+ viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN
+ I_Assert(viewmobj != NULL);
+ viewz = viewmobj->z + 20*FRACUNIT;
+ aimingangle = player->awayviewaiming;
+ viewangle = viewmobj->angle;
+ }
+ else if (!player->spectator && chasecam)
+ // use outside cam view
+ {
+ viewmobj = NULL;
+ viewz = thiscam->z + (thiscam->height>>1);
+ aimingangle = thiscam->aiming;
+ viewangle = thiscam->angle;
+ }
+ else
+ // use the player's eyes view
+ {
+ viewz = player->viewz;
+
+ viewmobj = player->mo;
+ I_Assert(viewmobj != NULL);
+
+ aimingangle = player->aiming;
+ viewangle = viewmobj->angle;
+
+ if (!demoplayback && player->playerstate != PST_DEAD)
+ {
+ if (player == &players[consoleplayer])
+ {
+ viewangle = localangle; // WARNING: camera uses this
+ aimingangle = localaiming;
+ }
+ else if (player == &players[secondarydisplayplayer])
+ {
+ viewangle = localangle2;
+ aimingangle = localaiming2;
+ }
+ }
+ }
+ viewz += quake.z;
+
+ viewplayer = player;
+
+ if (chasecam && !player->awayviewtics && !player->spectator)
+ {
+ viewx = thiscam->x;
+ viewy = thiscam->y;
+ viewx += quake.x;
+ viewy += quake.y;
+
+ if (thiscam->subsector)
+ viewsector = thiscam->subsector->sector;
+ else
+ viewsector = R_PointInSubsector(viewx, viewy)->sector;
+ }
+ else
+ {
+ viewx = viewmobj->x;
+ viewy = viewmobj->y;
+ viewx += quake.x;
+ viewy += quake.y;
+
+ if (viewmobj->subsector)
+ viewsector = viewmobj->subsector->sector;
+ else
+ viewsector = R_PointInSubsector(viewx, viewy)->sector;
+ }
+
+ viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT);
+ viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
+
+ R_SetupFreelook();
+}
+
+void R_SkyboxFrame(player_t *player)
+{
camera_t *thiscam;
if (splitscreen && player == &players[secondarydisplayplayer]
@@ -868,144 +1007,7 @@ void R_SkyboxFrame(player_t *player)
viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT);
viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
- // recalc necessary stuff for mouseaiming
- // slopes are already calculated for the full possible view (which is 4*viewheight).
-
- if (rendermode == render_soft)
- {
- // clip it in the case we are looking a hardware 90 degrees full aiming
- // (lmps, network and use F12...)
- G_SoftwareClipAimingPitch((INT32 *)&aimingangle);
-
- dy = AIMINGTODY(aimingangle) * viewwidth/BASEVIDWIDTH;
-
- yslope = &yslopetab[(3*viewheight/2) - dy];
- }
- centery = (viewheight/2) + dy;
- centeryfrac = centery<climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode)
- chasecam = true; // force chasecam on
- else if (player->spectator) // no spectator chasecam
- chasecam = false; // force chasecam off
-
- if (chasecam && !thiscam->chase)
- {
- P_ResetCamera(player, thiscam);
- thiscam->chase = true;
- }
- else if (!chasecam)
- thiscam->chase = false;
-
- viewsky = !skybox;
- if (player->awayviewtics)
- {
- // cut-away view stuff
- viewmobj = player->awayviewmobj; // should be a MT_ALTVIEWMAN
- I_Assert(viewmobj != NULL);
- viewz = viewmobj->z + 20*FRACUNIT;
- aimingangle = player->awayviewaiming;
- viewangle = viewmobj->angle;
- }
- else if (!player->spectator && chasecam)
- // use outside cam view
- {
- viewmobj = NULL;
- viewz = thiscam->z + (thiscam->height>>1);
- aimingangle = thiscam->aiming;
- viewangle = thiscam->angle;
- }
- else
- // use the player's eyes view
- {
- viewz = player->viewz;
-
- viewmobj = player->mo;
- I_Assert(viewmobj != NULL);
-
- aimingangle = player->aiming;
- viewangle = viewmobj->angle;
-
- if (!demoplayback && player->playerstate != PST_DEAD)
- {
- if (player == &players[consoleplayer])
- {
- viewangle = localangle; // WARNING: camera uses this
- aimingangle = localaiming;
- }
- else if (player == &players[secondarydisplayplayer])
- {
- viewangle = localangle2;
- aimingangle = localaiming2;
- }
- }
- }
- viewz += quake.z;
-
- viewplayer = player;
-
- if (chasecam && !player->awayviewtics && !player->spectator)
- {
- viewx = thiscam->x;
- viewy = thiscam->y;
- viewx += quake.x;
- viewy += quake.y;
-
- if (thiscam->subsector)
- viewsector = thiscam->subsector->sector;
- else
- viewsector = R_PointInSubsector(viewx, viewy)->sector;
- }
- else
- {
- viewx = viewmobj->x;
- viewy = viewmobj->y;
- viewx += quake.x;
- viewy += quake.y;
-
- if (viewmobj->subsector)
- viewsector = viewmobj->subsector->sector;
- else
- viewsector = R_PointInSubsector(viewx, viewy)->sector;
- }
-
- viewsin = FINESINE(viewangle>>ANGLETOFINESHIFT);
- viewcos = FINECOSINE(viewangle>>ANGLETOFINESHIFT);
-
- // recalc necessary stuff for mouseaiming
- // slopes are already calculated for the full possible view (which is 4*viewheight).
-
- if (rendermode == render_soft)
- {
- // clip it in the case we are looking a hardware 90 degrees full aiming
- // (lmps, network and use F12...)
- G_SoftwareClipAimingPitch((INT32 *)&aimingangle);
-
- dy = AIMINGTODY(aimingangle) * viewwidth/BASEVIDWIDTH;
-
- yslope = &yslopetab[(3*viewheight/2) - dy];
- }
- centery = (viewheight/2) + dy;
- centeryfrac = centery<= vid.width) x1 = vid.width - 1;
+ angle = (currentplane->viewangle + currentplane->plangle)>>ANGLETOFINESHIFT;
+ planecos = FINECOSINE(angle);
+ planesin = FINESINE(angle);
+
if (planeheight != cachedheight[y])
{
cachedheight[y] = planeheight;
distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]);
ds_xstep = cachedxstep[y] = FixedMul(distance, basexscale);
ds_ystep = cachedystep[y] = FixedMul(distance, baseyscale);
+
+ if ((span = abs(centery-y)))
+ {
+ ds_xstep = cachedxstep[y] = FixedMul(planesin, planeheight) / span;
+ ds_ystep = cachedystep[y] = FixedMul(planecos, planeheight) / span;
+ }
}
else
{
@@ -301,13 +293,8 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2)
ds_ystep = cachedystep[y];
}
- length = FixedMul (distance,distscale[x1]);
- angle = (currentplane->viewangle + currentplane->plangle + xtoviewangle[x1])>>ANGLETOFINESHIFT;
- /// \note Wouldn't it be faster just to add viewx and viewy
- // to the plane's x/yoffs anyway??
-
- ds_xfrac = FixedMul(FINECOSINE(angle), length) + xoffs;
- ds_yfrac = yoffs - FixedMul(FINESINE(angle), length);
+ ds_xfrac = xoffs + FixedMul(planecos, distance) + (x1 - centerx) * ds_xstep;
+ ds_yfrac = yoffs - FixedMul(planesin, distance) + (x1 - centerx) * ds_ystep;
#ifndef NOWATER
if (itswater)
@@ -315,8 +302,9 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2)
const INT32 yay = (wtofs + (distance>>9) ) & 8191;
// ripples da water texture
bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS;
+ angle = (currentplane->viewangle + currentplane->plangle + xtoviewangle[x1])>>ANGLETOFINESHIFT;
- angle = (angle + 2048) & 8191; //90�
+ angle = (angle + 2048) & 8191; // 90 degrees
ds_xfrac += FixedMul(FINECOSINE(angle), (bgofs<> LIGHTZSHIFT;
-
if (pindex >= MAXLIGHTZ)
pindex = MAXLIGHTZ - 1;
@@ -365,8 +352,6 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2)
// R_ClearPlanes
// At begining of frame.
//
-// NOTE: Uses con_clipviewtop, so that when console is on,
-// we don't draw the part of the view hidden under the console.
void R_ClearPlanes(void)
{
INT32 i, p;
@@ -376,12 +361,12 @@ void R_ClearPlanes(void)
for (i = 0; i < viewwidth; i++)
{
floorclip[i] = (INT16)viewheight;
- ceilingclip[i] = (INT16)con_clipviewtop;
+ ceilingclip[i] = -1;
frontscale[i] = INT32_MAX;
for (p = 0; p < MAXFFLOORS; p++)
{
ffloor[p].f_clip[i] = (INT16)viewheight;
- ffloor[p].c_clip[i] = (INT16)con_clipviewtop;
+ ffloor[p].c_clip[i] = -1;
}
}
@@ -816,11 +801,7 @@ void R_DrawSinglePlane(visplane_t *pl)
else // Opaque, but allow transparent flat pixels
spanfunc = splatfunc;
-#ifdef SHITPLANESPARENCY
- if (spanfunc == splatfunc || (pl->extra_colormap && pl->extra_colormap->fog))
-#else
if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2))
-#endif
light = (pl->lightlevel >> LIGHTSEGSHIFT);
else
light = LIGHTLEVELS-1;
@@ -874,11 +855,7 @@ void R_DrawSinglePlane(visplane_t *pl)
else // Opaque, but allow transparent flat pixels
spanfunc = splatfunc;
-#ifdef SHITPLANESPARENCY
- if (spanfunc == splatfunc || (pl->extra_colormap && pl->extra_colormap->fog))
-#else
if (!pl->extra_colormap || !(pl->extra_colormap->fog & 2))
-#endif
light = (pl->lightlevel >> LIGHTSEGSHIFT);
else
light = LIGHTLEVELS-1;
diff --git a/src/r_plane.h b/src/r_plane.h
index dff58669a..6e6a6d49d 100644
--- a/src/r_plane.h
+++ b/src/r_plane.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -21,7 +21,6 @@
//
// Now what is a visplane, anyway?
// Simple: kinda floor/ceiling polygon optimised for SRB2 rendering.
-// 7764 bytes! (for win32, anyway)
//
typedef struct visplane_s
{
@@ -39,25 +38,12 @@ typedef struct visplane_s
extracolormap_t *extra_colormap;
// leave pads for [minx-1]/[maxx+1]
-
- // words sucks .. should get rid of that.. but eats memory
- // THIS IS UNSIGNED! VERY IMPORTANT!!
- UINT16 pad1;
- UINT16 top[MAXVIDWIDTH];
- UINT16 pad2;
- UINT16 pad3;
- UINT16 bottom[MAXVIDWIDTH];
- UINT16 pad4;
-
+ UINT16 padtopstart, top[MAXVIDWIDTH], padtopend;
+ UINT16 padbottomstart, bottom[MAXVIDWIDTH], padbottomend;
INT32 high, low; // R_PlaneBounds should set these.
fixed_t xoffs, yoffs; // Scrolling flats.
- // SoM: frontscale should be stored in the first seg of the subsector
- // where the planes themselves are stored. I'm doing this now because
- // the old way caused trouble with the drawseg array was re-sized.
- INT32 scaleseg;
-
struct ffloor_s *ffloor;
#ifdef POLYOBJECTS_PLANES
polyobj_t *polyobj;
@@ -75,17 +61,15 @@ extern INT16 *lastopening, *openings;
extern size_t maxopenings;
extern INT16 floorclip[MAXVIDWIDTH], ceilingclip[MAXVIDWIDTH];
-extern fixed_t frontscale[MAXVIDWIDTH], yslopetab[MAXVIDHEIGHT*4];
+extern fixed_t frontscale[MAXVIDWIDTH], yslopetab[MAXVIDHEIGHT*16];
extern fixed_t cachedheight[MAXVIDHEIGHT];
extern fixed_t cacheddistance[MAXVIDHEIGHT];
extern fixed_t cachedxstep[MAXVIDHEIGHT];
extern fixed_t cachedystep[MAXVIDHEIGHT];
extern fixed_t basexscale, baseyscale;
-extern lighttable_t **planezlight;
-
extern fixed_t *yslope;
-extern fixed_t distscale[MAXVIDWIDTH];
+extern lighttable_t **planezlight;
void R_InitPlanes(void);
void R_PortalStoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale);
@@ -134,8 +118,8 @@ typedef struct planemgr_s
#ifdef POLYOBJECTS_PLANES
polyobj_t *polyobj;
#endif
-} planemgr_t;
+} visffloor_t;
-extern planemgr_t ffloor[MAXFFLOORS];
+extern visffloor_t ffloor[MAXFFLOORS];
extern INT32 numffloors;
#endif
diff --git a/src/r_segs.c b/src/r_segs.c
index d33696729..b59d6ded2 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -885,16 +885,18 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
leftheight -= viewz;
rightheight -= viewz;
-#define OVERFLOWTEST(height, scale) \
- overflow_test = (INT64)centeryfrac - (((INT64)height*scale)>>FRACBITS); \
- if (overflow_test < 0) overflow_test = -overflow_test; \
- if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) continue;
+#define CLAMPMAX INT32_MAX
+#define CLAMPMIN (-INT32_MAX) // This is not INT32_MIN on purpose! INT32_MIN makes the drawers freak out.
+ // Monster Iestyn (25/03/18): do not skip these lights if they fail overflow test, just clamp them instead so they behave.
+ overflow_test = (INT64)centeryfrac - (((INT64)leftheight*ds->scale1)>>FRACBITS);
+ if (overflow_test > (INT64)CLAMPMAX) rlight->height = CLAMPMAX;
+ else if (overflow_test > (INT64)CLAMPMIN) rlight->height = (fixed_t)overflow_test;
+ else rlight->height = CLAMPMIN;
- OVERFLOWTEST(leftheight, ds->scale1)
- OVERFLOWTEST(rightheight, ds->scale2)
-
- rlight->height = (centeryfrac) - FixedMul(leftheight, ds->scale1);
- rlight->heightstep = (centeryfrac) - FixedMul(rightheight, ds->scale2);
+ overflow_test = (INT64)centeryfrac - (((INT64)rightheight*ds->scale2)>>FRACBITS);
+ if (overflow_test > (INT64)CLAMPMAX) rlight->heightstep = CLAMPMAX;
+ else if (overflow_test > (INT64)CLAMPMIN) rlight->heightstep = (fixed_t)overflow_test;
+ else rlight->heightstep = CLAMPMIN;
rlight->heightstep = (rlight->heightstep-rlight->height)/(range);
#else
if (light->height < *pfloor->bottomheight)
@@ -916,12 +918,16 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
leftheight -= viewz;
rightheight -= viewz;
- OVERFLOWTEST(leftheight, ds->scale1)
- OVERFLOWTEST(rightheight, ds->scale2)
-#undef OVERFLOWTEST
+ // Monster Iestyn (25/03/18): do not skip these lights if they fail overflow test, just clamp them instead so they behave.
+ overflow_test = (INT64)centeryfrac - (((INT64)leftheight*ds->scale1)>>FRACBITS);
+ if (overflow_test > (INT64)CLAMPMAX) rlight->botheight = CLAMPMAX;
+ else if (overflow_test > (INT64)CLAMPMIN) rlight->botheight = (fixed_t)overflow_test;
+ else rlight->botheight = CLAMPMIN;
- rlight->botheight = (centeryfrac) - FixedMul(leftheight, ds->scale1);
- rlight->botheightstep = (centeryfrac) - FixedMul(rightheight, ds->scale2);
+ overflow_test = (INT64)centeryfrac - (((INT64)rightheight*ds->scale2)>>FRACBITS);
+ if (overflow_test > (INT64)CLAMPMAX) rlight->botheightstep = CLAMPMAX;
+ else if (overflow_test > (INT64)CLAMPMIN) rlight->botheightstep = (fixed_t)overflow_test;
+ else rlight->botheightstep = CLAMPMIN;
rlight->botheightstep = (rlight->botheightstep-rlight->botheight)/(range);
#else
lheight = *light->caster->bottomheight;// > *pfloor->topheight ? *pfloor->topheight + FRACUNIT : *light->caster->bottomheight;
@@ -1102,9 +1108,6 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
}
#endif
-#define CLAMPMAX INT32_MAX
-#define CLAMPMIN (-INT32_MAX) // This is not INT32_MIN on purpose! INT32_MIN makes the drawers freak out.
-
// draw the columns
for (dc_x = x1; dc_x <= x2; dc_x++)
{
@@ -1669,26 +1672,11 @@ static void R_RenderSegLoop (void)
}
for (i = 0; i < numffloors; i++)
- {
-#ifdef POLYOBJECTS_PLANES
- if (ffloor[i].polyobj && (!curline->polyseg || ffloor[i].polyobj != curline->polyseg))
- continue;
-#endif
-
ffloor[i].f_frac += ffloor[i].f_step;
- }
for (i = 0; i < numbackffloors; i++)
{
- INT32 y_w;
-
-#ifdef POLYOBJECTS_PLANES
- if (ffloor[i].polyobj && (!curline->polyseg || ffloor[i].polyobj != curline->polyseg))
- continue;
-#endif
- y_w = ffloor[i].b_frac >> HEIGHTBITS;
-
- ffloor[i].f_clip[rw_x] = ffloor[i].c_clip[rw_x] = (INT16)(y_w & 0xFFFF);
+ ffloor[i].f_clip[rw_x] = ffloor[i].c_clip[rw_x] = (INT16)((ffloor[i].b_frac >> HEIGHTBITS) & 0xFFFF);
ffloor[i].b_frac += ffloor[i].b_step;
}
@@ -1698,6 +1686,23 @@ static void R_RenderSegLoop (void)
}
}
+// Uses precalculated seg->length
+static INT64 R_CalcSegDist(seg_t* seg, INT64 x2, INT64 y2)
+{
+ if (!seg->linedef->dy)
+ return llabs(y2 - seg->v1->y);
+ else if (!seg->linedef->dx)
+ return llabs(x2 - seg->v1->x);
+ else
+ {
+ INT64 dx = (seg->v2->x)-(seg->v1->x);
+ INT64 dy = (seg->v2->y)-(seg->v1->y);
+ INT64 vdx = x2-(seg->v1->x);
+ INT64 vdy = y2-(seg->v1->y);
+ return ((dy*vdx)-(dx*vdy))/(seg->length);
+ }
+}
+
//
// R_StoreWallRange
// A wall segment will be drawn
@@ -1708,6 +1713,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
fixed_t hyp;
fixed_t sineval;
angle_t distangle, offsetangle;
+ boolean longboi;
#ifndef ESLOPE
fixed_t vtop;
#endif
@@ -1753,10 +1759,15 @@ void R_StoreWallRange(INT32 start, INT32 stop)
offsetangle = ANGLE_90;
distangle = ANGLE_90 - offsetangle;
- hyp = R_PointToDist (curline->v1->x, curline->v1->y);
sineval = FINESINE(distangle>>ANGLETOFINESHIFT);
- rw_distance = FixedMul (hyp, sineval);
+ hyp = R_PointToDist(curline->v1->x, curline->v1->y);
+ rw_distance = FixedMul(hyp, sineval);
+ longboi = (hyp >= INT32_MAX);
+
+ // big room fix
+ if (longboi)
+ rw_distance = (fixed_t)R_CalcSegDist(curline,viewx,viewy);
ds_p->x1 = rw_x = start;
ds_p->x2 = stop;
@@ -2604,8 +2615,18 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (offsetangle > ANGLE_90)
offsetangle = ANGLE_90;
- sineval = FINESINE(offsetangle >>ANGLETOFINESHIFT);
- rw_offset = FixedMul (hyp, sineval);
+ sineval = FINESINE(offsetangle>>ANGLETOFINESHIFT);
+ rw_offset = FixedMul(hyp, sineval);
+
+ // big room fix
+ if (longboi)
+ {
+ INT64 dx = (curline->v2->x)-(curline->v1->x);
+ INT64 dy = (curline->v2->y)-(curline->v1->y);
+ INT64 vdx = viewx-(curline->v1->x);
+ INT64 vdy = viewy-(curline->v1->y);
+ rw_offset = ((dx*vdx-dy*vdy))/(curline->length);
+ }
if (rw_normalangle-rw_angle1 < ANGLE_180)
rw_offset = -rw_offset;
@@ -2797,20 +2818,26 @@ void R_StoreWallRange(INT32 start, INT32 stop)
{
for (i = 0; i < numffloors; i++)
{
-#ifdef POLYOBJECTS_PLANES
- if (ffloor[i].polyobj && (!curline->polyseg || ffloor[i].polyobj != curline->polyseg))
- continue;
-#endif
-
ffloor[i].f_pos >>= 4;
#ifdef ESLOPE
ffloor[i].f_pos_slope >>= 4;
- ffloor[i].f_frac = (centeryfrac>>4) - FixedMul(ffloor[i].f_pos, rw_scale);
- ffloor[i].f_step = ((centeryfrac>>4) - FixedMul(ffloor[i].f_pos_slope, ds_p->scale2) - ffloor[i].f_frac)/(range);
-#else
- ffloor[i].f_step = FixedMul(-rw_scalestep, ffloor[i].f_pos);
- ffloor[i].f_frac = (centeryfrac>>4) - FixedMul(ffloor[i].f_pos, rw_scale);
#endif
+ if (linedef->special == 41) // Horizon lines extend FOFs in contact with them too.
+ {
+ ffloor[i].f_step = 0;
+ ffloor[i].f_frac = (centeryfrac>>4);
+ topfrac++; // Prevent 1px HOM
+ }
+ else
+ {
+#ifdef ESLOPE
+ ffloor[i].f_frac = (centeryfrac>>4) - FixedMul(ffloor[i].f_pos, rw_scale);
+ ffloor[i].f_step = ((centeryfrac>>4) - FixedMul(ffloor[i].f_pos_slope, ds_p->scale2) - ffloor[i].f_frac)/(range);
+#else
+ ffloor[i].f_step = FixedMul(-rw_scalestep, ffloor[i].f_pos);
+ ffloor[i].f_frac = (centeryfrac>>4) - FixedMul(ffloor[i].f_pos, rw_scale);
+#endif
+ }
}
}
@@ -3082,7 +3109,12 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (ceilingplane) //SoM: 3/29/2000: Check for null ceiling planes
ceilingplane = R_CheckPlane (ceilingplane, rw_x, rw_stopx-1);
else
- markceiling = 0;
+ markceiling = false;
+
+ // Don't mark ceiling flat lines for polys unless this line has an upper texture, otherwise we get flat leakage pulling downward
+ // (If it DOES have an upper texture and we do this, the ceiling won't render at all)
+ if (curline->polyseg && !curline->sidedef->toptexture)
+ markceiling = false;
}
// get a new or use the same visplane
@@ -3091,7 +3123,12 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (floorplane) //SoM: 3/29/2000: Check for null planes
floorplane = R_CheckPlane (floorplane, rw_x, rw_stopx-1);
else
- markfloor = 0;
+ markfloor = false;
+
+ // Don't mark floor flat lines for polys unless this line has a lower texture, otherwise we get flat leakage pulling upward
+ // (If it DOES have a lower texture and we do this, the floor won't render at all)
+ if (curline->polyseg && !curline->sidedef->bottomtexture)
+ markfloor = false;
}
ds_p->numffloorplanes = 0;
diff --git a/src/r_segs.h b/src/r_segs.h
index 4187d36e0..92d0100e2 100644
--- a/src/r_segs.h
+++ b/src/r_segs.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_sky.c b/src/r_sky.c
index 898424a99..817e8368a 100644
--- a/src/r_sky.c
+++ b/src/r_sky.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_sky.h b/src/r_sky.h
index aa4bda375..86b615595 100644
--- a/src/r_sky.h
+++ b/src/r_sky.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_splats.c b/src/r_splats.c
index f6d7e78f3..9ab671274 100644
--- a/src/r_splats.c
+++ b/src/r_splats.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -365,9 +365,8 @@ static void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, UINT8 *pTe
#else
lighttable_t **planezlight;
fixed_t planeheight;
- angle_t angle;
- fixed_t distance;
- fixed_t length;
+ angle_t angle, planecos, planesin;
+ fixed_t distance, span;
size_t indexr;
INT32 light;
#endif
@@ -473,12 +472,22 @@ static void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, UINT8 *pTe
if (x2 >= vid.width)
x2 = vid.width - 1;
+ angle = (currentplane->viewangle + currentplane->plangle)>>ANGLETOFINESHIFT;
+ planecos = FINECOSINE(angle);
+ planesin = FINESINE(angle);
+
if (planeheight != cachedheight[y])
{
cachedheight[y] = planeheight;
distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]);
ds_xstep = cachedxstep[y] = FixedMul(distance,basexscale);
ds_ystep = cachedystep[y] = FixedMul(distance,baseyscale);
+
+ if ((span = abs(centery-y)))
+ {
+ ds_xstep = cachedxstep[y] = FixedMul(planesin, planeheight) / span;
+ ds_ystep = cachedystep[y] = FixedMul(planecos, planeheight) / span;
+ }
}
else
{
@@ -486,10 +495,9 @@ static void R_RenderFloorSplat(floorsplat_t *pSplat, vertex_t *verts, UINT8 *pTe
ds_xstep = cachedxstep[y];
ds_ystep = cachedystep[y];
}
- length = FixedMul(distance, distscale[x1]);
- angle = (viewangle + xtoviewangle[x1])>>ANGLETOFINESHIFT;
- ds_xfrac = viewx + FixedMul(FINECOSINE(angle), length);
- ds_yfrac = -viewy - FixedMul(FINESINE(angle), length);
+
+ ds_xfrac = xoffs + FixedMul(planecos, distance) + (x1 - centerx) * ds_xstep;
+ ds_yfrac = yoffs - FixedMul(planesin, distance) + (x1 - centerx) * ds_ystep;
ds_xfrac -= offsetx;
ds_yfrac += offsety;
diff --git a/src/r_splats.h b/src/r_splats.h
index 349d8fa7a..387b29582 100644
--- a/src/r_splats.h
+++ b/src/r_splats.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_state.h b/src/r_state.h
index 91c2092e9..9c8ce51d6 100644
--- a/src/r_state.h
+++ b/src/r_state.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/r_things.c b/src/r_things.c
index e2f1a896b..e8d679b53 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -380,10 +380,9 @@ void R_AddSpriteDefs(UINT16 wadnum)
char wadname[MAX_WADPATH];
// Find the sprites section in this resource file.
- if (wadfiles[wadnum]->type == RET_PK3)
- start = W_CheckNumForFolderStartPK3("Sprites/", wadnum, 0);
- else
+ switch (wadfiles[wadnum]->type)
{
+ case RET_WAD:
start = W_CheckNumForNamePwad("S_START", wadnum, 0);
if (start == INT16_MAX)
start = W_CheckNumForNamePwad("SS_START", wadnum, 0); //deutex compatib.
@@ -391,21 +390,23 @@ void R_AddSpriteDefs(UINT16 wadnum)
start = 0; //let say S_START is lump 0
else
start++; // just after S_START
+
+ end = W_CheckNumForNamePwad("S_END",wadnum,start);
+ if (end == INT16_MAX)
+ end = W_CheckNumForNamePwad("SS_END",wadnum,start); //deutex compatib.
+ break;
+ case RET_PK3:
+ start = W_CheckNumForFolderStartPK3("Sprites/", wadnum, 0);
+ end = W_CheckNumForFolderEndPK3("Sprites/", wadnum, start);
+ break;
+ default:
+ return;
}
// ignore skin wads (we don't want skin sprites interfering with vanilla sprites)
if (start == 0 && W_CheckNumForNamePwad("S_SKIN", wadnum, 0) != UINT16_MAX)
return;
- if (wadfiles[wadnum]->type == RET_PK3)
- end = W_CheckNumForFolderEndPK3("Sprites/", wadnum, start);
- else
- {
- end = W_CheckNumForNamePwad("S_END",wadnum,start);
- if (end == INT16_MAX)
- end = W_CheckNumForNamePwad("SS_END",wadnum,start); //deutex compatib.
- }
-
if (end == INT16_MAX)
{
CONS_Debug(DBG_SETUP, "no sprites in pwad %d\n", wadnum);
@@ -1868,7 +1869,7 @@ static void R_CreateDrawNodes(void)
plane = ds->curline->polyseg->visplane;
R_PlaneBounds(plane);
- if (plane->low < con_clipviewtop || plane->high > vid.height || plane->high > plane->low)
+ if (plane->low < 0 || plane->high > vid.height || plane->high > plane->low)
;
else {
// Put it in!
@@ -1897,7 +1898,7 @@ static void R_CreateDrawNodes(void)
plane = ds->ffloorplanes[p];
R_PlaneBounds(plane);
- if (plane->low < con_clipviewtop || plane->high > vid.height || plane->high > plane->low || plane->polyobj)
+ if (plane->low < 0 || plane->high > vid.height || plane->high > plane->low || plane->polyobj)
{
ds->ffloorplanes[p] = NULL;
continue;
@@ -1934,7 +1935,7 @@ static void R_CreateDrawNodes(void)
plane = PolyObjects[i].visplane;
R_PlaneBounds(plane);
- if (plane->low < con_clipviewtop || plane->high > vid.height || plane->high > plane->low)
+ if (plane->low < 0 || plane->high > vid.height || plane->high > plane->low)
{
PolyObjects[i].visplane = NULL;
continue;
@@ -2441,7 +2442,7 @@ void R_DrawMasked(void)
// ==========================================================================
INT32 numskins = 0;
-skin_t skins[MAXSKINS+1];
+skin_t skins[MAXSKINS];
// FIXTHIS: don't work because it must be inistilised before the config load
//#define SKINVALUES
#ifdef SKINVALUES
@@ -2631,7 +2632,7 @@ void SetPlayerSkin(INT32 playernum, const char *skinname)
if (P_IsLocalPlayer(player))
CONS_Alert(CONS_WARNING, M_GetText("Skin '%s' not found.\n"), skinname);
- else if(server || adminplayer == consoleplayer)
+ else if(server || IsPlayerAdmin(consoleplayer))
CONS_Alert(CONS_WARNING, M_GetText("Player %d (%s) skin '%s' not found\n"), playernum, player_names[playernum], skinname);
SetPlayerSkinByNum(playernum, 0);
@@ -2715,7 +2716,7 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
if (P_IsLocalPlayer(player))
CONS_Alert(CONS_WARNING, M_GetText("Requested skin not found\n"));
- else if(server || adminplayer == consoleplayer)
+ else if(server || IsPlayerAdmin(consoleplayer))
CONS_Alert(CONS_WARNING, "Player %d (%s) skin not found\n", playernum, player_names[playernum]);
SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin
}
@@ -2954,7 +2955,7 @@ void R_AddSkins(UINT16 wadnum)
// advance by default
lastlump = lump + 1;
- if (numskins > MAXSKINS)
+ if (numskins >= MAXSKINS)
{
CONS_Debug(DBG_RENDER, "ignored skin (%d skins maximum)\n", MAXSKINS);
continue; // so we know how many skins couldn't be added
diff --git a/src/r_things.h b/src/r_things.h
index 508da6a54..1003103ca 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -206,7 +206,7 @@ typedef struct drawnode_s
} drawnode_t;
extern INT32 numskins;
-extern skin_t skins[MAXSKINS + 1];
+extern skin_t skins[MAXSKINS];
void SetPlayerSkin(INT32 playernum,const char *skinname);
void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002
@@ -246,6 +246,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE char R_Frame2Char(UINT8 frame)
FUNCMATH FUNCINLINE static ATTRINLINE UINT8 R_Char2Frame(char cn)
{
#if 0 // 2.1 compat
+ if (cn == '+') return '\\' - 'A'; // PK3 can't use backslash, so use + instead
return cn - 'A';
#else
if (cn >= 'A' && cn <= 'Z') return cn - 'A';
diff --git a/src/s_sound.c b/src/s_sound.c
index ada4be6df..93588f081 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -39,6 +39,10 @@ extern INT32 msg_id;
#include "fastcmp.h"
#include "m_misc.h" // for tunes command
+#if defined(HAVE_BLUA) && defined(HAVE_LUA_MUSICPLUS)
+#include "lua_hook.h" // MusicChange hook
+#endif
+
#ifdef HW3SOUND
// 3D Sound Interface
#include "hardware/hw3sound.h"
@@ -51,6 +55,11 @@ static void SetChannelsNum(void);
static void Command_Tunes_f(void);
static void Command_RestartAudio_f(void);
+// Sound system toggles
+static void GameMIDIMusic_OnChange(void);
+static void GameSounds_OnChange(void);
+static void GameDigiMusic_OnChange(void);
+
// commands for music and sound servers
#ifdef MUSSERV
consvar_t musserver_cmd = {"musserver_cmd", "musserver", CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -97,6 +106,11 @@ consvar_t cv_numChannels = {"snd_channels", "32", CV_SAVE|CV_CALL, CV_Unsigned,
static consvar_t surround = {"surround", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_resetmusic = {"resetmusic", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
+// Sound system toggles, saved into the config
+consvar_t cv_gamedigimusic = {"digimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameDigiMusic_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_gamemidimusic = {"midimusic", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameMIDIMusic_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_gamesounds = {"sounds", "On", CV_SAVE|CV_CALL|CV_NOINIT, CV_OnOff, GameSounds_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
#define S_MAX_VOLUME 127
// when to clip out sounds
@@ -252,6 +266,9 @@ void S_RegisterSoundStuff(void)
CV_RegisterVar(&surround);
CV_RegisterVar(&cv_samplerate);
CV_RegisterVar(&cv_resetmusic);
+ CV_RegisterVar(&cv_gamesounds);
+ CV_RegisterVar(&cv_gamedigimusic);
+ CV_RegisterVar(&cv_gamemidimusic);
COM_AddCommand("tunes", Command_Tunes_f);
COM_AddCommand("restartaudio", Command_RestartAudio_f);
@@ -1360,6 +1377,12 @@ static void *music_data;
static UINT16 music_flags;
static boolean music_looping;
+static char queue_name[7];
+static UINT16 queue_flags;
+static boolean queue_looping;
+static UINT32 queue_position;
+static UINT32 queue_fadeinms;
+
/// ------------------------
/// Music Status
/// ------------------------
@@ -1394,6 +1417,11 @@ musictype_t S_MusicType(void)
return I_SongType();
}
+const char *S_MusicName(void)
+{
+ return music_name;
+}
+
boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping)
{
if (!I_SongPlaying())
@@ -1424,6 +1452,35 @@ boolean S_SpeedMusic(float speed)
return I_SetSongSpeed(speed);
}
+/// ------------------------
+/// Music Seeking
+/// ------------------------
+
+UINT32 S_GetMusicLength(void)
+{
+ return I_GetSongLength();
+}
+
+boolean S_SetMusicLoopPoint(UINT32 looppoint)
+{
+ return I_SetSongLoopPoint(looppoint);
+}
+
+UINT32 S_GetMusicLoopPoint(void)
+{
+ return I_GetSongLoopPoint();
+}
+
+boolean S_SetMusicPosition(UINT32 position)
+{
+ return I_SetSongPosition(position);
+}
+
+UINT32 S_GetMusicPosition(void)
+{
+ return I_GetSongPosition();
+}
+
/// ------------------------
/// Music Playback
/// ------------------------
@@ -1496,12 +1553,13 @@ static void S_UnloadMusic(void)
music_looping = false;
}
-static boolean S_PlayMusic(boolean looping)
+static boolean S_PlayMusic(boolean looping, UINT32 fadeinms)
{
if (S_MusicDisabled())
return false;
- if (!I_PlaySong(looping))
+ if ((!fadeinms && !I_PlaySong(looping)) ||
+ (fadeinms && !I_FadeInPlaySong(fadeinms, looping)))
{
S_UnloadMusic();
return false;
@@ -1511,38 +1569,93 @@ static boolean S_PlayMusic(boolean looping)
return true;
}
-void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping)
+static void S_QueueMusic(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 fadeinms)
{
+ strncpy(queue_name, mmusic, 7);
+ queue_flags = mflags;
+ queue_looping = looping;
+ queue_position = position;
+ queue_fadeinms = fadeinms;
+}
+
+static void S_ClearQueue(void)
+{
+ queue_name[0] = queue_flags = queue_looping = queue_position = queue_fadeinms = 0;
+}
+
+static void S_ChangeMusicToQueue(void)
+{
+ S_ChangeMusicEx(queue_name, queue_flags, queue_looping, queue_position, 0, queue_fadeinms);
+ S_ClearQueue();
+}
+
+void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms)
+{
+ char newmusic[7];
+
if (S_MusicDisabled())
return;
- // No Music (empty string)
- if (mmusic[0] == 0)
- {
- S_StopMusic();
+ strncpy(newmusic, mmusic, 7);
+#if defined(HAVE_BLUA) && defined(HAVE_LUA_MUSICPLUS)
+ if(LUAh_MusicChange(music_name, newmusic, &mflags, &looping, &position, &prefadems, &fadeinms))
+ return;
+#endif
+ newmusic[6] = 0;
+
+ // No Music (empty string)
+ if (newmusic[0] == 0)
+ {
+ if (prefadems)
+ I_FadeSong(0, prefadems, &S_StopMusic);
+ else
+ S_StopMusic();
return;
}
- if (strnicmp(music_name, mmusic, 6))
+ if (prefadems && S_MusicPlaying()) // queue music change for after fade // allow even if the music is the same
{
- S_StopMusic(); // shutdown old music
+ CONS_Debug(DBG_DETAILED, "Now fading out song %s\n", music_name);
+ S_QueueMusic(newmusic, mflags, looping, position, fadeinms);
+ I_FadeSong(0, prefadems, S_ChangeMusicToQueue);
+ return;
+ }
+ else if (strnicmp(music_name, newmusic, 6) || (mflags & MUSIC_FORCERESET))
+ {
+ CONS_Debug(DBG_DETAILED, "Now playing song %s\n", newmusic);
- if (!S_LoadMusic(mmusic))
+ S_StopMusic();
+
+ if (!S_LoadMusic(newmusic))
{
- CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded!\n", mmusic);
+ CONS_Alert(CONS_ERROR, "Music %.6s could not be loaded!\n", newmusic);
return;
}
music_flags = mflags;
music_looping = looping;
- if (!S_PlayMusic(looping))
- {
- CONS_Alert(CONS_ERROR, "Music %.6s could not be played!\n", mmusic);
+ if (!S_PlayMusic(looping, fadeinms))
+ {
+ CONS_Alert(CONS_ERROR, "Music %.6s could not be played!\n", newmusic);
return;
}
+
+ if (position)
+ I_SetSongPosition(position);
+
+ I_SetSongTrack(mflags & MUSIC_TRACKMASK);
+ }
+ else if (fadeinms) // let fades happen with same music
+ {
+ I_SetSongPosition(position);
+ I_FadeSong(100, fadeinms, NULL);
+ }
+ else // reset volume to 100 with same music
+ {
+ I_StopFadingSong();
+ I_FadeSong(100, 500, NULL);
}
- I_SetSongTrack(mflags & MUSIC_TRACKMASK);
}
void S_StopMusic(void)
@@ -1623,6 +1736,32 @@ void S_SetMusicVolume(INT32 digvolume, INT32 seqvolume)
}
}
+/// ------------------------
+/// Music Fading
+/// ------------------------
+
+void S_SetInternalMusicVolume(INT32 volume)
+{
+ I_SetInternalMusicVolume(min(max(volume, 0), 100));
+}
+
+void S_StopFadingMusic(void)
+{
+ I_StopFadingSong();
+}
+
+boolean S_FadeMusicFromVolume(UINT8 target_volume, INT16 source_volume, UINT32 ms)
+{
+ if (source_volume < 0)
+ return I_FadeSong(target_volume, ms, NULL);
+ else
+ return I_FadeSongFromVolume(target_volume, source_volume, ms, NULL);
+}
+
+boolean S_FadeOutStopMusic(UINT32 ms)
+{
+ return I_FadeSong(0, ms, &S_StopMusic);
+}
/// ------------------------
/// Init & Others
@@ -1640,22 +1779,24 @@ void S_Start(void)
strncpy(mapmusname, mapheaderinfo[gamemap-1]->musname, 7);
mapmusname[6] = 0;
mapmusflags = (mapheaderinfo[gamemap-1]->mustrack & MUSIC_TRACKMASK);
+ mapmusposition = mapheaderinfo[gamemap-1]->muspos;
}
if (cv_resetmusic.value)
S_StopMusic();
- S_ChangeMusic(mapmusname, mapmusflags, true);
+ S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
}
static void Command_Tunes_f(void)
{
const char *tunearg;
UINT16 tunenum, track = 0;
+ UINT32 position = 0;
const size_t argc = COM_Argc();
if (argc < 2) //tunes slot ...
{
- CONS_Printf("tunes [track] [speed] / <-show> / <-default> / <-none>:\n");
+ CONS_Printf("tunes [track] [speed] [position] / <-show> / <-default> / <-none>:\n");
CONS_Printf(M_GetText("Play an arbitrary music lump. If a map number is used, 'MAP##M' is played.\n"));
CONS_Printf(M_GetText("If the format supports multiple songs, you can specify which one to play.\n\n"));
CONS_Printf(M_GetText("* With \"-show\", shows the currently playing tune and track.\n"));
@@ -1702,10 +1843,15 @@ static void Command_Tunes_f(void)
snprintf(mapmusname, 7, "%sM", G_BuildMapName(tunenum));
else
strncpy(mapmusname, tunearg, 7);
+
+ if (argc > 4)
+ position = (UINT32)atoi(COM_Argv(4));
+
mapmusname[6] = 0;
mapmusflags = (track & MUSIC_TRACKMASK);
+ mapmusposition = position;
- S_ChangeMusic(mapmusname, mapmusflags, true);
+ S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0);
if (argc > 3)
{
@@ -1731,3 +1877,104 @@ static void Command_RestartAudio_f(void)
if (Playing()) // Gotta make sure the player is in a level
P_RestoreMusic(&players[consoleplayer]);
}
+
+void GameSounds_OnChange(void)
+{
+ if (M_CheckParm("-nosound"))
+ return;
+
+ if (sound_disabled)
+ {
+ sound_disabled = false;
+ S_InitSfxChannels(cv_soundvolume.value);
+ S_StartSound(NULL, sfx_strpst);
+ }
+ else
+ {
+ sound_disabled = true;
+ S_StopSounds();
+ }
+}
+
+void GameDigiMusic_OnChange(void)
+{
+ if (M_CheckParm("-nomusic"))
+ return;
+ else if (M_CheckParm("-nodigmusic"))
+ return;
+
+ if (digital_disabled)
+ {
+ digital_disabled = false;
+ I_InitMusic();
+ S_StopMusic();
+ if (Playing())
+ P_RestoreMusic(&players[consoleplayer]);
+ else
+ S_ChangeMusicInternal("_clear", false);
+ }
+ else
+ {
+ digital_disabled = true;
+ if (S_MusicType() != MU_MID)
+ {
+ if (midi_disabled)
+ S_StopMusic();
+ else
+ {
+ char mmusic[7];
+ UINT16 mflags;
+ boolean looping;
+
+ if (S_MusicInfo(mmusic, &mflags, &looping) && S_MIDIExists(mmusic))
+ {
+ S_StopMusic();
+ S_ChangeMusic(mmusic, mflags, looping);
+ }
+ else
+ S_StopMusic();
+ }
+ }
+ }
+}
+
+void GameMIDIMusic_OnChange(void)
+{
+ if (M_CheckParm("-nomusic"))
+ return;
+ else if (M_CheckParm("-nomidimusic"))
+ return;
+
+ if (midi_disabled)
+ {
+ midi_disabled = false;
+ I_InitMusic();
+ if (Playing())
+ P_RestoreMusic(&players[consoleplayer]);
+ else
+ S_ChangeMusicInternal("_clear", false);
+ }
+ else
+ {
+ midi_disabled = true;
+ if (S_MusicType() == MU_MID)
+ {
+ if (digital_disabled)
+ S_StopMusic();
+ else
+ {
+ char mmusic[7];
+ UINT16 mflags;
+ boolean looping;
+
+ if (S_MusicInfo(mmusic, &mflags, &looping) && S_DigExists(mmusic))
+ {
+ S_StopMusic();
+ S_ChangeMusic(mmusic, mflags, looping);
+ }
+ else
+ S_StopMusic();
+ }
+ }
+ }
+}
diff --git a/src/s_sound.h b/src/s_sound.h
index 0fcaace5e..3550ed0bd 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -27,6 +27,9 @@ extern consvar_t stereoreverse;
extern consvar_t cv_soundvolume, cv_closedcaptioning, cv_digmusicvolume, cv_midimusicvolume;
extern consvar_t cv_numChannels;
extern consvar_t cv_resetmusic;
+extern consvar_t cv_gamedigimusic;
+extern consvar_t cv_gamemidimusic;
+extern consvar_t cv_gamesounds;
#ifdef SNDSERV
extern consvar_t sndserver_cmd, sndserver_arg;
@@ -137,29 +140,49 @@ boolean S_MusicDisabled(void);
boolean S_MusicPlaying(void);
boolean S_MusicPaused(void);
musictype_t S_MusicType(void);
+const char *S_MusicName(void);
boolean S_MusicInfo(char *mname, UINT16 *mflags, boolean *looping);
boolean S_MusicExists(const char *mname, boolean checkMIDI, boolean checkDigi);
#define S_DigExists(a) S_MusicExists(a, false, true)
#define S_MIDIExists(a) S_MusicExists(a, true, false)
-
//
-// Music Properties
+// Music Effects
//
// Set Speed of Music
boolean S_SpeedMusic(float speed);
//
-// Music Routines
+// Music Seeking
+//
+
+// Get Length of Music
+UINT32 S_GetMusicLength(void);
+
+// Set LoopPoint of Music
+boolean S_SetMusicLoopPoint(UINT32 looppoint);
+
+// Get LoopPoint of Music
+UINT32 S_GetMusicLoopPoint(void);
+
+// Set Position of Music
+boolean S_SetMusicPosition(UINT32 position);
+
+// Get Position of Music
+UINT32 S_GetMusicPosition(void);
+
+//
+// Music Playback
//
// Start music track, arbitrary, given its name, and set whether looping
// note: music flags 12 bits for tracknum (gme, other formats with more than one track)
// 13-15 aren't used yet
// and the last bit we ignore (internal game flag for resetting music on reload)
-#define S_ChangeMusicInternal(a,b) S_ChangeMusic(a,0,b)
-void S_ChangeMusic(const char *mmusic, UINT16 mflags, boolean looping);
+void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms);
+#define S_ChangeMusicInternal(a,b) S_ChangeMusicEx(a,0,b,0,0,0)
+#define S_ChangeMusic(a,b,c) S_ChangeMusicEx(a,b,c,0,0,0)
// Stops the music.
void S_StopMusic(void);
@@ -168,6 +191,17 @@ void S_StopMusic(void);
void S_PauseAudio(void);
void S_ResumeAudio(void);
+//
+// Music Fading
+//
+
+void S_SetInternalMusicVolume(INT32 volume);
+void S_StopFadingMusic(void);
+boolean S_FadeMusicFromVolume(UINT8 target_volume, INT16 source_volume, UINT32 ms);
+#define S_FadeMusic(a, b) S_FadeMusicFromVolume(a, -1, b)
+#define S_FadeInChangeMusic(a,b,c,d) S_ChangeMusicEx(a,b,c,0,0,d)
+boolean S_FadeOutStopMusic(UINT32 ms);
+
//
// Updates music & sounds
//
diff --git a/src/screen.c b/src/screen.c
index 9ea996bf9..ac7878c4a 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -310,14 +310,6 @@ void SCR_Recalc(void)
if (automapactive)
AM_Stop();
- // r_plane stuff: visplanes, openings, floorclip, ceilingclip, spanstart,
- // spanstop, yslope, distscale, cachedheight, cacheddistance,
- // cachedxstep, cachedystep
- // -> allocated at the maximum vidsize, static.
-
- // r_main: xtoviewangle, allocated at the maximum size.
- // r_things: negonearray, screenheightarray allocated max. size.
-
// set the screen[x] ptrs on the new vidbuffers
V_Init();
diff --git a/src/screen.h b/src/screen.h
index 4d4fbb88b..7aa6fdb63 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index 7f6771262..7f8f052ba 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -3,7 +3,18 @@
set(SRB2_CONFIG_SDL2_USEMIXER ON CACHE BOOL "Use SDL2_mixer or regular sdl sound")
if(${SRB2_CONFIG_SDL2_USEMIXER})
- find_package(SDL2_mixer)
+ if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
+ set(SDL2_MIXER_FOUND ON)
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ set(SDL2_MIXER_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/x86_64-w64-mingw32/include/SDL2)
+ set(SDL2_MIXER_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/x86_64-w64-mingw32/lib -lSDL2_mixer")
+ else() # 32-bit
+ set(SDL2_MIXER_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/i686-w64-mingw32/include/SDL2)
+ set(SDL2_MIXER_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/i686-w64-mingw32/lib -lSDL2_mixer")
+ endif()
+ else()
+ find_package(SDL2_mixer)
+ endif()
if(${SDL2_MIXER_FOUND})
set(SRB2_HAVE_MIXER ON)
set(SRB2_SDL2_SOUNDIMPL mixer_sound.c)
@@ -42,12 +53,25 @@ set(SRB2_SDL2_HEADERS
source_group("Interface Code" FILES ${SRB2_SDL2_SOURCES} ${SRB2_SDL2_HEADERS})
# Dependency
-find_package(SDL2)
+if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
+ set(SDL2_FOUND ON)
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ set(SDL2_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2/x86_64-w64-mingw32/include/SDL2)
+ set(SDL2_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2/x86_64-w64-mingw32/lib -lSDL2")
+ else() # 32-bit
+ set(SDL2_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2/i686-w64-mingw32/include/SDL2)
+ set(SDL2_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2/i686-w64-mingw32/lib -lSDL2")
+ endif()
+else()
+ find_package(SDL2)
+endif()
if(${SDL2_FOUND})
set(SRB2_SDL2_TOTAL_SOURCES
${SRB2_CORE_SOURCES}
${SRB2_CORE_HEADERS}
+ ${SRB2_PNG_SOURCES}
+ ${SRB2_PNG_HEADERS}
${SRB2_CORE_RENDER_SOURCES}
${SRB2_CORE_GAME_SOURCES}
${SRB2_LUA_SOURCES}
@@ -58,7 +82,8 @@ if(${SDL2_FOUND})
${SRB2_SDL2_HEADERS}
)
- source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS})
+ source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS}
+ ${SRB2_PNG_SOURCES} ${SRB2_PNG_HEADERS})
source_group("Renderer" FILES ${SRB2_CORE_RENDER_SOURCES})
source_group("Game" FILES ${SRB2_CORE_GAME_SOURCES})
source_group("Assembly" FILES ${SRB2_ASM_SOURCES} ${SRB2_NASM_SOURCES})
@@ -185,7 +210,18 @@ if(${SDL2_FOUND})
endif()
if(MSVC)
- find_package(SDL2_MAIN REQUIRED)
+ if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
+ set(SDL2_MAIN_FOUND ON)
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ set(SDL2_MAIN_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2/x86_64-w64-mingw32/include/SDL2)
+ set(SDL2_MAIN_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2/x86_64-w64-mingw32/lib -lSDL2main")
+ else() # 32-bit
+ set(SDL2_MAIN_INCLUDE_DIRS ${CMAKE_SOURCE_DIR}/libs/SDL2/i686-w64-mingw32/include/SDL2)
+ set(SDL2_MAIN_LIBRARIES "-L${CMAKE_SOURCE_DIR}/libs/SDL2/i686-w64-mingw32/lib -lSDL2main")
+ endif()
+ else()
+ find_package(SDL2_MAIN REQUIRED)
+ endif()
target_link_libraries(SRB2SDL2 PRIVATE
${SDL2_MAIN_LIBRARIES}
)
@@ -241,17 +277,49 @@ if(${SDL2_FOUND})
if(${CMAKE_SYSTEM} MATCHES Windows)
set(win_extra_dll_list "")
macro(getwinlib dllname defaultname)
- find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}")
- list(APPEND win_extra_dll_list ${SRB2_SDL2_DLL_${dllname}})
+ if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES})
+ if (${CMAKE_GENERATOR} STREQUAL "MinGW Makefiles")
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}"
+ HINTS ${CMAKE_SOURCE_DIR}/libs/dll-binaries/x86_64
+ HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2/x86_64-w64-mingw32/bin
+ HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/x86_64-w64-mingw32/bin
+ )
+ else()
+ find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}"
+ HINTS ${CMAKE_SOURCE_DIR}/libs/dll-binaries/i686
+ HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2/i686-w64-mingw32/bin
+ HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/i686-w64-mingw32/bin
+ )
+ endif()
+ else()
+ if(${SRB2_SYSTEM_BITS} EQUAL 64)
+ find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}"
+ HINTS ${CMAKE_SOURCE_DIR}/libs/dll-binaries/x86_64
+ HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2/lib/x64
+ HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/lib/x64
+ )
+ else()
+ find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}"
+ HINTS ${CMAKE_SOURCE_DIR}/libs/dll-binaries/i686
+ HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2/lib/x86
+ HINTS ${CMAKE_SOURCE_DIR}/libs/SDL2_mixer/lib/x86
+ )
+ endif()
+ endif()
+
+ list(APPEND win_extra_dll_list ${SRB2_SDL2_DLL_${dllname}})
+ else()
+ find_library(SRB2_SDL2_DLL_${dllname} "${defaultname}")
+ list(APPEND win_extra_dll_list ${SRB2_SDL2_DLL_${dllname}})
+ endif()
endmacro()
getwinlib(SDL2 "SDL2.dll")
if(${SRB2_CONFIG_SDL2_USEMIXER})
getwinlib(SDL2_mixer "SDL2_mixer.dll")
- getwinlib(libmikmod-2 "libmikmod-2.dll")
getwinlib(libogg_0 "libogg-0.dll")
getwinlib(libvorbis_0 "libvorbis-0.dll")
getwinlib(libvorbisfile_3 "libvorbisfile-3.dll")
- getwinlib(smpeg2 "smpeg2.dll")
endif()
if(${SRB2_CONFIG_HAVE_GME})
getwinlib(libgme "libgme.dll")
diff --git a/src/sdl/SDL_icon.xpm b/src/sdl/SDL_icon.xpm
index 30259d55e..1d0f9d314 100644
--- a/src/sdl/SDL_icon.xpm
+++ b/src/sdl/SDL_icon.xpm
@@ -1,213 +1,163 @@
/* XPM */
-static const char *SDL_icon_xpm[] = {
-/* columns rows colors chars-per-pixel */
-"32 32 175 2 ",
-" c None",
-". c #2E2E2E",
-"X c #3C3C3C",
-"o c #493939",
-"O c #4E473F",
-"+ c #161658",
-"@ c #131369",
-"# c #06067B",
-"$ c #111173",
-"% c #16167F",
-"& c #252567",
-"* c #372B7C",
-"= c #3D3679",
-"- c #41414A",
-"; c #575655",
-": c #6A5841",
-"> c #5B4B72",
-", c #616160",
-"< c #7B7B7B",
-"1 c #906E49",
-"2 c #89685D",
-"3 c #A67B4A",
-"4 c #AA7F50",
-"5 c #9B7560",
-"6 c #856C78",
-"7 c #997B7D",
-"8 c #B48552",
-"9 c #BA8A55",
-"0 c #A48665",
-"q c #B98F67",
-"w c #B9946A",
-"e c #B7937A",
-"r c #C8955C",
-"t c #CA9966",
-"y c #DAA469",
-"u c #C9A37B",
-"i c #D7AB7B",
-"p c #DFB07D",
-"a c #EBAE6A",
-"s c #E5B27A",
-"d c #F1B779",
-"f c #0A0A83",
-"g c #05058B",
-"h c #060687",
-"j c #101089",
-"k c #131382",
-"l c #040494",
-"z c #02029D",
-"x c #0C0B9C",
-"c c #120F9E",
-"v c #19199B",
-"b c #382D84",
-"n c #39398D",
-"m c #222296",
-"M c #0101A6",
-"N c #0A0AA2",
-"B c #0202AC",
-"V c #1919A2",
-"C c #1616AD",
-"Z c #0000B5",
-"A c #0202BC",
-"S c #0C0CB6",
-"D c #1313B3",
-"F c #1011BD",
-"G c #1B1BBE",
-"H c #2B2BAC",
-"J c #3737A1",
-"K c #2A26BE",
-"L c #2A29B4",
-"P c #3B3BB8",
-"I c #48478B",
-"U c #57578A",
-"Y c #4A499A",
-"T c #524F95",
-"R c #565399",
-"E c #4C4CA8",
-"W c #524DA7",
-"Q c #5353A4",
-"! c #5555A9",
-"~ c #5555B4",
-"^ c #5656B7",
-"/ c #6464A6",
-"( c #6F67B5",
-") c #0404C3",
-"_ c #0707CA",
-"` c #1414CB",
-"' c #1A1AC6",
-"] c #0A0AD3",
-"[ c #0D0DDC",
-"{ c #1A1AD4",
-"} c #1010DF",
-"| c #1E1EDE",
-" . c #1817DE",
-".. c #221FCA",
-"X. c #2B2BCC",
-"o. c #2727C9",
-"O. c #3434C3",
-"+. c #3434D4",
-"@. c #0F0FE2",
-"#. c #1313E5",
-"$. c #1515ED",
-"%. c #1B1BEA",
-"&. c #1C1CE4",
-"*. c #1515F4",
-"=. c #1818F3",
-"-. c #1717FD",
-";. c #1818FF",
-":. c #2B2BE9",
-">. c #2424FF",
-",. c #2A2AFF",
-"<. c #2222F1",
-"1. c #3737FF",
-"2. c #5D5DC3",
-"3. c #5F5FC9",
-"4. c #5655C2",
-"5. c #4747D1",
-"6. c #5B5BD4",
-"7. c #6565C8",
-"8. c #6363DA",
-"9. c #4545FF",
-"0. c #4D4DFC",
-"q. c #5454FF",
-"w. c #5959FF",
-"e. c #6969E5",
-"r. c #6B6CEA",
-"t. c #6666E7",
-"y. c #6B6BFE",
-"u. c #6767F8",
-"i. c #7070F6",
-"p. c #7373FF",
-"a. c #7C7CFF",
-"s. c #91918F",
-"d. c #8F9090",
-"f. c #979797",
-"g. c #9C9C9C",
-"h. c #8585A1",
-"j. c #9C9CA7",
-"k. c #9292B6",
-"l. c #A4A4A4",
-"z. c #BDB2A4",
-"x. c #A4A4B1",
-"c. c #BFBFBD",
-"v. c #BABAB7",
-"b. c #C8AA87",
-"n. c #DAAE82",
-"m. c #DBB081",
-"M. c #EBBA85",
-"N. c #F3BF84",
-"B. c #F2BE88",
-"V. c #C2B3A3",
-"C. c #FBC386",
-"Z. c #FCC68C",
-"A. c #FFC88F",
-"S. c #F4C387",
-"D. c #FFC990",
-"F. c #C3C1BF",
-"G. c #8F8FCB",
-"H. c #BDBDC2",
-"J. c #BDBDD1",
-"K. c #8888F9",
-"L. c #A4A4FB",
-"P. c #CDCDCC",
-"I. c #CECAC6",
-"U. c #D3CFCA",
-"Y. c #D3D0CC",
-"T. c #C0C0D5",
-"R. c #D6D5D4",
-"E. c #D7D7DD",
-"W. c #E1E1DF",
-"Q. c #DEDEE1",
-"!. c #E4E4E4",
-"~. c #E8E8E8",
-"^. c #F0F0EE",
-"/. c #F5F5F2",
-"(. c #FFFFFF",
-/* pixels */
-" ",
-" ",
-" ",
-" I Q T = ",
-" Q 7.e.r.i.8.E E 3.r.6.J ",
-" H ~ n 4.r.p.p.p.p.8.R > 5.^ w.,.-.{ v ",
-" { 9.^ & P t.p.p.p.p.p.8.I 5 q K L <.;.;.;.-.' ",
-" { %.H +.y.p.p.p.p.p.e.Y 2 a n.K F $.*.$.@.} ] N ",
-" x D :.y.p.p.p.p.p.p.r.R 8 C.u ..F A ) A Z M h $ ",
-" f =.q.p.p.p.p.p.p.p.p.i.( e 6 $.` l B M g ",
-" ` ;.q.p.p.p.p.p.a.K.a.p.p.4.L -.` l N % ",
-" V =.-.>.q.y.p.p.p.L.L.K.i.w.,.-.;.$.<.q.u.2. ",
-" D { =.-.;.>.1.1.9.( h.h.Q &.-.-.-.;.9.p.p.p.r.! ",
-" U j.o.-.;.-.;.-.P x.Q.^.R.~ *.-.;.;.>.1.q.y.p.i.2. ",
-" H./.! *.;.;.;.o.x./.(.(.(.J.| -.-.;.-.-.;.,.9.u.p.7. ",
-" !.(.k.#.;.-.=./ !.(.(.(.(.Q.X.-.;.;.;.;.-.-.;.;.1.w.6. ",
-" ~.(.H.G ;.-.D j.(.(.(.(.(.!.O.-.-.;.;.;.-.;.-.;.-.;.,.O. ",
-" ~.(.v.@ *.$.+ d.(.(.(.(.(.E.o.-.-.;.;.-.;.;.;.*.=.=.*.$.v ",
-" ~.(.l.- Y T ; < (.(.(.(.(.J.&.-.;.;.$.@.[ ] _ ) ) Z B B f ",
-" P.(.F.X c.I.X f.(.(.(.(.(.G.=.-.=.] A Z Z Z Z z f $ ",
-" l.!.R.s.F.I.g.W.(.(.(.(.R.E .[ A Z Z Z B g $ ",
-" . , ; - 0 M.b.V.U.R.Y.z.u n.7 c Z Z B g # + ",
-" : w p Z.D.A.S.p u i M.A.A.S.* Z B h z ] C ",
-" s D.D.A.A.A.A.A.A.A.i B.B.b A Z Z @.-.` ",
-" 1 y C.D.A.A.A.A.A.M.u Z.e c A Z Z [ ;.&. ",
-" 8 y d C.A.A.A.C.B.t * B Z Z Z A #.=.m ",
-" 3 9 r t r 9 8 o @ $ # f j l B #.V ",
-" j k ",
-" ",
-" ",
-" ",
-" "
-};
+const char * SDL_icon_xpm[] = {
+"96 96 64 1",
+" c None",
+". c #040656",
+"+ c #0100B2",
+"@ c #04056E",
+"# c #0000BD",
+"$ c #0B0C09",
+"% c #0B0D26",
+"& c #090C42",
+"* c #060AA7",
+"= c #1604DA",
+"- c #020CD5",
+"; c #100F8D",
+"> c #040DE4",
+", c #11129B",
+"' c #1D1A83",
+") c #2A10FD",
+"! c #1318FA",
+"~ c #25225B",
+"{ c #252271",
+"] c #312E2B",
+"^ c #33334D",
+"/ c #363775",
+"( c #3D3B69",
+"_ c #3A3B8B",
+": c #373AFF",
+"< c #4142AA",
+"[ c #4B4864",
+"} c #4D4B4A",
+"| c #60492F",
+"1 c #4F4C57",
+"2 c #4A4A9E",
+"3 c #4F4E85",
+"4 c #474ADE",
+"5 c #4E4FFE",
+"6 c #5D5CB3",
+"7 c #686663",
+"8 c #666682",
+"9 c #676875",
+"0 c #66659E",
+"a c #8B6538",
+"b c #6465D5",
+"c c #7F694F",
+"d c #6767FF",
+"e c #7272FF",
+"f c #91795C",
+"g c #7677FD",
+"h c #828396",
+"i c #A78153",
+"j c #888989",
+"k c #8D897E",
+"l c #9190FD",
+"m c #CA9048",
+"n c #C09968",
+"o c #A9A8A1",
+"p c #A6A8B0",
+"q c #B0B1FB",
+"r c #EEAC61",
+"s c #E3B478",
+"t c #C3C4BE",
+"u c #FFC68C",
+"v c #FCCD90",
+"w c #D4D7D3",
+"x c #E3E5E0",
+"y c #FCFFFB",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ttj7777777joot ",
+" 9hh8830000088hh9 ",
+" 9888(//__-*{^kt ",
+" &,5b60^ (02*{} ",
+" tp,-)!5egb3} ~_<4dgggeeeeeeeeeeeeeegb6/_2[amusf'#!<_'>))))))))))))!)>+{~ ",
+" p;-))!5gb2^^'#5eggeeeeeeeeeeeeeeegg6/_23amrusi{#!+;;>))))))))))))))!!-'8p ",
+" tp'#!)):d6(@*>5egeeeeeeeeeeeeeeeegg6_/<(amrrvvn{+)-,;>))))))!!!!!!)))!!>,~j ",
+" p;#!))-'{'+-5eggeeeeeeeeeeeeeeeegb222(cmrruvvn{+)>,@>!)!!)!!>>>>======>-,/8 ",
+" ;#)!-*.;-!5eggeeeeeeeeeeeeeeeegb2_<6|mrrsvvvn{+)!,.-!!!!>>=--######+++-#@(k ",
+" h@-)+@.*>!5egeeeeeeeeeeeeeeeeeegb_]mrrruvvvn{+))*@->>--###++++++###+;@{(9j ",
+" kh,#+@@,>!:dggeeeeeeeeeeeeeeeeeeebbb_]mrruuvvsf'#)!*.+-###+++++++##+*;'3(&^9 ",
+" 8*,@@*)):dggeeeeeeeeeeeeeeeeeeeeggg<(|iruvvvsc,=!!*.;*++++++++###+,@&1o ",
+" 8@@@-!)!5eeeeeeeeeeeeeeeeeeeeeeeeeggb2[csvvvn^#)!!+@;*#+++++###*@~[ ",
+" 9&@*!)):5geeeeeeeeeeeeeeeeeeeeeeeeegge637nsvf{>))!+;;*-######*;{.^ ",
+" 9%;!!)):dgeeeeeeeeeeeeeeeeeeeeeeeeeeeggb_1ir7;>))!+;;,++++++*'(} ",
+" 9{+!))!5egeeeeeeeeeeeeeeeeeddddeeeeeeeege2}|~#!))!#;@...@@@.^hp ",
+" 8,=!))):dggeeeeeeeeeeeeeeeeggggeeeeeeeeggb_~,>!))!+@@@;;;;@&^o ",
+" }(-)))))!:eegeeeeeeeeeeeeeegllllgeeeeeeeegd5+=))))!+;,#>--#,'/hj ",
+" o8.>))))))!:dgggeeeeeeeeeeellqqqqlgeeeeggg5:!!!)))))-*+>)!:55db631 ",
+" p8<*!)))))))!:5deggggggeeeegqqqqqqqqlggged5:!))))))))>->!!:5ddeegb3/ ",
+" oh'#!))))))))))!:ddeeeeeeeeglqqqqqqqqlgedd:!)))))))))))))!:dggggeggg239 ",
+" ^*>!))!)))))))))!::55dddeegglll600333_4:!!)))))))))))))):dggeeeeeeggb6(9o ",
+" ~+=-+#>))))))))))!!!:::::5554<3889988[/,=)))))))))))))):5gggeeeeeeeggb6087 ",
+" ~**@~'+>!))))))))))))))))!!>*{1kkooook7(,-!)))))))))))!:5deeeeeeeeeeeggb289 ",
+" ~,'1o7(*>!))))))))))))))))=,[jtttwxxxwto^;>!))))))))))!!!::5deggeeeeeeegbb3] ",
+" ~@/oxt7'#))))))))))))))))=,3ktwxxyyyyyyxk/+!))))))))))))))!:::5degggeeegggb3^ ",
+" ^&8xyyt^,)))))))))))))))>,3otwxyyyyyyyyyxh'>)))))))))))))))))):5ddeeeeeeeggb3^ ",
+" 771pyyyx7'=!)))))))))))!!#(jtxxyyyyyyyyyyyt3-)))))))))))))))))))!!::degggeeegb2[o ",
+" 77tyyyxk/+!!)))))))))))-;9owxyyyyyyyyyyyywh*>)))))))))))))))))))))!::5ddgggggb68j ",
+" owyyyyt8;>))))))))))))*(otwyyyyyyyyyyyyyxp'-)))))))))))))))))))))))!!:5deeeggg_8j ",
+" jtxyyyyxh'>)))))))))!!#_ktxyyyyyyyyyyyyyyyt_+))))))))))))))))))))))))))!!:5deggg63j ",
+" 7jwyyyyyyp/=))))))))))>,3owxyyyyyyyyyyyyyyyw/+))))))))))))))))))))))))))))!::5degb689 ",
+" 7xyyyyyyo[#))))))))))-/jtwyyyyyyyyyyyyyyyyw/*)))))))))))))))))))))))))))))))!:5dgg_/ ",
+" }xyyyyyyt9*=))))))))=*9owyyyyyyyyyyyyyyyyyw/*)))))))))))))))))))))))))))))))))!!:5d3} ",
+" }xyyyyyywj'#!))))))!#@7oxyyyyyyyyyyyyyyyyyw/*)))))))))))))))))))))))))))))))))))!!:4/7 ",
+" 7xyyyyyyxj&,!!))))!!,%}oyyyyyyyyyyyyyyyyyyw/*))))))))))))))))))))))))))))))))))))))>487 ",
+" 7xyyyyyywk$@!!)))!!-.$]oyyyyyyyyyyyyyyyyyyw/+))))))))))))))))))))))))))))))!!!!))))!>' ",
+" }xyyyyyywj$&+!!)!)>;%$]jyyyyyyyyyyyyyyyyyyt{#)))))))))))))))))))))!!!!!!))!)!!!!!!))!#' ",
+" 7xyyyyyyt7$%@-!)!>*[]$$jyyyyyyyyyyyyyyyyyxp;-))))))))))))))))))!!!!!!!!!!!!>>>>>>>>>>!,^ ",
+" 7xyyyyyyt}$][;-)=,(o7$$7yyyyyyyyyyyyyyyyyxp,-)))))))))))!!!!)!!!!>>>>=-----########--=+'9 ",
+" jwyyyyyyo}$}o(';@~7wj$$7yyyyyyyyyyyyyyyyywh*>)))))))))))!>>>=>=---#####+########+++***;@17 ",
+" otxyyyyyt}$7t7}1}7kw7$$7yyyyyyyyyyyyyyyyyt0-)))))))))!!!>--####+++++++++++++##+***,;''.&] ",
+" ooowyyyyyt}$}j7owwojo}$$jyyyyyyyyyyyyyyyyyp2>)))))))!!!=##++++++++++++++###+*;@.~[8[9hph ",
+" ojtyyyyywj$$}jwyyxo}$$]jyyyyyyyyyyyyyyyyyp'>))))))!>>-#++++++++++++####+,;'_3/&^}77kot ",
+" 7tyyyyyxo]$$oxyyyt]$$}tyyyyyyyyyyyyyyyyx0*!)))!!!>-#++++++++++++#+##+*;.&1ko ",
+" 7tyyyyyyx7]}xyyyyxj}]oxyyyyyyyyyyyyyyyyp<=)!!!!>-#++++++++++++####*;.(8h ",
+" owxyxxyytooywptwwtppxyyyyyyyyyyyyyyyxp3,-=!)!>-#++++++++++###+*,'_{&1k ",
+" jtwwttwtwwtj7kjowxyyyyyyyyyyyyyyyyxt7~'',+>=#+++++++++++###*;@&^j ",
+" ]joojj7}]}]|innfc7jtwyyyyyyyyyyyxtjcfnnnf[@*#+++++++++###+@.&%% ",
+" ]$}77}}$$$$]fsssnnifkkotwwwwwwwtpjkfinvvvsi}@*#++++++###*;@.@@&[ ",
+" o7$]]]]]$$]|isvvvvvusifckopppopok7cisvvvvvvvn(,#++++++#+*@.&@*#;3o ",
+" }}$]|||fnnsvvvuvvvuuvvsniffffffnnsvvvvuuuvvvc{*+#++##*@&.@*+#--<7 ",
+" }]cninsuvvvvuuuuuuvvvvusnnnnnssuvvvvvuuuuvvc~*+#+++*@.@;*##=>>,^ ",
+" 7fvvvvvvuuuuuuuuuuuuvvvvvvvvvvvuuuuuuuuuvvc~*+#+#+,.@*###->!!*~ ",
+" pkivvvvuuuuuuuuuuuuuuuvvvvvvvvuuuvsnsuuuvvf~*+#++++*+++->!!)!#. ",
+" kfsuvvuuuuuuuuuuuuuuuuuuuuuuuuuvvnfsuvuvvc{++#++++###->!!))!-;h ",
+" kisvvvuuuuuuuuuuuuuuuuuuuuuuuvvvicsvvvvs1@##+++++++#>!!))))=,ho ",
+" 7imuvvvuuuuuuuuuuuuuuuuuuuuvusfcivvuvvn~;##+++++++#>!!))))!#8k ",
+" cimruuuuuvuuuuuuuuuuuuuuuuvsnfisuvvvsc@*#+++++++++#>!!))))-3} ",
+" 7amrruuuuuuuuuuuuuuuuuuuuvsnnsvvuvvi^,##++++++++++#>!!)))>/^ ",
+" kfamrruuuuvvvuuuuuuuuuuuuuvvvvvvvn1@+#++++++++++++#>!)))>{~ ",
+" 7|iimrrruuuuuuuuuuuuuuuuvvvvuusn1'+#########++++++->!))>; ",
+" 7cammrrrrruuuuuuvvvvvuuuuurrm|.*-#+#######+###+++->!!!*' ",
+" ookcaimmrrrrrruuuuurrrrrmi|]%.@@@@@;,*,*+########->!!*6o ",
+" p7}|ainiimmmmmmmmmmminnia|$%.....{3322_{''',,**+#=!!#6k ",
+" j7||aaiiiiiaa||7j ookok711^&.';,*+=!>>
+
+ Debug
+ ARM
+
+
+ Debug
+ ARM64
+
Debug
Win32
+
+ Release
+ ARM
+
+
+ Release
+ ARM64
+
Release
Win32
@@ -23,27 +39,50 @@
{61BA7D3C-F77D-4D31-B718-1177FE482CF2}
Win32Proj
Srb2SDL
- 8.1
+ 10.0.16299.0
Srb2Win
-
- v140
-
+ v140
true
+
+ v141
+ true
+ true
+
+ v140
false
true
+
+ v141
+ false
+ true
+ true
+
+ v140
true
+
+ v141
+ true
+ true
+
+ v140
false
true
+
+ v141
+ false
+ true
+ true
+
@@ -60,23 +99,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<_ProjectFileVersion>10.0.30319.1
false
+
+
+ ProgramDatabase
+ false
+
+
+ setupapi.lib;winmm.lib;imm32.lib;version.lib;ole32.lib;advapi32.lib;shell32.lib;gdi32.lib;oleaut32.lib;uuid.lib;%(AdditionalDependencies)
+
+
+
+
+ ProgramDatabase
+ false
+
+
+
+
+ setupapi.lib;winmm.lib;imm32.lib;version.lib;ole32.lib;advapi32.lib;shell32.lib;gdi32.lib;oleaut32.lib;uuid.lib;%(AdditionalDependencies)
+
+
{72b01aca-7a1a-4f7b-acef-2607299cf052}
@@ -89,6 +164,7 @@
+
@@ -241,6 +317,7 @@
+
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index bf9e4ac13..ca6bd38d2 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -294,6 +294,9 @@
LUA
+
+ M_Misc
+
M_Misc
diff --git a/src/sdl/Srb2SDL-vc9.vcproj b/src/sdl/Srb2SDL-vc9.vcproj
index d2a268f8d..3898aeba4 100644
--- a/src/sdl/Srb2SDL-vc9.vcproj
+++ b/src/sdl/Srb2SDL-vc9.vcproj
@@ -2834,6 +2834,50 @@
RelativePath="..\m_argv.h"
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index 0963cc288..7a30d4c85 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -893,8 +893,8 @@ void I_GetJoystickEvents(void)
UINT64 joyhats = 0;
#if 0
UINT64 joybuttons = 0;
-#endif
Sint16 axisx, axisy;
+#endif
if (!joystick_started) return;
@@ -962,6 +962,7 @@ void I_GetJoystickEvents(void)
}
}
+#if 0
// send joystick axis positions
event.type = ev_joystick;
@@ -1012,6 +1013,7 @@ void I_GetJoystickEvents(void)
}
D_PostEvent(&event);
}
+#endif
}
/** \brief Open joystick handle
@@ -1175,8 +1177,8 @@ void I_GetJoystick2Events(void)
UINT64 joyhats = 0;
#if 0
INT64 joybuttons = 0;
-#endif
INT32 axisx, axisy;
+#endif
if (!joystick2_started)
return;
@@ -1246,6 +1248,7 @@ void I_GetJoystick2Events(void)
}
}
+#if 0
// send joystick axis positions
event.type = ev_joystick2;
@@ -1296,7 +1299,7 @@ void I_GetJoystick2Events(void)
}
D_PostEvent(&event);
}
-
+#endif
}
/** \brief Open joystick handle
@@ -1398,7 +1401,13 @@ static int joy_open2(const char *fname)
void I_InitJoystick(void)
{
I_ShutdownJoystick();
- SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
+
+ if (M_CheckParm("-noxinput"))
+ SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
+
+ if (M_CheckParm("-nohidapi"))
+ SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
+
if (!strcmp(cv_usejoystick.string, "0") || M_CheckParm("-nojoy"))
return;
if (joy_open(cv_usejoystick.string) != -1)
@@ -1414,7 +1423,13 @@ void I_InitJoystick(void)
void I_InitJoystick2(void)
{
I_ShutdownJoystick2();
- SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
+
+ if (M_CheckParm("-noxinput"))
+ SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
+
+ if (M_CheckParm("-nohidapi"))
+ SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
+
if (!strcmp(cv_usejoystick2.string, "0") || M_CheckParm("-nojoy"))
return;
if (joy_open2(cv_usejoystick2.string) != -1)
@@ -1452,18 +1467,28 @@ INT32 I_NumJoys(void)
return numjoy;
}
+static char joyname[255]; // MAX_PATH; joystick name is straight from the driver
+
const char *I_GetJoyName(INT32 joyindex)
{
- const char *joyname = "NA";
+ const char *tempname = NULL;
joyindex--; //SDL's Joystick System starts at 0, not 1
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
{
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) != -1)
- joyname = SDL_JoystickNameForIndex(joyindex);
+ {
+ tempname = SDL_JoystickNameForIndex(joyindex);
+ if (tempname)
+ strncpy(joyname, tempname, 255);
+ }
SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
}
else
- joyname = SDL_JoystickNameForIndex(joyindex);
+ {
+ tempname = SDL_JoystickNameForIndex(joyindex);
+ if (tempname)
+ strncpy(joyname, tempname, 255);
+ }
return joyname;
}
@@ -2031,8 +2056,8 @@ static void I_ShutdownTimer(void)
//
tic_t I_GetTime (void)
{
- static Uint32 basetime = 0;
- Uint32 ticks = SDL_GetTicks();
+ static Uint64 basetime = 0;
+ Uint64 ticks = SDL_GetTicks();
if (!basetime)
basetime = ticks;
@@ -2138,6 +2163,8 @@ void I_Quit(void)
printf("\r");
ShowEndTxt();
}
+ if (myargmalloc)
+ free(myargv); // Deallocate allocated memory
death:
W_Shutdown();
exit(0);
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 05ed527de..2c8deddb4 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -200,7 +200,10 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
}
// Reposition window only in windowed mode
SDL_SetWindowSize(window, width, height);
- SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
+ SDL_SetWindowPosition(window,
+ SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window)),
+ SDL_WINDOWPOS_CENTERED_DISPLAY(SDL_GetWindowDisplayIndex(window))
+ );
}
}
else
@@ -357,6 +360,7 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code)
static void SDLdoUngrabMouse(void)
{
+ SDL_ShowCursor(SDL_ENABLE);
SDL_SetWindowGrab(window, SDL_FALSE);
wrapmouseok = SDL_FALSE;
SDL_SetRelativeMouseMode(SDL_FALSE);
@@ -366,6 +370,7 @@ void SDLforceUngrabMouse(void)
{
if (SDL_WasInit(SDL_INIT_VIDEO)==SDL_INIT_VIDEO && window != NULL)
{
+ SDL_ShowCursor(SDL_ENABLE);
SDL_SetWindowGrab(window, SDL_FALSE);
wrapmouseok = SDL_FALSE;
SDL_SetRelativeMouseMode(SDL_FALSE);
@@ -575,6 +580,8 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
if (cv_usemouse.value) I_StartupMouse();
}
//else firsttimeonmouse = SDL_FALSE;
+
+ capslock = !!( SDL_GetModState() & KMOD_CAPS );// in case CL changes
}
else if (!mousefocus && !kbfocus)
{
@@ -767,6 +774,33 @@ static void Impl_HandleJoystickAxisEvent(SDL_JoyAxisEvent evt)
D_PostEvent(&event);
}
+#if 0
+static void Impl_HandleJoystickHatEvent(SDL_JoyHatEvent evt)
+{
+ event_t event;
+ SDL_JoystickID joyid[2];
+
+ // Determine the Joystick IDs for each current open joystick
+ joyid[0] = SDL_JoystickInstanceID(JoyInfo.dev);
+ joyid[1] = SDL_JoystickInstanceID(JoyInfo2.dev);
+
+ if (evt.hat >= JOYHATS)
+ return; // ignore hats with too high an index
+
+ if (evt.which == joyid[0])
+ {
+ event.data1 = KEY_HAT1 + (evt.hat*4);
+ }
+ else if (evt.which == joyid[1])
+ {
+ event.data1 = KEY_2HAT1 + (evt.hat*4);
+ }
+ else return;
+
+ // NOTE: UNFINISHED
+}
+#endif
+
static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type)
{
event_t event;
@@ -804,6 +838,8 @@ static void Impl_HandleJoystickButtonEvent(SDL_JoyButtonEvent evt, Uint32 type)
if (event.type != ev_console) D_PostEvent(&event);
}
+
+
void I_GetEvent(void)
{
SDL_Event evt;
@@ -844,6 +880,11 @@ void I_GetEvent(void)
case SDL_JOYAXISMOTION:
Impl_HandleJoystickAxisEvent(evt.jaxis);
break;
+#if 0
+ case SDL_JOYHATMOTION:
+ Impl_HandleJoystickHatEvent(evt.jhat)
+ break;
+#endif
case SDL_JOYBUTTONUP:
case SDL_JOYBUTTONDOWN:
Impl_HandleJoystickButtonEvent(evt.jbutton, evt.type);
@@ -1520,9 +1561,18 @@ void I_StartupGraphics(void)
realheight = (Uint16)vid.height;
VID_Command_Info_f();
- if (!disable_mouse) SDL_ShowCursor(SDL_DISABLE);
SDLdoUngrabMouse();
+ SDL_RaiseWindow(window);
+
+ if (mousegrabok && !disable_mouse)
+ {
+ SDL_ShowCursor(SDL_DISABLE);
+ SDL_SetRelativeMouseMode(SDL_TRUE);
+ wrapmouseok = SDL_TRUE;
+ SDL_SetWindowGrab(window, SDL_TRUE);
+ }
+
graphics_started = true;
}
diff --git a/src/sdl/macosx/Srb2mac.icns b/src/sdl/macosx/Srb2mac.icns
index 4baedc1c5..96cb8a36d 100644
Binary files a/src/sdl/macosx/Srb2mac.icns and b/src/sdl/macosx/Srb2mac.icns differ
diff --git a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
index eaac87deb..a8ecbf7f8 100644
--- a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
+++ b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
@@ -43,6 +43,7 @@
1E44AF0D0B67CDE900BAD059 /* m_fixed.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AEFE0B67CDE900BAD059 /* m_fixed.c */; };
1E44AF0F0B67CDE900BAD059 /* m_menu.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF000B67CDE900BAD059 /* m_menu.c */; };
1E44AF110B67CDE900BAD059 /* m_misc.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* m_misc.c */; };
+ 1E44AF110B67CDE900BAD059 /* apng.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF020B67CDE900BAD059 /* apng.c */; };
1E44AF130B67CDE900BAD059 /* m_random.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF040B67CDE900BAD059 /* m_random.c */; };
1E44AF1A0B67CE2A00BAD059 /* info.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF180B67CE2A00BAD059 /* info.c */; };
1E44AF1E0B67CE3600BAD059 /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 1E44AF1C0B67CE3600BAD059 /* md5.c */; };
@@ -253,7 +254,9 @@
1E44AF000B67CDE900BAD059 /* m_menu.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_menu.c; path = ../../m_menu.c; sourceTree = SOURCE_ROOT; };
1E44AF010B67CDE900BAD059 /* m_menu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_menu.h; path = ../../m_menu.h; sourceTree = SOURCE_ROOT; };
1E44AF020B67CDE900BAD059 /* m_misc.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_misc.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; };
+ 1E44AF020B67CDE900BAD059 /* apng.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = apng.c; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; };
1E44AF030B67CDE900BAD059 /* m_misc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_misc.h; path = ../../m_misc.h; sourceTree = SOURCE_ROOT; };
+ 1E44AF020B67CDE900BAD059 /* apng.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = apng.h; path = ../../m_misc.c; sourceTree = SOURCE_ROOT; };
1E44AF040B67CDE900BAD059 /* m_random.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = m_random.c; path = ../../m_random.c; sourceTree = SOURCE_ROOT; };
1E44AF050B67CDE900BAD059 /* m_random.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_random.h; path = ../../m_random.h; sourceTree = SOURCE_ROOT; };
1E44AF060B67CDE900BAD059 /* m_swap.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = m_swap.h; path = ../../m_swap.h; sourceTree = SOURCE_ROOT; };
@@ -679,6 +682,8 @@
1E44AEFF0B67CDE900BAD059 /* m_fixed.h */,
1E44AF020B67CDE900BAD059 /* m_misc.c */,
1E44AF030B67CDE900BAD059 /* m_misc.h */,
+ 1E44AF020B67CDE900BAD059 /* apng.c */,
+ 1E44AF030B67CDE900BAD059 /* apng.h */,
676BB51C0E0DE06100C95963 /* m_queue.c */,
676BB51D0E0DE06100C95963 /* m_queue.h */,
1E44AF040B67CDE900BAD059 /* m_random.c */,
@@ -1214,7 +1219,7 @@
C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.20;
+ CURRENT_PROJECT_VERSION = 2.1.23;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
NORMALSRB2,
@@ -1226,7 +1231,7 @@
C01FCF4C08A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.20;
+ CURRENT_PROJECT_VERSION = 2.1.23;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index a3c421991..dde62fc7a 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -50,8 +50,8 @@
#ifdef HAVE_LIBGME
#include "gme/gme.h"
-#define GME_TREBLE 5.0
-#define GME_BASS 1.0
+#define GME_TREBLE 5.0f
+#define GME_BASS 1.0f
#ifdef HAVE_ZLIB
#ifndef _MSC_VER
@@ -75,15 +75,41 @@
UINT8 sound_started = false;
static Mix_Music *music;
-static UINT8 music_volume, sfx_volume;
+static UINT8 music_volume, sfx_volume, internal_volume;
static float loop_point;
+static float song_length; // length in seconds
static boolean songpaused;
+static UINT32 music_bytes;
+static boolean is_looping;
+
+// fading
+static boolean is_fading;
+static UINT8 fading_source;
+static UINT8 fading_target;
+static UINT32 fading_timer;
+static UINT32 fading_duration;
+static INT32 fading_id;
+static void (*fading_callback)(void);
#ifdef HAVE_LIBGME
static Music_Emu *gme;
static INT32 current_track;
#endif
+static void var_cleanup(void)
+{
+ loop_point = song_length =\
+ music_bytes = fading_source = fading_target =\
+ fading_timer = fading_duration = 0;
+
+ songpaused = is_looping =\
+ is_fading = false;
+
+ fading_callback = NULL;
+
+ internal_volume = 100;
+}
+
/// ------------------------
/// Audio System
/// ------------------------
@@ -92,6 +118,12 @@ void I_StartupSound(void)
{
I_Assert(!sound_started);
+#ifdef _WIN32
+ // Force DirectSound instead of WASAPI
+ // SDL 2.0.6+ defaults to the latter and it screws up our sound effects
+ SDL_setenv("SDL_AUDIODRIVER", "directsound", 1);
+#endif
+
// EE inits audio first so we're following along.
if (SDL_WasInit(SDL_INIT_AUDIO) == SDL_INIT_AUDIO)
{
@@ -105,6 +137,8 @@ void I_StartupSound(void)
return;
}
+ var_cleanup();
+
music = NULL;
music_volume = sfx_volume = 0;
@@ -330,6 +364,7 @@ void *I_GetSfx(sfxinfo_t *sfx)
len = (info->play_length * 441 / 10) << 2;
mem = malloc(len);
gme_play(emu, len >> 1, mem);
+ gme_free_info(info);
gme_delete(emu);
return Mix_QuickLoad_RAW((Uint8 *)mem, len);
@@ -402,6 +437,7 @@ void *I_GetSfx(sfxinfo_t *sfx)
len = (info->play_length * 441 / 10) << 2;
mem = malloc(len);
gme_play(emu, len >> 1, mem);
+ gme_free_info(info);
gme_delete(emu);
return Mix_QuickLoad_RAW((Uint8 *)mem, len);
@@ -476,14 +512,102 @@ void I_SetSfxVolume(UINT8 volume)
sfx_volume = volume;
}
+/// ------------------------
+/// Music Utilities
+/// ------------------------
+
+static UINT32 get_real_volume(UINT8 volume)
+{
+#ifdef _WIN32
+ if (I_SongType() == MU_MID)
+ // HACK: Until we stop using native MIDI,
+ // disable volume changes
+ return ((UINT32)31*128/31); // volume = 31
+ else
+#endif
+ // convert volume to mixer's 128 scale
+ // then apply internal_volume as a percentage
+ return ((UINT32)volume*128/31) * (UINT32)internal_volume / 100;
+}
+
+static UINT32 get_adjusted_position(UINT32 position)
+{
+ // all in milliseconds
+ UINT32 length = I_GetSongLength();
+ UINT32 looppoint = I_GetSongLoopPoint();
+ if (length)
+ return position >= length ? (position % (length-looppoint)) : position;
+ else
+ return position;
+}
+
+static void do_fading_callback(void)
+{
+ if (fading_callback)
+ (*fading_callback)();
+ fading_callback = NULL;
+}
+
/// ------------------------
/// Music Hooks
/// ------------------------
+static void count_music_bytes(int chan, void *stream, int len, void *udata)
+{
+ (void)chan;
+ (void)stream;
+ (void)udata;
+
+ if (!music || I_SongType() == MU_GME || I_SongType() == MU_MOD || I_SongType() == MU_MID)
+ return;
+ music_bytes += len;
+}
+
static void music_loop(void)
{
- Mix_PlayMusic(music, 0);
- Mix_SetMusicPosition(loop_point);
+ if (is_looping)
+ {
+ Mix_PlayMusic(music, 0);
+ Mix_SetMusicPosition(loop_point);
+ music_bytes = loop_point*44100.0L*4; //assume 44.1khz, 4-byte length (see I_GetSongPosition)
+ }
+ else
+ I_StopSong();
+}
+
+static UINT32 music_fade(UINT32 interval, void *param)
+{
+ (void)param;
+
+ if (!is_fading ||
+ internal_volume == fading_target ||
+ fading_duration == 0)
+ {
+ I_StopFadingSong();
+ do_fading_callback();
+ return 0;
+ }
+ else if (songpaused) // don't decrement timer
+ return interval;
+ else if ((fading_timer -= 10) <= 0)
+ {
+ internal_volume = fading_target;
+ Mix_VolumeMusic(get_real_volume(music_volume));
+ I_StopFadingSong();
+ do_fading_callback();
+ return 0;
+ }
+ else
+ {
+ UINT8 delta = abs(fading_target - fading_source);
+ fixed_t factor = FixedDiv(fading_duration - fading_timer, fading_duration);
+ if (fading_target < fading_source)
+ internal_volume = max(min(internal_volume, fading_source - FixedMul(delta, factor)), fading_target);
+ else if (fading_target > fading_source)
+ internal_volume = min(max(internal_volume, fading_source + FixedMul(delta, factor)), fading_target);
+ Mix_VolumeMusic(get_real_volume(music_volume));
+ return interval;
+ }
}
#ifdef HAVE_LIBGME
@@ -503,7 +627,7 @@ static void mix_gme(void *udata, Uint8 *stream, int len)
// apply volume to stream
for (i = 0, p = (short *)stream; i < len/2; i++, p++)
- *p = ((INT32)*p) * music_volume*2 / 42;
+ *p = ((INT32)*p) * (music_volume*internal_volume/100)*2 / 42;
}
#endif
@@ -581,6 +705,194 @@ boolean I_SetSongSpeed(float speed)
return false;
}
+/// ------------------------
+/// MUSIC SEEKING
+/// ------------------------
+
+UINT32 I_GetSongLength(void)
+{
+ INT32 length;
+
+#ifdef HAVE_LIBGME
+ if (gme)
+ {
+ gme_info_t *info;
+ gme_err_t gme_e = gme_track_info(gme, &info, current_track);
+
+ if (gme_e != NULL)
+ {
+ CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
+ length = 0;
+ }
+ else
+ {
+ // reconstruct info->play_length, from GME source
+ // we only want intro + 1 loop, not 2
+ length = info->length;
+ if (length <= 0)
+ {
+ length = info->intro_length + info->loop_length; // intro + 1 loop
+ if (length <= 0)
+ length = 150 * 1000; // 2.5 minutes
+ }
+ }
+
+ gme_free_info(info);
+ return max(length, 0);
+ }
+ else
+#endif
+ if (!music || I_SongType() == MU_MOD || I_SongType() == MU_MID)
+ return 0;
+ else
+ {
+ // VERY IMPORTANT to set your LENGTHMS= in your song files, folks!
+ // SDL mixer can't read music length itself.
+ length = (UINT32)(song_length*1000);
+ if (!length)
+ CONS_Debug(DBG_DETAILED, "Getting music length: music is missing LENGTHMS= tag. Needed for seeking.\n");
+ return length;
+ }
+}
+
+boolean I_SetSongLoopPoint(UINT32 looppoint)
+{
+ if (!music || I_SongType() == MU_GME || I_SongType() == MU_MOD || I_SongType() == MU_MID || !is_looping)
+ return false;
+ else
+ {
+ UINT32 length = I_GetSongLength();
+
+ if (length > 0)
+ looppoint %= length;
+
+ loop_point = max((float)(looppoint / 1000.0L), 0);
+ return true;
+ }
+}
+
+UINT32 I_GetSongLoopPoint(void)
+{
+#ifdef HAVE_LIBGME
+ if (gme)
+ {
+ INT32 looppoint;
+ gme_info_t *info;
+ gme_err_t gme_e = gme_track_info(gme, &info, current_track);
+
+ if (gme_e != NULL)
+ {
+ CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
+ looppoint = 0;
+ }
+ else
+ looppoint = info->intro_length > 0 ? info->intro_length : 0;
+
+ gme_free_info(info);
+ return max(looppoint, 0);
+ }
+ else
+#endif
+ if (!music || I_SongType() == MU_MOD || I_SongType() == MU_MID)
+ return 0;
+ else
+ return (UINT32)(loop_point * 1000);
+}
+
+boolean I_SetSongPosition(UINT32 position)
+{
+ UINT32 length;
+#ifdef HAVE_LIBGME
+ if (gme)
+ {
+ // this is unstable, so fail silently
+ return true;
+ // this isn't required technically, but GME thread-locks for a second
+ // if you seek too high from the counter
+ // length = I_GetSongLength();
+ // if (length)
+ // position = get_adjusted_position(position);
+
+ // SDL_LockAudio();
+ // gme_err_t gme_e = gme_seek(gme, position);
+ // SDL_UnlockAudio();
+
+ // if (gme_e != NULL)
+ // {
+ // CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
+ // return false;
+ // }
+ // else
+ // return true;
+ }
+ else
+#endif
+ if (!music || I_SongType() == MU_MID)
+ return false;
+ else if (I_SongType() == MU_MOD)
+ return Mix_SetMusicPosition(position); // Goes by channels
+ else
+ {
+ // Because SDL mixer can't identify song length, if you have
+ // a position input greater than the real length, then
+ // music_bytes becomes inaccurate.
+
+ length = I_GetSongLength(); // get it in MS
+ if (length)
+ position = get_adjusted_position(position);
+
+ Mix_RewindMusic(); // needed for mp3
+ if(Mix_SetMusicPosition((float)(position/1000.0L)) == 0)
+ music_bytes = position/1000.0L*44100.0L*4; //assume 44.1khz, 4-byte length (see I_GetSongPosition)
+ else
+ // NOTE: This block fires on incorrect song format,
+ // NOT if position input is greater than song length.
+ music_bytes = 0;
+
+ return true;
+ }
+}
+
+UINT32 I_GetSongPosition(void)
+{
+#ifdef HAVE_LIBGME
+ if (gme)
+ {
+ INT32 position = gme_tell(gme);
+
+ gme_info_t *info;
+ gme_err_t gme_e = gme_track_info(gme, &info, current_track);
+
+ if (gme_e != NULL)
+ {
+ CONS_Alert(CONS_ERROR, "GME error: %s\n", gme_e);
+ return position;
+ }
+ else
+ {
+ // adjust position, since GME's counter keeps going past loop
+ if (info->length > 0)
+ position %= info->length;
+ else if (info->intro_length + info->loop_length > 0)
+ position = position >= (info->intro_length + info->loop_length) ? (position % info->loop_length) : position;
+ else
+ position %= 150 * 1000; // 2.5 minutes
+ }
+
+ gme_free_info(info);
+ return max(position, 0);
+ }
+ else
+#endif
+ if (!music || I_SongType() == MU_MID)
+ return 0;
+ else
+ return music_bytes/44100.0L*1000.0L/4; //assume 44.1khz
+ // 4 = byte length for 16-bit samples (AUDIO_S16SYS), stereo (2-channel)
+ // This is hardcoded in I_StartupSound. Other formats for factor:
+ // 8M: 1 | 8S: 2 | 16M: 2 | 16S: 4
+}
+
/// ------------------------
/// Music Playback
/// ------------------------
@@ -590,9 +902,21 @@ boolean I_LoadSong(char *data, size_t len)
const char *key1 = "LOOP";
const char *key2 = "POINT=";
const char *key3 = "MS=";
+ const char *key4 = "LENGTHMS=";
const size_t key1len = strlen(key1);
const size_t key2len = strlen(key2);
const size_t key3len = strlen(key3);
+ const size_t key4len = strlen(key4);
+
+ // for mp3 wide chars
+ const char *key1w = "L\0O\0O\0P\0";
+ const char *key2w = "P\0O\0I\0N\0T\0\0\0\xFF\xFE";
+ const char *key3w = "M\0S\0\0\0\xFF\xFE";
+ const char *key4w = "L\0E\0N\0G\0T\0H\0M\0S\0\0\0\xFF\xFE";
+ const char *wterm = "\0\0";
+ char wval[10];
+
+ size_t wstart, wp;
char *p = data;
SDL_RWops *rw;
@@ -603,6 +927,9 @@ boolean I_LoadSong(char *data, size_t len)
)
I_UnloadSong();
+ // always do this whether or not a music already exists
+ var_cleanup();
+
#ifdef HAVE_LIBGME
if ((UINT8)data[0] == 0x1F
&& (UINT8)data[1] == 0x8B)
@@ -712,30 +1039,88 @@ boolean I_LoadSong(char *data, size_t len)
// Find the OGG loop point.
loop_point = 0.0f;
+ song_length = 0.0f;
while ((UINT32)(p - data) < len)
{
- if (strncmp(p++, key1, key1len))
- continue;
- p += key1len-1; // skip OOP (the L was skipped in strncmp)
- if (!strncmp(p, key2, key2len)) // is it LOOPPOINT=?
+ if (fpclassify(loop_point) == FP_ZERO && !strncmp(p, key1, key1len))
{
- p += key2len; // skip POINT=
- loop_point = (float)((44.1L+atoi(p)) / 44100.0L); // LOOPPOINT works by sample count.
- // because SDL_Mixer is USELESS and can't even tell us
- // something simple like the frequency of the streaming music,
- // we are unfortunately forced to assume that ALL MUSIC is 44100hz.
- // This means a lot of tracks that are only 22050hz for a reasonable downloadable file size will loop VERY badly.
+ p += key1len; // skip LOOP
+ if (!strncmp(p, key2, key2len)) // is it LOOPPOINT=?
+ {
+ p += key2len; // skip POINT=
+ loop_point = (float)((44.1L+atoi(p)) / 44100.0L); // LOOPPOINT works by sample count.
+ // because SDL_Mixer is USELESS and can't even tell us
+ // something simple like the frequency of the streaming music,
+ // we are unfortunately forced to assume that ALL MUSIC is 44100hz.
+ // This means a lot of tracks that are only 22050hz for a reasonable downloadable file size will loop VERY badly.
+ }
+ else if (!strncmp(p, key3, key3len)) // is it LOOPMS=?
+ {
+ p += key3len; // skip MS=
+ loop_point = (float)(atoi(p) / 1000.0L); // LOOPMS works by real time, as miliseconds.
+ // Everything that uses LOOPMS will work perfectly with SDL_Mixer.
+ }
}
- else if (!strncmp(p, key3, key3len)) // is it LOOPMS=?
+ else if (fpclassify(song_length) == FP_ZERO && !strncmp(p, key4, key4len)) // is it LENGTHMS=?
{
- p += key3len; // skip MS=
- loop_point = (float)(atoi(p) / 1000.0L); // LOOPMS works by real time, as miliseconds.
- // Everything that uses LOOPMS will work perfectly with SDL_Mixer.
+ p += key4len; // skip LENGTHMS
+ song_length = (float)(atoi(p) / 1000.0L);
+ }
+ // below: search MP3 or other tags that use wide char encoding
+ else if (fpclassify(loop_point) == FP_ZERO && !memcmp(p, key1w, key1len*2)) // LOOP wide char
+ {
+ p += key1len*2;
+ if (!memcmp(p, key2w, (key2len+1)*2)) // POINT= wide char
+ {
+ p += (key2len+1)*2;
+ wstart = (size_t)p;
+ wp = 0;
+ while (wp < 9 && memcmp(p, wterm, 2))
+ {
+ wval[wp] = *p;
+ p += 2;
+ wp = ((size_t)(p-wstart))/2;
+ }
+ wval[min(wp, 9)] = 0;
+ loop_point = (float)((44.1L+atoi(wval) / 44100.0L));
+ }
+ else if (!memcmp(p, key3w, (key3len+1)*2)) // MS= wide char
+ {
+ p += (key3len+1)*2;
+ wstart = (size_t)p;
+ wp = 0;
+ while (wp < 9 && memcmp(p, wterm, 2))
+ {
+ wval[wp] = *p;
+ p += 2;
+ wp = ((size_t)(p-wstart))/2;
+ }
+ wval[min(wp, 9)] = 0;
+ loop_point = (float)(atoi(wval) / 1000.0L);
+ }
+ }
+ else if (fpclassify(song_length) == FP_ZERO && !memcmp(p, key4w, (key4len+1)*2)) // LENGTHMS= wide char
+ {
+ p += (key4len+1)*2;
+ wstart = (size_t)p;
+ wp = 0;
+ while (wp < 9 && memcmp(p, wterm, 2))
+ {
+ wval[wp] = *p;
+ p += 2;
+ wp = ((size_t)(p-wstart))/2;
+ }
+ wval[min(wp, 9)] = 0;
+ song_length = (float)(atoi(wval) / 1000.0L);
}
- // Neither?! Continue searching.
- }
+ if (fpclassify(loop_point) != FP_ZERO && fpclassify(song_length) != FP_ZERO && song_length > loop_point) // Got what we needed
+ // the last case is a sanity check, in case the wide char searches were false matches.
+ break;
+ else // continue searching
+ p++;
+ }
return true;
}
@@ -772,20 +1157,37 @@ boolean I_PlaySong(boolean looping)
if (!music)
return false;
- if (Mix_PlayMusic(music, looping && loop_point == 0.0f ? -1 : 0) == -1)
+ if (fpclassify(song_length) == FP_ZERO && (I_SongType() == MU_OGG || I_SongType() == MU_MP3 || I_SongType() == MU_FLAC))
+ CONS_Debug(DBG_DETAILED, "This song is missing a LENGTHMS= tag! Required to make seeking work properly.\n");
+
+ if (I_SongType() != MU_MOD && I_SongType() != MU_MID && Mix_PlayMusic(music, 0) == -1)
+ {
+ CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
+ return false;
+ }
+ else if ((I_SongType() == MU_MOD || I_SongType() == MU_MID) && Mix_PlayMusic(music, looping ? -1 : 0) == -1) // if MOD, loop forever
{
CONS_Alert(CONS_ERROR, "Mix_PlayMusic: %s\n", Mix_GetError());
return false;
}
- Mix_VolumeMusic((UINT32)music_volume*128/31);
- if (loop_point != 0.0f)
- Mix_HookMusicFinished(music_loop);
+ is_looping = looping;
+
+ I_SetMusicVolume(music_volume);
+
+ if (I_SongType() != MU_MOD && I_SongType() != MU_MID)
+ Mix_HookMusicFinished(music_loop); // don't bother counting if MOD
+
+ if(I_SongType() != MU_MOD && I_SongType() != MU_MID && !Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL))
+ CONS_Alert(CONS_WARNING, "Error registering SDL music position counter: %s\n", Mix_GetError());
+
return true;
}
void I_StopSong(void)
{
+ I_StopFadingSong();
+
#ifdef HAVE_LIBGME
if (gme)
{
@@ -795,19 +1197,40 @@ void I_StopSong(void)
#endif
if (music)
{
+ Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
Mix_HookMusicFinished(NULL);
Mix_HaltMusic();
}
+
+ var_cleanup();
}
void I_PauseSong(void)
{
+ if(I_SongType() == MU_MID) // really, SDL Mixer? why can't you pause MIDI???
+ return;
+
+ if(I_SongType() != MU_GME && I_SongType() != MU_MOD && I_SongType() != MU_MID)
+ Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes);
+
Mix_PauseMusic();
songpaused = true;
}
void I_ResumeSong(void)
{
+ if (I_SongType() == MU_MID)
+ return;
+
+ if (I_SongType() != MU_GME && I_SongType() != MU_MOD && I_SongType() != MU_MID)
+ {
+ while(Mix_UnregisterEffect(MIX_CHANNEL_POST, count_music_bytes) != 0) { }
+ // HACK: fixes issue of multiple effect callbacks being registered
+
+ if(music && I_SongType() != MU_MOD && I_SongType() != MU_MID && !Mix_RegisterEffect(MIX_CHANNEL_POST, count_music_bytes, NULL, NULL))
+ CONS_Alert(CONS_WARNING, "Error registering SDL music position counter: %s\n", Mix_GetError());
+ }
+
Mix_ResumeMusic();
songpaused = false;
}
@@ -826,7 +1249,7 @@ void I_SetMusicVolume(UINT8 volume)
#endif
music_volume = volume;
- Mix_VolumeMusic((UINT32)music_volume*128/31);
+ Mix_VolumeMusic(get_real_volume(music_volume));
}
boolean I_SetSongTrack(int track)
@@ -855,9 +1278,100 @@ boolean I_SetSongTrack(int track)
SDL_UnlockAudio();
return false;
}
+ else
#endif
+ if (I_SongType() == MU_MOD)
+ return !Mix_SetMusicPosition(track);
(void)track;
return false;
}
+/// ------------------------
+/// MUSIC FADING
+/// ------------------------
+
+void I_SetInternalMusicVolume(UINT8 volume)
+{
+ internal_volume = volume;
+ if (!I_SongPlaying())
+ return;
+ Mix_VolumeMusic(get_real_volume(music_volume));
+}
+
+void I_StopFadingSong(void)
+{
+ if (fading_id)
+ SDL_RemoveTimer(fading_id);
+ is_fading = false;
+ fading_source = fading_target = fading_timer = fading_duration = fading_id = 0;
+}
+
+boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void))
+{
+ INT16 volume_delta;
+
+ source_volume = min(source_volume, 100);
+ volume_delta = (INT16)(target_volume - source_volume);
+
+ I_StopFadingSong();
+
+ if (!ms && volume_delta)
+ {
+ I_SetInternalMusicVolume(target_volume);
+ if (callback)
+ (*callback)();
+ return true;
+
+ }
+ else if (!volume_delta)
+ {
+ if (callback)
+ (*callback)();
+ return true;
+ }
+
+ // Round MS to nearest 10
+ // If n - lower > higher - n, then round up
+ ms = (ms - ((ms / 10) * 10) > (((ms / 10) * 10) + 10) - ms) ?
+ (((ms / 10) * 10) + 10) // higher
+ : ((ms / 10) * 10); // lower
+
+ if (!ms)
+ I_SetInternalMusicVolume(target_volume);
+ else if (source_volume != target_volume)
+ {
+ fading_id = SDL_AddTimer(10, music_fade, NULL);
+ if (fading_id)
+ {
+ is_fading = true;
+ fading_timer = fading_duration = ms;
+ fading_source = source_volume;
+ fading_target = target_volume;
+ fading_callback = callback;
+
+ if (internal_volume != source_volume)
+ I_SetInternalMusicVolume(source_volume);
+ }
+ }
+
+ return is_fading;
+}
+
+boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void))
+{
+ return I_FadeSongFromVolume(target_volume, internal_volume, ms, callback);
+}
+
+boolean I_FadeOutStopSong(UINT32 ms)
+{
+ return I_FadeSongFromVolume(0, internal_volume, ms, &I_StopSong);
+}
+
+boolean I_FadeInPlaySong(UINT32 ms, boolean looping)
+{
+ if (I_PlaySong(looping))
+ return I_FadeSongFromVolume(100, 0, ms, NULL);
+ else
+ return false;
+}
#endif
diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c
index ebd615de2..d9967ae03 100644
--- a/src/sdl/sdl_sound.c
+++ b/src/sdl/sdl_sound.c
@@ -1186,6 +1186,12 @@ void I_StartupSound(void)
// Configure sound device
CONS_Printf("I_StartupSound:\n");
+#ifdef _WIN32
+ // Force DirectSound instead of WASAPI
+ // SDL 2.0.6+ defaults to the latter and it screws up our sound effects
+ SDL_setenv("SDL_AUDIODRIVER", "directsound", 1);
+#endif
+
// EE inits audio first so we're following along.
if (SDL_WasInit(SDL_INIT_AUDIO) == SDL_INIT_AUDIO)
CONS_Printf("SDL Audio already started\n");
@@ -1369,6 +1375,37 @@ boolean I_SetSongSpeed(float speed)
return false;
}
+/// ------------------------
+// MUSIC SEEKING
+/// ------------------------
+
+UINT32 I_GetSongLength(void)
+{
+ return 0;
+}
+
+boolean I_SetSongLoopPoint(UINT32 looppoint)
+{
+ (void)looppoint;
+ return false;
+}
+
+UINT32 I_GetSongLoopPoint(void)
+{
+ return 0;
+}
+
+boolean I_SetSongPosition(UINT32 position)
+{
+ (void)position;
+ return false;
+}
+
+UINT32 I_GetSongPosition(void)
+{
+ return 0;
+}
+
/// ------------------------
// MUSIC PLAYBACK
/// ------------------------
@@ -1437,6 +1474,47 @@ boolean I_SetSongTrack(int track)
return false;
}
+/// ------------------------
+/// MUSIC FADING
+/// ------------------------
+
+void I_SetInternalMusicVolume(UINT8 volume)
+{
+ (void)volume;
+}
+
+void I_StopFadingSong(void)
+{
+}
+
+boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void))
+{
+ (void)target_volume;
+ (void)source_volume;
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void))
+{
+ (void)target_volume;
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeOutStopSong(UINT32 ms)
+{
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeInPlaySong(UINT32 ms, boolean looping)
+{
+ (void)ms;
+ (void)looping;
+ return false;
+}
+
/// ------------------------
// MUSIC LOADING AND CLEANUP
// \todo Split logic between loading and playing,
diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h
index fea1e1648..d12daaa8a 100644
--- a/src/sdl/sdlmain.h
+++ b/src/sdl/sdlmain.h
@@ -1,7 +1,7 @@
// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
-// Copyright (C) 2006 by Sonic Team Jr.
+// Copyright (C) 2006-2018 by Sonic Team Jr.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
diff --git a/src/sdl/srb2icon.png b/src/sdl/srb2icon.png
new file mode 100644
index 000000000..cdee18a84
Binary files /dev/null and b/src/sdl/srb2icon.png differ
diff --git a/src/sounds.c b/src/sounds.c
index 35b21e590..66d498838 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -229,6 +229,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"hoop3", false, 192, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Hoop++"},
{"hidden", false, 204, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Discovery"},
{"prloop", false, 104, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Gust of wind"},
+ {"timeup", true, 256, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ominous Countdown"},
{"ngjump", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Jump"},
{"peww", false, 96, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pew"},
diff --git a/src/sounds.h b/src/sounds.h
index 5fffc7b2e..fd1142aba 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -295,6 +295,7 @@ typedef enum
sfx_hoop3,
sfx_hidden,
sfx_prloop,
+ sfx_timeup,
sfx_ngjump,
sfx_peww,
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 986c2f48b..9ad04b5ce 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -1873,7 +1873,7 @@ static void ST_drawNiGHTSHUD(void)
}
}
-static inline void ST_drawWeaponSelect(INT32 xoffs, INT32 y)
+static void ST_drawWeaponSelect(INT32 xoffs, INT32 y)
{
INT32 q = stplyr->weapondelay, del = 0, p = 16;
while (q)
@@ -2085,7 +2085,7 @@ static void ST_drawTextHUD(void)
}
if (!splitscreen && dof12)
- textHUDdraw(M_GetText("\x82""F12:""\x80 Switch view"))
+ textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
if (circuitmap)
{
diff --git a/src/st_stuff.h b/src/st_stuff.h
index 9c48a38af..e1d8a8a92 100644
--- a/src/st_stuff.h
+++ b/src/st_stuff.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/string.c b/src/string.c
index d7f8b3679..2a03e8729 100644
--- a/src/string.c
+++ b/src/string.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2006 by Graue.
-// Copyright (C) 2006-2016 by Sonic Team Junior.
+// Copyright (C) 2006-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/tables.c b/src/tables.c
index e181b9aa8..a31572d70 100644
--- a/src/tables.c
+++ b/src/tables.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -37,6 +37,15 @@ unsigned SlopeDiv(unsigned num, unsigned den)
return ans <= SLOPERANGE ? ans : SLOPERANGE;
}
+UINT64 SlopeDivEx(unsigned int num, unsigned int den)
+{
+ UINT64 ans;
+ if (den < 512)
+ return SLOPERANGE;
+ ans = ((UINT64)num<<3)/(den>>8);
+ return ans <= SLOPERANGE ? ans : SLOPERANGE;
+}
+
fixed_t AngleFixed(angle_t af)
{
angle_t wa = ANGLE_180;
diff --git a/src/tables.h b/src/tables.h
index 03e80788a..60db2b5a6 100644
--- a/src/tables.h
+++ b/src/tables.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -81,6 +81,8 @@ extern angle_t tantoangle[SLOPERANGE+1];
// Utility function, called by R_PointToAngle.
FUNCMATH unsigned SlopeDiv(unsigned num, unsigned den);
+// Only called by R_PointToAngleEx
+UINT64 SlopeDivEx(unsigned int num, unsigned int den);
// 360 - angle_t(ANGLE_45) = ANGLE_315
FUNCMATH FUNCINLINE static ATTRINLINE angle_t InvAngle(angle_t a)
diff --git a/src/tmap.nas b/src/tmap.nas
index bb4a8c672..c72c1890a 100644
--- a/src/tmap.nas
+++ b/src/tmap.nas
@@ -1,7 +1,7 @@
;; SONIC ROBO BLAST 2
;;-----------------------------------------------------------------------------
;; Copyright (C) 1998-2000 by DooM Legacy Team.
-;; Copyright (C) 1999-2016 by Sonic Team Junior.
+;; Copyright (C) 1999-2018 by Sonic Team Junior.
;;
;; This program is free software distributed under the
;; terms of the GNU General Public License, version 2.
diff --git a/src/tmap.s b/src/tmap.s
index ea1f1c26c..05c644798 100644
--- a/src/tmap.s
+++ b/src/tmap.s
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -13,7 +13,7 @@
// structures, must match the C structures!
#include "asm_defs.inc"
-// Rappel: seuls EAX, ECX, EDX peuvent ˆtre ‚cras‚s librement.
+// Rappel: seuls EAX, ECX, EDX peuvent �tre �cras�s librement.
// il faut sauver esi,edi, cd...gs
/* Attention aux comparaisons! */
@@ -28,7 +28,7 @@
/* cmp A,B // B-A , set flags */
/* jg B_greater_than_A */
/* */
-/* (soustrait l'op‚rande source DE l'op‚rande destination, */
+/* (soustrait l'op�rande source DE l'op�rande destination, */
/* comme sur Motorola! ) */
// RAPPEL: Intel
@@ -66,7 +66,7 @@ C(vidwidth): .long 0 //use this one out of the inner loops
.globl C(ASM_PatchRowBytes)
C(ASM_PatchRowBytes):
pushl %ebp
- movl %esp, %ebp // assure l'"adressabilit‚ du stack"
+ movl %esp, %ebp // assure l'"adressabilit� du stack"
movl ARG1, %edx // read first arg
movl %edx, C(vidwidth)
diff --git a/src/tmap_asm.s b/src/tmap_asm.s
index e8b7af1cd..99cb0b627 100644
--- a/src/tmap_asm.s
+++ b/src/tmap_asm.s
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/tmap_mmx.nas b/src/tmap_mmx.nas
index 011b2a062..c512de8e9 100644
--- a/src/tmap_mmx.nas
+++ b/src/tmap_mmx.nas
@@ -1,7 +1,7 @@
;; SONIC ROBO BLAST 2
;;-----------------------------------------------------------------------------
;; Copyright (C) 1998-2000 by DOSDOOM.
-;; Copyright (C) 2010-2016 by Sonic Team Junior.
+;; Copyright (C) 2010-2018 by Sonic Team Junior.
;;
;; This program is free software distributed under the
;; terms of the GNU General Public License, version 2.
diff --git a/src/tmap_vc.nas b/src/tmap_vc.nas
index 8fe39ec89..e943d48d8 100644
--- a/src/tmap_vc.nas
+++ b/src/tmap_vc.nas
@@ -1,7 +1,7 @@
;; SONIC ROBO BLAST 2
;;-----------------------------------------------------------------------------
;; Copyright (C) 1998-2000 by DooM Legacy Team.
-;; Copyright (C) 1999-2016 by Sonic Team Junior.
+;; Copyright (C) 1999-2018 by Sonic Team Junior.
;;
;; This program is free software distributed under the
;; terms of the GNU General Public License, version 2.
diff --git a/src/v_video.c b/src/v_video.c
index ae7c08511..08f65e98b 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -74,6 +74,8 @@ consvar_t cv_csaturation = {"csaturation", "10", CV_SAVE|CV_CALL, saturation_con
consvar_t cv_bsaturation = {"bsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_msaturation = {"msaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_allcaps = {"allcaps", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+
static CV_PossibleValue_t constextsize_cons_t[] = {
{V_NOSCALEPATCH, "Small"}, {V_SMALLSCALEPATCH, "Medium"}, {V_MEDSCALEPATCH, "Large"}, {0, "Huge"},
{0, NULL}};
@@ -547,8 +549,8 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t
return;
#ifdef HWRENDER
- // oh please
- if (rendermode != render_soft && !con_startup)
+ //if (rendermode != render_soft && !con_startup) // Why?
+ if (rendermode != render_soft)
{
HWR_DrawFixedPatch((GLPatch_t *)patch, x, y, pscale, scrn, colormap);
return;
@@ -1311,6 +1313,145 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
memset(dest, c, w * vid.bpp);
}
+#ifdef HWRENDER
+// This is now a function since it's otherwise repeated 2 times and honestly looks retarded:
+static UINT32 V_GetHWConsBackColor(void)
+{
+ UINT32 hwcolor;
+ switch (cons_backcolor.value)
+ {
+ case 0: hwcolor = 0xffffff00; break; // White
+ case 1: hwcolor = 0x80808000; break; // Gray
+ case 2: hwcolor = 0xdeb88700; break; // Sepia
+ case 3: hwcolor = 0x40201000; break; // Brown
+ case 4: hwcolor = 0xfa807200; break; // Pink
+ case 5: hwcolor = 0xff69b400; break; // Raspberry
+ case 6: hwcolor = 0xff000000; break; // Red
+ case 7: hwcolor = 0xffd68300; break; // Creamsicle
+ case 8: hwcolor = 0xff800000; break; // Orange
+ case 9: hwcolor = 0xdaa52000; break; // Gold
+ case 10: hwcolor = 0x80800000; break; // Yellow
+ case 11: hwcolor = 0x00ff0000; break; // Emerald
+ case 12: hwcolor = 0x00800000; break; // Green
+ case 13: hwcolor = 0x4080ff00; break; // Cyan
+ case 14: hwcolor = 0x4682b400; break; // Steel
+ case 15: hwcolor = 0x1e90ff00; break; // Periwinkle
+ case 16: hwcolor = 0x0000ff00; break; // Blue
+ case 17: hwcolor = 0xff00ff00; break; // Purple
+ case 18: hwcolor = 0xee82ee00; break; // Lavender
+ // Default green
+ default: hwcolor = 0x00800000; break;
+ }
+ return hwcolor;
+}
+#endif
+
+
+// THANK YOU MPC!!!
+
+void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
+{
+ UINT8 *dest;
+ INT32 u, v;
+ UINT8 *fadetable;
+ UINT32 alphalevel = 0;
+
+ if (rendermode == render_none)
+ return;
+
+#ifdef HWRENDER
+ if (rendermode != render_soft && rendermode != render_none)
+ {
+ UINT32 hwcolor = V_GetHWConsBackColor();
+ HWR_DrawConsoleFill(x, y, w, h, hwcolor, c); // we still use the regular color stuff but only for flags. actual draw color is "hwcolor" for this.
+ return;
+ }
+#endif
+
+ if (!(c & V_NOSCALESTART))
+ {
+ INT32 dupx = vid.dupx, dupy = vid.dupy;
+
+ if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
+ { // Clear the entire screen, from dest to deststop. Yes, this really works.
+ memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp);
+ return;
+ }
+
+ x *= dupx;
+ y *= dupy;
+ w *= dupx;
+ h *= dupy;
+
+ // Center it if necessary
+ if (vid.width != BASEVIDWIDTH * dupx)
+ {
+ // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
+ // so center this imaginary screen
+ if (c & V_SNAPTORIGHT)
+ x += (vid.width - (BASEVIDWIDTH * dupx));
+ else if (!(c & V_SNAPTOLEFT))
+ x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
+ }
+ if (vid.height != BASEVIDHEIGHT * dupy)
+ {
+ // same thing here
+ if (c & V_SNAPTOBOTTOM)
+ y += (vid.height - (BASEVIDHEIGHT * dupy));
+ else if (!(c & V_SNAPTOTOP))
+ y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
+ }
+ }
+
+ if (x >= vid.width || y >= vid.height)
+ return; // off the screen
+ if (x < 0) {
+ w += x;
+ x = 0;
+ }
+ if (y < 0) {
+ h += y;
+ y = 0;
+ }
+
+ if (w <= 0 || h <= 0)
+ return; // zero width/height wouldn't draw anything
+ if (x + w > vid.width)
+ w = vid.width-x;
+ if (y + h > vid.height)
+ h = vid.height-y;
+
+ dest = screens[0] + y*vid.width + x;
+
+ if ((alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT)))
+ {
+ if (alphalevel == 13)
+ alphalevel = hudminusalpha[cv_translucenthud.value];
+ else if (alphalevel == 14)
+ alphalevel = 10 - cv_translucenthud.value;
+ else if (alphalevel == 15)
+ alphalevel = hudplusalpha[cv_translucenthud.value];
+
+ if (alphalevel >= 10)
+ return; // invis
+ }
+
+ c &= 255;
+
+ // Jimita (12-04-2018)
+ w = min(w, vid.width);
+ h = min(h, vid.height);
+ fadetable = ((UINT8 *)transtables + ((alphalevel-1)<> V_CHARCOLORSHIFT)
{
@@ -1599,6 +1726,32 @@ void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed)
V_DrawScaledPatch(x, y, flags, hu_font[c]);
}
+// Writes a single character for the chat. (draw WHITE if bit 7 set)
+// Essentially the same as the above but it's small or big depending on what resolution you've chosen to huge..
+//
+void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UINT8 *colormap)
+{
+ INT32 w, flags;
+ //const UINT8 *colormap = V_GetStringColormap(c);
+
+ flags = c & ~(V_CHARCOLORMASK | V_PARAMMASK);
+ c &= 0x7f;
+ if (lowercaseallowed)
+ c -= HU_FONTSTART;
+ else
+ c = toupper(c) - HU_FONTSTART;
+ if (c < 0 || c >= HU_FONTSIZE || !hu_font[c])
+ return;
+
+ w = (vid.width < 640 ) ? (SHORT(hu_font[c]->width)/2) : (SHORT(hu_font[c]->width)); // use normal sized characters if we're using a terribly low resolution.
+ if (x + w > vid.width)
+ return;
+
+ V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, (vid.width < 640) ? (FRACUNIT) : (FRACUNIT/2), flags, hu_font[c], colormap);
+
+
+}
+
// Precompile a wordwrapped string to any given width.
// This is a muuuch better method than V_WORDWRAP.
char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
diff --git a/src/v_video.h b/src/v_video.h
index 84a027963..43748692e 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -31,7 +31,8 @@ extern consvar_t cv_ticrate, cv_constextsize,\
cv_globalgamma, cv_globalsaturation, \
cv_rhue, cv_yhue, cv_ghue, cv_chue, cv_bhue, cv_mhue,\
cv_rgamma, cv_ygamma, cv_ggamma, cv_cgamma, cv_bgamma, cv_mgamma, \
-cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation;
+cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation,\
+cv_allcaps;
// Allocates buffer screens, call before R_Init.
void V_Init(void);
@@ -151,6 +152,7 @@ void V_DrawScaledPic (INT32 px1, INT32 py1, INT32 scrn, INT32 lumpnum);
// fill a box with a single color
void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c);
+void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c);
// fill a box with a flat as a pattern
void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum);
@@ -162,11 +164,16 @@ void V_DrawPromptBack(INT32 boxheight, INT32 color);
// draw a single character
void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed);
+// draw a single character, but for the chat
+void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UINT8 *colormap);
+
+UINT8 *V_GetStringColormap(INT32 colorflags);
void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string);
// wordwrap a string using the hu_font
char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string);
+UINT8 *V_GetStringColormap(INT32 colorflags);
// draw a string using the hu_font
void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string);
diff --git a/src/vid_copy.s b/src/vid_copy.s
index 9d8e7d4e0..050a80999 100644
--- a/src/vid_copy.s
+++ b/src/vid_copy.s
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/w_wad.c b/src/w_wad.c
index 258552950..a0f93a14b 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -77,14 +77,25 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#define O_BINARY 0
#endif
-#if defined(_MSC_VER)
-#pragma pack(1)
+#ifdef HAVE_ZLIB
+#ifndef _MSC_VER
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
#endif
-#if defined(_MSC_VER)
-#pragma pack()
+#ifndef _LFS64_LARGEFILE
+#define _LFS64_LARGEFILE
#endif
+#ifndef _FILE_OFFSET_BITS
+#define _FILE_OFFSET_BITS 0
+#endif
+
+#include "zlib.h"
+#endif
+
+
typedef struct
{
const char *name;
@@ -119,10 +130,11 @@ void W_Shutdown(void)
while (numwadfiles--)
{
fclose(wadfiles[numwadfiles]->handle);
- Z_Free(wadfiles[numwadfiles]->lumpinfo);
Z_Free(wadfiles[numwadfiles]->filename);
while (wadfiles[numwadfiles]->numlumps--)
Z_Free(wadfiles[numwadfiles]->lumpinfo[wadfiles[numwadfiles]->numlumps].name2);
+
+ Z_Free(wadfiles[numwadfiles]->lumpinfo);
Z_Free(wadfiles[numwadfiles]);
}
}
@@ -183,9 +195,10 @@ FILE *W_OpenWadFile(const char **filename, boolean useerrors)
}
// Look for all DEHACKED and Lua scripts inside a PK3 archive.
-static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum)
+static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum, boolean mainfile)
{
UINT16 posStart, posEnd;
+#ifdef HAVE_BLUA
posStart = W_CheckNumForFolderStartPK3("Lua/", wadnum, 0);
if (posStart != INT16_MAX)
{
@@ -194,10 +207,12 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum)
for (; posStart < posEnd; posStart++)
LUA_LoadLump(wadnum, posStart);
}
+#endif
posStart = W_CheckNumForFolderStartPK3("SOC/", wadnum, 0);
if (posStart != INT16_MAX)
{
posEnd = W_CheckNumForFolderEndPK3("SOC/", wadnum, posStart);
+
posStart++; // first "lump" will be "SOC/" folder itself, so ignore it
for(; posStart < posEnd; posStart++)
{
@@ -206,16 +221,15 @@ static inline void W_LoadDehackedLumpsPK3(UINT16 wadnum)
char *name = malloc(length + 1);
sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2);
name[length] = '\0';
-
CONS_Printf(M_GetText("Loading SOC from %s\n"), name);
- DEH_LoadDehackedLumpPwad(wadnum, posStart);
+ DEH_LoadDehackedLumpPwad(wadnum, posStart, mainfile);
free(name);
}
}
}
// search for all DEHACKED lump in all wads and load it
-static inline void W_LoadDehackedLumps(UINT16 wadnum)
+static inline void W_LoadDehackedLumps(UINT16 wadnum, boolean mainfile)
{
UINT16 lump;
@@ -234,24 +248,24 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum)
for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++)
if (memcmp(lump_p->name,"SOC_",4)==0) // Check for generic SOC lump
{ // shameless copy+paste of code from LUA_LoadLump
- size_t len = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
- char *name = malloc(len+1);
+ size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name
+ char *name = malloc(length + 1);
sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2);
- name[len] = '\0';
+ name[length] = '\0';
CONS_Printf(M_GetText("Loading SOC from %s\n"), name);
- DEH_LoadDehackedLumpPwad(wadnum, lump);
+ DEH_LoadDehackedLumpPwad(wadnum, lump, mainfile);
free(name);
}
else if (memcmp(lump_p->name,"MAINCFG",8)==0) // Check for MAINCFG
{
CONS_Printf(M_GetText("Loading main config from %s\n"), wadfiles[wadnum]->filename);
- DEH_LoadDehackedLumpPwad(wadnum, lump);
+ DEH_LoadDehackedLumpPwad(wadnum, lump, mainfile);
}
else if (memcmp(lump_p->name,"OBJCTCFG",8)==0) // Check for OBJCTCFG
{
CONS_Printf(M_GetText("Loading object config from %s\n"), wadfiles[wadnum]->filename);
- DEH_LoadDehackedLumpPwad(wadnum, lump);
+ DEH_LoadDehackedLumpPwad(wadnum, lump, mainfile);
}
}
@@ -313,6 +327,324 @@ static void W_InvalidateLumpnumCache(void)
memset(lumpnumcache, 0, sizeof (lumpnumcache));
}
+/** Detect a file type.
+ * \todo Actually detect the wad/pkzip headers and whatnot, instead of just checking the extensions.
+ */
+static restype_t ResourceFileDetect (const char* filename)
+{
+ if (!stricmp(&filename[strlen(filename) - 4], ".pk3"))
+ return RET_PK3;
+ if (!stricmp(&filename[strlen(filename) - 4], ".soc"))
+ return RET_SOC;
+ if (!stricmp(&filename[strlen(filename) - 4], ".lua"))
+ return RET_LUA;
+
+ return RET_WAD;
+}
+
+/** Create a 1-lump lumpinfo_t for standalone files.
+ */
+static lumpinfo_t* ResGetLumpsStandalone (FILE* handle, UINT16* numlumps, const char* lumpname)
+{
+ lumpinfo_t* lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
+ lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
+ lumpinfo->position = 0;
+ fseek(handle, 0, SEEK_END);
+ lumpinfo->size = ftell(handle);
+ fseek(handle, 0, SEEK_SET);
+ strcpy(lumpinfo->name, lumpname);
+ // Allocate the lump's full name.
+ lumpinfo->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+ strcpy(lumpinfo->name2, lumpname);
+ lumpinfo->name2[8] = '\0';
+ *numlumps = 1;
+ return lumpinfo;
+}
+
+/** Create a lumpinfo_t array for a WAD file.
+ */
+static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filename)
+{
+ UINT16 numlumps = *nlmp;
+ lumpinfo_t* lumpinfo;
+ size_t i;
+ INT32 compressed = 0;
+
+ wadinfo_t header;
+ lumpinfo_t *lump_p;
+ filelump_t *fileinfo;
+ void *fileinfov;
+
+ // read the header
+ if (fread(&header, 1, sizeof header, handle) < sizeof header)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("Can't read wad header because %s\n"), strerror(ferror(handle)));
+ return NULL;
+ }
+
+ if (memcmp(header.identification, "ZWAD", 4) == 0)
+ compressed = 1;
+ else if (memcmp(header.identification, "IWAD", 4) != 0
+ && memcmp(header.identification, "PWAD", 4) != 0
+ && memcmp(header.identification, "SDLL", 4) != 0)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("Invalid WAD header\n"));
+ return NULL;
+ }
+
+ header.numlumps = LONG(header.numlumps);
+ header.infotableofs = LONG(header.infotableofs);
+
+ // read wad file directory
+ i = header.numlumps * sizeof (*fileinfo);
+ fileinfov = fileinfo = malloc(i);
+ if (fseek(handle, header.infotableofs, SEEK_SET) == -1
+ || fread(fileinfo, 1, i, handle) < i)
+ {
+ CONS_Alert(CONS_ERROR, M_GetText("Corrupt wadfile directory (%s)\n"), strerror(ferror(handle)));
+ free(fileinfov);
+ return NULL;
+ }
+
+ numlumps = header.numlumps;
+
+ // fill in lumpinfo for this wad
+ lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL);
+ for (i = 0; i < numlumps; i++, lump_p++, fileinfo++)
+ {
+ lump_p->position = LONG(fileinfo->filepos);
+ lump_p->size = lump_p->disksize = LONG(fileinfo->size);
+ if (compressed) // wad is compressed, lump might be
+ {
+ UINT32 realsize = 0;
+ if (fseek(handle, lump_p->position, SEEK_SET)
+ == -1 || fread(&realsize, 1, sizeof realsize,
+ handle) < sizeof realsize)
+ {
+ I_Error("corrupt compressed file: %s; maybe %s", /// \todo Avoid the bailout?
+ filename, strerror(ferror(handle)));
+ }
+ realsize = LONG(realsize);
+ if (realsize != 0)
+ {
+ lump_p->size = realsize;
+ lump_p->compression = CM_LZF;
+ }
+ else
+ {
+ lump_p->size -= 4;
+ lump_p->compression = CM_NOCOMPRESSION;
+ }
+
+ lump_p->position += 4;
+ lump_p->disksize -= 4;
+ }
+ else
+ lump_p->compression = CM_NOCOMPRESSION;
+ memset(lump_p->name, 0x00, 9);
+ strncpy(lump_p->name, fileinfo->name, 8);
+ // Allocate the lump's full name.
+ lump_p->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
+ strncpy(lump_p->name2, fileinfo->name, 8);
+ lump_p->name2[8] = '\0';
+ }
+ free(fileinfov);
+ *nlmp = numlumps;
+ return lumpinfo;
+}
+
+/** Optimized pattern search in a file.
+ */
+static boolean ResFindSignature (FILE* handle, char endPat[], UINT32 startpos)
+{
+ char *s;
+ int c;
+
+ fseek(handle, startpos, SEEK_SET);
+ s = endPat;
+ while((c = fgetc(handle)) != EOF)
+ {
+ if (*s != c && s > endPat) // No match?
+ s = endPat; // We "reset" the counter by sending the s pointer back to the start of the array.
+ if (*s == c)
+ {
+ s++;
+ if (*s == 0x00) // The array pointer has reached the key char which marks the end. It means we have matched the signature.
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+#if defined(_MSC_VER)
+#pragma pack(1)
+#endif
+typedef struct zend_s
+{
+ char signature[4];
+ UINT16 diskpos;
+ UINT16 cdirdisk;
+ UINT16 diskentries;
+ UINT16 entries;
+ UINT32 cdirsize;
+ UINT32 cdiroffset;
+ UINT16 commentlen;
+} ATTRPACK zend_t;
+
+typedef struct zentry_s
+{
+ char signature[4];
+ UINT16 version;
+ UINT16 versionneeded;
+ UINT16 flags;
+ UINT16 compression;
+ UINT16 modtime;
+ UINT16 moddate;
+ UINT32 CRC32;
+ UINT32 compsize;
+ UINT32 size;
+ UINT16 namelen;
+ UINT16 xtralen;
+ UINT16 commlen;
+ UINT16 diskstart;
+ UINT16 attrint;
+ UINT32 attrext;
+ UINT32 offset;
+} ATTRPACK zentry_t;
+
+typedef struct zlentry_s
+{
+ char signature[4];
+ UINT16 versionneeded;
+ UINT16 flags;
+ UINT16 compression;
+ UINT16 modtime;
+ UINT16 moddate;
+ UINT32 CRC32;
+ UINT32 compsize;
+ UINT32 size;
+ UINT16 namelen;
+ UINT16 xtralen;
+} ATTRPACK zlentry_t;
+#if defined(_MSC_VER)
+#pragma pack()
+#endif
+
+/** Create a lumpinfo_t array for a PKZip file.
+ */
+static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp)
+{
+ zend_t zend;
+ zentry_t* zentries;
+ zentry_t* zentry;
+
+ UINT16 numlumps = *nlmp;
+ lumpinfo_t* lumpinfo;
+ lumpinfo_t *lump_p;
+ size_t i;
+
+ char pat_central[] = {0x50, 0x4b, 0x01, 0x02, 0x00};
+ char pat_end[] = {0x50, 0x4b, 0x05, 0x06, 0x00};
+
+ // Look for central directory end signature near end of file.
+ // Contains entry number (number of lumps), and central directory start offset.
+ fseek(handle, 0, SEEK_END);
+ if (!ResFindSignature(handle, pat_end, max(0, ftell(handle) - (22 + 65536))))
+ {
+ CONS_Alert(CONS_ERROR, "Missing central directory\n");
+ return NULL;
+ }
+
+ fseek(handle, -4, SEEK_CUR);
+ if (fread(&zend, 1, sizeof zend, handle) < sizeof zend)
+ {
+ CONS_Alert(CONS_ERROR, "Corrupt central directory (%s)\n", strerror(ferror(handle)));
+ return NULL;
+ }
+ numlumps = zend.entries;
+
+ lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL);
+ zentry = zentries = malloc(numlumps * sizeof (*zentries));
+
+ fseek(handle, zend.cdiroffset, SEEK_SET);
+ for (i = 0; i < numlumps; i++, zentry++, lump_p++)
+ {
+ char* fullname;
+ char* trimname;
+ char* dotpos;
+
+ if (fread(zentry, 1, sizeof(zentry_t), handle) < sizeof(zentry_t))
+ {
+ CONS_Alert(CONS_ERROR, "Failed to read central directory (%s)\n", strerror(ferror(handle)));
+ Z_Free(lumpinfo);
+ free(zentry);
+ return NULL;
+ }
+ if (memcmp(zentry->signature, pat_central, 4))
+ {
+ CONS_Alert(CONS_ERROR, "Central directory is corrupt\n");
+ Z_Free(lumpinfo);
+ free(zentry);
+ return NULL;
+ }
+
+ lump_p->position = zentry->offset + zentry->namelen + zentry->xtralen + sizeof(zlentry_t);
+ lump_p->disksize = zentry->compsize;
+ lump_p->size = zentry->size;
+
+ fullname = malloc(zentry->namelen + 1);
+ if (fgets(fullname, zentry->namelen + 1, handle) != fullname)
+ {
+ CONS_Alert(CONS_ERROR, "Unable to read lumpname (%s)\n", strerror(ferror(handle)));
+ Z_Free(lumpinfo);
+ free(zentry);
+ free(fullname);
+ return NULL;
+ }
+
+ // Strip away file address and extension for the 8char name.
+ if ((trimname = strrchr(fullname, '/')) != 0)
+ trimname++;
+ else
+ trimname = fullname; // Care taken for root files.
+
+ if ((dotpos = strrchr(trimname, '.')) == 0)
+ dotpos = fullname + strlen(fullname); // Watch for files without extension.
+
+ memset(lump_p->name, '\0', 9); // Making sure they're initialized to 0. Is it necessary?
+ strncpy(lump_p->name, trimname, min(8, dotpos - trimname));
+
+ lump_p->name2 = Z_Calloc(zentry->namelen + 1, PU_STATIC, NULL);
+ strncpy(lump_p->name2, fullname, zentry->namelen);
+
+ free(fullname);
+
+ switch(zentry->compression)
+ {
+ case 0:
+ lump_p->compression = CM_NOCOMPRESSION;
+ break;
+#ifdef HAVE_ZLIB
+ case 8:
+ lump_p->compression = CM_DEFLATE;
+ break;
+#endif
+ case 14:
+ lump_p->compression = CM_LZF;
+ break;
+ default:
+ CONS_Alert(CONS_WARNING, "%s: Unsupported compression method\n", fullname);
+ lump_p->compression = CM_UNSUPPORTED;
+ break;
+ }
+ }
+
+ *nlmp = numlumps;
+ return lumpinfo;
+}
+
// Allocate a wadfile, setup the lumpinfo (directory) and
// lumpcache, add the wadfile to the current active wadfiles
//
@@ -324,15 +656,14 @@ static void W_InvalidateLumpnumCache(void)
//
// Can now load dehacked files (.soc)
//
-UINT16 W_InitFile(const char *filename)
+UINT16 W_InitFile(const char *filename, boolean mainfile)
{
FILE *handle;
- lumpinfo_t *lumpinfo;
+ lumpinfo_t *lumpinfo = NULL;
wadfile_t *wadfile;
restype_t type;
- UINT16 numlumps;
+ UINT16 numlumps = 0;
size_t i;
- INT32 compressed = 0;
size_t packetsize;
UINT8 md5sum[16];
boolean important;
@@ -404,325 +735,30 @@ UINT16 W_InitFile(const char *filename)
}
#endif
- // detect dehacked file with the "soc" extension
- if (!stricmp(&filename[strlen(filename) - 4], ".soc"))
+ switch(type = ResourceFileDetect(filename))
{
- // This code emulates a wadfile with one lump name "OBJCTCFG"
- // at position 0 and size of the whole file.
- // This allows soc files to be like all wads, copied by network and loaded at the console.
- type = RET_SOC;
-
- numlumps = 1;
- lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
- lumpinfo->position = 0;
- fseek(handle, 0, SEEK_END);
- lumpinfo->size = ftell(handle);
- fseek(handle, 0, SEEK_SET);
- strcpy(lumpinfo->name, "OBJCTCFG");
- // Allocate the lump's full name.
- lumpinfo->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
- strcpy(lumpinfo->name2, "OBJCTCFG");
- lumpinfo->name2[8] = '\0';
- }
+ case RET_SOC:
+ lumpinfo = ResGetLumpsStandalone(handle, &numlumps, "OBJCTCFG");
+ break;
#ifdef HAVE_BLUA
- // detect lua script with the "lua" extension
- else if (!stricmp(&filename[strlen(filename) - 4], ".lua"))
- {
- // This code emulates a wadfile with one lump name "LUA_INIT"
- // at position 0 and size of the whole file.
- // This allows soc files to be like all wads, copied by network and loaded at the console.
- type = RET_LUA;
-
- numlumps = 1;
- lumpinfo = Z_Calloc(sizeof (*lumpinfo), PU_STATIC, NULL);
- lumpinfo->position = 0;
- fseek(handle, 0, SEEK_END);
- lumpinfo->size = ftell(handle);
- fseek(handle, 0, SEEK_SET);
- strcpy(lumpinfo->name, "LUA_INIT");
- // Allocate the lump's full name.
- lumpinfo->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
- strcpy(lumpinfo->name2, "LUA_INIT");
- lumpinfo->name2[8] = '\0';
- }
+ case RET_LUA:
+ lumpinfo = ResGetLumpsStandalone(handle, &numlumps, "LUA_INIT");
+ break;
#endif
- else if (!stricmp(&filename[strlen(filename) - 4], ".pk3"))
- {
- char curHeader[4];
- unsigned long size;
- char seekPat[] = {0x50, 0x4b, 0x01, 0x02, 0x00};
- char endPat[] = {0x50, 0x4b, 0x05, 0x06, 0x00};
- char *s;
- int c;
- UINT32 position;
- boolean matched = false;
- lumpinfo_t *lump_p;
-
- type = RET_PK3;
-
- // Obtain the file's size.
- fseek(handle, 0, SEEK_END);
- size = ftell(handle);
- CONS_Debug(DBG_SETUP, "PK3 size is: %ld\n", size);
-
- // We must look for the central directory through the file. (Thanks to JTE for this algorithm.)
- // All of the central directory entry headers have a signature of 0x50 0x4b 0x01 0x02.
- // The first entry found means the beginning of the central directory.
- fseek(handle, 0-min(size, (22 + 65536)), SEEK_CUR);
- s = endPat;
- while((c = fgetc(handle)) != EOF)
- {
- if (*s != c && s > endPat) // No match?
- s = endPat; // We "reset" the counter by sending the s pointer back to the start of the array.
- if (*s == c)
- {
- s++;
- if (*s == 0x00) // The array pointer has reached the key char which marks the end. It means we have matched the signature.
- {
- matched = true;
- CONS_Debug(DBG_SETUP, "Found PK3 central directory at position %ld.\n", ftell(handle));
- break;
- }
- }
- }
-
- // Error if we couldn't find the central directory at all. It likely means this is not a ZIP/PK3 file.
- if (matched == false)
- {
- CONS_Alert(CONS_ERROR, "No central directory inside PK3! File may be corrupted or incomplete.\n");
- return INT16_MAX;
- }
-
- fseek(handle, 4, SEEK_CUR);
- fread(&numlumps, 1, 2, handle);
- fseek(handle, 6, SEEK_CUR);
- fread(&position, 1, 4, handle);
- lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL);
- fseek(handle, position, SEEK_SET);
-
- // Since we found the central directory, now we can map our lumpinfo table.
- // We will look for file headers inside it, until we reach the central directory end signature.
- // We exactly know what data to expect this time, so now we don't need to do a byte-by-byte search.
- CONS_Debug(DBG_SETUP, "Now finding central directory file headers...\n");
- for (i = 0; i < numlumps; i++, lump_p++)
- {
- fread(curHeader, 1, 4, handle);
-
- // We found a central directory entry signature?
- if (!strncmp(curHeader, seekPat, 3))
- {
- // Let's fill in the fields that we actually need.
- // (Declaring all those vars might not be the optimal way to do this, sorry.)
- char *eName;
- int namePos;
- int nameEnd;
- unsigned short int eNameLen = 8;
- unsigned short int eXFieldLen = 0;
- unsigned short int lNameLen = 0;
- unsigned short int lXFieldLen = 0;
- unsigned short int eCommentLen = 0;
- unsigned short int eCompression = 0;
- unsigned int eSize = 0;
- unsigned int eCompSize = 0;
- unsigned int eLocalHeaderOffset = 0;
- unsigned long int rememberPos = 0;
-
- // We get the compression type indicator value.
- fseek(handle, 6, SEEK_CUR);
- fread(&eCompression, 1, 2, handle);
- // Get the size
- fseek(handle, 8, SEEK_CUR);
- fread(&eCompSize, 1, 4, handle);
- fread(&eSize, 1, 4, handle);
- // We get the variable length fields.
- fread(&eNameLen, 1, 2, handle);
- fread(&eXFieldLen, 1, 2, handle);
- fread(&eCommentLen, 1, 2, handle);
- fseek(handle, 8, SEEK_CUR);
- fread(&eLocalHeaderOffset, 1, 4, handle); // Get the offset.
-
- eName = malloc(sizeof(char)*(eNameLen + 1));
- fgets(eName, eNameLen + 1, handle);
-
- // Don't load lump if folder.
-// if (*(eName + eNameLen - 1) == '/')
-// continue;
-
- // We must calculate the position for the actual data.
- // Why not eLocalHeaderOffset + 30 + eNameLen + eXFieldLen? That's because the extra field and name lengths MAY be different in the local headers.
- rememberPos = ftell(handle);
- fseek(handle, eLocalHeaderOffset + 26, SEEK_SET);
- fread(&lNameLen, 1, 2, handle);
- fread(&lXFieldLen, 1, 2, handle);
- lump_p->position = ftell(handle) + lNameLen + lXFieldLen;
-
- fseek(handle, rememberPos, SEEK_SET); // Let's go back to the central dir.
- lump_p->disksize = eCompSize;
- lump_p->size = eSize;
-
- // We will trim the file's full name so that only the filename is left.
- namePos = eNameLen - 1;
- while(namePos--)
- if(eName[namePos] == '/')
- break;
- namePos++;
- // We will remove the file extension too.
- nameEnd = 0;
- while(nameEnd++ < 8)
- if(eName[namePos + nameEnd] == '.')
- break;
- memset(lump_p->name, '\0', 9);
- strncpy(lump_p->name, eName + namePos, nameEnd);
-
- lump_p->name2 = Z_Malloc((eNameLen+1)*sizeof(char), PU_STATIC, NULL);
- strncpy(lump_p->name2, eName, eNameLen);
- lump_p->name2[eNameLen] = '\0';
-
- // We set the compression type from what we're supporting so far.
- switch(eCompression)
- {
- case 0:
- lump_p->compression = CM_NOCOMPRESSION;
- break;
- case 8:
- lump_p->compression = CM_DEFLATE;
- break;
- case 14:
- lump_p->compression = CM_LZF;
- break;
- default:
- CONS_Alert(CONS_WARNING, "Lump has an unsupported compression type!\n");
- lump_p->compression = CM_UNSUPPORTED;
- break;
- }
- CONS_Debug(DBG_SETUP, "File %s, data begins at: %ld\n", eName, lump_p->position);
- fseek(handle, eXFieldLen + eCommentLen, SEEK_CUR); // We skip to where we expect the next central directory entry or end marker to be.
- free(eName);
- }
- // We found the central directory end signature?
- else if (!strncmp(curHeader, endPat, 4))
- {
- CONS_Debug(DBG_SETUP, "Central directory end signature found at: %ld\n", ftell(handle));
-
- /*// We will create a "virtual" marker lump at the very end of lumpinfo for convenience.
- // This marker will be used by the different lump-seeking (eg. textures, sprites, etc.) in PK3-specific cases in an auxiliary way.
- lumpinfo = (lumpinfo_t*) Z_Realloc(lumpinfo, (numlumps + 1)*sizeof(*lumpinfo), PU_STATIC, NULL);
- strcpy(lumpinfo[numlumps].name, "PK3_ENDM\0");
- lumpinfo[numlumps].name2 = Z_Malloc(14 * sizeof(char), PU_STATIC, NULL);
- strcpy(lumpinfo[numlumps].name2, "PK3_ENDMARKER\0");
- lumpinfo[numlumps].position = 0;
- lumpinfo[numlumps].size = 0;
- lumpinfo[numlumps].disksize = 0;
- lumpinfo[numlumps].compression = CM_NOCOMPRESSION;
- numlumps++;*/
- break;
- }
- // ... None of them? We're only expecting either a central directory signature entry or the central directory end signature.
- // The file may be broken or incomplete...
- else
- {
- CONS_Alert(CONS_WARNING, "Expected central directory header signature, got something else!");
- return INT16_MAX;
- }
- }
- // If we've reached this far, then it means our dynamically stored lumpinfo has to be ready.
- // Now we finally build our... incorrectly called wadfile.
- // TODO: Maybe we should give them more generalized names, like resourcefile or resfile or something.
- // Mostly for clarity and better understanding when reading the code.
+ case RET_PK3:
+ lumpinfo = ResGetLumpsZip(handle, &numlumps);
+ break;
+ case RET_WAD:
+ lumpinfo = ResGetLumpsWad(handle, &numlumps, filename);
+ break;
+ default:
+ CONS_Alert(CONS_ERROR, "Unsupported file format\n");
}
- // assume wad file
- else
+
+ if (lumpinfo == NULL)
{
- wadinfo_t header;
- lumpinfo_t *lump_p;
- filelump_t *fileinfo;
- void *fileinfov;
-
- type = RET_WAD;
-
- // read the header
- if (fread(&header, 1, sizeof header, handle) < sizeof header)
- {
- CONS_Alert(CONS_ERROR, M_GetText("Can't read wad header from %s because %s\n"), filename, strerror(ferror(handle)));
- if (handle)
- fclose(handle);
- return INT16_MAX;
- }
-
- if (memcmp(header.identification, "ZWAD", 4) == 0)
- compressed = 1;
- else if (memcmp(header.identification, "IWAD", 4) != 0
- && memcmp(header.identification, "PWAD", 4) != 0
- && memcmp(header.identification, "SDLL", 4) != 0)
- {
- CONS_Alert(CONS_ERROR, M_GetText("%s does not have a valid WAD header\n"), filename);
- if (handle)
- fclose(handle);
- return INT16_MAX;
- }
-
- header.numlumps = LONG(header.numlumps);
- header.infotableofs = LONG(header.infotableofs);
-
- // read wad file directory
- i = header.numlumps * sizeof (*fileinfo);
- fileinfov = fileinfo = malloc(i);
- if (fseek(handle, header.infotableofs, SEEK_SET) == -1
- || fread(fileinfo, 1, i, handle) < i)
- {
- CONS_Alert(CONS_ERROR, M_GetText("Wadfile directory in %s is corrupted (%s)\n"), filename, strerror(ferror(handle)));
- free(fileinfov);
- if (handle)
- fclose(handle);
- return INT16_MAX;
- }
-
- numlumps = header.numlumps;
-
- // fill in lumpinfo for this wad
- lump_p = lumpinfo = Z_Malloc(numlumps * sizeof (*lumpinfo), PU_STATIC, NULL);
- for (i = 0; i < numlumps; i++, lump_p++, fileinfo++)
- {
- lump_p->position = LONG(fileinfo->filepos);
- lump_p->size = lump_p->disksize = LONG(fileinfo->size);
- if (compressed) // wad is compressed, lump might be
- {
- UINT32 realsize = 0;
-
- if (fseek(handle, lump_p->position, SEEK_SET)
- == -1 || fread(&realsize, 1, sizeof realsize,
- handle) < sizeof realsize)
- {
- I_Error("corrupt compressed file: %s; maybe %s",
- filename, strerror(ferror(handle)));
- }
- realsize = LONG(realsize);
- if (realsize != 0)
- {
- lump_p->size = realsize;
- lump_p->compression = CM_LZF;
- }
- else
- {
- lump_p->size -= 4;
- lump_p->compression = CM_NOCOMPRESSION;
- }
-
- lump_p->position += 4;
- lump_p->disksize -= 4;
- }
- else
- {
- lump_p->compression = CM_NOCOMPRESSION;
- }
- memset(lump_p->name, 0x00, 9);
- strncpy(lump_p->name, fileinfo->name, 8);
- // Allocate the lump's full name.
- lump_p->name2 = Z_Malloc(9 * sizeof(char), PU_STATIC, NULL);
- strncpy(lump_p->name2, fileinfo->name, 8);
- lump_p->name2[8] = '\0';
- }
- free(fileinfov);
+ fclose(handle);
+ return INT16_MAX;
}
//
@@ -737,6 +773,7 @@ UINT16 W_InitFile(const char *filename)
wadfile->important = important;
fseek(handle, 0, SEEK_END);
wadfile->filesize = (unsigned)ftell(handle);
+ wadfile->type = type;
// already generated, just copy it over
M_Memcpy(&wadfile->md5sum, &md5sum, 16);
@@ -761,25 +798,26 @@ UINT16 W_InitFile(const char *filename)
// TODO: HACK ALERT - Load Lua & SOC stuff right here. I feel like this should be out of this place, but... Let's stick with this for now.
switch (wadfile->type)
{
- case RET_WAD:
- W_LoadDehackedLumps(numwadfiles - 1);
- break;
- case RET_PK3:
- W_LoadDehackedLumpsPK3(numwadfiles - 1);
- break;
- case RET_SOC:
- CONS_Printf(M_GetText("Loading SOC from %s\n"), wadfile->filename);
- DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0);
- break;
- case RET_LUA:
- LUA_LoadLump(numwadfiles - 1, 0);
- break;
- default:
- break;
+ case RET_WAD:
+ W_LoadDehackedLumps(numwadfiles - 1, mainfile);
+ break;
+ case RET_PK3:
+ W_LoadDehackedLumpsPK3(numwadfiles - 1, mainfile);
+ break;
+ case RET_SOC:
+ CONS_Printf(M_GetText("Loading SOC from %s\n"), wadfile->filename);
+ DEH_LoadDehackedLumpPwad(numwadfiles - 1, 0, mainfile);
+ break;
+#ifdef HAVE_BLUA
+ case RET_LUA:
+ LUA_LoadLump(numwadfiles - 1, 0);
+ break;
+#endif
+ default:
+ break;
}
W_InvalidateLumpnumCache();
-
return wadfile->numlumps;
}
@@ -794,7 +832,7 @@ UINT16 W_InitFile(const char *filename)
* \return 1 if all files were loaded, 0 if at least one was missing or
* invalid.
*/
-INT32 W_InitMultipleFiles(char **filenames)
+INT32 W_InitMultipleFiles(char **filenames, UINT16 mainfiles)
{
INT32 rc = 1;
@@ -805,7 +843,7 @@ INT32 W_InitMultipleFiles(char **filenames)
for (; *filenames; filenames++)
{
//CONS_Debug(DBG_SETUP, "Loading %s\n", *filenames);
- rc &= (W_InitFile(*filenames) != INT16_MAX) ? 1 : 0;
+ rc &= (W_InitFile(*filenames, numwadfiles < mainfiles) != INT16_MAX) ? 1 : 0;
}
if (!numwadfiles)
@@ -1040,7 +1078,6 @@ lumpnum_t W_CheckNumForNameInBlock(const char *name, const char *blockstart, con
if (check < beid)
return (i<<16)+check; // found it, in our constraints
}
-
}
return LUMPERROR;
}
@@ -1095,6 +1132,7 @@ boolean W_IsLumpWad(lumpnum_t lumpnum)
return false; // WADs should never be inside non-PK3s as far as SRB2 is concerned
}
+#ifdef HAVE_ZLIB
/* report a zlib or i/o error */
void zerr(int ret)
{
@@ -1119,6 +1157,24 @@ void zerr(int ret)
CONS_Printf("zlib version mismatch!\n");
}
}
+#endif
+
+#define NO_PNG_LUMPS
+
+#ifdef NO_PNG_LUMPS
+static void ErrorIfPNG(UINT8 *d, size_t s, char *f, char *l)
+{
+ if (s < 67) // http://garethrees.org/2007/11/14/pngcrush/
+ return;
+ // Check for PNG file signature using memcmp
+ // As it may be faster on CPUs with slow unaligned memory access
+ // Ref: http://www.libpng.org/pub/png/spec/1.2/PNG-Rationale.html#R.PNG-file-signature
+ if (memcmp(&d[0], "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8) == 0)
+ {
+ I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .PNG - please convert to either Doom or Flat (raw) image format.", l, f);
+ }
+}
+#endif
/** Reads bytes from the head of a lump.
* Note: If the lump is compressed, the whole thing has to be read anyway.
@@ -1159,7 +1215,15 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
switch(wadfiles[wad]->lumpinfo[lump].compression)
{
case CM_NOCOMPRESSION: // If it's uncompressed, we directly write the data into our destination, and return the bytes read.
+#ifdef NO_PNG_LUMPS
+ {
+ size_t bytesread = fread(dest, 1, size, handle);
+ ErrorIfPNG(dest, bytesread, wadfiles[wad]->filename, l->name2);
+ return bytesread;
+ }
+#else
return fread(dest, 1, size, handle);
+#endif
case CM_LZF: // Is it LZF compressed? Used by ZWADs.
{
#ifdef ZWAD
@@ -1188,21 +1252,27 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
{
I_Error("wad %d, lump %d: decompressed to wrong number of bytes (expected %s, got %s)", wad, lump, sizeu1(l->size), sizeu2(retval));
}
+
if (!decData) // Did we get no data at all?
return 0;
M_Memcpy(dest, decData + offset, size);
Z_Free(rawData);
Z_Free(decData);
+#ifdef NO_PNG_LUMPS
+ ErrorIfPNG(dest, size, wadfiles[wad]->filename, l->name2);
+#endif
return size;
#else
//I_Error("ZWAD files not supported on this platform.");
return 0;
#endif
+
}
+#ifdef HAVE_ZLIB
case CM_DEFLATE: // Is it compressed via DEFLATE? Very common in ZIPs/PK3s, also what most doom-related editors support.
{
- z_const Bytef *rawData; // The lump's raw data.
- Bytef *decData; // Lump's decompressed real data.
+ UINT8 *rawData; // The lump's raw data.
+ UINT8 *decData; // Lump's decompressed real data.
int zErr; // Helper var.
z_stream strm;
@@ -1242,7 +1312,6 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
}
else
{
- CONS_Printf("whopet\n");
size = 0;
zerr(zErr);
}
@@ -1250,8 +1319,12 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
Z_Free(rawData);
Z_Free(decData);
+#ifdef NO_PNG_LUMPS
+ ErrorIfPNG(dest, size, wadfiles[wad]->filename, l->name2);
+#endif
return size;
}
+#endif
default:
I_Error("wad %d, lump %d: unsupported compression type!", wad, lump);
}
diff --git a/src/w_wad.h b/src/w_wad.h
index 16d97dd4c..8ffcc1d03 100644
--- a/src/w_wad.h
+++ b/src/w_wad.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -22,6 +22,22 @@
#pragma interface
#endif
+// a raw entry of the wad directory
+// NOTE: This sits here and not in w_wad.c because p_setup.c makes use of it to load map WADs inside PK3s.
+#if defined(_MSC_VER)
+#pragma pack(1)
+#endif
+typedef struct
+{
+ UINT32 filepos; // file offset of the resource
+ UINT32 size; // size of the resource
+ char name[8]; // name of the resource
+} ATTRPACK filelump_t;
+#if defined(_MSC_VER)
+#pragma pack()
+#endif
+
+
// ==============================================================
// WAD FILE STRUCTURE DEFINITIONS
// ==============================================================
@@ -34,19 +50,13 @@ typedef struct
UINT32 infotableofs; // the 'directory' of resources
} wadinfo_t;
-// a raw entry of the wad directory
-typedef struct
-{
- UINT32 filepos; // file offset of the resource
- UINT32 size; // size of the resource
- char name[8]; // name of the resource
-} ATTRPACK filelump_t;
-
// Available compression methods for lumps.
typedef enum
{
CM_NOCOMPRESSION,
+#ifdef HAVE_ZLIB
CM_DEFLATE,
+#endif
CM_LZF,
CM_UNSUPPORTED
} compmethod;
@@ -59,7 +69,6 @@ typedef struct
char name[9]; // filelump_t name[]
char *name2; // Used by PK3s. Dynamically allocated name.
size_t size; // real (uncompressed) size
- INT32 compressed; // i
compmethod compression; // lump compression method
} lumpinfo_t;
@@ -83,7 +92,8 @@ typedef enum restype
RET_WAD,
RET_SOC,
RET_LUA,
- RET_PK3
+ RET_PK3,
+ RET_UNKNOWN,
} restype_t;
typedef struct wadfile_s
@@ -99,6 +109,7 @@ typedef struct wadfile_s
FILE *handle;
UINT32 filesize; // for network
UINT8 md5sum[16];
+
boolean important; // also network - !W_VerifyNMUSlumps
} wadfile_t;
@@ -115,11 +126,11 @@ void W_Shutdown(void);
// Opens a WAD file. Returns the FILE * handle for the file, or NULL if not found or could not be opened
FILE *W_OpenWadFile(const char **filename, boolean useerrors);
// Load and add a wadfile to the active wad files, returns numbers of lumps, INT16_MAX on error
-UINT16 W_InitFile(const char *filename);
+UINT16 W_InitFile(const char *filename, boolean mainfile);
// W_InitMultipleFiles returns 1 if all is okay, 0 otherwise,
// so that it stops with a message if a file was not found, but not if all is okay.
-INT32 W_InitMultipleFiles(char **filenames);
+INT32 W_InitMultipleFiles(char **filenames, UINT16 mainfiles);
const char *W_CheckNameForNumPwad(UINT16 wad, UINT16 lump);
const char *W_CheckNameForNum(lumpnum_t lumpnum);
@@ -141,7 +152,9 @@ size_t W_LumpLength(lumpnum_t lumpnum);
boolean W_IsLumpWad(lumpnum_t lumpnum); // for loading maps from WADs in PK3s
+#ifdef HAVE_ZLIB
void zerr(int ret); // zlib error checking
+#endif
size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, size_t offset);
size_t W_ReadLumpHeader(lumpnum_t lump, void *dest, size_t size, size_t offest); // read all or a part of a lump
diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg
index a73858bd4..def2fe682 100644
--- a/src/win32/Makefile.cfg
+++ b/src/win32/Makefile.cfg
@@ -7,8 +7,6 @@
#
ifdef MINGW64
- NOASM=1
- NONX86=1
HAVE_LIBGME=1
LIBGME_CFLAGS=-I../libs/gme/include
LIBGME_LDFLAGS=-L../libs/gme/win64 -lgme
diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj
index ed6b46b8f..774ce5cbe 100644
--- a/src/win32/Srb2win-vc10.vcxproj
+++ b/src/win32/Srb2win-vc10.vcxproj
@@ -1,10 +1,26 @@

+
+ Debug
+ ARM
+
+
+ Debug
+ ARM64
+
Debug
Win32
+
+ Release
+ ARM
+
+
+ Release
+ ARM64
+
Release
Win32
@@ -23,26 +39,49 @@
{0F554F1D-ED49-4D65-A9A7-F63C57F277BE}
Win32Proj
Srb2win
- 8.1
+ 10.0.17763.0
-
- v140
-
+ v140
true
+
+ true
+ true
+ v141
+
+ v140
false
true
+
+ false
+ true
+ true
+ v141
+
+ v140
true
+
+ true
+ true
+ v141
+
+ v140
false
true
+
+ false
+ true
+ true
+ v141
+
@@ -58,25 +97,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<_ProjectFileVersion>10.0.30319.1
false
+
+
+ true
+ gdi32.lib;%(AdditionalDependencies)
+
+
+ ProgramDatabase
+ false
+
+
+
+
+ true
+ gdi32.lib;%(AdditionalDependencies)
+
+
+
+
+ ProgramDatabase
+ false
+
+
+ gdi32.lib;%(AdditionalDependencies)
+
+
+
+
+ gdi32.lib;%(AdditionalDependencies)
+
+
+
+
+ gdi32.lib;%(AdditionalDependencies)
+
+
+
+
+ gdi32.lib;%(AdditionalDependencies)
+
+
+
+
+ gdi32.lib;%(AdditionalDependencies)
+
+
+
+
+ gdi32.lib;%(AdditionalDependencies)
+
+
+
@@ -225,6 +331,7 @@
+
diff --git a/src/win32/Srb2win-vc9.vcproj b/src/win32/Srb2win-vc9.vcproj
index 24235bc7b..a64b8638c 100644
--- a/src/win32/Srb2win-vc9.vcproj
+++ b/src/win32/Srb2win-vc9.vcproj
@@ -2851,6 +2851,50 @@
RelativePath="..\m_misc.h"
>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc
index 426a5e013..b60ba750d 100644
--- a/src/win32/Srb2win.rc
+++ b/src/win32/Srb2win.rc
@@ -36,18 +36,18 @@ IDI_ICON1 ICON DISCARDABLE "Srb2win.ico"
// TEXTINCLUDE
//
-1 TEXTINCLUDE DISCARDABLE
+1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
-2 TEXTINCLUDE DISCARDABLE
+2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
-3 TEXTINCLUDE DISCARDABLE
+3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
@@ -84,7 +84,7 @@ BEGIN
VALUE "FileDescription", "Sonic Robo Blast 2\0"
VALUE "FileVersion", "1, 09\0"
VALUE "InternalName", "srb2\0"
- VALUE "LegalCopyright", "Copyright © 1998-2005 Sonic Team Junior\0"
+ VALUE "LegalCopyright", "Copyright � 1998-2018 by Sonic Team Junior\0"
VALUE "LegalTrademarks", "Sonic the Hedgehog and related characters are trademarks of Sega.\0"
VALUE "OriginalFilename", "srb2win.exe\0"
VALUE "PrivateBuild", "\0"
diff --git a/src/win32/win_snd.c b/src/win32/win_snd.c
index 454c53e37..f3e3bbed4 100644
--- a/src/win32/win_snd.c
+++ b/src/win32/win_snd.c
@@ -815,6 +815,60 @@ void I_SetMusicVolume(UINT8 volume)
FMR_MUSIC(FMOD_Channel_SetVolume(music_channel, music_volume / 31.0));
}
+UINT32 I_GetSongLength(void)
+{
+ UINT32 length;
+ if (I_SongType() == MU_MID)
+ return 0;
+ FMR_MUSIC(FMOD_Sound_GetLength(music_stream, &length, FMOD_TIMEUNIT_MS));
+ return length;
+}
+
+boolean I_SetSongLoopPoint(UINT32 looppoint)
+{
+ (void)looppoint;
+ return false;
+}
+
+UINT32 I_GetSongLoopPoint(void)
+{
+ return 0;
+}
+
+boolean I_SetSongPosition(UINT32 position)
+{
+ FMOD_RESULT e;
+ if(I_SongType() == MU_MID)
+ // Dummy out; this works for some MIDI, but not others.
+ // SDL does not support this for any MIDI.
+ return false;
+ e = FMOD_Channel_SetPosition(music_channel, position, FMOD_TIMEUNIT_MS);
+ if (e == FMOD_OK)
+ return true;
+ else if (e == FMOD_ERR_UNSUPPORTED // Only music modules, numbnuts!
+ || e == FMOD_ERR_INVALID_POSITION) // Out-of-bounds!
+ return false;
+ else // Congrats, you horribly broke it somehow
+ {
+ FMR_MUSIC(e);
+ return false;
+ }
+}
+
+UINT32 I_GetSongPosition(void)
+{
+ FMOD_RESULT e;
+ unsigned int fmposition = 0;
+ if(I_SongType() == MU_MID)
+ // Dummy out because unsupported, even though FMOD does this correctly.
+ return 0;
+ e = FMOD_Channel_GetPosition(music_channel, &fmposition, FMOD_TIMEUNIT_MS);
+ if (e == FMOD_OK)
+ return (UINT32)fmposition;
+ else
+ return 0;
+}
+
boolean I_SetSongTrack(INT32 track)
{
if (track != current_track) // If the track's already playing, then why bother?
@@ -859,3 +913,46 @@ boolean I_SetSongTrack(INT32 track)
}
return false;
}
+
+/// ------------------------
+/// MUSIC FADING
+/// ------------------------
+
+void I_SetInternalMusicVolume(UINT8 volume)
+{
+ (void)volume;
+}
+
+void I_StopFadingSong(void)
+{
+}
+
+boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms, void (*callback)(void))
+{
+ (void)target_volume;
+ (void)source_volume;
+ (void)ms;
+ (void)callback;
+ return false;
+}
+
+boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void))
+{
+ (void)target_volume;
+ (void)ms;
+ (void)callback;
+ return false;
+}
+
+boolean I_FadeOutStopSong(UINT32 ms)
+{
+ (void)ms;
+ return false;
+}
+
+boolean I_FadeInPlaySong(UINT32 ms, boolean looping)
+{
+ (void)ms;
+ (void)looping;
+ return false;
+}
diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c
index 316da61d4..77a21f7f3 100644
--- a/src/win32/win_sys.c
+++ b/src/win32/win_sys.c
@@ -771,6 +771,8 @@ void I_Quit(void)
ShowEndTxt(co);
}
fflush(stderr);
+ if (myargmalloc)
+ free(myargv); // Deallocate allocated memory
W_Shutdown();
exit(0);
}
diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c
index e9b5d7e9f..e2f32fa61 100644
--- a/src/win32/win_vid.c
+++ b/src/win32/win_vid.c
@@ -1000,7 +1000,7 @@ static INT32 WINAPI VID_SetDirectDrawMode(viddef_t *lvid, vmode_t *currentmode)
// but rather render to memory bitmap buffer
lvid->direct = NULL;
- if (!cv_stretch.value && (float)vid.width/vid.height != ((float)BASEVIDWIDTH/BASEVIDHEIGHT))
+ if (!cv_stretch.value && fabsf((float)vid.width/vid.height - ((float)BASEVIDWIDTH/BASEVIDHEIGHT)) > 1.0E-36f)
vid.height = (int)(vid.width * ((float)BASEVIDHEIGHT/BASEVIDWIDTH));// Adjust the height to match
return 1;
diff --git a/src/y_inter.c b/src/y_inter.c
index 1a1675fdd..992193964 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -1,6 +1,6 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
-// Copyright (C) 2004-2016 by Sonic Team Junior.
+// Copyright (C) 2004-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
@@ -833,7 +833,17 @@ void Y_Ticker(void)
if (!intertic) // first time only
{
- S_ChangeMusicInternal("_clear", false); // don't loop it
+ if (mapheaderinfo[gamemap-1]->musinterfadeout
+#ifdef _WIN32
+ // can't fade midi due to win32 volume hack
+ && S_MusicType() != MU_MID
+#endif
+ )
+ S_FadeOutStopMusic(mapheaderinfo[gamemap-1]->musinterfadeout);
+ else if (mapheaderinfo[gamemap-1]->musintername[0] && S_MusicExists(mapheaderinfo[gamemap-1]->musintername, !midi_disabled, !digital_disabled))
+ S_ChangeMusicInternal(mapheaderinfo[gamemap-1]->musintername, false); // don't loop it
+ else
+ S_ChangeMusicInternal("_clear", false); // don't loop it
tallydonetic = -1;
}
@@ -898,7 +908,17 @@ void Y_Ticker(void)
if (!intertic) // first time only
{
- S_ChangeMusicInternal("_clear", false); // don't loop it
+ if (mapheaderinfo[gamemap-1]->musinterfadeout
+#ifdef _WIN32
+ // can't fade midi due to win32 volume hack
+ && S_MusicType() != MU_MID
+#endif
+ )
+ S_FadeOutStopMusic(mapheaderinfo[gamemap-1]->musinterfadeout);
+ else if (mapheaderinfo[gamemap-1]->musintername[0] && S_MusicExists(mapheaderinfo[gamemap-1]->musintername, !midi_disabled, !digital_disabled))
+ S_ChangeMusicInternal(mapheaderinfo[gamemap-1]->musintername, false); // don't loop it
+ else
+ S_ChangeMusicInternal("_clear", false); // don't loop it
tallydonetic = -1;
}
diff --git a/src/y_inter.h b/src/y_inter.h
index 26f7dc390..4c6ad2bdf 100644
--- a/src/y_inter.h
+++ b/src/y_inter.h
@@ -1,6 +1,6 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
-// Copyright (C) 2004-2016 by Sonic Team Junior.
+// Copyright (C) 2004-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/z_zone.c b/src/z_zone.c
index b5799b583..001c69bb3 100644
--- a/src/z_zone.c
+++ b/src/z_zone.c
@@ -1,7 +1,7 @@
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 2006 by Graue.
-// Copyright (C) 2006-2016 by Sonic Team Junior.
+// Copyright (C) 2006-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/src/z_zone.h b/src/z_zone.h
index 205c9ed79..8d32e74f1 100644
--- a/src/z_zone.h
+++ b/src/z_zone.h
@@ -2,7 +2,7 @@
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
-// Copyright (C) 1999-2016 by Sonic Team Junior.
+// Copyright (C) 1999-2018 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
diff --git a/tools/masterserver/structure.sql b/tools/masterserver/structure.sql
new file mode 100644
index 000000000..3cc2cb15b
--- /dev/null
+++ b/tools/masterserver/structure.sql
@@ -0,0 +1,117 @@
+SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
+SET AUTOCOMMIT = 0;
+START TRANSACTION;
+SET time_zone = "+00:00";
+
+
+/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
+/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
+/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
+/*!40101 SET NAMES utf8mb4 */;
+
+--
+-- Database: `srb2ms`
+--
+
+CREATE DATABASE IF NOT EXISTS `srb2ms` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
+USE `srb2ms`;
+
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `ms_bans`
+--
+
+CREATE TABLE `ms_bans` (
+ `bid` int(11) DEFAULT NULL,
+ `ipstart` int(11) DEFAULT NULL,
+ `ipend` int(11) DEFAULT NULL,
+ `full_endtime` int(11) DEFAULT NULL,
+ `permanent` tinyint(1) DEFAULT NULL,
+ `hostonly` tinyint(1) DEFAULT NULL,
+ `reason` text COLLATE utf8mb4_unicode_ci
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `ms_rooms`
+--
+
+CREATE TABLE `ms_rooms` (
+ `room_id` int(11) NOT NULL,
+ `title` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `motd` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `visible` tinyint(1) NOT NULL,
+ `order` int(11) NOT NULL,
+ `private` tinyint(1) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Dumping data for table `ms_rooms`
+--
+
+INSERT INTO `ms_rooms` (`room_id`, `title`, `motd`, `visible`, `order`, `private`) VALUES
+(10, 'Example', 'Example Room', 1, 0, 0),
+(0, 'All', 'View all of the servers currently being hosted.', 1, 1, 1);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `ms_servers`
+--
+
+CREATE TABLE `ms_servers` (
+ `sid` int(11) NOT NULL,
+ `name` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `ip` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `port` int(11) NOT NULL,
+ `version` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `timestamp` int(11) NOT NULL,
+ `room` int(11) NOT NULL,
+ `key` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `room_override` int(11) NOT NULL,
+ `upnow` tinyint(1) NOT NULL,
+ `permanent` tinyint(1) NOT NULL,
+ `delisted` tinyint(1) NOT NULL,
+ `sticky` int(11) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `ms_versions`
+--
+
+CREATE TABLE `ms_versions` (
+ `mod_id` int(11) NOT NULL,
+ `mod_version` int(11) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+--
+-- Dumping data for table `ms_versions`
+--
+
+INSERT INTO `ms_versions` (`mod_id`, `mod_version`) VALUES
+(12, 25);
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `user`
+--
+
+CREATE TABLE `user` (
+ `userid` int(11) NOT NULL,
+ `username` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `live_authkey` blob NOT NULL,
+ `live_publickey` blob NOT NULL,
+ `salt` blob NOT NULL,
+ `password` blob NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+COMMIT;
+
+/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
+/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
+/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;